|
Packit |
bbb0ff |
#!/usr/bin/env python
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
# Authors: Jiri Jaburek <jjaburek@redhat.com>
|
|
Packit |
bbb0ff |
#
|
|
Packit |
bbb0ff |
# Description: Daemonization backend for rlDaemonize
|
|
Packit |
bbb0ff |
#
|
|
Packit |
bbb0ff |
# Copyright (c) 2012 Red Hat, Inc. All rights reserved. This copyrighted
|
|
Packit |
bbb0ff |
# material is made available to anyone wishing to use, modify, copy, or
|
|
Packit |
bbb0ff |
# redistribute it subject to the terms and conditions of the GNU General
|
|
Packit |
bbb0ff |
# Public License v.2.
|
|
Packit |
bbb0ff |
#
|
|
Packit |
bbb0ff |
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
Packit |
bbb0ff |
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
Packit |
bbb0ff |
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
Packit |
bbb0ff |
# for more details.
|
|
Packit |
bbb0ff |
#
|
|
Packit |
bbb0ff |
# You should have received a copy of the GNU General Public License
|
|
Packit |
bbb0ff |
# along with this program; if not, write to the Free Software
|
|
Packit |
bbb0ff |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
import os, sys
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
from pwd import getpwnam
|
|
Packit |
bbb0ff |
from grp import getgrnam
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
from optparse import OptionParser
|
|
Packit |
bbb0ff |
from shlex import shlex
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
def file_write(filename, content):
|
|
Packit |
bbb0ff |
fd = open(filename, 'w')
|
|
Packit |
bbb0ff |
fd.write(content + '\n')
|
|
Packit |
bbb0ff |
fd.close()
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
def close_all_fds():
|
|
Packit |
bbb0ff |
try:
|
|
Packit |
bbb0ff |
maxfd = os.sysconf('SC_OPEN_MAX') # same as _SC_OPEN_MAX in C
|
|
Packit |
bbb0ff |
except:
|
|
Packit |
bbb0ff |
maxfd = 1024
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
for fd in range(0, maxfd):
|
|
Packit |
bbb0ff |
try:
|
|
Packit |
bbb0ff |
os.close(fd)
|
|
Packit |
bbb0ff |
except OSError:
|
|
Packit |
bbb0ff |
pass
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
# daemonize `command' (list) with arguments,
|
|
Packit |
bbb0ff |
# optionally change argv[0] to `alias',
|
|
Packit |
bbb0ff |
# write child pid to file named `pidfile',
|
|
Packit |
bbb0ff |
# fully daemonize or just background (fork) - `true_daemon',
|
|
Packit |
bbb0ff |
# change effective+real user/group to `su' (list, user and group)
|
|
Packit |
bbb0ff |
# (when true_daemon=True) redirect stdin/out/err to filenames
|
|
Packit |
bbb0ff |
# specified in ioredir vector (list),
|
|
Packit |
bbb0ff |
def daemonize(command, alias=None, pidfile=None, true_daemon=True, su=None, ioredir=None):
|
|
Packit |
bbb0ff |
if not alias:
|
|
Packit |
bbb0ff |
alias = command[0]
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
if not true_daemon:
|
|
Packit |
bbb0ff |
pid = os.fork()
|
|
Packit |
bbb0ff |
if (pid != 0):
|
|
Packit |
bbb0ff |
# parent, write pidfile and _exit,
|
|
Packit |
bbb0ff |
# avoiding possible double cleanups/flushes
|
|
Packit |
bbb0ff |
if pidfile:
|
|
Packit |
bbb0ff |
file_write(pidfile, str(pid))
|
|
Packit |
bbb0ff |
os._exit(0)
|
|
Packit |
bbb0ff |
else:
|
|
Packit |
bbb0ff |
# child, simply execute
|
|
Packit |
bbb0ff |
os.execvp(command[0],[alias]+command[1:])
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
else:
|
|
Packit |
bbb0ff |
pid = os.fork()
|
|
Packit |
bbb0ff |
if (pid != 0):
|
|
Packit |
bbb0ff |
# parent, just exit, since final pid depends on the second fork
|
|
Packit |
bbb0ff |
os._exit(0)
|
|
Packit |
bbb0ff |
else:
|
|
Packit |
bbb0ff |
os.setsid()
|
|
Packit |
bbb0ff |
pid = os.fork()
|
|
Packit |
bbb0ff |
if (pid != 0):
|
|
Packit |
bbb0ff |
# parent of the second child
|
|
Packit |
bbb0ff |
if pidfile:
|
|
Packit |
bbb0ff |
file_write(pidfile, str(pid))
|
|
Packit |
bbb0ff |
os._exit(0)
|
|
Packit |
bbb0ff |
else:
|
|
Packit |
bbb0ff |
# second child, real daemon!
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
os.chdir('/')
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
# change real and effective uid/gid of a process
|
|
Packit |
bbb0ff |
if su:
|
|
Packit |
bbb0ff |
uid = getpwnam(su[0]).pw_uid
|
|
Packit |
bbb0ff |
gid = getgrnam(su[1]).gr_gid
|
|
Packit |
bbb0ff |
os.setgroups([])
|
|
Packit |
bbb0ff |
os.setregid(gid, gid)
|
|
Packit |
bbb0ff |
os.setreuid(uid, uid)
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
# pre-create possible in/out files with default umask,
|
|
Packit |
bbb0ff |
# with original stderr (in case of errors), but with new uid/gid
|
|
Packit |
bbb0ff |
if ioredir:
|
|
Packit |
bbb0ff |
os.open(ioredir[0], os.O_RDWR)
|
|
Packit |
bbb0ff |
os.open(ioredir[1], os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0666)
|
|
Packit |
bbb0ff |
os.open(ioredir[2], os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0666)
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
os.umask(0)
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
close_all_fds()
|
|
Packit |
bbb0ff |
if ioredir:
|
|
Packit |
bbb0ff |
for ioport in ioredir:
|
|
Packit |
bbb0ff |
os.open(ioport, os.O_RDWR)
|
|
Packit |
bbb0ff |
else:
|
|
Packit |
bbb0ff |
os.open(os.devnull, os.O_RDWR)
|
|
Packit |
bbb0ff |
os.dup2(0,1)
|
|
Packit |
bbb0ff |
os.dup2(0,2)
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
# execute
|
|
Packit |
bbb0ff |
os.execvp(command[0],[alias]+command[1:])
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
# argument parsing
|
|
Packit |
bbb0ff |
def error(msg):
|
|
Packit |
bbb0ff |
print >> sys.stderr, "error: " + str(msg)
|
|
Packit |
bbb0ff |
sys.exit(1)
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
parser = OptionParser(usage='%prog [options] COMMAND')
|
|
Packit |
bbb0ff |
parser.add_option('--alias', action='store', type='string', metavar='NAME',
|
|
Packit |
bbb0ff |
dest='alias', help='specify custom argv[0]')
|
|
Packit |
bbb0ff |
parser.add_option('--background', action='store_true',
|
|
Packit |
bbb0ff |
dest='background', help='background (fork) only, nothing else')
|
|
Packit |
bbb0ff |
parser.add_option('--su', action='store', type='string', metavar='USER:GROUP',
|
|
Packit |
bbb0ff |
dest='su', help='run daemon under another user')
|
|
Packit |
bbb0ff |
parser.add_option('--ioredir', action='store', type='string', metavar='IN,OUT,ERR',
|
|
Packit |
bbb0ff |
dest='ioredir', help='redirect std{in,out,err} of the daemon to files')
|
|
Packit |
bbb0ff |
parser.add_option('--pidfile', action='store', type='string', metavar='FILE',
|
|
Packit |
bbb0ff |
dest='pidfile', help='write daemon pid to a file')
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
(opts, args) = parser.parse_args()
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
# additional parsing
|
|
Packit |
bbb0ff |
if opts.su:
|
|
Packit |
bbb0ff |
su = opts.su.split(':')
|
|
Packit |
bbb0ff |
else:
|
|
Packit |
bbb0ff |
su = None
|
|
Packit |
bbb0ff |
if opts.ioredir:
|
|
Packit |
bbb0ff |
ioredir = opts.ioredir.split(',')
|
|
Packit |
bbb0ff |
else:
|
|
Packit |
bbb0ff |
ioredir = None
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
# sanity checks
|
|
Packit |
bbb0ff |
if not args:
|
|
Packit |
bbb0ff |
error("no COMMAND specified")
|
|
Packit |
bbb0ff |
if len(args) > 1:
|
|
Packit |
bbb0ff |
error("COMMAND can be only one argument, quote it")
|
|
Packit |
bbb0ff |
if opts.su:
|
|
Packit |
bbb0ff |
if len(su) != 2:
|
|
Packit |
bbb0ff |
error("wrong --su argument specification")
|
|
Packit |
bbb0ff |
for i in su:
|
|
Packit |
bbb0ff |
if not i:
|
|
Packit |
bbb0ff |
error("wrong --su argument specification")
|
|
Packit |
bbb0ff |
if opts.ioredir:
|
|
Packit |
bbb0ff |
if len(ioredir) != 3:
|
|
Packit |
bbb0ff |
error("wrong --ioredir argument specification")
|
|
Packit |
bbb0ff |
for i in ioredir:
|
|
Packit |
bbb0ff |
if not i:
|
|
Packit |
bbb0ff |
error("wrong --ioredir argument specification")
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
# shell-expand the COMMAND into list
|
|
Packit |
bbb0ff |
lex = shlex(args[0])
|
|
Packit |
bbb0ff |
lex.whitespace_split = True
|
|
Packit |
bbb0ff |
args = list(lex)
|
|
Packit |
bbb0ff |
|
|
Packit |
bbb0ff |
# input parsing finished
|
|
Packit |
bbb0ff |
daemonize(args, alias=opts.alias, pidfile=opts.pidfile,
|
|
Packit |
bbb0ff |
true_daemon=(not opts.background), su=su, ioredir=ioredir)
|