#!/usr/bin/python """Service launcher that creates pidfiles and can redirect output to a file.""" import subprocess, sys, os, optparse, signal, pwd, grp, re def stop_service(pidfile): """ Stop a service using a pidfile. Read the first line of a file for an integer that should refer to a pid, send a SIGTERM to that pid #. @param pidfile: file to read for the process id number """ pidfh = open(pidfile) pid = int(pidfh.readline()) os.kill(pid, signal.SIGTERM) def start_service(cmd, pidfile, logfile=os.devnull, chdir=None): """ Start cmd in the background and write the pid to pidfile. @param cmd: command to run with arguments @param pidfile: pidfile to write the pid to @param logfile: file to write stderr/stdout to @param chdir: Directory to change to before starting the application """ logfh = open(logfile, 'a') pidfh = open(pidfile, 'w') proc = subprocess.Popen(cmd, stdout=logfh, stderr=logfh, cwd=chdir) pidfh.write(str(proc.pid)) pidfh.close() def get_user_name_id(user): """ Get the user id # and name. @param user: integer or string containing either the uid # or a string username @returns a tuple of the user name, user id """ if re.match('\d+', str(user)): pass_info = pwd.getpwuid(user) else: pass_info = pwd.getpwnam(user) return pass_info[0], pass_info[2] def get_group_name_id(group): """ Get the group id # and name @param group: integer or string containing either the uid # or a string username @returns a tuple of group name, group id """ if re.match('\d+', str(group)): group_info = grp.getgrgid(group) else: group_info = grp.getgrnam(group) return group_info[0], group_info[2] def set_group_user(group=None, user=None): """ Set the group and user id if gid or uid is defined. @param group: Change the group id the program is run under @param user: Change the user id the program is run under """ if group: _, gid = get_group_name_id(group) os.setgid(gid) os.setegid(gid) if user: username, uid = get_user_name_id(user) # Set environment for programs that use those to find running user for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'): os.environ[name] = username os.setuid(uid) os.seteuid(uid) def main(): parser = optparse.OptionParser() parser.allow_interspersed_args = False parser.add_option('-l', '--logfile', action='store', default=None, help='File to redirect stdout to') parser.add_option('-c', '--chdir', action='store', default=None, help='Change to dir before starting the process') parser.add_option('-s', '--start-service', action='store_true', default=False, help='Start service') parser.add_option('-k', '--stop-service', action='store_true', default=False, help='Stop service') parser.add_option('-p', '--pidfile', action='store', default=None, help='Pid file location (Required)') parser.add_option('-u', '--chuid', action='store', default=None, help='UID to run process as') parser.add_option('-g', '--chgid', action='store', default=None, help='GID to run process as') options, args = parser.parse_args() if not options.pidfile: print 'A pidfile must always be supplied' parser.print_help() sys.exit(1) set_group_user(group=options.chgid, user=options.chuid) if options.start_service: start_service(args, options.pidfile, options.logfile, options.chdir) elif options.stop_service: stop_service(options.pidfile) else: print 'Nothing to do, you must specify to start or stop a service' parser.print_help() if __name__ == '__main__': main()