• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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