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