1 /*
2 * Copyright (C) 1998 Bjorn Borud <borud@guardian.no>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 1, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 *
18 */
19
20 static const volatile char rcsid[] = "@(#)$Id: ircdwatch.c,v 1.10 2004/10/02 01:20:43 chopin Exp $";
21
22 #include <stdio.h>
23 #include <stdlib.h> /* atol() */
24 #include <unistd.h> /* fork() exec() */
25 #include <sys/types.h>
26 #include <sys/stat.h> /* stat() */
27 #include <signal.h>
28 #include <syslog.h>
29 #include <string.h> /* strncmp() */
30 #include <time.h>
31
32 #include "os.h"
33 #include "config.h"
34
35 /*
36 * Try and find the correct name to use with getrlimit() for setting
37 * the max. number of files allowed to be open by this process.
38 * (borrowed from ircd/s_bsd.c)
39 */
40 #ifdef RLIMIT_FDMAX
41 # define RLIMIT_FD_MAX RLIMIT_FDMAX
42 #else
43 # ifdef RLIMIT_NOFILE
44 # define RLIMIT_FD_MAX RLIMIT_NOFILE
45 # else
46 # ifdef RLIMIT_OPEN_MAX
47 # define RLIMIT_FD_MAX RLIMIT_OPEN_MAX
48 # else
49 # undef RLIMIT_FD_MAX
50 # endif
51 # endif
52 #endif
53
54 #define PID_LEN 7 /* overkill, but hey */
55 #define MAX_OPEN_FILEDESCRIPTORS_WILD_GUESS 256
56
57 static int want_to_quit = 0;
58
finalize(int i)59 static void finalize(int i)
60 {
61 #ifdef IRCDWATCH_USE_SYSLOG
62 syslog(LOG_NOTICE, "ircdwatch daemon exiting");
63 closelog();
64 #endif
65 exit(i);
66 }
67
68 #if 0 /* seems unused */
69 static int daemonize(void)
70 {
71 pid_t pid;
72 int i;
73 int open_max;
74
75 #ifdef RLIMIT_FD_MAX
76 struct rlimit rlim;
77
78 if (getrlimit(RLIMIT_FD_MAX, &rlim) != 0) {
79 perror("getrlimit");
80 finalize(1);
81 }
82
83 /* if we get a lame answer we just take a wild guess */
84 if (rlim.rlim_max == 0) {
85 open_max = MAX_OPEN_FILEDESCRIPTORS_WILD_GUESS;
86 }
87
88 open_max = rlim.rlim_max;
89
90 #else
91
92 /* no RLIMIT_FD_MAX? take a wild guess */
93 open_max = MAX_OPEN_FILEDESCRIPTORS_WILD_GUESS;
94
95 #endif
96
97 pid = fork();
98 if (pid < 0) {
99 perror("fork");
100 exit(1);
101 }
102
103 /* parent process dies */
104 if (pid != 0) {
105 exit(0);
106 }
107
108 setsid();
109 umask(0);
110
111 /* close file descriptors */
112 for (i=0; i < open_max; i++) {
113 close(i);
114 }
115
116 return(0);
117 }
118 #endif
119
sig_handler(int signo)120 static void sig_handler (int signo)
121 {
122 if (signo == SIGHUP) {
123 want_to_quit = 1;
124 return;
125 }
126
127 if (signo == SIGALRM) {
128
129 #ifndef POSIX_SIGNALS
130 (void)signal(SIGALRM, &sig_handler);
131 #endif
132
133 return;
134 }
135 }
136
137
set_up_signals(void)138 static void set_up_signals(void)
139 {
140 #ifdef POSIX_SIGNALS
141 struct sigaction act;
142
143 act.sa_handler = sig_handler;
144 sigemptyset(&act.sa_mask);
145 act.sa_flags = 0;
146
147 if (sigaction(SIGHUP, &act, NULL) < 0) {
148 perror("sigaction");
149 }
150
151 if (sigaction(SIGALRM, &act, NULL) < 0) {
152 perror("sigaction");
153 }
154 #else
155 (void)signal(SIGHUP, &sig_handler);
156 (void)signal(SIGALRM, &sig_handler);
157 #endif
158
159 /* ignore it if child processes die */
160 signal(SIGCHLD, SIG_IGN);
161 }
162
163 #if 0 /* seems unused */
164 static int write_my_pid(void)
165 {
166 FILE *f;
167
168 f = fopen(IRCDWATCH_PID_FILENAME, "w");
169 if (f == NULL) {
170 return(-1);
171 }
172
173 fprintf(f, "%d\n", (int) getpid());
174 fclose(f);
175
176 return(0);
177 }
178 #endif
179
180
181 #ifdef IRCDWATCH_HUP_ON_CONFIG_CHANGE
file_modified(char * s)182 static int file_modified(char *s)
183 {
184 struct stat st;
185
186 if (stat(s, &st) < 0) {
187 return(-1);
188 }
189 return(st.st_ctime);
190 }
191
192 #endif
193
spawn(char * cmd)194 static int spawn (char *cmd)
195 {
196 pid_t pid;
197
198 pid = fork();
199
200 if (pid == -1) {
201 #ifdef IRCDWATCH_USE_SYSLOG
202 syslog(LOG_ERR, "spawn() unable to fork, errno=%d", errno);
203 #endif
204 return(-1);
205 }
206
207 if (pid == 0) {
208 execl("/bin/sh", "sh", "-c", cmd, (char *) NULL);
209 _exit(127);
210 }
211 return(0);
212 }
213
read_pid(char * pid_filename)214 static int read_pid(char *pid_filename)
215 {
216 FILE *f;
217 char pidbuf[PID_LEN];
218 pid_t pid;
219
220 f = fopen(pid_filename, "r");
221 if (f == NULL) {
222 #ifdef IRCDWATCH_USE_SYSLOG
223 syslog(LOG_ERR, "unable to fopen() %s: %s", pid_filename, strerror(errno));
224 #endif
225 return(-1);
226 }
227
228 if (fgets(pidbuf, PID_LEN, f) != NULL) {
229 pid = atol(pidbuf);
230 } else {
231 #ifdef IRCDWATCH_USE_SYSLOG
232 syslog(LOG_ERR, "fgets() %s: %s", pid_filename, strerror(errno));
233 #endif
234 fclose(f);
235 return(-1);
236 }
237
238 fclose(f);
239 return(pid);
240 }
241
file_exists(char * s)242 static int file_exists (char *s)
243 {
244 struct stat st;
245 if ((stat(s, &st) < 0) && (errno == ENOENT)) {
246 return(0);
247 }
248 return(1);
249 }
250
251 /* yeah, I'll get around to these in some later version */
252
253 #if 0 /* seems unused */
254 static int file_readable (char *s)
255 {
256 return(access(s, R_OK) == 0);
257 }
258
259 static int file_writable (char *s)
260 {
261 return(access(s, W_OK) == 0);
262 }
263 #endif
264
file_executable(char * s)265 static int file_executable (char *s)
266 {
267 int rc;
268 rc = (access(IRCD_PATH, X_OK) == 0);
269 return rc;
270 }
271
verify_pid(int pid)272 static int verify_pid(int pid)
273 {
274 int res;
275
276 res = kill(pid, 0);
277 if (res < 0 && errno == EPERM) {
278 fprintf(stderr, "Not process owner\n");
279 exit(1);
280 }
281
282 return(res == 0);
283 }
284
ircdwatch_running(void)285 static int ircdwatch_running (void)
286 {
287 int pid;
288
289 if (file_exists(IRCDWATCH_PID_FILENAME)) {
290 pid = read_pid(IRCDWATCH_PID_FILENAME);
291 if (pid > 0) {
292 return(verify_pid(pid) == 1);
293 } else {
294 return(-1);
295 }
296 }
297 return(0);
298 }
299
ircd_running(void)300 static int ircd_running (void)
301 {
302 int pid;
303
304 if (file_exists(IRCDPID_PATH)) {
305 pid = read_pid(IRCDPID_PATH);
306 if (pid > 0) {
307 return(verify_pid(pid) == 1);
308 } else {
309 return(-1);
310 }
311 }
312 return(0);
313 }
314
315 #ifdef IRCDWATCH_HUP_ON_CONFIG_CHANGE
hup_ircd(void)316 static void hup_ircd (void)
317 {
318 int pid;
319 int res;
320
321 if (file_exists(IRCDPID_PATH)) {
322 pid = read_pid(IRCDPID_PATH);
323 if (pid > 0) {
324 res = kill(pid, SIGHUP);
325 if (res < 0 && errno == EPERM) {
326 #ifdef IRCDWATCH_USE_SYSLOG
327 syslog(LOG_ERR, "not allowed to send SIGHUP to ircd");
328 #endif
329 finalize(1);
330 }
331 }
332 }
333 }
334 #endif
335
336
daemon_run(void)337 static void daemon_run (void)
338 {
339 int i;
340 #ifdef IRCDWATCH_HUP_ON_CONFIG_CHANGE
341 int last_config_time = 0;
342 #endif
343
344 /* is ircdwatch already running? */
345 i = ircdwatch_running();
346 if (i == -1) {
347 /* unable to open pid file. wrong user? */
348 fprintf(stderr, "ircdwatch pid file exists but is unreadable\n");
349 exit(1);
350 } else if (i) {
351 fprintf(stderr, "ircdwatch is already running\n");
352 exit(0);
353 }
354
355 /* is ircd running? */
356 i = ircd_running();
357 if (i == -1) {
358 /* unable to open pid file. wrong user? */
359 fprintf(stderr, "ircdwatch pid file exists but is unreadable\n");
360 exit(1);
361 } else if (!i) {
362 fprintf(stderr, "ircd not running. attempting to start ircd...\n");
363 if (file_exists(IRCD_PATH)) {
364 if (file_executable(IRCD_PATH)) {
365 spawn(IRCD_PATH);
366 } else {
367 fprintf(stderr, "%s not executable\n", IRCD_PATH);
368 exit(1);
369 }
370 } else {
371 fprintf(stderr, "%s does not exist\n", IRCD_PATH);
372 exit(1);
373 }
374 }
375
376 set_up_signals();
377 closelog();
378 /* daemonize(); */
379
380 #ifdef IRCDWATCH_USE_SYSLOG
381 openlog(IRCDWATCH_SYSLOG_IDENT,
382 IRCDWATCH_SYSLOG_OPTIONS,
383 IRCDWATCH_SYSLOG_FACILITY);
384
385 syslog(LOG_NOTICE, "starting ircdwatch daemon");
386 #endif
387
388 alarm(IRCDWATCH_POLLING_INTERVAL);
389 pause();
390
391 while (!want_to_quit) {
392 if (! ircd_running() ) {
393
394 #ifdef IRCDWATCH_USE_SYSLOG
395 syslog(LOG_ERR, "spawning %s", IRCD_PATH);
396 #endif
397
398 spawn(IRCD_PATH);
399 }
400
401 #ifdef IRCDWATCH_HUP_ON_CONFIG_CHANGE
402 i = file_modified(IRCDCONF_PATH);
403 if (i != -1) {
404
405 if (last_config_time == 0) {
406 last_config_time = i;
407 }
408
409 else if (i > last_config_time) {
410 last_config_time = i;
411 hup_ircd();
412
413 #ifdef IRCDWATCH_USE_SYSLOG
414 syslog(LOG_NOTICE, "config change, HUPing ircd");
415 #endif
416
417 }
418 }
419 #endif
420
421 alarm(IRCDWATCH_POLLING_INTERVAL);
422 pause();
423 }
424 return;
425 }
426
kill_ircd(void)427 static void kill_ircd (void)
428 {
429 int pid;
430 int res;
431
432 if (file_exists(IRCDPID_PATH)) {
433 pid = read_pid(IRCDPID_PATH);
434 if (pid > 0) {
435 res = kill(pid, SIGTERM);
436 if (res < 0) {
437 perror("ircd kill");
438 finalize(1);
439 } else {
440 fprintf(stderr, "killed ircd\n");
441 }
442 }
443 } else {
444 fprintf(stderr, "File %s does not exist\n", IRCDPID_PATH);
445 }
446 }
447
kill_ircdwatch(void)448 static void kill_ircdwatch (void)
449 {
450 int pid;
451 int res;
452
453 if (file_exists(IRCDWATCH_PID_FILENAME)) {
454 pid = read_pid(IRCDWATCH_PID_FILENAME);
455 if (pid > 0) {
456 res = kill(pid, SIGHUP);
457 if (res < 0) {
458 perror("ircdwatch kill");
459 finalize(1);
460 } else {
461 fprintf(stderr, "Sent HUP to ircdwatch. it will die soon...\n");
462 }
463 }
464 } else {
465 fprintf(stderr, "File %s does not exist\n", IRCDWATCH_PID_FILENAME);
466 }
467 }
468
469
usage(void)470 static void usage (void)
471 {
472 fprintf(stderr,"\n\
473 Usage:\n\
474 ircdwatch [--kill | --rest | --help]\n\
475 \n\
476 --kill, stop both ircdwatch and ircd\n\
477 --rest, stop ircdwatch but let ircd alone\n\
478 --help, display this text\n\
479 \n\
480 %s\n", rcsid);
481 }
482
483 int
main(int argc,char * argv[])484 main (int argc, char *argv[])
485 {
486
487 #ifdef IRCDWATCH_USE_SYSLOG
488 openlog(IRCDWATCH_SYSLOG_IDENT,
489 IRCDWATCH_SYSLOG_OPTIONS,
490 IRCDWATCH_SYSLOG_FACILITY);
491 #endif
492
493 if (argc > 1) {
494 if (strncmp(argv[1], "--rest", 6) == 0) {
495 kill_ircdwatch();
496 exit(0);
497 }
498
499 if (strncmp(argv[1], "--kill", 6) == 0) {
500 kill_ircdwatch();
501 kill_ircd();
502 exit(0);
503 }
504
505 usage();
506 exit(0);
507 }
508
509 daemon_run();
510 finalize(0);
511 return 0;
512 }
513