1 /* Copyright (C) 2007-2009 Hendrik Baecker <andurin@process-zero.de>
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 as
5 * published by the Free Software Foundation;
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
15 */
16
17 #include "../include/config.h"
18 #include "../include/pnp.h"
19
20 typedef void (*sighandler_t)(int);
21
22 void *processfile(void *);
23 static void *exit_handler_mem(void *);
24 extern int process_arguments(int, char **);
25 extern void process_configfile(char *);
26 extern void check_sig(int);
27 extern int prepare_vars(void);
28 extern int drop_privileges(char *, char *);
29 extern sighandler_t handle_signal(int, sighandler_t);
30 extern int is_file(const struct dirent *d);
31 extern int check_needed_config_options();
32
33 double getload(int);
34
35 static int thread_counter = 0;
36 int max_threads = 5;
37 int daemon_mode = FALSE;
38 int use_syslog = TRUE;
39 int loglevel = 0;
40 int max_logfile_size = 10485760; /* default 10Mbyte */
41 int use_load_threshold = FALSE;
42 int we_should_stop = FALSE;
43 int sleeptime = 15;
44 int identmyself = TRUE;
45 double load_threshold = 10.0;
46
47 extern int sighup_detected;
48
49 char *command, *command_args, *user, *group, *pidfile;
50 char *macro_x[CONFIG_OPT_COUNT];
51 char *log_file, *log_type;
52 char *config_file = NULL;
53
54 const char *directory = NULL;
55 const char progname[5] = "npcd";
56
start_daemon(const char * log_name,int facility)57 static void start_daemon(const char *log_name, int facility) {
58 int i;
59 pid_t pid;
60
61 /* Kill parent after for to get a waise */
62 if ((pid = fork()) != 0)
63 exit(EXIT_SUCCESS);
64
65 /* When dropping privileges from root, the `setgroups` call will
66 * remove any extraneous groups. If we don't call this, then
67 * even though our uid has dropped, we may still have groups
68 * that enable us to do super-user things. This will fail if we
69 * aren't root, so don't bother checking the return value, this
70 * is just done as an optimistic privilege dropping function.
71 */
72 setgroups(0, NULL);
73
74 /* Get this waise to sessionleader */
75 if (setsid() < 0) {
76 printf("%s could not get sessionleader\n", log_name);
77 exit(EXIT_FAILURE);
78 }
79
80 /* Ignore SIGHUP */
81 handle_signal(SIGHUP, SIG_IGN);
82
83 /* terminate child */
84 if ((pid = fork()) != 0)
85 exit(EXIT_SUCCESS);
86
87 /* for core dump handling and better unmounting behavior */
88 /* Its return should not be ignored.
89 * npcd.c:79: warning: ignoring return value of ‘chdir’ */
90 if (chdir("/") != 0)
91 exit(EXIT_FAILURE);
92
93 /* change umask to defined value - be independet from parent umask */
94 umask(002);
95
96 /* close all possible file handles */
97 for (i = sysconf(_SC_OPEN_MAX); i > 0; i--)
98 close(i);
99
100 /* to hear the daemon you are calling... use syslog */
101 if (use_syslog == TRUE && log_name != NULL)
102 openlog(log_name, LOG_PID | LOG_CONS | LOG_NDELAY, facility);
103
104 /* close existing stdin, stdout, stderr */
105 close(0);
106 close(1);
107 close(2);
108
109 /* re-open stdin, stdout, stderr with known values */
110 open("/dev/null",O_RDONLY);
111 open("/dev/null",O_WRONLY);
112 open("/dev/null",O_WRONLY);
113
114 }
115
main(int argc,char ** argv)116 int main(int argc, char **argv) {
117
118 int i = 0;
119 int filecounter = 0, pthread_ret = 0;
120 double load;
121 char buffer[MAX_LOGMESSAGE_SIZE];
122
123 FILE *fppid = NULL;
124
125 struct dirent **namelist;
126
127 load = 0.0;
128
129 if (process_arguments(argc, argv) == EXIT_FAILURE)
130 exit(EXIT_FAILURE);
131
132 process_configfile(config_file);
133
134 if (loglevel == -1) {
135 printf("DEBUG: Config File = %s\n", config_file);
136 printf("CONFIG_OPT_LOGTYPE = %s\n", macro_x[CONFIG_OPT_LOGTYPE]);
137 printf("CONFIG_OPT_LOGFILE = %s\n", macro_x[CONFIG_OPT_LOGFILE]);
138 printf("CONFIG_OPT_LOGFILESIZE = %s\n", macro_x[CONFIG_OPT_LOGFILESIZE]);
139 printf("CONFIG_OPT_LOGLEVEL = %s\n", macro_x[CONFIG_OPT_LOGLEVEL]);
140 printf("CONFIG_OPT_SCANDIR = %s\n", macro_x[CONFIG_OPT_SCANDIR]);
141 printf("CONFIG_OPT_RUNCMD = %s\n", macro_x[CONFIG_OPT_RUNCMD]);
142 printf("CONFIG_OPT_RUNCMD_ARG = %s\n", macro_x[CONFIG_OPT_RUNCMD_ARG]);
143 printf("CONFIG_OPT_MAXTHREADS = %s\n", macro_x[CONFIG_OPT_MAXTHREADS]);
144 printf("CONFIG_OPT_LOAD = %s\n", macro_x[CONFIG_OPT_LOAD]);
145 printf("CONFIG_OPT_USER = %s\n", macro_x[CONFIG_OPT_USER]);
146 printf("CONFIG_OPT_GROUP = %s\n", macro_x[CONFIG_OPT_GROUP]);
147 printf("CONFIG_OPT_PIDFILE = %s\n", macro_x[CONFIG_OPT_PIDFILE]);
148 printf("CONFIG_OPT_SLEEPTIME = %s\n", macro_x[CONFIG_OPT_SLEEPTIME]);
149 printf("CONFIG_OPT_IDENTMYSELF = %s\n", macro_x[CONFIG_OPT_IDENTMYSELF]);
150 printf("---------------------------\n");
151 if (check_needed_config_options() != 0) {
152 printf("There is an Error! Exiting...\n");
153 exit(EXIT_FAILURE);
154 }
155 }
156
157 if (prepare_vars() != 0)
158 exit(EXIT_FAILURE);
159 if (loglevel == -1)
160 printf("DEBUG: load_threshold is %s - ('%f')\n",
161 use_load_threshold ? "enabled" : "disabled", load_threshold);
162
163 pthread_t th[max_threads];
164 for (i=0;i<max_threads;i++){
165 th[i] = (pthread_t) NULL;
166 }
167 i = 0;
168
169 /* Nice point for another function to set
170 * the internal vars from macro_x[] */
171
172 /* Start in Daemon Mode or in foreground? */
173 if (daemon_mode == TRUE)
174 start_daemon("NPCD", LOG_LOCAL0);
175
176 else if (use_syslog)
177 openlog("NPCD", LOG_PID | LOG_CONS | LOG_NDELAY, LOG_LOCAL0);
178
179 /* Create PID File or exit on failure */
180 if (daemon_mode == TRUE && sighup_detected == FALSE) {
181 fppid = fopen(pidfile, "w");
182
183 if (fppid == NULL) {
184 printf("Could not open pidfile '%s': %s\n", pidfile,
185 strerror(errno));
186 exit(EXIT_FAILURE);
187 } else {
188 fprintf(fppid, "%d", getpid());
189 fclose(fppid);
190 }
191 }
192
193 /* Try to drop the privileges */
194 if (drop_privileges(user, group) == EXIT_FAILURE)
195 exit(EXIT_FAILURE);
196
197 snprintf(buffer, sizeof(buffer) - 1,
198 "%s Daemon (%s) started with PID=%d\n", progname, PACKAGE_VERSION,
199 getpid());
200 LOG(0, buffer);
201 snprintf(buffer, sizeof(buffer) - 1,
202 "Please have a look at '%s -V' to get license information\n",
203 progname);
204 LOG(0, buffer);
205
206 //sigemptyset();
207 handle_signal(SIGINT, check_sig);
208 handle_signal(SIGHUP, check_sig);
209 handle_signal(SIGTERM, check_sig);
210
211 snprintf(buffer, sizeof(buffer) - 1,
212 "HINT: load_threshold is %s - ('%f')\n",
213 use_load_threshold ? "enabled" : "disabled", load_threshold);
214 LOG(0, buffer);
215
216 /* beginn main loop */
217 while (1) {
218
219 if (chdir(directory) != 0)
220 exit(EXIT_FAILURE);
221
222 /* is_file() filter may cause trouble on some systems
223 * like Solaris or HP-UX that don't have a d-type
224 * member in struct dirent
225 */
226 /* #ifdef HAVE_STRUCT_DIRENT_D_TYPE
227 if ( ( filecounter = scandir( directory, &namelist, is_file, alphasort ) ) < 0 ) {
228 #else */
229 if ((filecounter = scandir(directory, &namelist, 0, alphasort)) < 0) {
230 /* #endif */
231 snprintf(buffer, sizeof(buffer) - 1,
232 "Error while get file list from spooldir (%s) - %s\n",
233 directory, strerror(errno));
234 LOG(0, buffer);
235 snprintf(buffer, sizeof(buffer) - 1, "Exiting...\n");
236 LOG(0, buffer);
237
238 if (daemon_mode != TRUE)
239 printf("Error while get file list from spooldir (%s) - %s\n",
240 directory, strerror(errno));
241 break;
242 }
243
244 snprintf(buffer, sizeof(buffer) - 1, "Found %d files in %s\n",
245 filecounter, directory);
246 LOG(2, buffer);
247
248 for (i = 0, namelist; i < filecounter; i++) {
249
250 #ifdef HAVE_GETLOADAVG
251 if (use_load_threshold == TRUE) {
252 load = getload(1);
253 snprintf(buffer, sizeof(buffer) - 1, "DEBUG: load %f/%f\n",
254 load, load_threshold);
255 LOG(2, buffer);
256 }
257
258 if (use_load_threshold && (load > load_threshold)) {
259
260 snprintf(buffer, sizeof(buffer) - 1,
261 "WARN: MAX load reached: load %f/%f at i=%d", load,
262 load_threshold, i);
263 LOG(0, buffer);
264
265 if (i > 0)
266 i--;
267 sleep(sleeptime);
268 continue;
269 }
270 #endif
271
272 snprintf(buffer, sizeof(buffer) - 1,
273 "ThreadCounter %d/%d File is %s\n", thread_counter,
274 max_threads, namelist[i]->d_name);
275 LOG(2, buffer);
276
277 struct stat attribute;
278
279 if (stat(namelist[i]->d_name, &attribute) == -1) {
280 LOG(0, "Error while getting file status");
281 break;
282 }
283
284 if (strstr((namelist[i]->d_name), "-PID-") != NULL) {
285 snprintf(
286 buffer,
287 sizeof(buffer) - 1,
288 "File '%s' is an already in process PNP file. Leaving it untouched.\n",
289 namelist[i]->d_name);
290
291 LOG(1, buffer);
292 continue;
293 }
294
295 if (S_ISREG(attribute.st_mode)) {
296 snprintf(buffer, sizeof(buffer) - 1, "Regular File: %s\n",
297 namelist[i]->d_name);
298 LOG(2, buffer);
299
300 /* only start new threads if the max_thread config option is not reached */
301 if (thread_counter < max_threads && we_should_stop == FALSE) {
302
303 if ((pthread_ret = pthread_create(&th[thread_counter],
304 NULL, processfile, namelist[i]->d_name)) != 0) {
305 snprintf(buffer, sizeof(buffer) - 1,
306 "Could not create thread... exiting with error '%s'\n", strerror(errno));
307 LOG(0, buffer);
308 exit(EXIT_FAILURE);
309 }
310
311 snprintf(buffer, sizeof(buffer) - 1,
312 "A thread was started on thread_counter = %d\n",
313 thread_counter);
314 LOG(2, buffer);
315
316 thread_counter++;
317
318 }
319
320 else if (we_should_stop == TRUE)
321 break;
322
323 else {
324
325 snprintf(
326 buffer,
327 sizeof(buffer) - 1,
328 "WARN: MAX Thread reached: %s comes later with ThreadCounter: %d\n",
329 namelist[i]->d_name, thread_counter);
330 LOG(2, buffer);
331
332 i--;
333
334 for (thread_counter = thread_counter; thread_counter > 0; thread_counter--) {
335 snprintf(buffer, sizeof(buffer) - 1,
336 "DEBUG: Will wait for th['%d']\n",
337 thread_counter - 1);
338 LOG(2, buffer);
339 pthread_join(th[thread_counter - 1], NULL);
340 }
341 }
342 }
343 }
344
345 if (thread_counter > 0) {
346 /* Wait for open threads before working on the next run */
347 snprintf(buffer, sizeof(buffer) - 1,
348 "Have to wait: Filecounter = %d - thread_counter = %d\n",
349 filecounter - 2, thread_counter);
350 LOG(2, buffer);
351
352 for (thread_counter = thread_counter; thread_counter > 0; thread_counter--)
353 pthread_join(th[thread_counter - 1], NULL);
354 }
355
356 if (we_should_stop == TRUE)
357 break;
358
359 for (i = 0, namelist; i < filecounter; i++) {
360 free(namelist[i]);
361 }
362
363 free(namelist);
364
365 snprintf(buffer, sizeof(buffer) - 1,
366 "No more files to process... waiting for %d seconds\n",
367 sleeptime);
368 LOG(1, buffer);
369
370 sleep(sleeptime);
371
372 }
373
374 snprintf(buffer, sizeof(buffer) - 1, "Daemon ended. PID was '%d'\n",
375 getpid());
376 LOG(0, buffer);
377
378 if (use_syslog)
379 closelog();
380 return EXIT_SUCCESS;
381 }
382
383 /* **************************************************************
384 *
385 * Function to parse and check the commandline arguments
386 *
387 * *************************************************************/
388
process_arguments(int argc,char ** argv)389 int process_arguments(int argc, char **argv) {
390 int c;
391 int error = FALSE;
392 int display_license = FALSE;
393 int display_help = FALSE;
394
395 #ifdef HAVE_GETOPT_H
396 int option_index = 0;
397 static struct option long_options[] = { { "help", no_argument, 0, 'h' }, {
398 "version", no_argument, 0, 'V' },
399 { "license", no_argument, 0, 'V' },
400 { "daemon", no_argument, 0, 'd' }, { "config", required_argument,
401 0, 'f' }, { 0, 0, 0, 0 } };
402 #endif
403
404 /* make sure we have the correct number of command line arguments */
405 if (argc < 2)
406 error = TRUE;
407
408 while (1) {
409
410 #ifdef HAVE_GETOPT_H
411 c = getopt_long(argc, argv, "+hVdf:", long_options, &option_index);
412 #else
413 c = getopt(argc, argv, "+hVdf:");
414 #endif
415
416 if (c == -1 || c == EOF)
417 break;
418
419 switch (c) {
420
421 case '?': /* usage */
422 case 'h':
423 display_help = TRUE;
424 break;
425
426 case 'V': /* version */
427 printf("%s %s - $Revision: 637 $\n\n", progname, PACKAGE_VERSION);
428 display_license = TRUE;
429 break;
430
431 case 'd': /* run in daemon mode */
432 daemon_mode = TRUE;
433 break;
434
435 case 'f': /* config file */
436 if (optarg != NULL)
437 config_file = optarg;
438 break;
439
440 default:
441 break;
442 }
443
444 }
445
446 if (display_license == TRUE) {
447
448 printf(
449 "This program is free software; you can redistribute it and/or modify\n");
450 printf(
451 "it under the terms of the GNU General Public License version 2 as\n");
452 printf("published by the Free Software Foundation.\n\n");
453 printf(
454 "This program is distributed in the hope that it will be useful,\n");
455 printf(
456 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
457 printf(
458 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
459 printf("GNU General Public License for more details.\n\n");
460 printf(
461 "You should have received a copy of the GNU General Public License\n");
462 printf("along with this program; if not, write to the Free Software\n");
463 printf(
464 "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n\n");
465
466 exit(EXIT_SUCCESS);
467 }
468
469 /* if there are no command line options (or if we encountered an error), print usage */
470 if (error == TRUE || display_help == TRUE) {
471 printf("\nUsage: %s -f <configfile> [-d] \n", argv[0]);
472 printf("\n");
473 printf("Options:\n");
474 printf("\n");
475 printf(" -d | --daemon \n");
476 printf("\t\tRun as daemon in background\n");
477 printf("\n");
478 printf(" -f | --config \n");
479 printf("\t\tPath to config file\n");
480 printf("\n");
481 printf(
482 "Visit the Website at http://sourceforge.net/projects/pnp4nagios/ for bug fixes, \n");
483 printf("new releases, online documentation, FAQs, Mailinglists.\n");
484 printf("\n");
485
486 exit(EXIT_FAILURE);
487 }
488 return EXIT_SUCCESS;
489 }
490
491 /********************************************************************
492 * *
493 * processfile - this is the function for each thread *
494 * *
495 ********************************************************************/
496
processfile(void * filename)497 void * processfile(void *filename) {
498
499 char *file = (char *) filename;
500 char command_line[MAX_COMMANDLINE_LENGTH];
501 char buffer[MAX_LOGMESSAGE_SIZE];
502 int result;
503 FILE *proc;
504
505 /* npcd.c:493: warning: ‘result’ may be used uninitialized in this function */
506 result = 0;
507
508 snprintf(command_line, sizeof(command_line), "%s %s %s %s/%s", command,
509 identmyself ? "-n" : "\b", command_args, directory, file);
510
511 pthread_cleanup_push((void *) &exit_handler_mem, file);
512
513 snprintf(buffer, sizeof(buffer) - 1,
514 "Processing file %s with ID %ld - going to exec %s\n", file,
515 pthread_self(), command_line);
516 LOG(2, buffer);
517
518 snprintf(buffer, sizeof(buffer) - 1, "Processing file '%s'\n", file);
519 LOG(1, buffer);
520
521 if ((proc = popen(command_line, "r")) != NULL)
522 result = pclose(proc);
523
524 result >>= 8;
525
526 if (result != 0) {
527 snprintf(buffer, sizeof(buffer) - 1,
528 "ERROR: Executed command exits with return code '%d'\n",
529 result);
530 LOG(0, buffer);
531
532 snprintf(buffer, sizeof(buffer) - 1,
533 "ERROR: Command line was '%s'\n", command_line);
534 LOG(0, buffer);
535
536 we_should_stop = FALSE;
537 }
538
539 if (loglevel == -1)
540 sleep(2);
541
542 pthread_cleanup_pop(1);
543 pthread_exit((void *) pthread_self());
544 }
545
546 /* ******************************************************************
547 * *
548 * processfile - this is the function for each thread *
549 * *
550 * ******************************************************************/
551
exit_handler_mem(void * arg)552 static void *exit_handler_mem(void * arg) {
553 // syslog( LOG_NOTICE, "Will now clean up thread %ld\n",pthread_self());
554 //if (thread_counter > 0)
555 //thread_counter--;
556 return 0;
557 }
558
559 #ifdef HAVE_GETLOADAVG
getload(int which_sample)560 double getload(int which_sample) {
561
562 double loadavg[3];
563 char buffer[MAX_LOGMESSAGE_SIZE];
564
565 if (which_sample == 1) {
566 which_sample = 0;
567 } else if (which_sample == 5) {
568 which_sample = 1;
569 } else if (which_sample == 15) {
570 which_sample = 2;
571 } else {
572 snprintf(buffer, sizeof(buffer) - 1,
573 "Invalid load sample %d - allowed is 1,5,15\n", which_sample);
574 LOG(0, buffer);
575
576 return -1;
577 }
578 getloadavg(loadavg, 3);
579 return loadavg[which_sample];
580 }
581 #endif
582