Explorar o código

- Added 'umask' option to process config. If you set this option,
supervisor will set the umask of the child program. (Thanks to
Ian Bicking for the suggestion).

Chris McDonough %!s(int64=18) %!d(string=hai) anos
pai
achega
1a58b5397d

+ 4 - 0
CHANGES.txt

@@ -147,6 +147,10 @@ Next Release
     option, supervisor will chdir to this directory before executing
     the child program (and thus it will be the child's cwd).
 
+  - Added 'umask' option to process config.  If you set this option,
+    supervisor will set the umask of the child program.  (Thanks to
+    Ian Bicking for the suggestion).
+
 3.0a2
 
   - Fixed the README.txt example for defining the supervisor RPC

+ 4 - 2
README.txt

@@ -552,8 +552,10 @@ Configuration File '[program:x]' Section Settings
   overridden here.  See "Subprocess Environment" below.
 
   'directory' -- a file path representing a directory to which
-  supervisord should chdir before exec'ing the child.  Default: no
-  cwd.
+  supervisord should chdir before exec'ing the child. Default: no cwd.
+
+  'umask' -- an octal number (e.g. 002, 022) representing the umask of
+  the process.  Default: no special umask (inherit supervisor's).
 
   Note that a '[program:x]' section actually represents a "homogeneous
   process group" to supervisor (new in 3.0).  The members of the group

+ 4 - 1
src/supervisor/datatypes.py

@@ -164,7 +164,10 @@ def dot_separated_user_group(arg):
         raise ValueError, 'Invalid user.group definition %s' % arg
 
 def octal_type(arg):
-    return int(arg, 8)
+    try:
+        return int(arg, 8)
+    except TypeError:
+        raise ValueError('%s is not convertable to an octal type' % arg)
 
 def name_to_uid(name):
     if name is None:

+ 15 - 6
src/supervisor/options.py

@@ -655,6 +655,10 @@ class ServerOptions(Options):
         stderr_cmaxbytes = byte_size(get(section,'stderr_capture_maxbytes','0'))
         directory = get(section, 'directory', None)
 
+        umask = get(section, 'umask', None)
+        if umask is not None:
+            umask = octal_type(umask)
+
         command = get(section, 'command', None)
         if command is None:
             raise ValueError, (
@@ -709,6 +713,8 @@ class ServerOptions(Options):
                 self,
                 name=expand(process_name, expansions, 'process_name'),
                 command=expand(command, expansions, 'command'),
+                directory=directory,
+                umask=umask,
                 priority=priority,
                 autostart=autostart,
                 autorestart=autorestart,
@@ -727,8 +733,7 @@ class ServerOptions(Options):
                 stopwaitsecs=stopwaitsecs,
                 exitcodes=exitcodes,
                 redirect_stderr=redirect_stderr,
-                environment=environment,
-                directory=directory)
+                environment=environment)
 
             programs.append(pconfig)
 
@@ -1119,6 +1124,9 @@ class ServerOptions(Options):
     def _exit(self, code):
         os._exit(code)
 
+    def setumask(self, mask):
+        os.umask(mask)
+
     def get_path(self):
         """Return a list corresponding to $PATH, or a default."""
         path = ["/bin", "/usr/bin", "/usr/local/bin"]
@@ -1317,17 +1325,19 @@ class Config:
                                                  self.name)
     
 class ProcessConfig(Config):
-    def __init__(self, options, name, command, priority, autostart,
-                 autorestart, startsecs, startretries, uid,
+    def __init__(self, options, name, command, directory, umask,
+                 priority, autostart, autorestart, startsecs, startretries, uid,
                  stdout_logfile, stdout_capture_maxbytes,
                  stdout_logfile_backups, stdout_logfile_maxbytes,
                  stderr_logfile, stderr_capture_maxbytes,
                  stderr_logfile_backups, stderr_logfile_maxbytes,
                  stopsignal, stopwaitsecs, exitcodes, redirect_stderr,
-                 environment=None, directory=None):
+                 environment=None):
         self.options = options
         self.name = name
         self.command = command
+        self.directory = directory
+        self.umask = umask
         self.priority = priority
         self.autostart = autostart
         self.autorestart = autorestart
@@ -1347,7 +1357,6 @@ class ProcessConfig(Config):
         self.exitcodes = exitcodes
         self.redirect_stderr = redirect_stderr
         self.environment = environment
