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