scripts/services/service.py, thanks Ross

This commit is contained in:
Massimo Di Pierro
2012-01-17 21:50:21 -06:00
parent c5b4e35590
commit 652e6fd461
3 changed files with 423 additions and 1 deletions
+221
View File
@@ -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']
+201
View File
@@ -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'