-        self.directory = directory
 
     def create_autochildlogs(self):
         # temporary logfiles which are erased at start time

+ 2 - 0
src/supervisor/process.py

@@ -294,6 +294,8 @@ class Subprocess:
                 options.write(2, msg)
             else:
                 try:
+                    if self.config.umask:
+                        options.setumask(self.config.umask)
                     options.execve(filename, argv, env)
                 except OSError, why:
                     code = errno.errorcode.get(why[0], why[0])

+ 8 - 2
src/supervisor/tests/base.py

@@ -67,6 +67,7 @@ class DummyOptions:
         self.serverurl = 'http://localhost:9001'
         self.changed_directory = False
         self.chdir_error = None
+        self.umaskset = None
 
     def getLogger(self, *args, **kw):
         logger = DummyLogger()
@@ -239,6 +240,9 @@ class DummyOptions:
             raise OSError(self.chdir_error)
         self.changed_directory = True
 
+    def setumask(self, mask):
+        self.umaskset = mask
+
 class DummyLogger:
     def __init__(self):
         self.reopened = False
@@ -409,7 +413,8 @@ class DummyProcess:
         self.transitioned = True
 
 class DummyPConfig:
-    def __init__(self, options, name, command, priority=999, autostart=True,
+    def __init__(self, options, name, command, directory=None, umask=None,
+                 priority=999, autostart=True,
                  autorestart=True, startsecs=10, startretries=999,
                  uid=None, stdout_logfile=None, stdout_capture_maxbytes=0,
                  stdout_logfile_backups=0, stdout_logfile_maxbytes=0,
@@ -417,7 +422,7 @@ class DummyPConfig:
                  stderr_logfile_backups=0, stderr_logfile_maxbytes=0,
                  redirect_stderr=False,
                  stopsignal=None, stopwaitsecs=10,
-                 exitcodes=(0,2), environment=None, directory=None):
+                 exitcodes=(0,2), environment=None):
         self.options = options
         self.name = name
         self.command = command
@@ -444,6 +449,7 @@ class DummyPConfig:
         self.exitcodes = exitcodes
         self.environment = environment
         self.directory = directory
+        self.umask = umask
         self.autochildlogs_created = False
 
     def create_autochildlogs(self):

+ 4 - 1
src/supervisor/tests/test_options.py

@@ -54,6 +54,7 @@ class ServerOptionsTests(unittest.TestCase):
         startsecs=5
         startretries=10
         directory=/tmp
+        umask=002
         
         [program:cat2]
         priority=2
@@ -129,6 +130,7 @@ class ServerOptionsTests(unittest.TestCase):
         self.assertEqual(proc1.stdout_logfile_backups, 10)
         self.assertEqual(proc1.exitcodes, [0,2])
         self.assertEqual(proc1.directory, '/tmp')
+        self.assertEqual(proc1.umask, 002)
 
         cat2 = options.process_group_configs[1]
         self.assertEqual(cat2.name, 'cat2')
@@ -686,7 +688,8 @@ class TestProcessConfig(unittest.TestCase):
 
     def _makeOne(self, *arg, **kw):
         defaults = {}
-        for name in ('name', 'command', 'priority', 'autostart', 'autorestart',
+        for name in ('name', 'command', 'directory', 'umask',
+                     'priority', 'autostart', 'autorestart',
                      'startsecs', 'startretries', 'uid',
                      'stdout_logfile', 'stdout_capture_maxbytes',
                      'stdout_logfile_backups', 'stdout_logfile_maxbytes',

+ 13 - 0
src/supervisor/tests/test_process.py

@@ -333,6 +333,19 @@ class SubprocessTests(unittest.TestCase):
         self.assertEqual(options._exitcode, 127)
         self.assertEqual(options.changed_directory, True)
 
+    def test_spawn_as_child_sets_umask(self):
+        options = DummyOptions()
+        options.forkpid = 0
+        config = DummyPConfig(options, 'good', '/good/filename', umask=002)
+        instance = self._makeOne(config)
+        result = instance.spawn()
+        self.assertEqual(result, None)
+        self.assertEqual(options.written, {})
+        self.assertEqual(options.execv_args,
+                         ('/good/filename', ['/good/filename']) )
+        self.assertEqual(options._exitcode, 127)
+        self.assertEqual(options.umaskset, 002)
+
     def test_spawn_as_child_cwd_fail(self):
         options = DummyOptions()
         options.forkpid = 0