-
-
Notifications
You must be signed in to change notification settings - Fork 366
/
Copy pathmain.c
2529 lines (2195 loc) · 74.2 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* main.c - Network UPS Tools driver core
Copyright (C)
1999 Russell Kroll <[email protected]>
2005 - 2017 Arnaud Quette <[email protected]>
2017 Eaton (author: Emilien Kia <[email protected]>)
2017 - 2022 Jim Klimov <[email protected]>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "common.h"
#include "main.h"
#include "nut_stdint.h"
#include "dstate.h"
#include "attribute.h"
#include "upsdrvquery.h"
#ifndef WIN32
# include <grp.h>
#endif
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
/* data which may be useful to the drivers */
TYPE_FD upsfd = ERROR_FD;
char *device_path = NULL;
const char *progname = NULL, *upsname = NULL, *device_name = NULL;
/* may be set by the driver to wake up while in dstate_poll_fds */
TYPE_FD extrafd = ERROR_FD;
#ifdef WIN32
static HANDLE mutex = INVALID_HANDLE_VALUE;
#endif
/* for ser_open */
int do_lock_port = 1;
/* for dstate->sock_connect, default to effectively
* asynchronous (0) with fallback to synchronous (1) */
int do_synchronous = -1;
/* for detecting -a values that don't match anything */
static int upsname_found = 0;
# ifndef DRIVERS_MAIN_WITHOUT_MAIN
static
# endif /* DRIVERS_MAIN_WITHOUT_MAIN */
vartab_t *vartab_h = NULL;
/* variables possibly set by the global part of ups.conf
* user and group may be set globally or per-driver
*/
time_t poll_interval = 2;
static char *chroot_path = NULL, *user = NULL, *group = NULL;
static int user_from_cmdline = 0, group_from_cmdline = 0;
/* signal handling */
int exit_flag = 0;
/* reload_flag is 0 most of the time (including initial config reading),
* and is briefly 1 when a reload signal is received and is being handled,
* or 2 if the reload attempt is allowed to exit the current driver (e.g.
* changed some ups.conf settings that can not be re-applied on the fly)
* assuming it gets restarted by external framework (systemd) or caller
* (like NUT driver CLI `-c reload-or-restart` handling), if needed.
*/
static int reload_flag = 0;
#ifndef DRIVERS_MAIN_WITHOUT_MAIN
/* Should this driver instance go to background (default)
* or stay foregrounded (default if -D/-d options are set on
* command line)? Note that debug_min in ups.conf allows for
* verbosity while backgrounded by default.
* Value is multi-state (FIXME: enum?):
* -1 (default) Decide based on debug verbosity or dump_mode
* 0 User required to background even if with -D or dump_mode,
* or did not require foregrounding/dumping/debug on CLI
* 1 User required to not background explicitly,
* or passed -D (or -d) and current value was -1
* 2 User required to not background explicitly,
* and yet to write the PID file, with -FF option
*/
static int foreground = -1;
#endif /* DRIVERS_MAIN_WITHOUT_MAIN */
/* Users can pass a -D[...] option to enable debugging.
* For the service tracing purposes, also the ups.conf
* can define a debug_min value in the global or device
* section, to set the minimal debug level (CLI provided
* value less than that would not have effect, can only
* have more). Finally, it can also be set over socket
* protocol, taking precedence over other inputs.
*/
static int nut_debug_level_args = -1;
static int nut_debug_level_global = -1;
static int nut_debug_level_driver = -1;
static int nut_debug_level_protocol = -1;
#ifndef DRIVERS_MAIN_WITHOUT_MAIN
/* everything else */
static char *pidfn = NULL;
static int dump_data = 0; /* Store the update_count requested */
#endif /* DRIVERS_MAIN_WITHOUT_MAIN */
/* pre-declare some private methods used */
static void assign_debug_level(void);
/* TODO: Equivalent for WIN32 - see SIGCMD_RELOAD in upd and upsmon */
static void set_reload_flag(
#ifndef WIN32
int
#else
char *
#endif
sig);
#ifndef DRIVERS_MAIN_WITHOUT_MAIN
/* Returns a result code from INSTCMD enum values */
static int handle_reload_flag(void);
#endif
/* Set in do_ups_confargs() for consumers like handle_reload_flag() */
static int reload_requires_restart = -1;
/* print the driver banner */
void upsdrv_banner (void)
{
int i;
printf("Network UPS Tools - %s %s (%s)\n", upsdrv_info.name, upsdrv_info.version, UPS_VERSION);
/* process sub driver(s) information */
for (i = 0; upsdrv_info.subdrv_info[i]; i++) {
if (!upsdrv_info.subdrv_info[i]->name) {
continue;
}
if (!upsdrv_info.subdrv_info[i]->version) {
continue;
}
printf("%s %s\n", upsdrv_info.subdrv_info[i]->name,
upsdrv_info.subdrv_info[i]->version);
}
}
#ifndef DRIVERS_MAIN_WITHOUT_MAIN
/* power down the attached load immediately */
static void forceshutdown(void)
__attribute__((noreturn));
static void forceshutdown(void)
{
upslogx(LOG_NOTICE, "Initiating UPS shutdown");
/* the driver must not block in this function */
upsdrv_shutdown();
/* the driver always exits here, to not block probable ongoing shutdown */
exit(exit_flag == -1 ? EXIT_FAILURE : EXIT_SUCCESS);
}
/* this function only prints the usage message; it does not call exit() */
static void help_msg(void)
{
vartab_t *tmp;
nut_report_config_flags();
printf("\nusage: %s (-a <id>|-s <id>) [OPTIONS]\n", progname);
printf(" -a <id> - autoconfig using ups.conf section <id>\n");
printf(" - note: -x after -a overrides ups.conf settings\n\n");
printf(" -s <id> - configure directly from cmd line arguments\n");
printf(" - note: must specify all driver parameters with successive -x\n");
printf(" - note: at least 'port' variable should be set\n");
printf(" - note: to explore the current values on a device from an\n");
printf(" unprivileged user account (with sufficient media access in\n");
printf(" the OS - e.g. to query networked devices), you can specify\n");
printf(" '-d 1' argument and `export NUT_STATEPATH=/tmp` beforehand\n\n");
printf(" -V - print version, then exit\n");
printf(" -L - print parseable list of driver variables\n");
printf(" -D - raise debugging level (and stay foreground by default)\n");
printf(" -d <count> - dump data to stdout after 'count' updates loop and exit\n");
printf(" -F - stay foregrounded even if no debugging is enabled\n");
printf(" -FF - stay foregrounded and still save the PID file\n");
printf(" -B - stay backgrounded even if debugging is bumped\n");
printf(" -q - raise log level threshold\n");
printf(" -h - display this help\n");
printf(" -k - force shutdown\n");
printf(" -c <command> - send <command> via signal to background process\n");
printf(" Supported commands:\n");
# ifndef WIN32
/* FIXME: port event loop from upsd/upsmon to allow messaging fellow drivers in WIN32 builds */
printf(" - reload: re-read configuration files, ignoring changed\n");
printf(" values which require a driver restart (can not be changed\n");
printf(" on the fly)\n");
# endif /* WIN32 */
/* Note: this one is beside signal-sending (goes via socket protocol): */
printf(" - reload-or-error: re-read configuration files, ignoring but\n");
printf(" counting changed values which require a driver restart (can\n");
printf(" not be changed on the fly), and return a success/fail code\n");
printf(" based on that count, so the caller can decide the fate of\n");
printf(" the currently running driver instance\n");
# ifndef WIN32
/* FIXME: port event loop from upsd/upsmon to allow messaging fellow drivers in WIN32 builds */
# ifdef SIGCMD_RELOAD_OR_RESTART
printf(" - reload-or-restart: re-read configuration files (close the\n");
printf(" old driver instance device connection if needed, and have\n");
printf(" it effectively restart)\n");
# endif
printf(" - reload-or-exit: re-read configuration files (exit the old\n");
printf(" driver instance if needed, so an external caller like the\n");
printf(" systemd or SMF frameworks would start another copy)\n");
/* NOTE for FIXME above: PID-signalling is non-WIN32-only for us */
printf(" -P <pid> - send the signal above to specified PID (bypassing PID file)\n");
# endif /* WIN32 */
printf(" -i <int> - poll interval\n");
printf(" -r <dir> - chroot to <dir>\n");
printf(" -u <user> - switch to <user> (if started as root)\n");
printf(" -g <group> - set pipe access to <group> (if started as root)\n");
printf(" -x <var>=<val> - set driver variable <var> to <val>\n");
printf(" - example: -x cable=940-0095B\n\n");
if (vartab_h) {
tmp = vartab_h;
printf("Acceptable values for -x or ups.conf in this driver:\n\n");
while (tmp) {
if (tmp->vartype == VAR_VALUE)
printf("%40s : -x %s=<value>\n",
tmp->desc, tmp->var);
else
printf("%40s : -x %s\n", tmp->desc, tmp->var);
tmp = tmp->next;
}
}
upsdrv_help();
}
#endif /* DRIVERS_MAIN_WITHOUT_MAIN */
/* store these in dstate as driver.(parameter|flag) */
# ifndef DRIVERS_MAIN_WITHOUT_MAIN
static
# endif /* DRIVERS_MAIN_WITHOUT_MAIN */
void dparam_setinfo(const char *var, const char *val)
{
char vtmp[SMALLBUF];
/* store these in dstate for debugging and other help */
if (val) {
snprintf(vtmp, sizeof(vtmp), "driver.parameter.%s", var);
dstate_setinfo(vtmp, "%s", val);
return;
}
/* no value = flag */
snprintf(vtmp, sizeof(vtmp), "driver.flag.%s", var);
dstate_setinfo(vtmp, "enabled");
}
/* cram var [= <val>] data into storage */
# ifndef DRIVERS_MAIN_WITHOUT_MAIN
static
# endif /* DRIVERS_MAIN_WITHOUT_MAIN */
void storeval(const char *var, char *val)
{
vartab_t *tmp, *last;
/* NOTE: (FIXME?) The override and default mechanisms here
* effectively bypass both VAR_SENSITIVE protections and
* the constraint of having previously defined the name by
* addvar() in a driver codebase, or of having a dot in it.
* See https://github.com/networkupstools/nut/issues/1891
* if this would need solving eventually. At the moment the
* sensitivity impacts certain auth values for netxml-ups
* and snmp-ups reading from vartab directly, and overrides
* are ignored - so no practical problem to solve right now.
*/
if (!strncasecmp(var, "override.", 9)) {
/* NOTE: No regard for VAR_SENSITIVE here */
dstate_setinfo(var+9, "%s", val);
dstate_setflags(var+9, ST_FLAG_IMMUTABLE);
dparam_setinfo(var, val);
return;
}
if (!strncasecmp(var, "default.", 8)) {
/* NOTE: No regard for VAR_SENSITIVE here */
dstate_setinfo(var+8, "%s", val);
dparam_setinfo(var, val);
return;
}
tmp = last = vartab_h;
while (tmp) {
last = tmp;
/* sanity check */
if (!tmp->var) {
tmp = tmp->next;
continue;
}
/* later definitions overwrite earlier ones */
if (!strcasecmp(tmp->var, var)) {
free(tmp->val);
if (val)
tmp->val = xstrdup(val);
/* don't keep things like SNMP community strings */
if ((tmp->vartype & VAR_SENSITIVE) == 0) {
dparam_setinfo(var, val);
} else {
upsdebugx(4, "%s: skip dparam_setinfo() "
"for sensitive variable '%s'",
__func__, var);
}
tmp->found = 1;
return;
}
tmp = tmp->next;
}
/* try to help them out */
printf("\nFatal error: '%s' is not a valid %s for this driver.\n", var,
val ? "variable name" : "flag");
printf("\n");
printf("Look in the man page or call this driver with -h for a list of\n");
printf("valid variable names and flags.\n");
if (!strcmp(progname, "nutdrv_qx")) {
/* First many entries are from nut_usb_addvars() implementations;
* the latter two (about langid) are from nutdrv_qx.c
*/
if (!strcmp(var, "vendor")
|| !strcmp(var, "product")
|| !strcmp(var, "serial")
|| !strcmp(var, "vendorid")
|| !strcmp(var, "productid")
|| !strcmp(var, "bus")
|| !strcmp(var, "device")
|| !strcmp(var, "busport")
|| !strcmp(var, "usb_set_altinterface")
|| !strcmp(var, "usb_config_index")
|| !strcmp(var, "usb_hid_rep_index")
|| !strcmp(var, "usb_hid_desc_index")
|| !strcmp(var, "usb_hid_ep_in")
|| !strcmp(var, "usb_hid_ep_out")
|| !strcmp(var, "allow_duplicates")
|| !strcmp(var, "langid_fix")
|| !strcmp(var, "noscanlangid")
) {
printf("\nNOTE: for driver '%s', options like '%s' are only available\n"
"if it was built with USB support. If you are running a custom build of NUT,\n"
"please check results of the `configure` checks, and consider an explicit\n"
"`--with-usb` option. Also make sure that both libusb library and headers\n"
"are installed in your build environment.\n\n", progname, var);
}
}
exit(EXIT_SUCCESS);
}
/* retrieve the value of variable <var> if possible */
char *getval(const char *var)
{
vartab_t *tmp = vartab_h;
while (tmp) {
if (!strcasecmp(tmp->var, var))
return(tmp->val);
tmp = tmp->next;
}
return NULL;
}
/* see if <var> has been defined, even if no value has been given to it */
int testvar(const char *var)
{
vartab_t *tmp = vartab_h;
while (tmp) {
if (!strcasecmp(tmp->var, var))
return tmp->found;
tmp = tmp->next;
}
return 0; /* not found */
}
/* See if <var> can be (re-)loaded now: either is reloadable by definition,
* or no value has been given to it yet. Returns "-1" if nothing needs to
* be done and that is not a failure (e.g. value not modified so we do not
* care if we may change it or not).
*/
int testvar_reloadable(const char *var, const char *val, int vartype)
{
vartab_t *tmp = vartab_h;
int verdict = -2;
/* FIXME: handle VAR_FLAG typed (bitmask) values specially somehow?
* Either we set the flag at some point (because its name is mentioned)
* or we do not (initially set - no way so far to know it got commented
* away before a reload on the fly). Might load new config info into a
* separate list and then compare missing points?..
*/
upsdebugx(6, "%s: searching for var=%s, vartype=%d, reload_flag=%d",
__func__, NUT_STRARG(var), vartype, reload_flag);
while (tmp) {
if (!strcasecmp(tmp->var, var)) {
/* variable name is known */
upsdebugx(6, "%s: found var=%s, val='%s' => '%s', vartype=%d => %d, found=%d, reloadable=%d, reload_flag=%d",
__func__, NUT_STRARG(var),
NUT_STRARG(tmp->val), NUT_STRARG(val),
tmp->vartype, vartype,
tmp->found, tmp->reloadable, reload_flag);
if (val && tmp->val) {
/* a value is already known by name
* and bitmask for VAR_FLAG/VAR_VALUE matches
*/
if ((vartype & tmp->vartype) && !strcasecmp(tmp->val, val)) {
if ((tmp->vartype & VAR_FLAG) && val == NULL) {
if (reload_flag) {
upsdebugx(1, "%s: setting '%s' "
"exists and is a flag; "
"new value was not specified",
__func__, var);
}
/* by default: apply flags initially, ignore later */
verdict = (
(!reload_flag) /* For initial config reads, legacy code trusted what it saw */
|| tmp->reloadable /* set in addvar*() */
);
goto finish;
}
if (reload_flag) {
upsdebugx(1, "%s: setting '%s' "
"exists and is unmodified",
__func__, var);
}
verdict = -1; /* no-op for caller */
goto finish;
} else {
/* warn loudly if we are reloading and
* can not change this modified value */
upsdebugx((reload_flag ? (tmp->reloadable ? 1 : 0) : 1),
"%s: setting '%s' exists and differs: "
"new type bitmask %d vs. %d, "
"new value '%s' vs. '%s'%s",
__func__, var,
vartype, tmp->vartype,
val, tmp->val,
((!reload_flag || tmp->reloadable) ? "" :
" (driver restart is needed to apply)")
);
/* FIXME: Define a special EXIT_RELOAD or something,
* for "not quite a failure"? Or close connections
* and re-exec() this driver from scratch (and so to
* keep MAINPID for systemd et al)?
*/
if (reload_flag == 2 && !tmp->reloadable)
fatalx(
#ifndef WIN32
(128 + SIGCMD_RELOAD_OR_EXIT)
#else
EXIT_SUCCESS
#endif
, "NUT driver reload-or-exit: setting %s was changed and requires a driver restart", var);
verdict = (
(!reload_flag) /* For initial config reads, legacy code trusted what it saw */
|| tmp->reloadable /* set in addvar*() */
);
/* handle reload-or-error reports */
if (verdict == 0) {
if (reload_requires_restart < 1)
reload_requires_restart = 1;
else
reload_requires_restart++;
}
goto finish;
}
}
/* okay to redefine if not yet defined, or if reload is allowed,
* or if initially loading the configs
*/
verdict = (
(!reload_flag)
|| ((!tmp->found) || tmp->reloadable)
);
goto finish;
}
tmp = tmp->next;
}
verdict = 1; /* not found, may (re)load the definition */
finish:
switch (verdict) {
case -1: /* no-op for caller, same value remains */
case 1: /* value may be (re-)applied */
if (reload_requires_restart < 0)
reload_requires_restart = 0;
break;
case 0: /* value may not be (re-)applied, but it may not have been required */
break;
}
upsdebugx(6, "%s: verdict for (re)loading var=%s value: %d",
__func__, NUT_STRARG(var), verdict);
return verdict;
}
/* Similar to testvar_reloadable() above which is for addvar*() defined
* entries, but for less streamlined stuff defined right here in main.c.
* See if value (probably saved in dstate) can be (re-)loaded now: either
* it is reloadable by parameter definition, or no value has been saved
* into it yet (<oldval> is NULL).
* Returns "-1" if nothing needs to be done and that is not a failure
* (e.g. value not modified so we do not care if we may change it or not).
*/
int testval_reloadable(const char *var, const char *oldval, const char *newval, int reloadable)
{
int verdict = -2;
upsdebugx(6, "%s: var=%s, oldval=%s, newval=%s, reloadable=%d, reload_flag=%d",
__func__, NUT_STRARG(var), NUT_STRARG(oldval), NUT_STRARG(newval),
reloadable, reload_flag);
/* Nothing saved yet? Okay to store new value! */
if (!oldval) {
verdict = 1;
goto finish;
}
/* Should not happen? Or... (commented-away etc.) */
if (!newval) {
upslogx(LOG_WARNING, "%s: new setting for '%s' is NULL", __func__, var);
verdict = ((!reload_flag) || reloadable);
goto finish;
}
/* a value is already known, another is desired */
if (!strcasecmp(oldval, newval)) {
if (reload_flag) {
upsdebugx(1, "%s: setting '%s' "
"exists and is unmodified",
__func__, var);
}
verdict = -1; /* no-op for caller */
goto finish;
} else {
/* warn loudly if we are reloading and
* can not change this modified value */
upsdebugx((reload_flag ? (reloadable ? 1 : 0) : 1),
"%s: setting '%s' exists and differs: "
"new value '%s' vs. '%s'%s",
__func__, var,
newval, oldval,
((!reload_flag || reloadable) ? "" :
" (driver restart is needed to apply)")
);
/* FIXME: Define a special EXIT_RELOAD or something,
* for "not quite a failure"? Or close connections
* and re-exec() this driver from scratch (and so to
* keep MAINPID for systemd et al)?
*/
if (reload_flag == 2 && !reloadable)
fatalx(
#ifndef WIN32
(128 + SIGCMD_RELOAD_OR_EXIT)
#else
EXIT_SUCCESS
#endif
, "NUT driver reload-or-exit: setting %s was changed and requires a driver restart", var);
/* For initial config reads, legacy code trusted what it saw */
verdict = ((!reload_flag) || reloadable);
/* handle reload-or-error reports */
if (verdict == 0) {
if (reload_requires_restart < 1)
reload_requires_restart = 1;
else
reload_requires_restart++;
}
goto finish;
}
finish:
switch (verdict) {
case -1: /* no-op for caller, same value remains */
case 1: /* value may be (re-)applied */
if (reload_requires_restart < 0)
reload_requires_restart = 0;
break;
case 0: /* value may not be (re-)applied, but it may not have been required */
break;
}
upsdebugx(6, "%s: verdict for (re)loading var=%s value: %d",
__func__, NUT_STRARG(var), verdict);
return verdict;
}
/* Similar to testvar_reloadable() above which is for addvar*() defined
* entries, but for less streamlined stuff defined right here in main.c.
* See if <var> (by <arg> name saved in dstate) can be (re-)loaded now:
* either it is reloadable by parameter definition, or no value has been
* saved into it yet (<oldval> is NULL).
* Returns "-1" if nothing needs to be done and that is not a failure
* (e.g. value not modified so we do not care if we may change it or not).
*/
int testinfo_reloadable(const char *var, const char *infoname, const char *newval, int reloadable)
{
int verdict = -2;
upsdebugx(6, "%s: var=%s, infoname=%s, newval=%s, reloadable=%d, reload_flag=%d",
__func__, NUT_STRARG(var), NUT_STRARG(infoname), NUT_STRARG(newval),
reloadable, reload_flag);
/* Keep legacy behavior: not reloading, trust the initial config */
if (!reload_flag || !infoname) {
verdict = 1;
goto finish;
}
/* Suffer the overhead of lookups only if reloading */
/* FIXME: handle "driver.flag.*" prefixed values specially somehow?
* Either we set the flag at some point (because its name is mentioned)
* or we do not (initially set - no way so far to know it got commented
* away before a reload on the fly). Might load new config info into a
* separate list and then compare missing points?..
*/
verdict = testval_reloadable(var, dstate_getinfo(infoname), newval, reloadable);
finish:
upsdebugx(6, "%s: verdict for (re)loading var=%s value: %d",
__func__, NUT_STRARG(var), verdict);
return verdict;
}
/* implement callback from driver - create the table for -x/conf entries */
static void do_addvar(int vartype, const char *name, const char *desc, int reloadable)
{
vartab_t *tmp, *last;
tmp = last = vartab_h;
while (tmp) {
last = tmp;
tmp = tmp->next;
}
tmp = xmalloc(sizeof(vartab_t));
tmp->vartype = vartype;
tmp->var = xstrdup(name);
tmp->val = NULL;
tmp->desc = xstrdup(desc);
tmp->found = 0;
tmp->reloadable = reloadable;
tmp->next = NULL;
if (last)
last->next = tmp;
else
vartab_h = tmp;
}
/* public callback from driver - create the table for -x/conf entries for reloadable values */
void addvar_reloadable(int vartype, const char *name, const char *desc)
{
do_addvar(vartype, name, desc, 1);
}
/* public callback from driver - create the table for -x/conf entries for set-once values */
void addvar(int vartype, const char *name, const char *desc)
{
do_addvar(vartype, name, desc, 0);
}
/* handle instant commands common for all drivers */
int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) {
char buf[SMALLBUF];
if (conn)
#ifndef WIN32
snprintf(buf, sizeof(buf), "socket %d", conn->fd);
#else
snprintf(buf, sizeof(buf), "handle %p", conn->fd);
#endif
else
snprintf(buf, sizeof(buf), "(null)");
upsdebugx(2, "entering main_instcmd(%s, %s) for [%s] on %s",
cmdname, extra, NUT_STRARG(upsname), buf);
if (!strcmp(cmdname, "driver.killpower")) {
if (!strcmp("1", dstate_getinfo("driver.flag.allow_killpower"))) {
upslogx(LOG_WARNING, "Requesting UPS [%s] to power off, "
"as/if handled by its driver by default (may exit), "
"due to socket protocol request", NUT_STRARG(upsname));
upsdrv_shutdown();
return STAT_INSTCMD_HANDLED;
} else {
upslogx(LOG_WARNING, "Got socket protocol request for UPS [%s] "
"to power off, but driver.flag.allow_killpower does not"
"permit this - request was currently ignored!",
NUT_STRARG(upsname));
return STAT_INSTCMD_INVALID;
}
}
#ifndef WIN32
/* TODO: Equivalent for WIN32 - see SIGCMD_RELOAD in upd and upsmon */
if (!strcmp(cmdname, "driver.reload")) {
set_reload_flag(SIGCMD_RELOAD);
/* TODO: sync mode to track that reload finished, and how?
* Especially to know if there were values we can not change
* on the fly, so caller may want to restart the driver itself.
*/
return STAT_INSTCMD_HANDLED;
}
if (!strcmp(cmdname, "driver.reload-or-exit")) {
set_reload_flag(SIGCMD_RELOAD_OR_EXIT);
return STAT_INSTCMD_HANDLED;
}
# ifdef SIGCMD_RELOAD_OR_RESTART
if (!strcmp(cmdname, "driver.reload-or-restart")) {
set_reload_flag(SIGCMD_RELOAD_OR_RESTART);
return STAT_INSTCMD_HANDLED;
}
# endif
#endif /* WIN32 */
#ifndef DRIVERS_MAIN_WITHOUT_MAIN
if (!strcmp(cmdname, "driver.reload-or-error")) {
/* sync-capable handling */
set_reload_flag(SIGCMD_RELOAD_OR_ERROR);
/* Returns a result code from INSTCMD enum values */
return handle_reload_flag();
}
#endif
/* By default, the driver-specific values are
* unknown to shared standard handler */
upsdebugx(2, "shared %s() does not handle command %s, "
"proceeding to driver-specific handler",
__func__, cmdname);
return STAT_INSTCMD_UNKNOWN;
}
/* handle setting variables common for all drivers */
int main_setvar(const char *varname, const char *val, conn_t *conn) {
char buf[SMALLBUF];
if (conn)
#ifndef WIN32
snprintf(buf, sizeof(buf), "socket %d", conn->fd);
#else
snprintf(buf, sizeof(buf), "handle %p", conn->fd);
#endif
else
snprintf(buf, sizeof(buf), "(null)");
upsdebugx(2, "entering main_setvar(%s, %s) for [%s] on %s",
varname, val, NUT_STRARG(upsname), buf);
if (!strcmp(varname, "driver.debug")) {
int num;
if (str_to_int(val, &num, 10)) {
if (num < 0) {
upsdebugx(nut_debug_level > 0 ? 1 : 0,
"NOTE: Will fall back to CLI/DriverConfig/GlobalConfig debug verbosity preference now");
num = -1;
}
if (nut_debug_level > 0 && num == 0)
upsdebugx(1, "NOTE: Will disable verbose debug now, due to socket protocol request");
nut_debug_level_protocol = num;
assign_debug_level();
return STAT_SET_HANDLED;
} else {
goto invalid;
}
}
if (!strcmp(varname, "driver.flag.allow_killpower")) {
int num = 0;
if (str_to_int(val, &num, 10)) {
if (num <= 0) {
num = 0;
} else num = 1;
} else {
/* support certain strings */
if (!strncmp(val, "enable", 6) /* "enabled" matches too */
|| !strcmp(val, "true")
|| !strcmp(val, "yes")
|| !strcmp(val, "on")
) num = 1;
}
upsdebugx(1, "%s: Setting %s=%d", __func__, varname, num);
dstate_setinfo("driver.flag.allow_killpower", "%d", num);
return STAT_SET_HANDLED;
}
/* By default, the driver-specific values are
* unknown to shared standard handler */
upsdebugx(2, "shared %s() does not handle variable %s, "
"proceeding to driver-specific handler",
__func__, varname);
return STAT_SET_UNKNOWN;
invalid:
upsdebugx(1, "Error: UPS [%s]: invalid %s value: %s",
NUT_STRARG(upsname), varname, val);
return STAT_SET_INVALID;
}
/* handle -x / ups.conf config details that are for this part of the code */
static int main_arg(char *var, char *val)
{
int do_handle = -2;
/* flags for main */
upsdebugx(3, "%s: var='%s' val='%s'",
__func__,
var ? var : "<null>", /* null should not happen... but... */
val ? val : "<null>");
/* !reload_flag simply forbids changing this flag on the fly, as
* it would have no effect anyway without a (serial) reconnection
*/
if (!strcmp(var, "nolock")) {
if (reload_flag) {
upsdebugx(6, "%s: SKIP: flag var='%s' can not be reloaded", __func__, var);
} else {
do_lock_port = 0;
dstate_setinfo("driver.flag.nolock", "enabled");
}
return 1; /* handled */
}
/* FIXME: this one we could potentially reload, but need to figure
* out that the flag line was commented away or deleted -- there is
* no setting value to flip in configs here
*/
if (!strcmp(var, "ignorelb")) {
if (reload_flag) {
upsdebugx(6, "%s: SKIP: flag var='%s' currently can not be reloaded", __func__, var);
} else {
dstate_setinfo("driver.flag.ignorelb", "enabled");
}
return 1; /* handled */
}
if (!strcmp(var, "allow_killpower")) {
if (reload_flag) {
upsdebugx(6, "%s: SKIP: flag var='%s' currently can not be reloaded "
"(but may be changed by protocol SETVAR)", __func__, var);
} else {
dstate_setinfo("driver.flag.allow_killpower", "1");
}
return 1; /* handled */
}
/* any other flags are for the driver code */
if (!val)
return 0; /* unhandled, pass it through to the driver */
/* In checks below, testinfo_reloadable(..., 0) should forbid
* re-population of the setting with a new value, but emit a
* warning if it did change (so driver restart is needed to apply)
*/
/* variables for main: port */
if (!strcmp(var, "port")) {
if (testinfo_reloadable(var, "driver.parameter.port", val, 0) > 0) {
device_path = xstrdup(val);
device_name = xbasename(device_path);
dstate_setinfo("driver.parameter.port", "%s", val);
}
return 1; /* handled */
}
/* user specified at the driver level overrides that on global level
* or the built-in default
*/
if (!strcmp(var, "user")) {
if (testval_reloadable(var, user, val, 0) > 0) {
if (user_from_cmdline) {
upsdebugx(0, "User '%s' specified in driver section "
"was ignored due to '%s' specified on command line",
val, user);
} else {
upsdebugx(1, "Overriding previously specified user '%s' "
"with '%s' specified for driver section",
user, val);
free(user);
user = xstrdup(val);
}
}
return 1; /* handled */
}
if (!strcmp(var, "group")) {
if (testval_reloadable(var, group, val, 0) > 0) {
if (group_from_cmdline) {
upsdebugx(0, "Group '%s' specified in driver section "
"was ignored due to '%s' specified on command line",
val, group);
} else {
upsdebugx(1, "Overriding previously specified group '%s' "
"with '%s' specified for driver section",
group, val);
free(group);
group = xstrdup(val);
}
}
return 1; /* handled */
}
if (!strcmp(var, "sddelay")) {
upslogx(LOG_INFO, "Obsolete value sddelay found in ups.conf");
return 1; /* handled */
}
/* Allow per-driver overrides of the global setting
* and allow to reload this, why not.
* Note: having both global+driver section definitions may
* cause noise, but it allows either to be commented away
* and the other to take hold. Both disappearing would not
* be noticed by the reload operation currently, however.
*/
if (!strcmp(var, "pollinterval")) {
char buf[SMALLBUF];
/* log a message if value changed; skip if no good buf */
if (snprintf(buf, sizeof(buf), "%" PRIdMAX, (intmax_t)poll_interval)) {
if ((do_handle = testval_reloadable(var, buf, val, 1)) == 0) {
/* Should not happen, but... */
fatalx(EXIT_FAILURE, "Error: failed to check "
"testval_reloadable() for pollinterval: "
"old %s vs. new %s", buf, NUT_STRARG(val));
}
}
if (do_handle > 0) {
int ipv = atoi(val);
if (ipv > 0) {
poll_interval = (time_t)ipv;
} else {
fatalx(EXIT_FAILURE, "Error: UPS [%s]: invalid pollinterval: %d",
NUT_STRARG(upsname), ipv);
}
} /* else: no-op */
return 1; /* handled */
}
/* Allow per-driver overrides of the global setting
* and allow to reload this, why not.