]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - make/template/inspircd
Relax fd bounds checking
[user/henk/code/inspircd.git] / make / template / inspircd
1 %mode 0750
2 #!/usr/bin/env perl
3
4 #
5 # InspIRCd -- Internet Relay Chat Daemon
6 #
7 #   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
8 #
9 # This file is part of InspIRCd.  InspIRCd is free software: you can
10 # redistribute it and/or modify it under the terms of the GNU General Public
11 # License as published by the Free Software Foundation, version 2.
12 #
13 # This program is distributed in the hope that it will be useful, but WITHOUT
14 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
16 # details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21
22
23 use strict;
24 use POSIX;
25 use Fcntl;
26
27 my $basepath    =       "@BASE_DIR@";
28 my $confpath    =       "@CONFIG_DIR@/";
29 my $binpath     =       "@BINARY_DIR@";
30 my $runpath     =       "@BASE_DIR@";
31 my $datadir     =       "@DATA_DIR@";
32 my $valgrindlogpath     =       "$basepath/valgrindlogs";
33 my $executable  =       "inspircd";
34 my $version     =       "@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@+@VERSION_LABEL@";
35 my $uid = "@UID@";
36
37 if (!("--runasroot" ~~ @ARGV) && ($< == 0 || $> == 0)) {
38         if ($uid !~ /^\d+$/) {
39                 # Named UID, look it up
40                 $uid = getpwnam $uid;
41         }
42         if (!$uid) {
43                 die "Cannot find a valid UID to change to";
44         }
45         # drop root if we were configured with an ircd UID
46         $< = $uid;
47         $> = $uid;
48         if ($< == 0 || $> == 0) {
49                 die "Could not drop root: $!";
50         }
51 }
52
53 our($pid,$pidfile);
54 # Lets see what they want to do.. Set the variable (Cause i'm a lazy coder)
55 my $arg = shift(@ARGV);
56 my $conf;
57 for my $a (@ARGV)
58 {
59         if ($a =~ m/^--config=(.*)$/)
60         {
61                 $conf = $1;
62                 last;
63         }
64 }
65 if (!defined $conf) {
66         $conf = $confpath . "inspircd.conf";
67         push @ARGV, '--config='.$conf;
68 }
69
70 getpidfile($conf);
71
72 # System for naming script command subs:
73 # cmd_<name> - Normal command for use by users.
74 # dev_<name> - Developer commands.
75 # hid_<name> - Hidden commands (ie Cheese-Sandwich)
76 # Ideally command subs shouldn't return.
77
78 my $subname = $arg;
79 $subname =~ s/-/_/g;
80 my $sub = main->can("cmd_$subname") || main->can("dev_$subname") || main->can("hid_$subname");
81 if (!defined($sub))
82 {
83         print STDERR "Invalid command or none given.\n";
84         cmd_help();
85         exit 1;
86 }
87 else
88 {
89         $sub->(@ARGV);
90         exit 0;
91 }
92
93 sub cmd_help()
94 {
95         my @subs = grep { $_ =~ m/^(cmd|dev)_/ && defined(main->can($_)) } keys(%::);
96         my @cmds = grep /^cmd_/, @subs;
97         my @devs = grep /^dev_/, @subs;
98         local $_;
99         $_ =~ s/^(cmd|dev)_// foreach (@cmds, @devs);
100         $_ =~ s/_/-/g foreach (@cmds, @devs);
101         print STDERR "Usage: ./inspircd (" . join("|", @cmds) . ")\n";
102         print STDERR "Developer arguments: (" . join("|", @devs) . ")\n";
103         exit 0;
104 }
105
106 sub cmd_status()
107 {
108         if (getstatus() == 1) {
109                 my $pid = getprocessid();
110                 print "InspIRCd is running (PID: $pid)\n";
111                 exit();
112         } else {
113                 print "InspIRCd is not running. (Or PID File not found)\n";
114                 exit();
115         }
116 }
117
118 sub cmd_rehash()
119 {
120         if (getstatus() == 1) {
121                 my $pid = getprocessid();
122                 system("kill -HUP $pid >/dev/null 2>&1");
123                 print "InspIRCd rehashed (pid: $pid).\n";
124                 exit();
125         } else {
126                 print "InspIRCd is not running. (Or PID File not found)\n";
127                 exit();
128         }
129 }
130
131 sub cmd_cron()
132 {
133         if (getstatus() == 0) { goto &cmd_start(); }
134         exit();
135 }
136
137 sub cmd_version()
138 {
139         print "InspIRCd version: $version\n";
140         exit();
141 }
142
143 sub cmd_restart(@)
144 {
145         cmd_stop();
146         unlink($pidfile) if (-e $pidfile);
147         goto &cmd_start;
148 }
149
150 sub hid_cheese_sandwich()
151 {
152         print "Creating Cheese Sandwich..\n";
153         print "Done.\n";
154         exit();
155 }
156
157 sub cmd_start(@)
158 {
159         # Check to see its not 'running' already.
160         if (getstatus() == 1) { print "InspIRCd is already running.\n"; return 0; }
161         # If we are still alive here.. Try starting the IRCd..
162         chdir $runpath;
163         print "$binpath/$executable doesn't exist\n" and return 0 unless(-e "$binpath/$executable");
164         print "$binpath/$executable is not executable\n" and return 0 unless(-f "$binpath/$executable" && -x "$binpath/$executable");
165
166         exec "$binpath/$executable", @_;
167         die "Failed to start IRCd: $!\n";
168 }
169
170 sub dev_debug(@)
171 {
172         # Check to see its not 'running' already.
173         if (getstatus() == 1) { print "InspIRCd is already running.\n"; return 0; }
174
175         chdir $runpath;
176         print "$binpath/$executable doesn't exist\n" and return 0 unless(-e "$binpath/$executable");
177         print "$binpath/$executable is not executable\n" and return 0 unless(-f "$binpath/$executable" && -x "$binpath/$executable");
178
179         # Check we have gdb
180         checkgdb();
181
182         # If we are still alive here.. Try starting the IRCd..
183         exec 'gdb', "--command=$basepath/.gdbargs", '--args', "$binpath/$executable", qw(--nofork --debug), @_;
184         die "Failed to start GDB: $!\n";
185 }
186
187 sub dev_screendebug(@)
188 {
189         # Check to see its not 'running' already.
190         if (getstatus() == 1) { print "InspIRCd is already running.\n"; return 0; }
191
192         chdir $runpath;
193         print "$binpath/$executable doesn't exist\n" and return 0 unless(-e "$binpath/$executable");
194
195         #Check we have gdb
196         checkgdb();
197         checkscreen();
198
199         # If we are still alive here.. Try starting the IRCd..
200         print "Starting InspIRCd in `screen`, type `screen -r` when the ircd crashes to view the gdb output and get a backtrace.\n";
201         print "Once you're inside the screen session press ^C + d to re-detach from the session\n";
202         exec qw(screen -m -d gdb), "--command=$basepath/.gdbargs", '-args', "$binpath/$executable", qw(--nofork --debug --nolog), @_;
203         die "Failed to start screen: $!\n";
204 }
205
206 sub dev_valdebug(@)
207 {
208         # Check to see its not 'running' already.
209         if (getstatus() == 1) { print "InspIRCd is already running.\n"; return 0; }
210
211         chdir $runpath;
212         print "$binpath/$executable doesn't exist\n" and return 0 unless(-e "$binpath/$executable");
213         print "$binpath/$executable is not executable\n" and return 0 unless(-f "$binpath/$executable" && -x "$binpath/$executable");
214
215         # Check we have valgrind and gdb
216         checkvalgrind();
217         checkgdb();
218
219         # If we are still alive here.. Try starting the IRCd..
220         # May want to do something with these args at some point: --suppressions=.inspircd.sup --gen-suppressions=yes
221         # Could be useful when we want to stop it complaining about things we're sure aren't issues.
222         exec qw(valgrind -v --tool=memcheck --leak-check=yes --db-attach=yes --num-callers=10), "$binpath/$executable", qw(--nofork --debug --nolog), @_;
223         die "Failed to start valgrind: $!\n";
224 }
225
226 sub dev_valdebug_unattended(@)
227 {
228         # NOTE: To make sure valgrind generates coredumps, set soft core limit in /etc/security/limits.conf to unlimited
229         # Check to see its not 'running' already.
230         if (getstatus() == 1) { print "InspIRCd is already running.\n"; return 0; }
231
232         chdir $runpath;
233         print "$binpath/$executable doesn't exist\n" and return 0 unless(-e "$binpath/$executable");
234         print "$binpath/$executable is not executable\n" and return 0 unless(-f "$binpath/$executable" && -x "$binpath/$executable");
235
236         # Check we have valgrind and gdb
237         checkvalgrind();
238         checkgdb();
239
240         # If we are still alive here.. Try starting the IRCd..
241         #
242         # NOTE: Saving the debug log (redirected stdout), while useful, is a potential security risk AND one hell of a spacehog. DO NOT SAVE THIS WHERE EVERYONE HAS ACCESS!
243         # Redirect stdout to /dev/null if you're worried about the security.
244         #
245         my $pid = fork;
246         if ($pid == 0) {
247                 POSIX::setsid();
248                 -d $valgrindlogpath or mkdir $valgrindlogpath or die "Cannot create $valgrindlogpath: $!\n";
249                 -e "$binpath/valgrind.sup" or do { open my $f, '>', "$binpath/valgrind.sup"; };
250                 my $suffix = strftime("%Y%m%d-%H%M%S", localtime(time)) . ".$$";
251                 open STDIN, '<', '/dev/null' or die "Can't redirect STDIN to /dev/null: $!\n";
252                 sysopen STDOUT, "$valgrindlogpath/out.$suffix", O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND, 0600 or die "Can't open $valgrindlogpath/out.$suffix: $!\n";
253                 sysopen STDERR, "$valgrindlogpath/valdebug.$suffix", O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND, 0666 or die "Can't open $valgrindlogpath/valdebug.$suffix: $!\n";
254         # May want to do something with these args at some point: --suppressions=.inspircd.sup --gen-suppressions=yes
255         # Could be useful when we want to stop it complaining about things we're sure aren't issues.
256                 exec qw(valgrind -v --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=15 --track-fds=yes),
257                         "--suppressions=$binpath/valgrind.sup", qw(--gen-suppressions=all),
258                         qw(--leak-resolution=med --time-stamp=yes --log-fd=2 --),
259                         "$binpath/$executable", qw(--nofork --debug --nolog), @_;
260                 die "Can't execute valgrind: $!\n";
261         }
262 }
263
264 sub dev_screenvaldebug(@)
265 {
266         # Check to see its not 'running' already.
267         if (getstatus() == 1) { print "InspIRCd is already running.\n"; return 0; }
268
269         chdir $runpath;
270         print "$binpath/$executable doesn't exist\n" and return 0 unless(-e "$binpath/$executable");
271         print "$binpath/$executable is not executable\n" and return 0 unless(-f "$binpath/$executable" && -x "$binpath/$executable");
272
273         #Check we have gdb
274         checkvalgrind();
275         checkgdb();
276         checkscreen();
277
278         # If we are still alive here.. Try starting the IRCd..
279         print "Starting InspIRCd in `screen`, type `screen -r` when the ircd crashes to view the valgrind and gdb output and get a backtrace.\n";
280         print "Once you're inside the screen session press ^C + d to re-detach from the session\n";
281         exec qw(screen -m -d valgrind -v --tool=memcheck --leak-check=yes --db-attach=yes --num-callers=10), "$binpath/$executable", qw(--nofork --debug --nolog), @_;
282         die "Failed to start screen: $!\n";
283 }
284
285 sub cmd_stop()
286 {
287         if (getstatus() == 0) { print "InspIRCd is not running. (Or PID File not found)\n"; return 0; }
288         # Get to here, we have something to kill.
289         my $pid = getprocessid();
290         print "Stopping InspIRCd (pid: $pid)...\n";
291         my $maxwait = (`ps -o command $pid` =~ /valgrind/i) ? 90 : 15;
292         kill TERM => $pid or die "Cannot terminate IRCd: $!\n";
293         for (1..$maxwait) {
294                 sleep 1;
295                 if (getstatus() == 0) {
296                         print "InspIRCd Stopped.\n";
297                         return;
298                 }
299         }
300         print "InspIRCd not dying quietly -- forcing kill\n";
301         kill KILL => $pid;
302         return 0;
303 }
304
305 ###
306 # Generic Helper Functions.
307 ###
308
309 # GetPidfile Version 2 - Now With Include Support..
310 # I beg for months for include support in insp, then..
311 # when it is added, it comes around and BITES ME IN THE ASS,
312 # because i then have to code support into this script.. Evil.
313
314 # Craig got bitten in the ass again --
315 # in 1.1 beta the include file is manditory, therefore
316 # if we cant find it, default to %conf%/inspircd.pid.
317 # Note, this also contains a fix for when the pid file is
318 # defined, but defined in a comment (line starts with #)
319 # -- Brain
320
321 my %filesparsed;
322
323 sub getpidfile
324 {
325         my ($file) = @_;
326         # Before we start, do we have a PID already? (Should never occur)
327         if ($pid ne "") {
328                 return;
329         }
330         # Are We using a relative path?
331         if ($file !~ /^\//) {
332                 # Convert it to a full path.
333                 $file = $runpath .'/'. $file;
334         }
335
336         # Have we checked this file before?
337         return if $filesparsed{$file};
338         $filesparsed{$file} = 1;
339
340         # Open the File..
341         open INFILE, '<', $file or return;
342         # Grab entire file contents..
343         my(@lines) = <INFILE>;
344         # Close the file
345         close INFILE;
346
347         # remove trailing spaces
348         chomp(@lines);
349         for my $i (@lines) {
350                 # clean it up
351                 $i =~ s/[^=]+=\s(.*)/\1/;
352                 # Does this file have a pid?
353                 if (($i =~ /<pid file=\"(\S+)\">/i) && ($i !~ /^#/))
354                 {
355                         # Set the PID file and return.
356                         $pidfile = $1;
357                         if (-f $pidfile)
358                         {
359                                 return;
360                         }
361                         elsif (-f "$runpath/$pidfile")
362                         {
363                                 $pidfile = "$runpath/$pidfile";
364                                 return;
365                         }
366                         return;
367                 }
368         }
369
370
371         # If we get here, NO PID FILE! -- Check for includes
372         for my $i (@lines) {
373                 $i =~ s/[^=]+=\s(.*)/\1/;
374                 if (($i =~ s/\<include file=\"(.+?)\"\>//i) && ($i !~ /^#/))
375                 {
376                         # Decend into that file, and check for PIDs.. (that sounds like an STD ;/)
377                         getpidfile($1);
378                         # Was a PID found?
379                         if ($pidfile ne "") {
380                                 # Yes, Return.
381                                 return;
382                         }
383                 }
384         }
385
386         # End of includes / No includes found. Using default.
387         $pidfile = $datadir . "/inspircd.pid";
388 }
389
390 sub getstatus {
391         my $pid = getprocessid();
392         return 0 if $pid == 0;
393         return kill 0, $pid;
394 }
395
396
397 sub getprocessid {
398         my $pid = 0;
399         open PIDFILE, '<', $pidfile or return 0;
400         while(<PIDFILE>)
401         {
402                 /^(\d+)$/ and $pid = $1;
403         }
404         close PIDFILE;
405         return $pid;
406 }
407
408 sub checkvalgrind
409 {
410         unless(`valgrind --version`)
411         {
412                 print "Couldn't start valgrind: $!\n";
413                 exit;
414         }
415 }
416
417 sub checkgdb
418 {
419         unless(`gdb --version`)
420         {
421                 print "Couldn't start gdb: $!\n";
422                 exit;
423         }
424 }
425
426 sub checkscreen
427 {
428         unless(`screen --version`)
429         {
430                 print "Couldn't start screen: $!\n";
431                 exit;
432         }
433 }