[PATCH] tuna: Add error handling to cpuset profile save function

John Kacur <[email protected]> Wed, 29 Jul 2026 10:20:36 -0400
Newsgroups org.kernel.vger.linux-rt-users
Message-ID <[email protected]>
The save_cpusets() function would crash with an unhandled exception
if the parent directory of the target file didn't exist. This is
problematic when users try to save to /etc/tuna/config.yaml and
the /etc/tuna directory hasn't been created yet.

This commit adds proper error handling:
- Automatically creates parent directories if they don't exist
  using os.makedirs() with exist_ok=True
- Catches PermissionError and OSError exceptions
- Returns 0 and prints clear error messages instead of crashing

Also moves the os import to the top of the file for cleaner code
and removes a duplicate import from within apply_cpusets().

This matches the error handling already present in apply_cpusets()
and provides a better user experience.

Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: John Kacur <[email protected]>
---
 tuna/profile.py | 35 ++++++++++++++++++++++++-----------
 1 file changed, 24 insertions(+), 11 deletions(-)

diff --git a/tuna/profile.py b/tuna/profile.py
index 8372d081546f..fc1a64188c7e 100644
--- a/tuna/profile.py
+++ b/tuna/profile.py
@@ -9,6 +9,7 @@ including cpuset definitions.
 """
 
 import datetime
+import os
 import sys
 from ruamel.yaml import YAML
 
@@ -64,16 +65,29 @@ def save_cpusets(filename, get_cpusets_info_func):
     yaml.default_flow_style = False
     yaml.width = 4096  # Avoid line wrapping
 
-    with open(filename, 'w') as f:
-        # Write header comments
-        f.write("# Tuna RT System Profile - Cpuset Configuration\n")
-        f.write(f"# Generated: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
-        f.write("#\n")
-        f.write("# This file can be applied with: tuna cpuset apply <filename>\n")
-        f.write("#\n\n")
-
-        # Write YAML structure
-        yaml.dump(profile, f)
+    try:
+        # Create parent directories if they don't exist
+        parent_dir = os.path.dirname(filename)
+        if parent_dir:  # Only if filename has a directory component
+            os.makedirs(parent_dir, exist_ok=True)
+
+        with open(filename, 'w') as f:
+            # Write header comments
+            f.write("# Tuna RT System Profile - Cpuset Configuration\n")
+            f.write(f"# Generated: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
+            f.write("#\n")
+            f.write("# This file can be applied with: tuna cpuset apply <filename>\n")
+            f.write("#\n\n")
+
+            # Write YAML structure
+            yaml.dump(profile, f)
+
+    except PermissionError:
+        print(f"Error: Permission denied writing to '{filename}'", file=sys.stderr)
+        return 0
+    except OSError as e:
+        print(f"Error writing profile to '{filename}': {e}", file=sys.stderr)
+        return 0
 
     return len(cpuset_entries)
 
@@ -144,7 +158,6 @@ def apply_cpusets(filename, cpuset_module, verbose=False):
 
         try:
             # Check if cpuset already exists
-            import os
             cpuset_path = os.path.join('/sys/fs/cgroup', name)
             if os.path.exists(cpuset_path):
                 print(f"Warning: Cpuset '{name}' already exists, skipping", file=sys.stderr)
-- 
2.55.0