add a newline because we're cool
[synarere.git] / synarere.py
blob6251483c53257c3357686d82b17c12a3346c197c
1 #!/usr/bin/env python
3 # synarere -- a highly modular and stable IRC bot.
4 # Copyright (C) 2010 Michael Rodriguez.
5 # Rights to this code are documented in docs/LICENSE.
7 '''Main program. This does basic routines, and calls core functions.'''
9 # Import required Python modules.
10 import getopt, os, signal, sys
12 # Import required core modules.
13 from core import module, irc, logger, io, var, confparse, command
15 # Import required core function.
16 from core import shutdown
18 def ctcp_version(conn, (nick, user, host), message):
19 '''Handle VERSION requests.'''
21 conn.sendq.appendleft('NOTICE %s :\001VERSION synarere-%s\001' % (nick, var.version))
22 return
24 def ctcp_ping(conn, (nick, user, host), message):
25 '''Handle PING requests.'''
27 conn.sendq.appendleft('NOTICE %s :\001PING %s\001' % (nick, message))
28 return
30 def print_clo_help():
31 '''Output command line options and their meanings.'''
33 print '-c (--config) <config>: Specify the configuration file to use.'
34 print '-h (--help): Output this message.'
35 print '-n (--nofork): Do not fork into the background (will output log messages)'
36 print '-p (--pydebug): Start in the Python Debugger.'
37 print '-v (--version): Output version information.'
39 def main(argv=sys.argv[1:]):
40 '''Our entry point.'''
42 # Are we root?
43 if os.geteuid() == 0:
44 print >> sys.stderr, 'synarere: will not run as root for security reasons'
45 sys.exit(0)
47 # Parse command line options and parameter list.
48 try:
49 opts, args = getopt.getopt(argv, 'c:hnpv', ['config=', 'help', 'nofork', 'pydebug', 'version'])
50 except getopt.GetoptError, err:
51 print >> sys.stderr, '%s\n' % err
52 print_clo_help()
53 sys.exit(os.EX_USAGE)
55 for opt, arg in opts:
56 if opt in ('-c', '--config'):
57 var.config_file = arg
58 elif opt in ('-h', '--help'):
59 print_clo_help()
60 sys.exit(os.EX_OK)
61 elif opt in ('-n', '--nofork'):
62 var.fork = False
63 elif opt in ('-p', '--pydebug'):
64 import pdb
65 pdb.set_trace()
66 elif opt in ('-v', '--version'):
67 print 'synarere: version %s' % var.version
68 sys.exit(os.EX_OK)
70 # Attach signals to handlers.
71 signal.signal(signal.SIGHUP, lambda h: var.conf.rehash(True))
72 signal.signal(signal.SIGINT, lambda i: shutdown(signal.SIGINT, 'Caught SIGINT (terminal interrupt)'))
73 signal.signal(signal.SIGTERM, lambda t: shutdown(signal.SIGTERM, 'Caught SIGTERM'))
74 signal.signal(signal.SIGPIPE, signal.SIG_IGN)
75 signal.signal(signal.SIGALRM, signal.SIG_IGN)
76 signal.signal(signal.SIGCHLD, signal.SIG_IGN)
77 signal.signal(signal.SIGWINCH, signal.SIG_IGN)
78 signal.signal(signal.SIGTTIN, signal.SIG_IGN)
79 signal.signal(signal.SIGTTOU, signal.SIG_IGN)
80 signal.signal(signal.SIGTSTP, signal.SIG_IGN)
82 print 'synarere: version %s' % var.version
84 # Initialize the configuration parser.
85 try:
86 var.conf = confparse.ConfigParser(var.config_file)
87 except confparse.Exception, errstr:
88 print >> sys.stderr, 'synarere: configuration error for %s: %s' % (var.config_file, errstr)
89 sys.exit(os.EX_CONFIG)
91 # Check to see if we are already running.
92 try:
93 pid_file = open(var.conf.get('options', 'pidfile')[0], 'r')
95 try:
96 pid = pid_file.read()
98 if pid:
99 pid = int(pid)
101 try:
102 os.kill(pid, 0)
103 except OSError:
104 pass
106 print >> sys.stderr, 'synarere: an instance is already running'
107 sys.exit(os.EX_SOFTWARE)
108 finally:
109 pid_file.close()
110 except IOError:
111 pass
113 # Fork into the background.
114 if var.fork:
115 try:
116 pid = os.fork()
117 except OSError, e:
118 return (e.errno, e.strerror)
120 # This is the child process.
121 if pid == 0:
122 os.setsid()
124 # Now the child fork()'s a child in order to prevent acquisition
125 # of a controlling terminal.
126 try:
127 pid = os.fork()
128 except OSError, e:
129 return (e.errno, e.strerror)
131 # This is the second child process.
132 if pid == 0:
133 os.chdir(os.getcwd())
134 os.umask(0)
136 # This is the first child.
137 else:
138 print 'synarere: pid', pid
139 print 'synarere: running in background mode from:', os.getcwd()
140 os._exit(0)
141 else:
142 os._exit(0)
144 # Try to write the PID file.
145 try:
146 pid_file = open(var.conf.get('options', 'pidfile')[0], 'w')
147 pid_file.write(str(os.getpid()))
148 pid_file.close()
149 except IOError, e:
150 print >> sys.stderr, 'synarere: unable to write pid file:', os.strerror(e.args[0])
152 # Try to close all open file descriptors.
153 # If we cant find the max number, just close the first 256.
154 try:
155 maxfd = os.sysconf('SC_OPEN_MAX')
156 except (AttributeError, ValueError):
157 maxfd = 256
159 for fd in range(0, maxfd):
160 try:
161 os.close(fd)
162 except OSError:
163 pass
165 # Redirect the standard file descriptors to /dev/null.
166 os.open('/dev/null', os.O_RDONLY)
167 os.open('/dev/null', os.O_RDWR)
168 os.open('/dev/null', os.O_RDWR)
169 else:
170 print 'synarere: pid', os.getpid()
171 print 'synarere: running in foreground mode from:', os.getcwd()
173 # Initialize the logger.
174 logger.init()
176 # These have to be in the main file.
177 command.add('VERSION', ctcp_version, command.ctcp)
178 command.add('PING', ctcp_ping, command.ctcp)
180 # Load all modules listed in the configuration.
181 module.load_all()
183 # Connect to all IRC networks.
184 irc.connect_to_all()
186 # Start the loop.
187 io.io()
189 # This should NEVER happen.
190 shutdown(os.EX_SOFTWARE, 'Main loop exited (?)')
192 if __name__ == '__main__':
193 main()