commit
[synarere.git] / synarere.py
blob30b5985d6929daee9026c31a79b3a3d615867e33
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 on_sighup(signum, frame):
31 '''Handle SIGHUP. This will rehash the configuration.'''
33 var.conf.rehash(True)
35 def on_sigint(signum, frame):
36 '''Handle SIGINT. This will exit gracefully.'''
38 shutdown(signum, 'Caught SIGINT (terminal interrupt)')
40 def on_sigterm(signum, frame):
41 '''Handle SIGTERM. This will exit gracefully.'''
43 shutdown(signum, 'Caught SIGTERM')
45 def print_clo_help():
46 '''Output command line options and their meanings.'''
48 print '-c (--config) <config>: Specify the configuration file to use.'
49 print '-h (--help): Output this message.'
50 print '-n (--nofork): Do not fork into the background (will output log messages)'
51 print '-p (--pydebug): Start in the Python Debugger.'
52 print '-v (--version): Output version information.'
54 def main(argv=sys.argv[1:]):
55 '''Our entry point.'''
57 # Are we root?
58 if os.geteuid() == 0:
59 print >> sys.stderr, 'synarere: will not run as root for security reasons'
60 sys.exit(0)
62 # Parse command line options and parameter list.
63 try:
64 opts, args = getopt.getopt(argv, 'c:hnpv', ['config=', 'help', 'nofork', 'pydebug', 'version'])
65 except getopt.GetoptError, err:
66 print >> sys.stderr, '%s\n' % err
67 print_clo_help()
68 sys.exit(os.EX_USAGE)
70 for opt, arg in opts:
71 if opt in ('-c', '--config'):
72 var.config_file = arg
73 elif opt in ('-h', '--help'):
74 print_clo_help()
75 sys.exit(os.EX_OK)
76 elif opt in ('-n', '--nofork'):
77 var.fork = False
78 elif opt in ('-p', '--pydebug'):
79 import pdb
80 pdb.set_trace()
81 elif opt in ('-v', '--version'):
82 print 'synarere: version %s' % var.version
83 sys.exit(os.EX_OK)
85 # Attach signals to handlers.
86 signal.signal(signal.SIGHUP, on_sighup)
87 signal.signal(signal.SIGINT, on_sigint)
88 signal.signal(signal.SIGTERM, on_sigterm)
89 signal.signal(signal.SIGPIPE, signal.SIG_IGN)
90 signal.signal(signal.SIGALRM, signal.SIG_IGN)
91 signal.signal(signal.SIGCHLD, signal.SIG_IGN)
92 signal.signal(signal.SIGWINCH, signal.SIG_IGN)
93 signal.signal(signal.SIGTTIN, signal.SIG_IGN)
94 signal.signal(signal.SIGTTOU, signal.SIG_IGN)
95 signal.signal(signal.SIGTSTP, signal.SIG_IGN)
97 print 'synarere: version %s' % var.version
99 # Initialize the configuration parser.
100 try:
101 var.conf = confparse.ConfigParser(var.config_file)
102 except confparse.Exception, errstr:
103 print >> sys.stderr, 'synarere: configuration error for %s: %s' % (var.config_file, errstr)
104 sys.exit(os.EX_CONFIG)
106 # Check to see if we are already running.
107 try:
108 pid_file = open(var.conf.get('options', 'pidfile')[0], 'r')
110 try:
111 pid = pid_file.read()
113 if pid:
114 pid = int(pid)
116 try:
117 os.kill(pid, 0)
118 except OSError:
119 pass
121 print >> sys.stderr, 'synarere: an instance is already running'
122 sys.exit(os.EX_SOFTWARE)
123 finally:
124 pid_file.close()
125 except IOError:
126 pass
128 # Fork into the background.
129 if var.fork:
130 try:
131 pid = os.fork()
132 except OSError, e:
133 return (e.errno, e.strerror)
135 # This is the child process.
136 if pid == 0:
137 os.setsid()
139 # Now the child fork()'s a child in order to prevent acquisition
140 # of a controlling terminal.
141 try:
142 pid = os.fork()
143 except OSError, e:
144 return (e.errno, e.strerror)
146 # This is the second child process.
147 if pid == 0:
148 os.chdir(os.getcwd())
149 os.umask(0)
151 # This is the first child.
152 else:
153 print 'synarere: pid', pid
154 print 'synarere: running in background mode from:', os.getcwd()
155 os._exit(0)
156 else:
157 os._exit(0)
159 # Try to write the PID file.
160 try:
161 pid_file = open(var.conf.get('options', 'pidfile')[0], 'w')
162 pid_file.write(str(os.getpid()))
163 pid_file.close()
164 except IOError, e:
165 print >> sys.stderr, 'synarere: unable to write pid file:', os.strerror(e.args[0])
167 # Try to close all open file descriptors.
168 # If we cant find the max number, just close the first 256.
169 try:
170 maxfd = os.sysconf('SC_OPEN_MAX')
171 except (AttributeError, ValueError):
172 maxfd = 256
174 for fd in range(0, maxfd):
175 try:
176 os.close(fd)
177 except OSError:
178 pass
180 # Redirect the standard file descriptors to /dev/null.
181 os.open('/dev/null', os.O_RDONLY)
182 os.open('/dev/null', os.O_RDWR)
183 os.open('/dev/null', os.O_RDWR)
184 else:
185 print 'synarere: pid', os.getpid()
186 print 'synarere: running in foreground mode from:', os.getcwd()
188 # Initialize the logger.
189 logger.init()
191 # These have to be in the main file.
192 command.add('VERSION', ctcp_version, command.ctcp)
193 command.add('PING', ctcp_ping, command.ctcp)
195 # Load all modules listed in the configuration.
196 module.load_all()
198 # Connect to all IRC networks.
199 irc.connect_to_all()
201 # Start the loop.
202 io.io()
204 # This should NEVER happen.
205 shutdown(os.EX_SOFTWARE, 'Main loop exited (?)')
207 if __name__ == '__main__':
208 main()