1 /* upsdrvctl.c - UPS driver controller
2
3 Copyright (C) 2001 Russell Kroll <rkroll@exploits.org>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include <sys/wait.h>
26
27 #include "config.h"
28 #include "proto.h"
29 #include "common.h"
30 #include "upsconf.h"
31
32 typedef struct {
33 char *upsname;
34 char *driver;
35 char *port;
36 int sdorder;
37 int maxstartdelay;
38 void *next;
39 } ups_t;
40
41 static ups_t *upstable = NULL;
42
43 static int maxsdorder = 0, testmode = 0, exec_error = 0;
44
45 /* timer - keeps us from getting stuck if a driver hangs */
46 static int maxstartdelay = 45;
47
48 /* counter - retry that many time(s) to start the driver if it fails to */
49 static int maxretry = 1;
50
51 /* timer - delay between each restart attempt of the driver(s) */
52 static int retrydelay = 5;
53
54 /* Directory where driver executables live */
55 static char *driverpath = NULL;
56
57 /* passthrough to the drivers: chroot path and new user name */
58 static char *pt_root = NULL, *pt_user = NULL;
59
do_upsconf_args(char * upsname,char * var,char * val)60 void do_upsconf_args(char *upsname, char *var, char *val)
61 {
62 ups_t *tmp, *last;
63
64 /* handle global declarations */
65 if (!upsname) {
66 if (!strcmp(var, "maxstartdelay"))
67 maxstartdelay = atoi(val);
68
69 if (!strcmp(var, "driverpath")) {
70 free(driverpath);
71 driverpath = xstrdup(val);
72 }
73
74 if (!strcmp(var, "maxretry"))
75 maxretry = atoi(val);
76
77 if (!strcmp(var, "retrydelay"))
78 retrydelay = atoi(val);
79
80 /* ignore anything else - it's probably for main */
81
82 return;
83 }
84
85 last = tmp = upstable;
86
87 while (tmp) {
88 last = tmp;
89
90 if (!strcmp(tmp->upsname, upsname)) {
91 if (!strcmp(var, "driver"))
92 tmp->driver = xstrdup(val);
93
94 if (!strcmp(var, "port"))
95 tmp->port = xstrdup(val);
96
97 if (!strcmp(var, "maxstartdelay"))
98 tmp->maxstartdelay = atoi(val);
99
100 if (!strcmp(var, "sdorder")) {
101 tmp->sdorder = atoi(val);
102
103 if (tmp->sdorder > maxsdorder)
104 maxsdorder = tmp->sdorder;
105 }
106
107 return;
108 }
109
110 tmp = tmp->next;
111 }
112
113 tmp = xmalloc(sizeof(ups_t));
114 tmp->upsname = xstrdup(upsname);
115 tmp->driver = NULL;
116 tmp->port = NULL;
117 tmp->next = NULL;
118 tmp->sdorder = 0;
119 tmp->maxstartdelay = -1; /* use global value by default */
120
121 if (!strcmp(var, "driver"))
122 tmp->driver = xstrdup(val);
123
124 if (!strcmp(var, "port"))
125 tmp->port = xstrdup(val);
126
127 if (last)
128 last->next = tmp;
129 else
130 upstable = tmp;
131 }
132
133 /* handle sending the signal */
stop_driver(const ups_t * ups)134 static void stop_driver(const ups_t *ups)
135 {
136 char pidfn[SMALLBUF];
137 int ret;
138 struct stat fs;
139
140 upsdebugx(1, "Stopping UPS: %s", ups->upsname);
141
142 snprintf(pidfn, sizeof(pidfn), "%s/%s-%s.pid", altpidpath(),
143 ups->driver, ups->upsname);
144 ret = stat(pidfn, &fs);
145
146 if ((ret != 0) && (ups->port != NULL)) {
147 snprintf(pidfn, sizeof(pidfn), "%s/%s-%s.pid", altpidpath(),
148 ups->driver, xbasename(ups->port));
149 ret = stat(pidfn, &fs);
150 }
151
152 if (ret != 0) {
153 upslog_with_errno(LOG_ERR, "Can't open %s", pidfn);
154 exec_error++;
155 return;
156 }
157
158 upsdebugx(2, "Sending signal to %s", pidfn);
159
160 if (testmode)
161 return;
162
163 ret = sendsignalfn(pidfn, SIGTERM);
164
165 if (ret < 0) {
166 upslog_with_errno(LOG_ERR, "Stopping %s failed", pidfn);
167 exec_error++;
168 return;
169 }
170 }
171
waitpid_timeout(const int sig)172 static void waitpid_timeout(const int sig)
173 {
174 /* do nothing */
175 return;
176 }
177
178 /* print out a command line at the given debug level. */
debugcmdline(int level,const char * msg,char * const argv[])179 static void debugcmdline(int level, const char *msg, char *const argv[])
180 {
181 char cmdline[LARGEBUF];
182
183 snprintf(cmdline, sizeof(cmdline), "%s", msg);
184
185 while (*argv) {
186 snprintfcat(cmdline, sizeof(cmdline), " %s", *argv++);
187 }
188
189 upsdebugx(level, "%s", cmdline);
190 }
191
forkexec(char * const argv[],const ups_t * ups)192 static void forkexec(char *const argv[], const ups_t *ups)
193 {
194 int ret;
195 pid_t pid;
196
197 pid = fork();
198
199 if (pid < 0)
200 fatal_with_errno(EXIT_FAILURE, "fork");
201
202 if (pid != 0) { /* parent */
203 int wstat;
204 struct sigaction sa;
205
206 sigemptyset(&sa.sa_mask);
207 sa.sa_flags = 0;
208 sa.sa_handler = waitpid_timeout;
209 sigaction(SIGALRM, &sa, NULL);
210
211 if (ups->maxstartdelay != -1)
212 alarm(ups->maxstartdelay);
213 else
214 alarm(maxstartdelay);
215
216 ret = waitpid(pid, &wstat, 0);
217
218 alarm(0);
219
220 if (ret == -1) {
221 upslogx(LOG_WARNING, "Startup timer elapsed, continuing...");
222 exec_error++;
223 return;
224 }
225
226 if (WIFEXITED(wstat) == 0) {
227 upslogx(LOG_WARNING, "Driver exited abnormally");
228 exec_error++;
229 return;
230 }
231
232 if (WEXITSTATUS(wstat) != 0) {
233 upslogx(LOG_WARNING, "Driver failed to start"
234 " (exit status=%d)", WEXITSTATUS(wstat));
235 exec_error++;
236 return;
237 }
238
239 /* the rest only work when WIFEXITED is nonzero */
240
241 if (WIFSIGNALED(wstat)) {
242 upslog_with_errno(LOG_WARNING, "Driver died after signal %d",
243 WTERMSIG(wstat));
244 exec_error++;
245 }
246
247 return;
248 }
249
250 /* child */
251
252 ret = execv(argv[0], argv);
253
254 /* shouldn't get here */
255 fatal_with_errno(EXIT_FAILURE, "execv");
256 }
257
start_driver(const ups_t * ups)258 static void start_driver(const ups_t *ups)
259 {
260 char *argv[8];
261 char dfn[SMALLBUF];
262 int ret, arg = 0;
263 int initial_exec_error = exec_error, drv_maxretry = maxretry;
264 struct stat fs;
265
266 upsdebugx(1, "Starting UPS: %s", ups->upsname);
267
268 snprintf(dfn, sizeof(dfn), "%s/%s", driverpath, ups->driver);
269 ret = stat(dfn, &fs);
270
271 if (ret < 0)
272 fatal_with_errno(EXIT_FAILURE, "Can't start %s", dfn);
273
274 argv[arg++] = dfn;
275 argv[arg++] = (char *)"-a"; /* FIXME: cast away const */
276 argv[arg++] = ups->upsname;
277
278 /* stick on the chroot / user args if given to us */
279 if (pt_root) {
280 argv[arg++] = (char *)"-r"; /* FIXME: cast away const */
281 argv[arg++] = pt_root;
282 }
283
284 if (pt_user) {
285 argv[arg++] = (char *)"-u"; /* FIXME: cast away const */
286 argv[arg++] = pt_user;
287 }
288
289 /* tie it off */
290 argv[arg++] = NULL;
291
292
293 while (drv_maxretry > 0) {
294 int cur_exec_error = exec_error;
295
296 upsdebugx(2, "%i remaining attempts", drv_maxretry);
297 debugcmdline(2, "exec: ", argv);
298 drv_maxretry--;
299
300 if (!testmode) {
301 forkexec(argv, ups);
302 }
303
304 /* driver command succeeded */
305 if (cur_exec_error == exec_error) {
306 drv_maxretry = 0;
307 exec_error = initial_exec_error;
308 }
309 else {
310 /* otherwise, retry if still needed */
311 if (drv_maxretry > 0)
312 sleep (retrydelay);
313 }
314 }
315 }
316
help(const char * progname)317 static void help(const char *progname)
318 {
319 printf("Starts and stops UPS drivers via ups.conf.\n\n");
320 printf("usage: %s [OPTIONS] (start | stop | shutdown) [<ups>]\n\n", progname);
321
322 printf(" -h display this help\n");
323 printf(" -r <path> drivers will chroot to <path>\n");
324 printf(" -t testing mode - prints actions without doing them\n");
325 printf(" -u <user> drivers started will switch from root to <user>\n");
326 printf(" -D raise debugging level\n");
327 printf(" start start all UPS drivers in ups.conf\n");
328 printf(" start <ups> only start driver for UPS <ups>\n");
329 printf(" stop stop all UPS drivers in ups.conf\n");
330 printf(" stop <ups> only stop driver for UPS <ups>\n");
331 printf(" shutdown shutdown all UPS drivers in ups.conf\n");
332 printf(" shutdown <ups> only shutdown UPS <ups>\n");
333
334 exit(EXIT_SUCCESS);
335 }
336
shutdown_driver(const ups_t * ups)337 static void shutdown_driver(const ups_t *ups)
338 {
339 char *argv[9];
340 char dfn[SMALLBUF];
341 int arg = 0;
342
343 upsdebugx(1, "Shutdown UPS: %s", ups->upsname);
344
345 snprintf(dfn, sizeof(dfn), "%s/%s", driverpath, ups->driver);
346
347 argv[arg++] = dfn;
348 argv[arg++] = (char *)"-a"; /* FIXME: cast away const */
349 argv[arg++] = ups->upsname;
350 argv[arg++] = (char *)"-k"; /* FIXME: cast away const */
351
352 /* stick on the chroot / user args if given to us */
353 if (pt_root) {
354 argv[arg++] = (char *)"-r"; /* FIXME: cast away const */
355 argv[arg++] = pt_root;
356 }
357
358 if (pt_user) {
359 argv[arg++] = (char *)"-u"; /* FIXME: cast away const */
360 argv[arg++] = pt_user;
361 }
362
363 argv[arg++] = NULL;
364
365 debugcmdline(2, "exec: ", argv);
366
367 if (!testmode) {
368 forkexec(argv, ups);
369 }
370 }
371
send_one_driver(void (* command)(const ups_t *),const char * upsname)372 static void send_one_driver(void (*command)(const ups_t *), const char *upsname)
373 {
374 ups_t *ups = upstable;
375
376 if (!ups)
377 fatalx(EXIT_FAILURE, "Error: no UPS definitions found in ups.conf!\n");
378
379 while (ups) {
380 if (!strcmp(ups->upsname, upsname)) {
381 command(ups);
382 return;
383 }
384
385 ups = ups->next;
386 }
387
388 fatalx(EXIT_FAILURE, "UPS %s not found in ups.conf", upsname);
389 }
390
391 /* walk UPS table and send command to all UPSes according to sdorder */
send_all_drivers(void (* command)(const ups_t *))392 static void send_all_drivers(void (*command)(const ups_t *))
393 {
394 ups_t *ups;
395 int i;
396
397 if (!upstable)
398 fatalx(EXIT_FAILURE, "Error: no UPS definitions found in ups.conf");
399
400 if (command != &shutdown_driver) {
401 ups = upstable;
402
403 while (ups) {
404 command(ups);
405
406 ups = ups->next;
407 }
408
409 return;
410 }
411
412 for (i = 0; i <= maxsdorder; i++) {
413 ups = upstable;
414
415 while (ups) {
416 if (ups->sdorder == i)
417 command(ups);
418
419 ups = ups->next;
420 }
421 }
422 }
423
exit_cleanup(void)424 static void exit_cleanup(void)
425 {
426 ups_t *tmp, *next;
427
428 tmp = upstable;
429
430 while (tmp) {
431 next = tmp->next;
432
433 free(tmp->driver);
434 free(tmp->port);
435 free(tmp->upsname);
436 free(tmp);
437
438 tmp = next;
439 }
440
441 free(driverpath);
442 }
443
main(int argc,char ** argv)444 int main(int argc, char **argv)
445 {
446 int i;
447 char *prog;
448 void (*command)(const ups_t *) = NULL;
449
450 printf("Network UPS Tools - UPS driver controller %s\n",
451 UPS_VERSION);
452
453 prog = argv[0];
454 while ((i = getopt(argc, argv, "+htu:r:DV")) != -1) {
455 switch(i) {
456 case 'r':
457 pt_root = optarg;
458 break;
459
460 case 't':
461 testmode = 1;
462 break;
463
464 case 'u':
465 pt_user = optarg;
466 break;
467
468 case 'V':
469 exit(EXIT_SUCCESS);
470
471 case 'D':
472 nut_debug_level++;
473 break;
474
475 case 'h':
476 default:
477 help(prog);
478 break;
479 }
480 }
481
482 argc -= optind;
483 argv += optind;
484
485 if (argc < 1)
486 help(prog);
487
488 if (testmode) {
489 printf("*** Testing mode: not calling exec/kill\n");
490
491 if (nut_debug_level < 2)
492 nut_debug_level = 2;
493 }
494
495 upsdebugx(2, "\n"
496 "If you're not a NUT core developer, chances are that you're told to enable debugging\n"
497 "to see why a driver isn't working for you. We're sorry for the confusion, but this is\n"
498 "the 'upsdrvctl' wrapper, not the driver you're interested in.\n\n"
499 "Below you'll find one or more lines starting with 'exec:' followed by an absolute\n"
500 "path to the driver binary and some command line option. This is what the driver\n"
501 "starts and you need to copy and paste that line and append the debug flags to that\n"
502 "line (less the 'exec:' prefix).\n");
503
504 if (!strcmp(argv[0], "start"))
505 command = &start_driver;
506
507 if (!strcmp(argv[0], "stop"))
508 command = &stop_driver;
509
510 if (!strcmp(argv[0], "shutdown"))
511 command = &shutdown_driver;
512
513 if (!command)
514 fatalx(EXIT_FAILURE, "Error: unrecognized command [%s]", argv[0]);
515
516 driverpath = xstrdup(DRVPATH); /* set default */
517
518 atexit(exit_cleanup);
519
520 read_upsconf();
521
522 if (argc == 1)
523 send_all_drivers(command);
524 else
525 send_one_driver(command, argv[1]);
526
527 if (exec_error)
528 exit(EXIT_FAILURE);
529
530 exit(EXIT_SUCCESS);
531 }
532