scripts/services/service.py, thanks Ross
This commit is contained in:
@@ -1 +1 @@
|
||||
Version 1.99.4 (2012-01-16 23:25:02) stable
|
||||
Version 1.99.4 (2012-01-17 21:50:01) stable
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
from service import ServiceBase
|
||||
import os, sys, time, subprocess, atexit
|
||||
from signal import SIGTERM
|
||||
|
||||
class LinuxService(ServiceBase):
|
||||
def __init__(self, name, label, stdout='/dev/null', stderr='/dev/null'):
|
||||
ServiceBase.__init__(self, name, label, stdout, stderr)
|
||||
self.pidfile = '/tmp/%s.pid' % name
|
||||
self.config_file = '/etc/%s.conf' % name
|
||||
|
||||
def daemonize(self):
|
||||
"""
|
||||
do the UNIX double-fork magic, see Stevens' "Advanced
|
||||
Programming in the UNIX Environment" for details (ISBN 0201563177)
|
||||
http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
|
||||
"""
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# exit first parent
|
||||
sys.exit(0)
|
||||
except OSError, e:
|
||||
sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
|
||||
return
|
||||
|
||||
# decouple from parent environment
|
||||
os.chdir("/")
|
||||
os.setsid()
|
||||
os.umask(0)
|
||||
|
||||
# do second fork
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# exit from second parent
|
||||
sys.exit(0)
|
||||
except OSError, e:
|
||||
sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
|
||||
return
|
||||
|
||||
# redirect standard file descriptors
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
si = file('/dev/null', 'r')
|
||||
so = file(self.stdout or '/dev/null', 'a+')
|
||||
se = file(self.stderr or '/dev/null', 'a+')
|
||||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
|
||||
def getpid(self):
|
||||
# Check for a pidfile to see if the daemon already runs
|
||||
try:
|
||||
pf = file(self.pidfile,'r')
|
||||
pid = int(pf.read().strip())
|
||||
pf.close()
|
||||
except IOError:
|
||||
pid = None
|
||||
|
||||
return pid
|
||||
|
||||
def status(self):
|
||||
pid = self.getpid()
|
||||
if pid:
|
||||
return 'Service running with PID %s.' % pid
|
||||
else:
|
||||
return 'Service is not running.'
|
||||
|
||||
def check_permissions(self):
|
||||
if not os.geteuid() == 0:
|
||||
return (False, 'This script must be run with root permissions.')
|
||||
else:
|
||||
return (True, '')
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Start the daemon
|
||||
"""
|
||||
pid = self.getpid()
|
||||
|
||||
if pid:
|
||||
message = "Service already running under PID %s\n"
|
||||
sys.stderr.write(message % self.pidfile)
|
||||
return
|
||||
|
||||
# Start the daemon
|
||||
self.daemonize()
|
||||
self.run()
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stop the daemon
|
||||
"""
|
||||
pid = self.getpid()
|
||||
|
||||
if not pid:
|
||||
message = "Service is not running\n"
|
||||
sys.stderr.write(message)
|
||||
return # not an error in a restart
|
||||
|
||||
# Try killing the daemon process
|
||||
try:
|
||||
while 1:
|
||||
os.kill(pid, SIGTERM)
|
||||
time.sleep(0.5)
|
||||
except OSError, err:
|
||||
err = str(err)
|
||||
if err.find("No such process") > 0:
|
||||
if os.path.exists(self.pidfile):
|
||||
os.remove(self.pidfile)
|
||||
else:
|
||||
print str(err)
|
||||
return
|
||||
|
||||
def restart(self):
|
||||
"""
|
||||
Restart the daemon
|
||||
"""
|
||||
self.stop()
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
atexit.register(self.terminate)
|
||||
|
||||
args = self.load_configuration()[0]
|
||||
stdout = open(self.stdout, 'a+')
|
||||
stderr = open(self.stderr, 'a+')
|
||||
process = subprocess.Popen(args, stdout=stdout, stderr=stderr)
|
||||
file(self.pidfile,'w+').write("%s\n" % process.pid)
|
||||
process.wait()
|
||||
|
||||
self.terminate()
|
||||
|
||||
def terminate(self):
|
||||
try:
|
||||
os.remove(self.pidfile)
|
||||
except:
|
||||
pass
|
||||
|
||||
def install(self):
|
||||
env = self.detect_environment()
|
||||
src_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'service.py')
|
||||
|
||||
# make sure this script is executable
|
||||
self.run_command('chmod', '+x', src_path)
|
||||
|
||||
# link this daemon to the service directory
|
||||
dest_path = env['rc.d-path'] + self.name
|
||||
os.symlink(src_path, dest_path)
|
||||
|
||||
# start the service at boot
|
||||
install_command = self.get_service_installer_command(env)
|
||||
result = self.run_command(*install_command)
|
||||
self.start()
|
||||
|
||||
def uninstall(self):
|
||||
self.stop()
|
||||
env = self.detect_environment()
|
||||
|
||||
# stop the service from autostarting
|
||||
uninstall_command = self.get_service_uninstaller_command(env)
|
||||
result = self.run_command(*uninstall_command)
|
||||
|
||||
# remove link to the script from the service directory
|
||||
path = env['rc.d-path'] + self.name
|
||||
os.remove(path)
|
||||
|
||||
def detect_environment(self):
|
||||
"""
|
||||
Returns a dictionary of command/path to the required command-line applications.
|
||||
One key is 'dist' which will either be 'debian' or 'redhat', which is the best
|
||||
guess as to which Linux distribution the current system is based on.
|
||||
"""
|
||||
check_for = [
|
||||
'chkconfig',
|
||||
'service',
|
||||
'update-rc.d',
|
||||
'rpm',
|
||||
'dpkg',
|
||||
]
|
||||
|
||||
env = dict()
|
||||
for cmd in check_for:
|
||||
result = self.run_command('which', cmd)
|
||||
if result[0]:
|
||||
env[cmd] = result[0].replace('\n', '')
|
||||
|
||||
if 'rpm' in env:
|
||||
env['dist'] = 'redhat'
|
||||
env['rc.d-path'] = '/etc/rc.d/init.d/'
|
||||
elif 'dpkg' in env:
|
||||
env['dist'] = 'debian'
|
||||
env['rc.d-path'] = '/etc/init.d/'
|
||||
else:
|
||||
env['dist'] = 'unknown'
|
||||
env['rc.d-path'] = '/dev/null/'
|
||||
|
||||
return env
|
||||
|
||||
def get_service_installer_command(self, env):
|
||||
"""
|
||||
Returns list of args required to set a service to run on boot.
|
||||
"""
|
||||
if env['dist'] == 'redhat':
|
||||
cmd = env['chkconfig']
|
||||
return [cmd, self.name, 'on']
|
||||
else:
|
||||
cmd = env['update-rc.d']
|
||||
return [cmd, self.name, 'defaults']
|
||||
|
||||
def get_service_uninstaller_command(self, env):
|
||||
"""
|
||||
Returns list of arge required to stop a service from running at boot.
|
||||
"""
|
||||
if env['dist'] == 'redhat':
|
||||
cmd = env['chkconfig']
|
||||
return [cmd, self.name, 'off']
|
||||
else:
|
||||
cmd = env['update-rc.d']
|
||||
return [cmd, self.name, 'remove']
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys, os, time, subprocess
|
||||
|
||||
class Base:
|
||||
def run_command(self, *args):
|
||||
"""
|
||||
Returns the output of a command as a tuple (output, error).
|
||||
"""
|
||||
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
return p.communicate()
|
||||
|
||||
class ServiceBase(Base):
|
||||
def __init__(self, name, label, stdout=None, stderr=None):
|
||||
self.name = name
|
||||
self.label = label
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
self.config_file = None
|
||||
|
||||
def load_configuration(self):
|
||||
"""
|
||||
Loads the configuration required to build the command-line string
|
||||
for running web2py. Returns a tuple (command_args, config_dict).
|
||||
"""
|
||||
s = os.path.sep
|
||||
|
||||
default = dict(
|
||||
python = 'python',
|
||||
web2py = os.path.join(s.join(__file__.split(s)[:-3]), 'web2py.py'),
|
||||
http_enabled = True,
|
||||
http_ip = '0.0.0.0',
|
||||
http_port = 8000,
|
||||
https_enabled = True,
|
||||
https_ip = '0.0.0.0',
|
||||
https_port = 8001,
|
||||
https_key = '',
|
||||
https_cert = '',
|
||||
password = '<recycle>',
|
||||
)
|
||||
|
||||
config = default
|
||||
if self.config_file:
|
||||
try:
|
||||
f = open(self.config_file, 'r')
|
||||
lines = f.readlines()
|
||||
f.close()
|
||||
|
||||
for line in lines:
|
||||
fields = line.split('=', 1)
|
||||
if len(fields) == 2:
|
||||
key, value = fields
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
config[key] = value
|
||||
except:
|
||||
pass
|
||||
|
||||
web2py_path = os.path.dirname(config['web2py'])
|
||||
os.chdir(web2py_path)
|
||||
|
||||
args = [config['python'], config['web2py']]
|
||||
interfaces = []
|
||||
ports = []
|
||||
|
||||
if config['http_enabled']:
|
||||
ip = config['http_ip']
|
||||
port = config['http_port']
|
||||
interfaces.append('%s:%s' % (ip, port))
|
||||
ports.append(port)
|
||||
if config['https_enabled']:
|
||||
ip = config['https_ip']
|
||||
port = config['https_port']
|
||||
key = config['https_key']
|
||||
cert = config['https_cert']
|
||||
if key != '' and cert != '':
|
||||
interfaces.append('%s:%s:%s:%s' % (ip, port, cert, key))
|
||||
ports.append(ports)
|
||||
if len(interfaces) == 0:
|
||||
sys.exit('Configuration error. Must have settings for http and/or https')
|
||||
|
||||
password = config['password']
|
||||
if not password == '<recycle>':
|
||||
from gluon import main
|
||||
for port in ports:
|
||||
main.save_password(password, port)
|
||||
|
||||
password = '<recycle>'
|
||||
|
||||
args.append('-a "%s"' % password)
|
||||
|
||||
interfaces = ';'.join(interfaces)
|
||||
args.append('--interfaces=%s' % interfaces)
|
||||
|
||||
if 'log_filename' in config.key():
|
||||
log_filename = config['log_filename']
|
||||
args.append('--log_filename=%s' % log_filename)
|
||||
|
||||
return (args, config)
|
||||
|
||||
def start(self):
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
def restart(self):
|
||||
pass
|
||||
|
||||
def status(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
pass
|
||||
|
||||
def install(self):
|
||||
pass
|
||||
|
||||
def uninstall(self):
|
||||
pass
|
||||
|
||||
def check_permissions(self):
|
||||
"""
|
||||
Does the script have permissions to install, uninstall, start, and stop services?
|
||||
Return value must be a tuple (True/False, error_message_if_False).
|
||||
"""
|
||||
return (False, 'Permissions check not implemented')
|
||||
|
||||
class WebServerBase(Base):
|
||||
def install(self):
|
||||
pass
|
||||
|
||||
def uninstall(self):
|
||||
pass
|
||||
|
||||
|
||||
def get_service():
|
||||
service_name = 'web2py'
|
||||
service_label = 'web2py Service'
|
||||
|
||||
if sys.platform == 'linux2':
|
||||
from linux import LinuxService as Service
|
||||
elif sys.platform == 'darwin':
|
||||
# from mac import MacService as Service
|
||||
sys.exit('Mac OS X is not yet supported.\n')
|
||||
elif sys.platform == 'win32':
|
||||
# from windows import WindowsService as Service
|
||||
sys.exit('Windows is not yet supported.\n')
|
||||
else:
|
||||
sys.exit('The following platform is not supported: %s.\n' % sys.platform)
|
||||
|
||||
service = Service(service_name, service_label)
|
||||
return service
|
||||
|
||||
if __name__ == '__main__':
|
||||
service = get_service()
|
||||
is_root, error_message = service.check_permissions()
|
||||
if not is_root:
|
||||
sys.exit(error_message)
|
||||
|
||||
if len(sys.argv) >= 2:
|
||||
command = sys.argv[1]
|
||||
if command == 'start':
|
||||
service.start()
|
||||
elif command == 'stop':
|
||||
service.stop()
|
||||
elif command == 'restart':
|
||||
service.restart()
|
||||
elif command == 'status':
|
||||
print service.status() + '\n'
|
||||
elif command == 'run':
|
||||
service.run()
|
||||
elif command == 'install':
|
||||
service.install()
|
||||
elif command == 'uninstall':
|
||||
service.uninstall()
|
||||
elif command == 'install-apache':
|
||||
# from apache import Apache
|
||||
# server = Apache()
|
||||
# server.install()
|
||||
sys.exit('Configuring Apache is not yet supported.\n')
|
||||
elif command == 'uninstall-apache':
|
||||
# from apache import Apache
|
||||
# server = Apache()
|
||||
# server.uninstall()
|
||||
sys.exit('Configuring Apache is not yet supported.\n')
|
||||
else:
|
||||
sys.exit('Unknown command: %s' % command)
|
||||
else:
|
||||
print 'Usage: %s [command] \n' % sys.argv[0] + \
|
||||
'\tCommands:\n' + \
|
||||
'\t\tstart Starts the service\n' + \
|
||||
'\t\tstop Stop the service\n' + \
|
||||
'\t\trestart Restart the service\n' + \
|
||||
'\t\tstatus Check if the service is running\n' + \
|
||||
'\t\trun Run service is blocking mode\n' + \
|
||||
'\t\t (Press Ctrl + C to exit)\n' + \
|
||||
'\t\tinstall Install the service\n' + \
|
||||
'\t\tuninstall Uninstall the service\n' + \
|
||||
'\t\tinstall-apache Install as an Apache site\n' + \
|
||||
'\t\tuninstall-apache Uninstall from Apache\n'
|
||||
Reference in New Issue
Block a user