summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Kreen2013-01-16 23:52:43 +0000
committerMarko Kreen2013-01-18 18:17:00 +0000
commit65e1aa1d21df0896bc0365098726790cc2bcce70 (patch)
tree48c88c531826b819e7ae3104ea007363df5dedeb
parenta81cb763a3a7d12337808c0b9e501af97c9c8b53 (diff)
scriptmgr: user= support
Daemon sections can now have user= option. It makes scriptmgr launch (and kill) daemons with sudo -u $user.
-rw-r--r--doc/scriptmgr.txt5
-rwxr-xr-xscripts/scriptmgr.py75
2 files changed, 69 insertions, 11 deletions
diff --git a/doc/scriptmgr.txt b/doc/scriptmgr.txt
index 370f820c..d1353e9e 100644
--- a/doc/scriptmgr.txt
+++ b/doc/scriptmgr.txt
@@ -81,6 +81,11 @@ script::
disabled::
If this service should be ignored.
+user::
+ Launch service as different unix user. Scriptmgr uses `sudo`
+ to switch users. So it either needs to be run as root,
+ or sudo config must allow it to launch daemons.
+
=== Example config file ===
[scriptmgr]
diff --git a/scripts/scriptmgr.py b/scripts/scriptmgr.py
index 5cba34c0..6aa82c78 100755
--- a/scripts/scriptmgr.py
+++ b/scripts/scriptmgr.py
@@ -47,9 +47,13 @@ import sys, os, signal, glob, ConfigParser, time
import pkgloader
pkgloader.require('skytools', '3.0')
-
import skytools
+try:
+ import pwd
+except ImportError:
+ pwd = None
+
command_usage = """
%prog [options] INI CMD [subcmd args]
@@ -68,6 +72,37 @@ def job_sort_cmp(j1, j2):
elif d1 > d2: return 1
else: return 0
+def launch_cmd(job, cmd):
+ if job['user']:
+ cmd = 'sudo -u "%s" %s' % (job['user'], cmd)
+ return os.system(cmd)
+
+def full_path(job, fn):
+ """Like os.path.expanduser() but works for other users.
+ """
+ if not fn:
+ return fn
+ if fn[0] == '~':
+ user, rest = fn.split('/',1)[0]
+ user = user[1:]
+ if not user:
+ user = job['user']
+
+ # find home
+ if user:
+ home = pwd.getpwuid(os.getuid()).pw_dir
+ elif 'HOME' in os.environ:
+ home = os.environ['HOME']
+ else:
+ home = os.pwd.getpwuid(os.getuid()).pw_dir
+
+ if rest:
+ return os.path.join(home, rest)
+ else:
+ return home
+ # always return full path
+ return os.path.join(job['cwd'], fn)
+
class ScriptMgr(skytools.DBScript):
__doc__ = __doc__
svc_list = []
@@ -91,6 +126,8 @@ class ScriptMgr(skytools.DBScript):
# load services
svc_list = self.cf.sections()
svc_list.remove(self.service_name)
+ with_user = 0
+ without_user = 0
for svc_name in svc_list:
cf = self.cf.clone(svc_name)
disabled = cf.getboolean('disabled', 0)
@@ -103,9 +140,16 @@ class ScriptMgr(skytools.DBScript):
'cwd': cf.getfile('cwd'),
'disabled': disabled,
'args': cf.get('args', ''),
+ 'user': cf.get('user', ''),
}
+ if svc['user']:
+ with_user += 1
+ else:
+ without_user += 1
self.svc_list.append(svc)
self.svc_map[svc_name] = svc
+ if with_user and without_user:
+ raise skytools.UsageError("Invalid config - some jobs have user=, some don't")
# generate config list
for tmp in self.cf.getlist('config_list'):
@@ -143,10 +187,15 @@ class ScriptMgr(skytools.DBScript):
'cwd': svc['cwd'],
'script': svc['script'],
'args': svc['args'],
+ 'user': svc['user'],
'service': svc['service'],
'job_name': cf.get('job_name'),
- 'pidfile': cf.getfile('pidfile', ''),
+ 'pidfile': cf.get('pidfile', ''),
}
+
+ if job['pidfile']:
+ job['pidfile'] = full_path(job, job['pidfile'])
+
self.job_list.append(job)
self.job_map[job['job_name']] = job
@@ -157,8 +206,6 @@ class ScriptMgr(skytools.DBScript):
except KeyError:
self.log.error ("Unknown job: %s", jn)
continue
- os.chdir(job['cwd'])
- cf = skytools.Config(job['service'], job['config'])
pidfile = job['pidfile']
name = job['job_name']
svc = job['service']
@@ -186,7 +233,6 @@ class ScriptMgr(skytools.DBScript):
if isinstance (job, int):
return job # ret.code
self.log.info('Starting %s' % job_name)
- os.chdir(job['cwd'])
pidfile = job['pidfile']
if not pidfile:
self.log.warning("No pidfile for %s, cannot launch" % job_name)
@@ -197,8 +243,9 @@ class ScriptMgr(skytools.DBScript):
return 0
else:
self.log.info("Ignoring stale pidfile for %s" % job_name)
+ os.chdir(job['cwd'])
cmd = "%(script)s %(config)s %(args)s -d" % job
- res = os.system(cmd)
+ res = launch_cmd(job, cmd)
self.log.debug(res)
if res != 0:
self.log.error('startup failed: %s' % job_name)
@@ -245,17 +292,23 @@ class ScriptMgr(skytools.DBScript):
return 0
def signal_job(self, job, sig):
- os.chdir(job['cwd'])
pidfile = job['pidfile']
if not pidfile:
self.log.warning("No pidfile for %s (%s)" % (job['job_name'], job['config']))
return
if os.path.isfile(pidfile):
pid = int(open(pidfile).read())
- try:
- os.kill(pid, sig)
- except Exception, det:
- self.log.warning("Signaling %s failed: %s" % (job['job_name'], str(det)))
+ if job['user']:
+ # run sudo + kill to avoid killing unrelated processes
+ res = os.system("sudo -u %s kill %d" % (job['user'], pid))
+ if res:
+ self.log.warning("Signaling %s failed" % (job['job_name'],))
+ else:
+ # direct kill
+ try:
+ os.kill(pid, sig)
+ except Exception, det:
+ self.log.warning("Signaling %s failed: %s" % (job['job_name'], str(det)))
else:
self.log.warning("Job %s not running" % job['job_name'])