1#!/usr/bin/env python 2 3### 4# Copyright (c) 2005, Jeremiah Fincher 5# Copyright (c) 2009, James McCoy 6# All rights reserved. 7# 8# Redistribution and use in source and binary forms, with or without 9# modification, are permitted provided that the following conditions are met: 10# 11# * Redistributions of source code must retain the above copyright notice, 12# this list of conditions, and the following disclaimer. 13# * Redistributions in binary form must reproduce the above copyright notice, 14# this list of conditions, and the following disclaimer in the 15# documentation and/or other materials provided with the distribution. 16# * Neither the name of the author of this software nor the name of 17# contributors to this software may be used to endorse or promote products 18# derived from this software without specific prior written consent. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30# POSSIBILITY OF SUCH DAMAGE. 31### 32 33VERBOSE = False 34 35def readPid(filename): 36 fd = open(filename) 37 try: 38 return int(fd.read().strip()) 39 finally: 40 fd.close() 41 42def isAlive(pid): 43 try: 44 os.kill(pid, 0) 45 return True 46 except OSError: 47 return False 48 49def debug(s): 50 if VERBOSE: 51 if not s.endswith(os.linesep): 52 s += os.linesep 53 sys.stdout.write(s) 54 55if __name__ == '__main__': 56 # XXX I wanted this for conf.version, but this will create directories. We 57 # really need to refactor conf so it either doesn't create directories, or 58 # so that static information (like the version) can be imported from 59 # somewhere else. 60 # import supybot.conf as conf 61 import os 62 import sys 63 import optparse 64 import subprocess 65 66 parser = optparse.OptionParser(usage='Usage: %prog [options]') 67 parser.add_option('', '--verbose', action='store_true', 68 help='Makes output verbose.') 69 parser.add_option('', '--botdir', 70 help='Determines what directory the bot resides in and ' 71 'should be started from.') 72 parser.add_option('', '--pidfile', 73 help='Determines what file to look in for the pid of ' 74 'the running bot. This should be relative to the ' 75 'given bot directory. Note that for this to actually ' 76 'work, you have to make a matching entry in the ' 77 'supybot.pidFile config in the supybot registry.') 78 parser.add_option('', '--supybot', default='supybot', 79 help='Determines where the supybot executable is ' 80 'located. If not given, assumes that supybot is ' 81 'in $PATH.') 82 parser.add_option('', '--conffile', 83 help='Determines what configuration file should be ' 84 'given to the supybot executable when (re)starting the ' 85 'bot.') 86 87 (options, args) = parser.parse_args() 88 VERBOSE = options.verbose 89 90 if args: 91 parser.error('Extra arguments given.') 92 if not options.botdir: 93 parser.error('No botdir given.') 94 if not options.pidfile: 95 parser.error('No pidfile given.') 96 if not options.conffile: 97 parser.error('No conffile given.') 98 99 os.chdir(options.botdir) 100 open(options.pidfile, 'a').close() 101 102 pid = None 103 try: 104 pid = readPid(options.pidfile) 105 debug('Found pidFile with proper pid contents of %s' % pid) 106 except ValueError as e: 107 foundBot = False 108 109 if pid is not None: 110 foundBot = isAlive(pid) 111 if foundBot: 112 debug('Pid %s is alive and belongs to us.' % pid) 113 else: 114 debug('Pid %s is not the bot.' % pid) 115 116 if not foundBot: 117 # First, we check if the pidfile is writable. If not, supybot will just exit, 118 # so we go ahead and refuse to start it. 119 try: 120 open(options.pidfile, 'r+') 121 except EnvironmentError as e: 122 debug('pidfile (%s) is not writable: %s' % (options.pidfile, e)) 123 sys.exit(-1) 124 debug('Bot not found, starting.') 125 cmdline = [options.supybot, '--daemon', options.conffile] 126 inst = subprocess.Popen(cmdline, close_fds=True, 127 stderr=subprocess.STDOUT, 128 stdin=None, stdout=subprocess.PIPE) 129 debug('Output from supybot: %r' % inst.stdout.read()) 130 ret = inst.wait() 131 debug('Bot started, command line %r returned %s.' % (' '.join(cmdline), 132 ret)) 133 sys.exit(ret) 134 else: 135 sys.exit(0) 136 137# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: 138