Pythonデーモンの詳細
13161 ワード
,
, ,
。
, , ……
" "、" ", daemon。
Unix , fork 。
import os
def daemonize():
# fork
pid = os.fork()
# pid ,
# 。
#
if pid != 0:
#
# 。
#
# ,
#
os._exit(0)
#
# ( )
os.system('python server.py')
# , os.system
# os.system python、server.py sh
, daemonize ,
《Unix 》 。
, 。
1. 。
Apache , root 80 nobody (www-data) 。
, root , root。
: ,
。
2. 。
, ,
kill -9 , 。
undead, SIGCHLD 。
3. stdin、stdout stderr。
daemon , 。
, , print 。
stdout、stderr 。
/dev/nul 。
stdin 。
4. setsid(), umask(), chdir() google。 。
5. pid xxx.pid 。
, pid pid,
kill PID。
, Python Daemon Zope zdaemon,
Daemon , zdaemon 。
, zdaemon , zdaemon 。
, OO 。
zdaemon " " , daemonize fork。
# zdaemon.py
import os
def zdaemonize():
# fork,
pid = os.fork()
if pid != 0:
os._exit(0)
# fork
pid = os.fork()
# , Daemon
if pid != 0:
#
# , manager_server
import manager_server
manger_server.serve_forever()
# ,
os.system('python server.py')
manger_server TCP , Unix Socket 。
, pid Unix Socket 。
Unix Socket (TCP Socket) ,
'start'、'status' 'stop',
( ) , 。
, 。
zdaemon , , ,
Unix Socket, 。
。
--《Unix 》
Python Python 。
Python , Java Python。
zdaemon, 270 。
Local XMLRPC Server (Unix Socket) zdaemon Socket ,
。
, Local XMLRPC Server , Python SimpleXMLRPCServer.py
, Local XMLRPC Server ,
, 。
XMLRPC Server ,
start、stop、status 。
daemon = Daemon(...)
daemon.register_function(...)
, , 。
daemon (daemon.py) ,
, , 。
。
、 daemon.py
#!/usr/bin/env python2.5
# -*- coding: utf-8 -*-
import os, sys
from os import fork
from time import sleep
from sys import stderr, stdout
from socket import error as SocketError
from traceback import print_exc
import schema # , 《 》 ?
import pyetc # ... :)
from daemon import Daemon, ServerProxy, Fault, \
error as DaemonError
## #
def usage():
print ' : %s start|stop|status' %sys.argv[0]
def start():
#
# demo.conf
pyetc.load(schema.ETC('demo.conf'), env=schema.env)
conf = schema.config.daemon
# ... 《 》
try:
# Daemon
daemon = Daemon(
address = conf.address, # /pid
program = conf.program, #
verbose = conf.verbose #
)
print ' '
#
# daemon(arg1, arg2, ...)
# arg1, arg2 ... ,
# :
# program.py arg1 arg2 ...
daemon()
except DaemonError, msg:
print ' , : ', msg
except:
print_exc(file=stderr)
def stop():
pyetc.load(schema.ETC('demo.conf'), env=schema.env)
conf = schema.config.daemon
#
try:
daemon = ServerProxy(conf.address)
daemon.stop()
print ' '
except SocketError:
print ' '
except Fault:
print ' '
except:
print_exc(file=stderr)
def status():
pyetc.load(schema.ETC('demo.conf'), env=schema.env)
conf = schema.config.daemon
try:
daemon = ServerProxy(conf.address)
status = daemon.status()
if status == 'running':
print ' "%s" ' %conf.program
elif status == 'stopped':
stdout.write( (' , "%s" , '
' ... '
) %conf.program )
stop()
else:
print ' , '
except SocketError:
print ' '
except:
print_exc(file=stderr)
## #
#
if len(sys.argv) != 2:
usage()
elif sys.argv[1] == 'start':
start()
elif sys.argv[1] == 'stop':
stop()
elif sys.argv[1] == 'status':
status()
else: usage()
、 : daemon.py
import os, sys
from httplib import HTTP, HTTPConnection
from pwd import getpwnam, getpwuid
from signal import signal as setsignal, SIGCHLD, SIGTERM
from sys import stderr, stdout
from urllib import urlopen
from SocketServer import UnixStreamServer
from socket import socket, error as SocketError, \
AF_UNIX, SOCK_STREAM
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher, \
SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
from xmlrpclib import Fault, Transport, dumps as xmlrpc_dumps, \
_Method as _XMLRPCMethod, ServerProxy as ServerProxy_N___G
from os import execv, chdir, chmod, fork, geteuid, getpid, \
kill, setgid, setuid, umask, unlink, waitpid, \
error as OSError, WNOHANG
class error(Exception): pass
class nul:
write = staticmethod(lambda s: None)
flush = staticmethod(lambda : None)
read = staticmethod(lambda n: '' )
class UnixStreamXMLRPCServer(UnixStreamServer, SimpleXMLRPCDispatcher):
def __init__(self, address, requestHandler=SimpleXMLRPCRequestHandler,
allow_none=False, encoding=None):
self.logRequests = False
SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
UnixStreamServer.__init__(self, address, requestHandler)
class Daemon:
def __init__(self, **args):
address = args[ 'address' ]
allow_none = args.get( 'allow_none', True )
encoding = args.get( 'encoding' , 'utf-8' )
self.verbose = args.get( 'verbose' , False )
self.stdout = args.get( 'stdout' , nul )
self.stderr = args.get( 'stderr' , nul )
try:
ServerProxy(address).ping()
except SocketError:
pass
else:
raise error, 'Another daemon is already up using socket %s' %repr(address)
if isinstance(address, str):
try:
unlink(address)
except OSError:
pass
self.manager = UnixStreamXMLRPCServer(address,
allow_none=allow_none, encoding=encoding)
self.pidfile = address
else:
self.manager = SimpleXMLRPCServer(address,
allow_none=allow_none, encoding=encoding)
self.pid = None
self.running = True
self.program = args[ 'program' ]
DaemonizeTools.setuid(
user = args.get( 'user' , None ) )
SignalTools.setsignals(self)
DaemonizeTools.daemonize(
directory = args.get( 'directory' , None ),
umask = args.get( 'umask' , 022 ) )
self.register_function = lambda *args: (
self.manager.register_function(*args) )
self.register_function(lambda: self.stop() , 'stop' )
self.register_function(lambda: self.status(), 'status')
self.register_function(lambda: True , 'ping' )
def __call__(self, *args):
if not self.verbose:
DaemonizeTools.close_files(self.stdout, self.stderr)
del self.stdout, self.stderr
pid = fork()
if pid != 0:
self.pid = pid
while self.running:
self.manager.handle_request()
return pid
else:
try:
for i in xrange(3, 100):
try:
os.close(i)
except OSError:
pass
try:
execv(sys.executable, tuple(
[sys.executable, self.program] + list(args) ) )
except OSError, err:
print >> stderr, ( 'can\'t exec %r: %s
'
% (self.program, err) )
finally:
os._exit(127)
def status(self):
if not self.pid:
return 'stopped'
else:
return 'running'
def stop(self):
if not self.pid:
self.running = False
if hasattr(self, 'pidfile'):
try:
unlink(self.pidfile)
except OSError:
pass
raise error, 'no subprocess running'
kill(self.pid, SIGTERM)
self.running = False
if hasattr(self, 'pidfile'):
try:
unlink(self.pidfile)
except OSError:
pass
class UnixStreamHTTPConnection(HTTPConnection):
def connect(self):
self.sock = socket(AF_UNIX, SOCK_STREAM)
self.sock.connect(self.host)
class UnixStreamHTTP(HTTP):
_connection_class = UnixStreamHTTPConnection
class UnixStreamTransport(Transport):
def make_connection(self, host):
return UnixStreamHTTP(host)
class UnixStreamServerProxy_NG:
def __init__(self, uri, transport=None, encoding=None, verbose=0,
allow_none=0, use_datetime=0):
self.__host = uri
self.__handler = '/RPC2'
if not transport:
self.__transport = UnixStreamTransport(use_datetime=use_datetime)
self.__encoding = encoding
self.__verbose = verbose
self.__allow_none = allow_none
def __request(self, methodname, params):
request = xmlrpc_dumps(params, methodname, encoding=self.__encoding,
allow_none=self.__allow_none)
response = self.__transport.request(
self.__host, self.__handler, request,
verbose=self.__verbose )
if len(response) == 1:
response = response[0]
return response
def __getattr__(self, name):
return _XMLRPCMethod(self.__request, name)
def ServerProxy(address, **args):
if isinstance(address, str):
return UnixStreamServerProxy_NG(address, **args)
else:
host, port = address
host = (host, '127.0.0.1')[host == '0.0.0.0']
return ServerProxy_N___G('http://%s:%d' %(host, port), **args)
class DaemonizeTools:
@staticmethod
def setuid(**args):
user = args['user']
if user is None:
return
try:
uid = int(user)
except ValueError:
try:
pwrec = pwd.getpwnam(user)
except KeyError:
raise error, 'username %r not found' % user
uid = pwrec[2]
else:
try:
pwrec = pwd.getpwuid(uid)
except KeyError:
raise error, 'uid %r not found' % user
euid = geteuid()
if euid != 0 and euid != uid:
raise error, 'only root can change users'
setgid(pwrec[3])
setuid(uid)
@staticmethod
def daemonize(**args):
pid = fork()
if pid != 0:
os._exit(0)
if args['directory']:
try:
chdir(args['directory'])
except OSError, err:
print >> stderr, ( 'can\'t chdir into %r: %s'
% (args['directory'], err) )
else:
print >> stderr, ( 'set current directory: %r'
% args['directory'] )
os.setsid()
umask(args['umask'])
@staticmethod
def close_files(stdout, stderr):
os.close(0)
sys.stdin = sys.__stdin__ = nul
os.close(1)
sys.stdout = sys.__stdout__ = stdout
os.close(2)
sys.stderr = sys.__stderr__ = stderr
class SignalTools:
daemon = None
@staticmethod
def setsignals(daemon):
SignalTools.daemon = daemon
setsignal(SIGCHLD, SignalTools.sigchild)
@staticmethod
def sigchild(sig, frame):
try:
pid, sts = waitpid(-1, WNOHANG)
if pid == SignalTools.daemon.pid:
SignalTools.daemon.pid = None
except OSError:
return