1 /* main.c - Network UPS Tools driver core
2
3 Copyright (C)
4 1999 Russell Kroll <rkroll@exploits.org>
5 2005 - 2017 Arnaud Quette <arnaud.quette@free.fr>
6 2017 Eaton (author: Emilien Kia <EmilienKia@Eaton.com>)
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #include "common.h"
24 #include "main.h"
25 #include "nut_stdint.h"
26 #include "dstate.h"
27 #include "attribute.h"
28
29 /* data which may be useful to the drivers */
30 int upsfd = -1;
31 char *device_path = NULL;
32 const char *progname = NULL, *upsname = NULL, *device_name = NULL;
33
34 /* may be set by the driver to wake up while in dstate_poll_fds */
35 int extrafd = -1;
36
37 /* for ser_open */
38 int do_lock_port = 1;
39
40 /* for dstate->sock_connect, default to asynchronous */
41 int do_synchronous = 0;
42
43 /* for detecting -a values that don't match anything */
44 static int upsname_found = 0;
45
46 static vartab_t *vartab_h = NULL;
47
48 /* variables possibly set by the global part of ups.conf */
49 time_t poll_interval = 2;
50 static char *chroot_path = NULL, *user = NULL;
51
52 /* signal handling */
53 int exit_flag = 0;
54
55 /* everything else */
56 static char *pidfn = NULL;
57 static int dump_data = 0; /* Store the update_count requested */
58
59 /* print the driver banner */
upsdrv_banner(void)60 void upsdrv_banner (void)
61 {
62 int i;
63
64 printf("Network UPS Tools - %s %s (%s)\n", upsdrv_info.name, upsdrv_info.version, UPS_VERSION);
65
66 /* process sub driver(s) information */
67 for (i = 0; upsdrv_info.subdrv_info[i]; i++) {
68
69 if (!upsdrv_info.subdrv_info[i]->name) {
70 continue;
71 }
72
73 if (!upsdrv_info.subdrv_info[i]->version) {
74 continue;
75 }
76
77 printf("%s %s\n", upsdrv_info.subdrv_info[i]->name,
78 upsdrv_info.subdrv_info[i]->version);
79 }
80 }
81
82 /* power down the attached load immediately */
83 static void forceshutdown(void)
84 __attribute__((noreturn));
85
forceshutdown(void)86 static void forceshutdown(void)
87 {
88 upslogx(LOG_NOTICE, "Initiating UPS shutdown");
89
90 /* the driver must not block in this function */
91 upsdrv_shutdown();
92 exit(EXIT_SUCCESS);
93 }
94
95 /* this function only prints the usage message; it does not call exit() */
help_msg(void)96 static void help_msg(void)
97 {
98 vartab_t *tmp;
99
100 printf("\nusage: %s (-a <id>|-s <id>) [OPTIONS]\n", progname);
101
102 printf(" -a <id> - autoconfig using ups.conf section <id>\n");
103 printf(" - note: -x after -a overrides ups.conf settings\n\n");
104
105 printf(" -s <id> - configure directly from cmd line arguments\n");
106 printf(" - note: must specify all driver parameters with successive -x\n");
107 printf(" - note: at least 'port' variable should be set\n");
108 printf(" - note: to explore the current values on a device from an\n");
109 printf(" unprivileged user account (with sufficient media access in\n");
110 printf(" the OS - e.g. to query networked devices), you can specify\n");
111 printf(" '-d 1' argument and `export NUT_STATEPATH=/tmp` beforehand\n\n");
112
113 printf(" -V - print version, then exit\n");
114 printf(" -L - print parseable list of driver variables\n");
115 printf(" -D - raise debugging level\n");
116 printf(" -d <count> - dump data to stdout after 'count' updates loop and exit\n");
117 printf(" -q - raise log level threshold\n");
118 printf(" -h - display this help\n");
119 printf(" -k - force shutdown\n");
120 printf(" -i <int> - poll interval\n");
121 printf(" -r <dir> - chroot to <dir>\n");
122 printf(" -u <user> - switch to <user> (if started as root)\n");
123 printf(" -x <var>=<val> - set driver variable <var> to <val>\n");
124 printf(" - example: -x cable=940-0095B\n\n");
125
126 if (vartab_h) {
127 tmp = vartab_h;
128
129 printf("Acceptable values for -x or ups.conf in this driver:\n\n");
130
131 while (tmp) {
132 if (tmp->vartype == VAR_VALUE)
133 printf("%40s : -x %s=<value>\n",
134 tmp->desc, tmp->var);
135 else
136 printf("%40s : -x %s\n", tmp->desc, tmp->var);
137 tmp = tmp->next;
138 }
139 }
140
141 upsdrv_help();
142 }
143
144 /* store these in dstate as driver.(parameter|flag) */
dparam_setinfo(const char * var,const char * val)145 static void dparam_setinfo(const char *var, const char *val)
146 {
147 char vtmp[SMALLBUF];
148
149 /* store these in dstate for debugging and other help */
150 if (val) {
151 snprintf(vtmp, sizeof(vtmp), "driver.parameter.%s", var);
152 dstate_setinfo(vtmp, "%s", val);
153 return;
154 }
155
156 /* no value = flag */
157
158 snprintf(vtmp, sizeof(vtmp), "driver.flag.%s", var);
159 dstate_setinfo(vtmp, "enabled");
160 }
161
162 /* cram var [= <val>] data into storage */
storeval(const char * var,char * val)163 static void storeval(const char *var, char *val)
164 {
165 vartab_t *tmp, *last;
166
167 if (!strncasecmp(var, "override.", 9)) {
168 dstate_setinfo(var+9, "%s", val);
169 dstate_setflags(var+9, ST_FLAG_IMMUTABLE);
170 return;
171 }
172
173 if (!strncasecmp(var, "default.", 8)) {
174 dstate_setinfo(var+8, "%s", val);
175 return;
176 }
177
178 tmp = last = vartab_h;
179
180 while (tmp) {
181 last = tmp;
182
183 /* sanity check */
184 if (!tmp->var) {
185 tmp = tmp->next;
186 continue;
187 }
188
189 /* later definitions overwrite earlier ones */
190 if (!strcasecmp(tmp->var, var)) {
191 free(tmp->val);
192
193 if (val)
194 tmp->val = xstrdup(val);
195
196 /* don't keep things like SNMP community strings */
197 if ((tmp->vartype & VAR_SENSITIVE) == 0)
198 dparam_setinfo(var, val);
199
200 tmp->found = 1;
201 return;
202 }
203
204 tmp = tmp->next;
205 }
206
207 /* try to help them out */
208 printf("\nFatal error: '%s' is not a valid %s for this driver.\n", var,
209 val ? "variable name" : "flag");
210 printf("\n");
211 printf("Look in the man page or call this driver with -h for a list of\n");
212 printf("valid variable names and flags.\n");
213
214 exit(EXIT_SUCCESS);
215 }
216
217 /* retrieve the value of variable <var> if possible */
getval(const char * var)218 char *getval(const char *var)
219 {
220 vartab_t *tmp = vartab_h;
221
222 while (tmp) {
223 if (!strcasecmp(tmp->var, var))
224 return(tmp->val);
225 tmp = tmp->next;
226 }
227
228 return NULL;
229 }
230
231 /* see if <var> has been defined, even if no value has been given to it */
testvar(const char * var)232 int testvar(const char *var)
233 {
234 vartab_t *tmp = vartab_h;
235
236 while (tmp) {
237 if (!strcasecmp(tmp->var, var))
238 return tmp->found;
239 tmp = tmp->next;
240 }
241
242 return 0; /* not found */
243 }
244
245 /* callback from driver - create the table for -x/conf entries */
addvar(int vartype,const char * name,const char * desc)246 void addvar(int vartype, const char *name, const char *desc)
247 {
248 vartab_t *tmp, *last;
249
250 tmp = last = vartab_h;
251
252 while (tmp) {
253 last = tmp;
254 tmp = tmp->next;
255 }
256
257 tmp = xmalloc(sizeof(vartab_t));
258
259 tmp->vartype = vartype;
260 tmp->var = xstrdup(name);
261 tmp->val = NULL;
262 tmp->desc = xstrdup(desc);
263 tmp->found = 0;
264 tmp->next = NULL;
265
266 if (last)
267 last->next = tmp;
268 else
269 vartab_h = tmp;
270 }
271
272 /* handle -x / ups.conf config details that are for this part of the code */
main_arg(char * var,char * val)273 static int main_arg(char *var, char *val)
274 {
275 /* flags for main */
276
277 if (!strcmp(var, "nolock")) {
278 do_lock_port = 0;
279 dstate_setinfo("driver.flag.nolock", "enabled");
280 return 1; /* handled */
281 }
282
283 if (!strcmp(var, "ignorelb")) {
284 dstate_setinfo("driver.flag.ignorelb", "enabled");
285 return 1; /* handled */
286 }
287
288 /* any other flags are for the driver code */
289 if (!val)
290 return 0;
291
292 /* variables for main: port */
293
294 if (!strcmp(var, "port")) {
295 device_path = xstrdup(val);
296 device_name = xbasename(device_path);
297 dstate_setinfo("driver.parameter.port", "%s", val);
298 return 1; /* handled */
299 }
300
301 if (!strcmp(var, "sddelay")) {
302 upslogx(LOG_INFO, "Obsolete value sddelay found in ups.conf");
303 return 1; /* handled */
304 }
305
306 /* allow per-driver overrides of the global setting */
307 if (!strcmp(var, "synchronous")) {
308 if (!strncmp(val, "yes", 3))
309 do_synchronous = 1;
310 else
311 do_synchronous = 0;
312
313 return 1; /* handled */
314 }
315
316 /* only for upsdrvctl - ignored here */
317 if (!strcmp(var, "sdorder"))
318 return 1; /* handled */
319
320 /* only for upsd (at the moment) - ignored here */
321 if (!strcmp(var, "desc"))
322 return 1; /* handled */
323
324 return 0; /* unhandled, pass it through to the driver */
325 }
326
do_global_args(const char * var,const char * val)327 static void do_global_args(const char *var, const char *val)
328 {
329 if (!strcmp(var, "pollinterval")) {
330 int ipv = atoi(val);
331 if (ipv > 0) {
332 poll_interval = (time_t)ipv;
333 } else {
334 fatalx(EXIT_FAILURE, "Error: invalid pollinterval: %d", ipv);
335 }
336 return;
337 }
338
339 if (!strcmp(var, "chroot")) {
340 free(chroot_path);
341 chroot_path = xstrdup(val);
342 }
343
344 if (!strcmp(var, "user")) {
345 free(user);
346 user = xstrdup(val);
347 }
348
349 if (!strcmp(var, "synchronous")) {
350 if (!strncmp(val, "yes", 3))
351 do_synchronous = 1;
352 else
353 do_synchronous = 0;
354 }
355
356
357 /* unrecognized */
358 }
359
do_upsconf_args(char * confupsname,char * var,char * val)360 void do_upsconf_args(char *confupsname, char *var, char *val)
361 {
362 char tmp[SMALLBUF];
363
364 /* handle global declarations */
365 if (!confupsname) {
366 do_global_args(var, val);
367 return;
368 }
369
370 /* no match = not for us */
371 if (strcmp(confupsname, upsname) != 0)
372 return;
373
374 upsname_found = 1;
375
376 if (main_arg(var, val))
377 return;
378
379 /* flags (no =) now get passed to the driver-level stuff */
380 if (!val) {
381
382 /* also store this, but it's a bit different */
383 snprintf(tmp, sizeof(tmp), "driver.flag.%s", var);
384 dstate_setinfo(tmp, "enabled");
385
386 storeval(var, NULL);
387 return;
388 }
389
390 /* don't let the user shoot themselves in the foot */
391 if (!strcmp(var, "driver")) {
392 if (strcmp(val, progname) != 0)
393 fatalx(EXIT_FAILURE, "Error: UPS [%s] is for driver %s, but I'm %s!\n",
394 confupsname, val, progname);
395 return;
396 }
397
398 /* allow per-driver overrides of the global setting */
399 if (!strcmp(var, "pollinterval")) {
400 int ipv = atoi(val);
401 if (ipv > 0) {
402 poll_interval = (time_t)ipv;
403 } else {
404 fatalx(EXIT_FAILURE, "Error: UPS [%s]: invalid pollinterval: %d",
405 confupsname, ipv);
406 }
407 return;
408 }
409
410 /* everything else must be for the driver */
411 storeval(var, val);
412 }
413
414 /* split -x foo=bar into 'foo' and 'bar' */
splitxarg(char * inbuf)415 static void splitxarg(char *inbuf)
416 {
417 char *eqptr, *val, *buf;
418
419 /* make our own copy - avoid changing argv */
420 buf = xstrdup(inbuf);
421
422 eqptr = strchr(buf, '=');
423
424 if (!eqptr)
425 val = NULL;
426 else {
427 *eqptr++ = '\0';
428 val = eqptr;
429 }
430
431 /* see if main handles this first */
432 if (main_arg(buf, val))
433 return;
434
435 /* otherwise store it for later */
436 storeval(buf, val);
437 }
438
439 /* dump the list from the vartable for external parsers */
listxarg(void)440 static void listxarg(void)
441 {
442 vartab_t *tmp;
443
444 tmp = vartab_h;
445
446 if (!tmp)
447 return;
448
449 while (tmp) {
450
451 switch (tmp->vartype) {
452 case VAR_VALUE: printf("VALUE"); break;
453 case VAR_FLAG: printf("FLAG"); break;
454 default: printf("UNKNOWN"); break;
455 }
456
457 printf(" %s \"%s\"\n", tmp->var, tmp->desc);
458
459 tmp = tmp->next;
460 }
461 }
462
vartab_free(void)463 static void vartab_free(void)
464 {
465 vartab_t *tmp, *next;
466
467 tmp = vartab_h;
468
469 while (tmp) {
470 next = tmp->next;
471
472 free(tmp->var);
473 free(tmp->val);
474 free(tmp->desc);
475 free(tmp);
476
477 tmp = next;
478 }
479 }
480
exit_cleanup(void)481 static void exit_cleanup(void)
482 {
483 free(chroot_path);
484 free(device_path);
485 free(user);
486
487 if (pidfn) {
488 unlink(pidfn);
489 free(pidfn);
490 }
491
492 dstate_free();
493 vartab_free();
494 }
495
set_exit_flag(int sig)496 static void set_exit_flag(int sig)
497 {
498 exit_flag = sig;
499 }
500
setup_signals(void)501 static void setup_signals(void)
502 {
503 struct sigaction sa;
504
505 sigemptyset(&sa.sa_mask);
506 sa.sa_flags = 0;
507
508 sa.sa_handler = set_exit_flag;
509 sigaction(SIGTERM, &sa, NULL);
510 sigaction(SIGINT, &sa, NULL);
511 sigaction(SIGQUIT, &sa, NULL);
512
513 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES)
514 # pragma GCC diagnostic push
515 # pragma GCC diagnostic ignored "-Wstrict-prototypes"
516 #endif
517 sa.sa_handler = SIG_IGN;
518 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES)
519 # pragma GCC diagnostic pop
520 #endif
521 sigaction(SIGHUP, &sa, NULL);
522 sigaction(SIGPIPE, &sa, NULL);
523 }
524
main(int argc,char ** argv)525 int main(int argc, char **argv)
526 {
527 struct passwd *new_uid = NULL;
528 int i, do_forceshutdown = 0;
529 int update_count = 0;
530
531 atexit(exit_cleanup);
532
533 /* pick up a default from configure --with-user */
534 user = xstrdup(RUN_AS_USER); /* xstrdup: this gets freed at exit */
535
536 progname = xbasename(argv[0]);
537 open_syslog(progname);
538
539 upsdrv_banner();
540
541 if (upsdrv_info.status == DRV_EXPERIMENTAL) {
542 printf("Warning: This is an experimental driver.\n");
543 printf("Some features may not function correctly.\n\n");
544 }
545
546 /* build the driver's extra (-x) variable table */
547 upsdrv_makevartable();
548
549 while ((i = getopt(argc, argv, "+a:s:kDd:hx:Lqr:u:Vi:")) != -1) {
550 switch (i) {
551 case 'a':
552 upsname = optarg;
553
554 read_upsconf();
555
556 if (!upsname_found)
557 fatalx(EXIT_FAILURE, "Error: Section %s not found in ups.conf",
558 optarg);
559 break;
560 case 's':
561 upsname = optarg;
562 upsname_found = 1;
563 break;
564 case 'D':
565 nut_debug_level++;
566 break;
567 case 'd':
568 dump_data = atoi(optarg);
569 break;
570 case 'i': { // scope
571 int ipv = atoi(optarg);
572 if (ipv > 0) {
573 poll_interval = (time_t)ipv;
574 } else {
575 fatalx(EXIT_FAILURE, "Error: command-line: invalid pollinterval: %d",
576 ipv);
577 }
578 }
579 break;
580 case 'k':
581 do_lock_port = 0;
582 do_forceshutdown = 1;
583 break;
584 case 'L':
585 listxarg();
586 exit(EXIT_SUCCESS);
587 case 'q':
588 nut_log_level++;
589 break;
590 case 'r':
591 chroot_path = xstrdup(optarg);
592 break;
593 case 'u':
594 free(user);
595 user = xstrdup(optarg);
596 break;
597 case 'V':
598 /* already printed the banner, so exit */
599 exit(EXIT_SUCCESS);
600 case 'x':
601 splitxarg(optarg);
602 break;
603 case 'h':
604 help_msg();
605 exit(EXIT_SUCCESS);
606 default:
607 fatalx(EXIT_FAILURE,
608 "Error: unknown option -%c. Try -h for help.", i);
609 }
610 }
611
612 argc -= optind;
613 argv += optind;
614
615 if (argc > 0) {
616 fatalx(EXIT_FAILURE,
617 "Error: too many non-option arguments. Try -h for help.");
618 }
619
620 if (!upsname_found) {
621 fatalx(EXIT_FAILURE,
622 "Error: specifying '-a id' or '-s id' is now mandatory. Try -h for help.");
623 }
624
625 /* we need to get the port from somewhere */
626 if (!device_path) {
627 fatalx(EXIT_FAILURE,
628 "Error: you must specify a port name in ups.conf or in '-x port=...' argument.\n"
629 "Try -h for help.");
630 }
631
632 upsdebugx(1, "debug level is '%d'", nut_debug_level);
633
634 new_uid = get_user_pwent(user);
635
636 if (chroot_path)
637 chroot_start(chroot_path);
638
639 become_user(new_uid);
640
641 /* Only switch to statepath if we're not powering off or just dumping data, for discovery */
642 /* This avoid case where ie /var is umounted */
643 if ((!do_forceshutdown) && (!dump_data) && (chdir(dflt_statepath())))
644 fatal_with_errno(EXIT_FAILURE, "Can't chdir to %s", dflt_statepath());
645
646 /* Setup signals to communicate with driver once backgrounded. */
647 if ((nut_debug_level == 0) && (!do_forceshutdown)) {
648 char buffer[SMALLBUF];
649
650 setup_signals();
651
652 snprintf(buffer, sizeof(buffer), "%s/%s-%s.pid", altpidpath(), progname, upsname);
653
654 /* Try to prevent that driver is started multiple times. If a PID file */
655 /* already exists, send a TERM signal to the process and try if it goes */
656 /* away. If not, retry a couple of times. */
657 for (i = 0; i < 3; i++) {
658 struct stat st;
659
660 if (stat(buffer, &st) != 0) {
661 /* PID file not found */
662 break;
663 }
664
665 if (sendsignalfn(buffer, SIGTERM) != 0) {
666 /* Can't send signal to PID, assume invalid file */
667 break;
668 }
669
670 upslogx(LOG_WARNING, "Duplicate driver instance detected (PID file %s exists)! Terminating other driver!", buffer);
671
672 /* Allow driver some time to quit */
673 sleep(5);
674 }
675
676 /* Only write pid if we're not just dumping data, for discovery */
677 if (!dump_data) {
678 pidfn = xstrdup(buffer);
679 writepid(pidfn); /* before backgrounding */
680 }
681 }
682
683 /* clear out callback handler data */
684 memset(&upsh, '\0', sizeof(upsh));
685
686 /* note: device.type is set early to be overridden by the driver
687 * when its a pdu! */
688 dstate_setinfo("device.type", "ups");
689
690 upsdrv_initups();
691
692 /* UPS is detected now, cleanup upon exit */
693 atexit(upsdrv_cleanup);
694
695 /* now see if things are very wrong out there */
696 if (upsdrv_info.status == DRV_BROKEN) {
697 fatalx(EXIT_FAILURE, "Fatal error: broken driver. It probably needs to be converted.\n");
698 }
699
700 if (do_forceshutdown)
701 forceshutdown();
702
703 /* publish the top-level data: version numbers, driver name */
704 dstate_setinfo("driver.version", "%s", UPS_VERSION);
705 dstate_setinfo("driver.version.internal", "%s", upsdrv_info.version);
706 dstate_setinfo("driver.name", "%s", progname);
707
708 /*
709 * If we are not debugging, send the early startup logs generated by
710 * upsdrv_initinfo() and upsdrv_updateinfo() to syslog, not just stderr.
711 * Otherwise these logs are lost.
712 */
713 if ((nut_debug_level == 0) && (!dump_data))
714 syslogbit_set();
715
716 /* get the base data established before allowing connections */
717 upsdrv_initinfo();
718 upsdrv_updateinfo();
719
720 if (dstate_getinfo("driver.flag.ignorelb")) {
721 int have_lb_method = 0;
722
723 if (dstate_getinfo("battery.charge") && dstate_getinfo("battery.charge.low")) {
724 upslogx(LOG_INFO, "using 'battery.charge' to set battery low state");
725 have_lb_method++;
726 }
727
728 if (dstate_getinfo("battery.runtime") && dstate_getinfo("battery.runtime.low")) {
729 upslogx(LOG_INFO, "using 'battery.runtime' to set battery low state");
730 have_lb_method++;
731 }
732
733 if (!have_lb_method) {
734 fatalx(EXIT_FAILURE,
735 "The 'ignorelb' flag is set, but there is no way to determine the\n"
736 "battery state of charge.\n\n"
737 "Only set this flag if both 'battery.charge' and 'battery.charge.low'\n"
738 "and/or 'battery.runtime' and 'battery.runtime.low' are available.\n");
739 }
740 }
741
742 /* now we can start servicing requests */
743 /* Only write pid if we're not just dumping data, for discovery */
744 if (!dump_data)
745 dstate_init(progname, upsname);
746
747 /* The poll_interval may have been changed from the default */
748 dstate_setinfo("driver.parameter.pollinterval", "%jd", (intmax_t)poll_interval);
749
750 /* The synchronous option may have been changed from the default */
751 dstate_setinfo("driver.parameter.synchronous", "%s",
752 (do_synchronous==1)?"yes":"no");
753
754 /* remap the device.* info from ups.* for the transition period */
755 if (dstate_getinfo("ups.mfr") != NULL)
756 dstate_setinfo("device.mfr", "%s", dstate_getinfo("ups.mfr"));
757 if (dstate_getinfo("ups.model") != NULL)
758 dstate_setinfo("device.model", "%s", dstate_getinfo("ups.model"));
759 if (dstate_getinfo("ups.serial") != NULL)
760 dstate_setinfo("device.serial", "%s", dstate_getinfo("ups.serial"));
761
762 if ( (nut_debug_level == 0) && (!dump_data) ) {
763 background();
764 writepid(pidfn); /* PID changes when backgrounding */
765 }
766
767 while (!exit_flag) {
768
769 struct timeval timeout;
770
771 gettimeofday(&timeout, NULL);
772 timeout.tv_sec += poll_interval;
773
774 upsdrv_updateinfo();
775
776 /* Dump the data tree (in upsc-like format) to stdout and exit */
777 if (dump_data) {
778 /* Wait for 'dump_data' update loops to ensure data completion */
779 if (update_count == dump_data) {
780 dstate_dump();
781 exit_flag = 1;
782 }
783 else
784 update_count++;
785 }
786 else {
787 while (!dstate_poll_fds(timeout, extrafd) && !exit_flag) {
788 /* repeat until time is up or extrafd has data */
789 }
790 }
791 }
792
793 /* if we get here, the exit flag was set by a signal handler */
794 /* however, avoid to "pollute" data dump output! */
795 if (!dump_data)
796 upslogx(LOG_INFO, "Signal %d: exiting", exit_flag);
797
798 exit(EXIT_SUCCESS);
799 }
800