mirror of
https://github.com/progval/Limnoria.git
synced 2025-04-30 07:21:10 -05:00
Move most of scripts/supybot's code to src/_main.py + add support for running a single loop iteration
So it can be ran without the main script, by an external program that imports it. Motivation: I'll use it with Pyoxide
This commit is contained in:
parent
e895444c8c
commit
0ae05f64d6
314
scripts/supybot
314
scripts/supybot
@ -34,38 +34,14 @@
|
||||
This is the main program to run Supybot.
|
||||
"""
|
||||
|
||||
import supybot
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import atexit
|
||||
import shutil
|
||||
import signal
|
||||
|
||||
if sys.version_info < (3, 4, 0):
|
||||
sys.stderr.write('This program requires Python 3.4 or later.')
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.exit(-1)
|
||||
|
||||
from io import StringIO # Import this after version check since this will fail on Python 2
|
||||
|
||||
def _termHandler(signalNumber, stackFrame):
|
||||
raise SystemExit('Signal #%s.' % signalNumber)
|
||||
|
||||
signal.signal(signal.SIGTERM, _termHandler)
|
||||
|
||||
import time
|
||||
import optparse
|
||||
import textwrap
|
||||
|
||||
started = time.time()
|
||||
|
||||
import supybot
|
||||
import supybot.utils as utils
|
||||
import supybot.registry as registry
|
||||
import supybot.questions as questions
|
||||
import supybot.ircutils as ircutils
|
||||
try:
|
||||
import supybot.i18n as i18n
|
||||
except ImportError:
|
||||
@ -82,295 +58,9 @@ except ImportError:
|
||||
|
||||
from supybot.version import version
|
||||
|
||||
def main():
|
||||
import supybot.conf as conf
|
||||
import supybot.world as world
|
||||
import supybot.drivers as drivers
|
||||
import supybot.schedule as schedule
|
||||
# We schedule this event rather than have it actually run because if there
|
||||
# is a failure between now and the time it takes the Owner plugin to load
|
||||
# all the various plugins, our registry file might be wiped. That's bad.
|
||||
interrupted = False
|
||||
when = conf.supybot.upkeepInterval()
|
||||
schedule.addPeriodicEvent(world.upkeep, when, name='upkeep', now=False)
|
||||
world.startedAt = started
|
||||
while world.ircs:
|
||||
try:
|
||||
drivers.run()
|
||||
except KeyboardInterrupt:
|
||||
if interrupted:
|
||||
# Interrupted while waiting for queues to clear. Let's clear
|
||||
# them ourselves.
|
||||
for irc in world.ircs:
|
||||
irc._reallyDie()
|
||||
continue
|
||||
else:
|
||||
interrupted = True
|
||||
log.info('Exiting due to Ctrl-C. '
|
||||
'If the bot doesn\'t exit within a few seconds, '
|
||||
'feel free to press Ctrl-C again to make it exit '
|
||||
'without flushing its message queues.')
|
||||
world.upkeep()
|
||||
for irc in world.ircs:
|
||||
quitmsg = conf.supybot.plugins.Owner.quitMsg() or \
|
||||
'Ctrl-C at console.'
|
||||
# Because we're quitting from the console, none of the
|
||||
# standard msg substitutions exist, and these will show as
|
||||
# raw strings by default. Substitute them here with
|
||||
# something meaningful instead.
|
||||
env = dict((key, '<console>')
|
||||
for key in ('who', 'nick', 'user', 'host'))
|
||||
quitmsg = ircutils.standardSubstitute(irc, None, quitmsg,
|
||||
env=env)
|
||||
irc.queueMsg(ircmsgs.quit(quitmsg))
|
||||
irc.die()
|
||||
except SystemExit as e:
|
||||
s = str(e)
|
||||
if s:
|
||||
log.info('Exiting due to %s', s)
|
||||
break
|
||||
except:
|
||||
try: # Ok, now we're *REALLY* paranoid!
|
||||
log.exception('Exception raised out of drivers.run:')
|
||||
except Exception as e:
|
||||
print('Exception raised in log.exception. This is *really*')
|
||||
print('bad. Hopefully it won\'t happen again, but tell us')
|
||||
print('about it anyway, this is a significant problem.')
|
||||
print('Anyway, here\'s the exception: %s' % \
|
||||
utils.gen.exnToString(e))
|
||||
except:
|
||||
print('Oh, this really sucks. Not only did log.exception')
|
||||
print('raise an exception, but freaking-a, it was a string')
|
||||
print('exception. People who raise string exceptions should')
|
||||
print('die a slow, painful death.')
|
||||
httpserver.stopServer()
|
||||
now = time.time()
|
||||
seconds = now - world.startedAt
|
||||
log.info('Total uptime: %s.', utils.gen.timeElapsed(seconds))
|
||||
(user, system, _, _, _) = os.times()
|
||||
log.info('Total CPU time taken: %.2f seconds.', user+system)
|
||||
log.info('No more Irc objects, exiting.')
|
||||
from supybot._main import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = optparse.OptionParser(usage='Usage: %prog [options] configFile',
|
||||
version='Limnoria %s running on Python %s' %
|
||||
(version, sys.version))
|
||||
parser.add_option('-P', '--profile', action='store_true', dest='profile',
|
||||
help='enables profiling')
|
||||
parser.add_option('-n', '--nick', action='store',
|
||||
dest='nick', default='',
|
||||
help='nick the bot should use')
|
||||
parser.add_option('-u', '--user', action='store',
|
||||
dest='user', default='',
|
||||
help='full username the bot should use')
|
||||
parser.add_option('-i', '--ident', action='store',
|
||||
dest='ident', default='',
|
||||
help='ident the bot should use')
|
||||
parser.add_option('-d', '--daemon', action='store_true',
|
||||
dest='daemon',
|
||||
help='Determines whether the bot will daemonize. '
|
||||
'This is a no-op on non-POSIX systems.')
|
||||
parser.add_option('', '--allow-default-owner', action='store_true',
|
||||
dest='allowDefaultOwner',
|
||||
help='Determines whether the bot will allow its '
|
||||
'defaultCapabilities not to include "-owner", thus '
|
||||
'giving all users the owner capability by default. '
|
||||
'This is a security risk since it allows anyone to run '
|
||||
'any command on your bot, so we advise not to use this.')
|
||||
parser.add_option('', '--allow-root', action='store_true',
|
||||
dest='allowRoot',
|
||||
help='Determines whether the bot will be allowed to run '
|
||||
'as root. This should not be used except in special '
|
||||
'circumstances, such as running inside a containerized '
|
||||
'environment.')
|
||||
parser.add_option('', '--debug', action='store_true', dest='debug',
|
||||
help='Determines whether some extra debugging stuff '
|
||||
'will be logged in this script.')
|
||||
parser.add_option('', '--disable-multiprocessing', action='store_true',
|
||||
dest='disableMultiprocessing',
|
||||
help='Disables multiprocessing stuff. May lead to '
|
||||
'vulnerabilities.')
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if os.name == 'posix':
|
||||
if (os.getuid() == 0 or os.geteuid() == 0) and not options.allowRoot:
|
||||
sys.stderr.write('Running as root is not supported by default (see --allow-root).')
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.exit(-1)
|
||||
|
||||
if len(args) > 1:
|
||||
parser.error("""Only one configuration file should be specified.""")
|
||||
elif not args:
|
||||
parser.error(utils.str.normalizeWhitespace("""It seems you've given me
|
||||
no configuration file. If you do have a configuration file, be sure to
|
||||
specify the filename. If you don't have a configuration file, read
|
||||
docs/GETTING_STARTED and follow the instructions."""))
|
||||
else:
|
||||
registryFilename = args.pop()
|
||||
try:
|
||||
# The registry *MUST* be opened before importing log or conf.
|
||||
registry.open_registry(registryFilename)
|
||||
shutil.copyfile(registryFilename, registryFilename + '.bak')
|
||||
except registry.InvalidRegistryFile as e:
|
||||
s = '%s in %s. Please fix this error and start supybot again.' % \
|
||||
(e, registryFilename)
|
||||
s = textwrap.fill(s)
|
||||
sys.stderr.write(s)
|
||||
sys.stderr.write(os.linesep)
|
||||
raise
|
||||
sys.exit(-1)
|
||||
except EnvironmentError as e:
|
||||
sys.stderr.write(str(e))
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.exit(-1)
|
||||
|
||||
i18n.getLocaleFromRegistryCache()
|
||||
|
||||
try:
|
||||
import supybot.log as log
|
||||
except supybot.registry.InvalidRegistryValue as e:
|
||||
# This is raised here because supybot.log imports supybot.conf.
|
||||
name = e.value._name
|
||||
errmsg = textwrap.fill('%s: %s' % (name, e),
|
||||
width=78, subsequent_indent=' '*len(name))
|
||||
sys.stderr.write(errmsg)
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.stderr.write('Please fix this error in your configuration file '
|
||||
'and restart your bot.')
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.exit(-1)
|
||||
import supybot.conf as conf
|
||||
import supybot.world as world
|
||||
i18n.import_conf()
|
||||
world.starting = True
|
||||
|
||||
def closeRegistry():
|
||||
# We only print if world.dying so we don't see these messages during
|
||||
# upkeep.
|
||||
logger = log.debug
|
||||
if world.dying:
|
||||
logger = log.info
|
||||
logger('Writing registry file to %s', registryFilename)
|
||||
registry.close(conf.supybot, registryFilename)
|
||||
logger('Finished writing registry file.')
|
||||
world.flushers.append(closeRegistry)
|
||||
world.registryFilename = registryFilename
|
||||
|
||||
nick = options.nick or conf.supybot.nick()
|
||||
user = options.user or conf.supybot.user()
|
||||
ident = options.ident or conf.supybot.ident()
|
||||
|
||||
networks = conf.supybot.networks()
|
||||
if not networks:
|
||||
questions.output("""No networks defined. Perhaps you should re-run the
|
||||
wizard?""", fd=sys.stderr)
|
||||
# XXX We should turn off logging here for a prettier presentation.
|
||||
sys.exit(-1)
|
||||
|
||||
if os.name == 'posix' and options.daemon:
|
||||
def fork():
|
||||
child = os.fork()
|
||||
if child != 0:
|
||||
if options.debug:
|
||||
print('Parent exiting, child PID: %s' % child)
|
||||
# We must us os._exit instead of sys.exit so atexit handlers
|
||||
# don't run. They shouldn't be dangerous, but they're ugly.
|
||||
os._exit(0)
|
||||
fork()
|
||||
os.setsid()
|
||||
# What the heck does this do? I wonder if it breaks anything...
|
||||
# ...It did. I don't know why, but it seems largely useless. It seems
|
||||
# to me reasonable that we should respect the user's umask.
|
||||
#os.umask(0)
|
||||
# Let's not do this for now (at least until I can make sure it works):
|
||||
# Actually, let's never do this -- we'll always have files open in the
|
||||
# bot directories, so they won't be able to be unmounted anyway.
|
||||
# os.chdir('/')
|
||||
fork()
|
||||
# Since this is the indicator that no writing should be done to stdout,
|
||||
# we'll set it to True before closing stdout et alii.
|
||||
conf.daemonized = True
|
||||
# Closing stdin shouldn't cause problems. We'll let it raise an
|
||||
# exception if it does.
|
||||
sys.stdin.close()
|
||||
# Closing these two might cause problems; we log writes to them as
|
||||
# level WARNING on upkeep.
|
||||
sys.stdout.close()
|
||||
sys.stderr.close()
|
||||
sys.stdout = StringIO()
|
||||
sys.stderr = StringIO()
|
||||
# We have to be really methodical here.
|
||||
os.close(0)
|
||||
os.close(1)
|
||||
os.close(2)
|
||||
fd = os.open('/dev/null', os.O_RDWR)
|
||||
os.dup2(fd, 0)
|
||||
os.dup2(fd, 1)
|
||||
os.dup2(fd, 2)
|
||||
signal.signal(signal.SIGHUP, signal.SIG_IGN)
|
||||
log.info('Completed daemonization. Current PID: %s', os.getpid())
|
||||
|
||||
# Stop setting our own umask. See comment above.
|
||||
#os.umask(077)
|
||||
|
||||
# Let's write the PID file. This has to go after daemonization, obviously.
|
||||
pidFile = conf.supybot.pidFile()
|
||||
if pidFile:
|
||||
try:
|
||||
fd = open(pidFile, 'w')
|
||||
pid = os.getpid()
|
||||
fd.write('%s%s' % (pid, os.linesep))
|
||||
fd.close()
|
||||
def removePidFile():
|
||||
try:
|
||||
os.remove(pidFile)
|
||||
except EnvironmentError as e:
|
||||
log.error('Could not remove pid file: %s', e)
|
||||
atexit.register(removePidFile)
|
||||
except EnvironmentError as e:
|
||||
log.critical('Error opening/writing pid file %s: %s', pidFile, e)
|
||||
sys.exit(-1)
|
||||
|
||||
conf.allowDefaultOwner = options.allowDefaultOwner
|
||||
world.disableMultiprocessing = options.disableMultiprocessing
|
||||
|
||||
if not os.path.exists(conf.supybot.directories.log()):
|
||||
os.mkdir(conf.supybot.directories.log())
|
||||
if not os.path.exists(conf.supybot.directories.conf()):
|
||||
os.mkdir(conf.supybot.directories.conf())
|
||||
if not os.path.exists(conf.supybot.directories.data()):
|
||||
os.mkdir(conf.supybot.directories.data())
|
||||
if not os.path.exists(conf.supybot.directories.data.tmp()):
|
||||
os.mkdir(conf.supybot.directories.tmp())
|
||||
|
||||
userdataFilename = os.path.join(conf.supybot.directories.conf(),
|
||||
'userdata.conf')
|
||||
# Let's open this now since we've got our directories setup.
|
||||
if not os.path.exists(userdataFilename):
|
||||
fd = open(userdataFilename, 'w')
|
||||
fd.write('\n')
|
||||
fd.close()
|
||||
registry.open_registry(userdataFilename)
|
||||
|
||||
import supybot.irclib as irclib
|
||||
import supybot.ircmsgs as ircmsgs
|
||||
import supybot.drivers as drivers
|
||||
import supybot.callbacks as callbacks
|
||||
import supybot.plugins.Owner as Owner
|
||||
|
||||
# These may take some resources, and it does not need to be run while boot, so
|
||||
# we import it as late as possible (but before plugins are loaded).
|
||||
import supybot.httpserver as httpserver
|
||||
|
||||
owner = Owner.Class()
|
||||
|
||||
if options.profile:
|
||||
import profile
|
||||
world.profiling = True
|
||||
profile.run('main()', '%s-%i.prof' % (nick, time.time()))
|
||||
else:
|
||||
main()
|
||||
|
||||
main()
|
||||
|
||||
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|
||||
|
372
src/_main.py
Normal file
372
src/_main.py
Normal file
@ -0,0 +1,372 @@
|
||||
###
|
||||
# Copyright (c) 2003-2004, Jeremiah Fincher
|
||||
# Copyright (c) 2009, James McCoy
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions, and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions, and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author of this software nor the name of
|
||||
# contributors to this software may be used to endorse or promote products
|
||||
# derived from this software without specific prior written consent.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
###
|
||||
|
||||
"""Main script to run Limnoria.
|
||||
This should only be imported by scripts/supybot."""
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import atexit
|
||||
import shutil
|
||||
import signal
|
||||
import platform
|
||||
|
||||
if sys.version_info < (3, 4, 0):
|
||||
sys.stderr.write('This program requires Python 3.4 or later.')
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.exit(-1)
|
||||
|
||||
from io import StringIO # Import this after version check since this will fail on Python 2
|
||||
|
||||
def _termHandler(signalNumber, stackFrame):
|
||||
raise SystemExit('Signal #%s.' % signalNumber)
|
||||
|
||||
signal.signal(signal.SIGTERM, _termHandler)
|
||||
|
||||
import time
|
||||
import optparse
|
||||
import textwrap
|
||||
|
||||
started = time.time()
|
||||
|
||||
import supybot
|
||||
import supybot.i18n as i18n
|
||||
import supybot.utils as utils
|
||||
import supybot.registry as registry
|
||||
import supybot.questions as questions
|
||||
import supybot.ircutils as ircutils
|
||||
from supybot.version import version
|
||||
|
||||
def _main(singleLoop):
|
||||
import supybot.log as log
|
||||
import supybot.conf as conf
|
||||
import supybot.world as world
|
||||
import supybot.ircmsgs as ircmsgs
|
||||
import supybot.drivers as drivers
|
||||
interrupted = False
|
||||
while world.ircs:
|
||||
try:
|
||||
drivers.run()
|
||||
except KeyboardInterrupt:
|
||||
if interrupted:
|
||||
# Interrupted while waiting for queues to clear. Let's clear
|
||||
# them ourselves.
|
||||
for irc in world.ircs:
|
||||
irc._reallyDie()
|
||||
continue
|
||||
else:
|
||||
interrupted = True
|
||||
log.info('Exiting due to Ctrl-C. '
|
||||
'If the bot doesn\'t exit within a few seconds, '
|
||||
'feel free to press Ctrl-C again to make it exit '
|
||||
'without flushing its message queues.')
|
||||
world.upkeep()
|
||||
for irc in world.ircs:
|
||||
quitmsg = conf.supybot.plugins.Owner.quitMsg() or \
|
||||
'Ctrl-C at console.'
|
||||
# Because we're quitting from the console, none of the
|
||||
# standard msg substitutions exist, and these will show as
|
||||
# raw strings by default. Substitute them here with
|
||||
# something meaningful instead.
|
||||
env = dict((key, '<console>')
|
||||
for key in ('who', 'nick', 'user', 'host'))
|
||||
quitmsg = ircutils.standardSubstitute(irc, None, quitmsg,
|
||||
env=env)
|
||||
irc.queueMsg(ircmsgs.quit(quitmsg))
|
||||
irc.die()
|
||||
except SystemExit as e:
|
||||
s = str(e)
|
||||
if s:
|
||||
log.info('Exiting due to %s', s)
|
||||
break
|
||||
except:
|
||||
try: # Ok, now we're *REALLY* paranoid!
|
||||
log.exception('Exception raised out of drivers.run:')
|
||||
except Exception as e:
|
||||
print('Exception raised in log.exception. This is *really*')
|
||||
print('bad. Hopefully it won\'t happen again, but tell us')
|
||||
print('about it anyway, this is a significant problem.')
|
||||
print('Anyway, here\'s the exception: %s' % \
|
||||
utils.gen.exnToString(e))
|
||||
except:
|
||||
print('Oh, this really sucks. Not only did log.exception')
|
||||
print('raise an exception, but freaking-a, it was a string')
|
||||
print('exception. People who raise string exceptions should')
|
||||
print('die a slow, painful death.')
|
||||
|
||||
if singleLoop:
|
||||
return
|
||||
httpserver.stopServer()
|
||||
now = time.time()
|
||||
seconds = now - world.startedAt
|
||||
log.info('Total uptime: %s.', utils.gen.timeElapsed(seconds))
|
||||
(user, system, _, _, _) = os.times()
|
||||
log.info('Total CPU time taken: %.2f seconds.', user+system)
|
||||
log.info('No more Irc objects, exiting.')
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser(usage='Usage: %prog [options] configFile',
|
||||
version='Limnoria %s running on Python %s' %
|
||||
(version, sys.version))
|
||||
parser.add_option('-P', '--profile', action='store_true', dest='profile',
|
||||
help='enables profiling')
|
||||
parser.add_option('-n', '--nick', action='store',
|
||||
dest='nick', default='',
|
||||
help='nick the bot should use')
|
||||
parser.add_option('-u', '--user', action='store',
|
||||
dest='user', default='',
|
||||
help='full username the bot should use')
|
||||
parser.add_option('-i', '--ident', action='store',
|
||||
dest='ident', default='',
|
||||
help='ident the bot should use')
|
||||
parser.add_option('-d', '--daemon', action='store_true',
|
||||
dest='daemon',
|
||||
help='Determines whether the bot will daemonize. '
|
||||
'This is a no-op on non-POSIX systems.')
|
||||
parser.add_option('', '--allow-default-owner', action='store_true',
|
||||
dest='allowDefaultOwner',
|
||||
help='Determines whether the bot will allow its '
|
||||
'defaultCapabilities not to include "-owner", thus '
|
||||
'giving all users the owner capability by default. '
|
||||
'This is a security risk since it allows anyone to run '
|
||||
'any command on your bot, so we advise not to use this.')
|
||||
parser.add_option('', '--allow-root', action='store_true',
|
||||
dest='allowRoot',
|
||||
help='Determines whether the bot will be allowed to run '
|
||||
'as root. This should not be used except in special '
|
||||
'circumstances, such as running inside a containerized '
|
||||
'environment.')
|
||||
parser.add_option('', '--debug', action='store_true', dest='debug',
|
||||
help='Determines whether some extra debugging stuff '
|
||||
'will be logged in this script.')
|
||||
parser.add_option('', '--disable-multiprocessing', action='store_true',
|
||||
dest='disableMultiprocessing',
|
||||
help='Disables multiprocessing stuff. May lead to '
|
||||
'vulnerabilities.')
|
||||
parser.add_option('', '--single-loop', action='store_true',
|
||||
dest='singleLoop',
|
||||
help='Do not use this unless you understand what it is.')
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if os.name == 'posix':
|
||||
if (os.getuid() == 0 or os.geteuid() == 0) and not options.allowRoot:
|
||||
sys.stderr.write('Running as root is not supported by default (see --allow-root).')
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.exit(-1)
|
||||
|
||||
if platform.system() != 'Emscripten' and options.singleLoop:
|
||||
sys.stderr.write(
|
||||
'Do not use --single-loop, unless it is from a web browser '
|
||||
'and you know what you are doing.')
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.exit(-1)
|
||||
|
||||
if len(args) > 1:
|
||||
parser.error("""Only one configuration file should be specified.""")
|
||||
elif not args:
|
||||
parser.error(utils.str.normalizeWhitespace("""It seems you've given me
|
||||
no configuration file. If you do have a configuration file, be sure to
|
||||
specify the filename. If you don't have a configuration file, read
|
||||
docs/GETTING_STARTED and follow the instructions."""))
|
||||
else:
|
||||
registryFilename = args.pop()
|
||||
try:
|
||||
# The registry *MUST* be opened before importing log or conf.
|
||||
registry.open_registry(registryFilename)
|
||||
shutil.copyfile(registryFilename, registryFilename + '.bak')
|
||||
except registry.InvalidRegistryFile as e:
|
||||
s = '%s in %s. Please fix this error and start supybot again.' % \
|
||||
(e, registryFilename)
|
||||
s = textwrap.fill(s)
|
||||
sys.stderr.write(s)
|
||||
sys.stderr.write(os.linesep)
|
||||
raise
|
||||
sys.exit(-1)
|
||||
except EnvironmentError as e:
|
||||
sys.stderr.write(str(e))
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.exit(-1)
|
||||
|
||||
i18n.getLocaleFromRegistryCache()
|
||||
|
||||
try:
|
||||
import supybot.log as log
|
||||
except supybot.registry.InvalidRegistryValue as e:
|
||||
# This is raised here because supybot.log imports supybot.conf.
|
||||
name = e.value._name
|
||||
errmsg = textwrap.fill('%s: %s' % (name, e),
|
||||
width=78, subsequent_indent=' '*len(name))
|
||||
sys.stderr.write(errmsg)
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.stderr.write('Please fix this error in your configuration file '
|
||||
'and restart your bot.')
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.exit(-1)
|
||||
import supybot.conf as conf
|
||||
import supybot.world as world
|
||||
i18n.import_conf()
|
||||
world.starting = True
|
||||
|
||||
def closeRegistry():
|
||||
# We only print if world.dying so we don't see these messages during
|
||||
# upkeep.
|
||||
logger = log.debug
|
||||
if world.dying:
|
||||
logger = log.info
|
||||
logger('Writing registry file to %s', registryFilename)
|
||||
registry.close(conf.supybot, registryFilename)
|
||||
logger('Finished writing registry file.')
|
||||
world.flushers.append(closeRegistry)
|
||||
world.registryFilename = registryFilename
|
||||
|
||||
nick = options.nick or conf.supybot.nick()
|
||||
user = options.user or conf.supybot.user()
|
||||
ident = options.ident or conf.supybot.ident()
|
||||
|
||||
networks = conf.supybot.networks()
|
||||
if not networks:
|
||||
questions.output("""No networks defined. Perhaps you should re-run the
|
||||
wizard?""", fd=sys.stderr)
|
||||
# XXX We should turn off logging here for a prettier presentation.
|
||||
sys.exit(-1)
|
||||
|
||||
if os.name == 'posix' and options.daemon:
|
||||
def fork():
|
||||
child = os.fork()
|
||||
if child != 0:
|
||||
if options.debug:
|
||||
print('Parent exiting, child PID: %s' % child)
|
||||
# We must us os._exit instead of sys.exit so atexit handlers
|
||||
# don't run. They shouldn't be dangerous, but they're ugly.
|
||||
os._exit(0)
|
||||
fork()
|
||||
os.setsid()
|
||||
# What the heck does this do? I wonder if it breaks anything...
|
||||
# ...It did. I don't know why, but it seems largely useless. It seems
|
||||
# to me reasonable that we should respect the user's umask.
|
||||
#os.umask(0)
|
||||
# Let's not do this for now (at least until I can make sure it works):
|
||||
# Actually, let's never do this -- we'll always have files open in the
|
||||
# bot directories, so they won't be able to be unmounted anyway.
|
||||
# os.chdir('/')
|
||||
fork()
|
||||
# Since this is the indicator that no writing should be done to stdout,
|
||||
# we'll set it to True before closing stdout et alii.
|
||||
conf.daemonized = True
|
||||
# Closing stdin shouldn't cause problems. We'll let it raise an
|
||||
# exception if it does.
|
||||
sys.stdin.close()
|
||||
# Closing these two might cause problems; we log writes to them as
|
||||
# level WARNING on upkeep.
|
||||
sys.stdout.close()
|
||||
sys.stderr.close()
|
||||
sys.stdout = StringIO()
|
||||
sys.stderr = StringIO()
|
||||
# We have to be really methodical here.
|
||||
os.close(0)
|
||||
os.close(1)
|
||||
os.close(2)
|
||||
fd = os.open('/dev/null', os.O_RDWR)
|
||||
os.dup2(fd, 0)
|
||||
os.dup2(fd, 1)
|
||||
os.dup2(fd, 2)
|
||||
signal.signal(signal.SIGHUP, signal.SIG_IGN)
|
||||
log.info('Completed daemonization. Current PID: %s', os.getpid())
|
||||
|
||||
# Stop setting our own umask. See comment above.
|
||||
#os.umask(077)
|
||||
|
||||
# Let's write the PID file. This has to go after daemonization, obviously.
|
||||
pidFile = conf.supybot.pidFile()
|
||||
if pidFile:
|
||||
try:
|
||||
fd = open(pidFile, 'w')
|
||||
pid = os.getpid()
|
||||
fd.write('%s%s' % (pid, os.linesep))
|
||||
fd.close()
|
||||
def removePidFile():
|
||||
try:
|
||||
os.remove(pidFile)
|
||||
except EnvironmentError as e:
|
||||
log.error('Could not remove pid file: %s', e)
|
||||
atexit.register(removePidFile)
|
||||
except EnvironmentError as e:
|
||||
log.critical('Error opening/writing pid file %s: %s', pidFile, e)
|
||||
sys.exit(-1)
|
||||
|
||||
conf.allowDefaultOwner = options.allowDefaultOwner
|
||||
world.disableMultiprocessing = options.disableMultiprocessing
|
||||
|
||||
if not os.path.exists(conf.supybot.directories.log()):
|
||||
os.mkdir(conf.supybot.directories.log())
|
||||
if not os.path.exists(conf.supybot.directories.conf()):
|
||||
os.mkdir(conf.supybot.directories.conf())
|
||||
if not os.path.exists(conf.supybot.directories.data()):
|
||||
os.mkdir(conf.supybot.directories.data())
|
||||
if not os.path.exists(conf.supybot.directories.data.tmp()):
|
||||
os.mkdir(conf.supybot.directories.tmp())
|
||||
|
||||
userdataFilename = os.path.join(conf.supybot.directories.conf(),
|
||||
'userdata.conf')
|
||||
# Let's open this now since we've got our directories setup.
|
||||
if not os.path.exists(userdataFilename):
|
||||
fd = open(userdataFilename, 'w')
|
||||
fd.write('\n')
|
||||
fd.close()
|
||||
registry.open_registry(userdataFilename)
|
||||
|
||||
import supybot.irclib as irclib
|
||||
import supybot.ircmsgs as ircmsgs
|
||||
import supybot.drivers as drivers
|
||||
import supybot.callbacks as callbacks
|
||||
import supybot.plugins.Owner as Owner
|
||||
|
||||
# These may take some resources, and it does not need to be run while boot, so
|
||||
# we import it as late as possible (but before plugins are loaded).
|
||||
import supybot.httpserver as httpserver
|
||||
|
||||
owner = Owner.Class()
|
||||
|
||||
# We schedule this event rather than have it actually run because if there
|
||||
# is a failure between now and the time it takes the Owner plugin to load
|
||||
# all the various plugins, our registry file might be wiped. That's bad.
|
||||
import supybot.schedule as schedule
|
||||
when = conf.supybot.upkeepInterval()
|
||||
schedule.addPeriodicEvent(world.upkeep, when, name='upkeep', now=False)
|
||||
world.startedAt = started
|
||||
|
||||
if options.profile:
|
||||
import profile
|
||||
world.profiling = True
|
||||
profile.run('main()', '%s-%i.prof' % (nick, time.time()))
|
||||
else:
|
||||
_main(options.singleLoop)
|
Loading…
x
Reference in New Issue
Block a user