Home | History | Annotate | Download | only in utils
      1 #!/usr/bin/python
      2 """Service launcher that creates pidfiles and can redirect output to a file."""
      3 import subprocess, sys, os, optparse, signal, pwd, grp, re
      4 
      5 
      6 def 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 
     19 def 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 
     35 def 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 
     52 def 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 
     69 def 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 
     90 def 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 
    134 if __name__ == '__main__':
    135     main()
    136