1#!/usr/bin/python 2"""Service launcher that creates pidfiles and can redirect output to a file.""" 3import subprocess, sys, os, optparse, signal, pwd, grp, re 4 5 6def stop_service(pidfile): 7 """ 8 Stop a service using a pidfile. 9 10 Read the first line of a file for an integer that should refer to a pid, 11 send a SIGTERM to that pid #. 12 @param pidfile: file to read for the process id number 13 """ 14 pidfh = open(pidfile) 15 pid = int(pidfh.readline()) 16 os.kill(pid, signal.SIGTERM) 17 18 19def start_service(cmd, pidfile, logfile=os.devnull, chdir=None): 20 """ 21 Start cmd in the background and write the pid to pidfile. 22 23 @param cmd: command to run with arguments 24 @param pidfile: pidfile to write the pid to 25 @param logfile: file to write stderr/stdout to 26 @param chdir: Directory to change to before starting the application 27 """ 28 logfh = open(logfile, 'a') 29 pidfh = open(pidfile, 'w') 30 proc = subprocess.Popen(cmd, stdout=logfh, stderr=logfh, cwd=chdir) 31 pidfh.write(str(proc.pid)) 32 pidfh.close() 33 34 35def get_user_name_id(user): 36 """ 37 Get the user id # and name. 38 39 @param user: integer or string containing either the uid # 40 or a string username 41 42 @returns a tuple of the user name, user id 43 """ 44 if re.match('\d+', str(user)): 45 pass_info = pwd.getpwuid(user) 46 else: 47 pass_info = pwd.getpwnam(user) 48 49 return pass_info[0], pass_info[2] 50 51 52def get_group_name_id(group): 53 """ 54 Get the group id # and name 55 56 @param group: integer or string containing either the uid # 57 or a string username 58 59 @returns a tuple of group name, group id 60 """ 61 if re.match('\d+', str(group)): 62 group_info = grp.getgrgid(group) 63 else: 64 group_info = grp.getgrnam(group) 65 66 return group_info[0], group_info[2] 67 68 69def set_group_user(group=None, user=None): 70 """ 71 Set the group and user id if gid or uid is defined. 72 73 @param group: Change the group id the program is run under 74 @param user: Change the user id the program is run under 75 """ 76 if group: 77 _, gid = get_group_name_id(group) 78 os.setgid(gid) 79 os.setegid(gid) 80 81 if user: 82 username, uid = get_user_name_id(user) 83 # Set environment for programs that use those to find running user 84 for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'): 85 os.environ[name] = username 86 os.setuid(uid) 87 os.seteuid(uid) 88 89 90def main(): 91 parser = optparse.OptionParser() 92 parser.allow_interspersed_args = False 93 parser.add_option('-l', '--logfile', action='store', 94 default=None, 95 help='File to redirect stdout to') 96 parser.add_option('-c', '--chdir', action='store', 97 default=None, 98 help='Change to dir before starting the process') 99 parser.add_option('-s', '--start-service', action='store_true', 100 default=False, 101 help='Start service') 102 parser.add_option('-k', '--stop-service', action='store_true', 103 default=False, 104 help='Stop service') 105 parser.add_option('-p', '--pidfile', action='store', 106 default=None, 107 help='Pid file location (Required)') 108 parser.add_option('-u', '--chuid', action='store', 109 default=None, 110 help='UID to run process as') 111 parser.add_option('-g', '--chgid', action='store', 112 default=None, 113 help='GID to run process as') 114 115 116 117 options, args = parser.parse_args() 118 119 if not options.pidfile: 120 print 'A pidfile must always be supplied' 121 parser.print_help() 122 sys.exit(1) 123 124 set_group_user(group=options.chgid, user=options.chuid) 125 if options.start_service: 126 start_service(args, options.pidfile, options.logfile, options.chdir) 127 elif options.stop_service: 128 stop_service(options.pidfile) 129 else: 130 print 'Nothing to do, you must specify to start or stop a service' 131 parser.print_help() 132 133 134if __name__ == '__main__': 135 main() 136