xref: /dflybsd-src/sbin/svc/svc.c (revision f999381066ef8eacfcf5dfd028fc05bcf9abc119)
13e2ea4ccSMatthew Dillon /*
23e2ea4ccSMatthew Dillon  * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
33e2ea4ccSMatthew Dillon  *
43e2ea4ccSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
53e2ea4ccSMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
63e2ea4ccSMatthew Dillon  *
73e2ea4ccSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
83e2ea4ccSMatthew Dillon  * modification, are permitted provided that the following conditions
93e2ea4ccSMatthew Dillon  * are met:
103e2ea4ccSMatthew Dillon  *
113e2ea4ccSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
123e2ea4ccSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
133e2ea4ccSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
143e2ea4ccSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
153e2ea4ccSMatthew Dillon  *    the documentation and/or other materials provided with the
163e2ea4ccSMatthew Dillon  *    distribution.
173e2ea4ccSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
183e2ea4ccSMatthew Dillon  *    contributors may be used to endorse or promote products derived
193e2ea4ccSMatthew Dillon  *    from this software without specific, prior written permission.
203e2ea4ccSMatthew Dillon  *
213e2ea4ccSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
223e2ea4ccSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
233e2ea4ccSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
243e2ea4ccSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
253e2ea4ccSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
263e2ea4ccSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
273e2ea4ccSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
283e2ea4ccSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
293e2ea4ccSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
303e2ea4ccSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
313e2ea4ccSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323e2ea4ccSMatthew Dillon  * SUCH DAMAGE.
333e2ea4ccSMatthew Dillon  */
343e2ea4ccSMatthew Dillon /*
353e2ea4ccSMatthew Dillon  * SERVICE MANAGER
363e2ea4ccSMatthew Dillon  *
373e2ea4ccSMatthew Dillon  * This program builds an environment to run a service in and provides
383e2ea4ccSMatthew Dillon  * numerous options for naming, tracking, and management.  It uses
393e2ea4ccSMatthew Dillon  * reapctl(2) to corral the processes under management.
403e2ea4ccSMatthew Dillon  */
413e2ea4ccSMatthew Dillon 
423e2ea4ccSMatthew Dillon #include "svc.h"
433e2ea4ccSMatthew Dillon 
443e2ea4ccSMatthew Dillon static int execute_remote(command_t *cmd, int (*func)(command_t *cmd));
453e2ea4ccSMatthew Dillon static int process_jailspec(command_t *cmd, const char *spec);
463e2ea4ccSMatthew Dillon 
473e2ea4ccSMatthew Dillon int
main(int ac,char ** av)483e2ea4ccSMatthew Dillon main(int ac, char **av)
493e2ea4ccSMatthew Dillon {
503e2ea4ccSMatthew Dillon 	command_t cmd;
513e2ea4ccSMatthew Dillon 	int rc;
523e2ea4ccSMatthew Dillon 
533e2ea4ccSMatthew Dillon 	signal(SIGPIPE, SIG_IGN);
543e2ea4ccSMatthew Dillon 
553e2ea4ccSMatthew Dillon 	rc = process_cmd(&cmd, stdout, ac, av);
563e2ea4ccSMatthew Dillon 	cmd.cmdline = 1;	/* commanded from front-end */
573e2ea4ccSMatthew Dillon 	cmd.commanded = 1;	/* commanded action (vs automatic) */
583e2ea4ccSMatthew Dillon 	if (rc == 0)
593e2ea4ccSMatthew Dillon 		rc = execute_cmd(&cmd);
603e2ea4ccSMatthew Dillon 	free_cmd(&cmd);
613e2ea4ccSMatthew Dillon 
623e2ea4ccSMatthew Dillon 	return rc;
633e2ea4ccSMatthew Dillon }
643e2ea4ccSMatthew Dillon 
653e2ea4ccSMatthew Dillon int
process_cmd(command_t * cmd,FILE * fp,int ac,char ** av)663e2ea4ccSMatthew Dillon process_cmd(command_t *cmd, FILE *fp, int ac, char **av)
673e2ea4ccSMatthew Dillon {
6821537d46SMatthew Dillon 	const char *optstr = "dfhp:r:R:xst:u:g:G:l:c:mj:k:T:F:";
693e2ea4ccSMatthew Dillon 	struct group *grent;
703e2ea4ccSMatthew Dillon 	struct passwd *pwent;
713e2ea4ccSMatthew Dillon 	char *sub;
723e2ea4ccSMatthew Dillon 	char *cpy;
733e2ea4ccSMatthew Dillon 	int rc = 1;
743e2ea4ccSMatthew Dillon 	int ch;
753e2ea4ccSMatthew Dillon 	int i;
763e2ea4ccSMatthew Dillon 
773e2ea4ccSMatthew Dillon 	bzero(cmd, sizeof(*cmd));
783e2ea4ccSMatthew Dillon 	cmd->fp = fp;				/* error and output reporting */
7982f39527SMatthew Dillon 	cmd->logfd = -1;
803e2ea4ccSMatthew Dillon 	sreplace(&cmd->piddir, "/var/run");	/* must not be NULL */
813e2ea4ccSMatthew Dillon 	cmd->termkill_timo = -1;		/* will use default value */
823e2ea4ccSMatthew Dillon 	cmd->orig_ac = ac;
833e2ea4ccSMatthew Dillon 	cmd->orig_av = av;
84ee6361d4SMatthew Dillon 	cmd->empty_label = 1;
853e2ea4ccSMatthew Dillon 
863e2ea4ccSMatthew Dillon 	optind = 1;
873e2ea4ccSMatthew Dillon 	opterr = 1;
883e2ea4ccSMatthew Dillon 	optreset = 1;
893e2ea4ccSMatthew Dillon 
903e2ea4ccSMatthew Dillon 	while ((ch = getopt(ac, av, optstr)) != -1) {
913e2ea4ccSMatthew Dillon 		switch(ch) {
923e2ea4ccSMatthew Dillon 		case 'd':
933e2ea4ccSMatthew Dillon 			cmd->debug = 1;
943e2ea4ccSMatthew Dillon 			cmd->foreground = 1;
953e2ea4ccSMatthew Dillon 			break;
963e2ea4ccSMatthew Dillon 		case 'f':
973e2ea4ccSMatthew Dillon 			cmd->foreground = 1;
983e2ea4ccSMatthew Dillon 			break;
99ee6361d4SMatthew Dillon 		case 'h':
100ee6361d4SMatthew Dillon 			execute_help(cmd);
101ee6361d4SMatthew Dillon 			exit(0);
102ee6361d4SMatthew Dillon 			break;
1033e2ea4ccSMatthew Dillon 		case 'p':
1043e2ea4ccSMatthew Dillon 			sreplace(&cmd->piddir, optarg);
1053e2ea4ccSMatthew Dillon 			break;
1063e2ea4ccSMatthew Dillon 		case 'r':
1073e2ea4ccSMatthew Dillon 			cmd->restart_some = 1;
1083e2ea4ccSMatthew Dillon 			cmd->restart_all = 0;
1093e2ea4ccSMatthew Dillon 			cmd->restart_timo = strtol(optarg, NULL, 0);
1103e2ea4ccSMatthew Dillon 			break;
1113e2ea4ccSMatthew Dillon 		case 'R':
1123e2ea4ccSMatthew Dillon 			cmd->restart_some = 0;
1133e2ea4ccSMatthew Dillon 			cmd->restart_all = 1;
1143e2ea4ccSMatthew Dillon 			cmd->restart_timo = strtol(optarg, NULL, 0);
1153e2ea4ccSMatthew Dillon 			break;
1163e2ea4ccSMatthew Dillon 		case 'x':
1173e2ea4ccSMatthew Dillon 			cmd->exit_mode = 1;
1183e2ea4ccSMatthew Dillon 			break;
1193e2ea4ccSMatthew Dillon 		case 's':
1203e2ea4ccSMatthew Dillon 			cmd->sync_mode = 1;
1213e2ea4ccSMatthew Dillon 			break;
1223e2ea4ccSMatthew Dillon 		case 't':
1233e2ea4ccSMatthew Dillon 			cmd->termkill_timo = strtoul(optarg, NULL, 0);
1243e2ea4ccSMatthew Dillon 			break;
1253e2ea4ccSMatthew Dillon 		case 'u':
1263e2ea4ccSMatthew Dillon 			if (isdigit(optarg[0])) {
1273e2ea4ccSMatthew Dillon 				pwent = getpwuid(strtol(optarg, NULL, 0));
12821537d46SMatthew Dillon 			} else {
12921537d46SMatthew Dillon 				pwent = getpwnam(optarg);
1303e2ea4ccSMatthew Dillon 			}
1313e2ea4ccSMatthew Dillon 			if (pwent == NULL) {
1323e2ea4ccSMatthew Dillon 				fprintf(fp, "Cannot find user %s: %s\n",
1333e2ea4ccSMatthew Dillon 					optarg,
1343e2ea4ccSMatthew Dillon 					strerror(errno));
1353e2ea4ccSMatthew Dillon 				goto failed;
1363e2ea4ccSMatthew Dillon 			}
13721537d46SMatthew Dillon 			cmd->uid_mode = 1;
1383e2ea4ccSMatthew Dillon 			sfree(&cmd->pwent.pw_name);
1393e2ea4ccSMatthew Dillon 			sfree(&cmd->pwent.pw_passwd);
1403e2ea4ccSMatthew Dillon 			sfree(&cmd->pwent.pw_class);
1413e2ea4ccSMatthew Dillon 			sfree(&cmd->pwent.pw_gecos);
1423e2ea4ccSMatthew Dillon 			sfree(&cmd->pwent.pw_dir);
1433e2ea4ccSMatthew Dillon 			sfree(&cmd->pwent.pw_shell);
1443e2ea4ccSMatthew Dillon 			cmd->pwent = *pwent;
1453e2ea4ccSMatthew Dillon 			sdup(&cmd->pwent.pw_name);
1463e2ea4ccSMatthew Dillon 			sdup(&cmd->pwent.pw_passwd);
1473e2ea4ccSMatthew Dillon 			sdup(&cmd->pwent.pw_class);
1483e2ea4ccSMatthew Dillon 			sdup(&cmd->pwent.pw_gecos);
1493e2ea4ccSMatthew Dillon 			sdup(&cmd->pwent.pw_dir);
1503e2ea4ccSMatthew Dillon 			sdup(&cmd->pwent.pw_shell);
1513e2ea4ccSMatthew Dillon 			break;
1523e2ea4ccSMatthew Dillon 		case 'g':
1533e2ea4ccSMatthew Dillon 			setgroupent(1);
1543e2ea4ccSMatthew Dillon 			if (isdigit(optarg[0])) {
1553e2ea4ccSMatthew Dillon 				grent = getgrgid(strtol(optarg, NULL, 0));
15621537d46SMatthew Dillon 			} else {
15721537d46SMatthew Dillon 				grent = getgrnam(optarg);
1583e2ea4ccSMatthew Dillon 			}
1593e2ea4ccSMatthew Dillon 			if (grent == NULL) {
1603e2ea4ccSMatthew Dillon 				fprintf(fp, "Cannot find group %s: %s\n",
1613e2ea4ccSMatthew Dillon 					optarg,
1623e2ea4ccSMatthew Dillon 					strerror(errno));
1633e2ea4ccSMatthew Dillon 				goto failed;
1643e2ea4ccSMatthew Dillon 			}
16521537d46SMatthew Dillon 			cmd->gid_mode = 1;
1663e2ea4ccSMatthew Dillon 			sfree(&cmd->grent.gr_name);
1673e2ea4ccSMatthew Dillon 			sfree(&cmd->grent.gr_passwd);
1683e2ea4ccSMatthew Dillon 			afree(&cmd->grent.gr_mem);
1693e2ea4ccSMatthew Dillon 			cmd->grent = *grent;
1703e2ea4ccSMatthew Dillon 			sdup(&cmd->grent.gr_name);
1713e2ea4ccSMatthew Dillon 			sdup(&cmd->grent.gr_passwd);
1723e2ea4ccSMatthew Dillon 			adup(&cmd->grent.gr_mem);
1733e2ea4ccSMatthew Dillon 			break;
1743e2ea4ccSMatthew Dillon 		case 'G':
1753e2ea4ccSMatthew Dillon 			setgroupent(1);
1763e2ea4ccSMatthew Dillon 			cpy = strdup(optarg);
1773e2ea4ccSMatthew Dillon 			sub = strtok(cpy, ",");
1783e2ea4ccSMatthew Dillon 			i = 0;
1793e2ea4ccSMatthew Dillon 			while (sub) {
18021537d46SMatthew Dillon 				if (isdigit(sub[0])) {
18121537d46SMatthew Dillon 					grent = getgrgid(strtol(sub, NULL, 0));
1823e2ea4ccSMatthew Dillon 				} else {
18321537d46SMatthew Dillon 					grent = getgrnam(sub);
1843e2ea4ccSMatthew Dillon 				}
1853e2ea4ccSMatthew Dillon 				if (grent == NULL) {
1863e2ea4ccSMatthew Dillon 					fprintf(fp,
1873e2ea4ccSMatthew Dillon 						"Cannot find group %s: %s\n",
18821537d46SMatthew Dillon 						sub, strerror(errno));
1893e2ea4ccSMatthew Dillon 					i = -1;
1903e2ea4ccSMatthew Dillon 				}
1913e2ea4ccSMatthew Dillon 				if (i == NGROUPS) {
1923e2ea4ccSMatthew Dillon 					fprintf(fp,
1933e2ea4ccSMatthew Dillon 						"Too many groups specified, "
1943e2ea4ccSMatthew Dillon 						"max %d\n", NGROUPS);
1953e2ea4ccSMatthew Dillon 					i = -1;
1963e2ea4ccSMatthew Dillon 				}
1973e2ea4ccSMatthew Dillon 				if (i >= 0)
1983e2ea4ccSMatthew Dillon 					cmd->groups[i++] = grent->gr_gid;
1993e2ea4ccSMatthew Dillon 				sub = strtok(NULL, ",");
2003e2ea4ccSMatthew Dillon 			}
2013e2ea4ccSMatthew Dillon 			free(cpy);
2023e2ea4ccSMatthew Dillon 			if (i < 0)
2033e2ea4ccSMatthew Dillon 				goto failed;
2043e2ea4ccSMatthew Dillon 			cmd->ngroups = i;
2053e2ea4ccSMatthew Dillon 			break;
2063e2ea4ccSMatthew Dillon 		case 'l':
2073e2ea4ccSMatthew Dillon 			sreplace(&cmd->logfile, optarg);
2083e2ea4ccSMatthew Dillon 			break;
2093e2ea4ccSMatthew Dillon 		case 'c':
2103e2ea4ccSMatthew Dillon 			sreplace(&cmd->rootdir, optarg);
2113e2ea4ccSMatthew Dillon 			break;
21221537d46SMatthew Dillon 		case 'm':
2133e2ea4ccSMatthew Dillon 			cmd->mountdev = 1;
21421537d46SMatthew Dillon 			break;
2153e2ea4ccSMatthew Dillon 		case 'j':
2163e2ea4ccSMatthew Dillon 			sreplace(&cmd->jaildir, optarg);
2173e2ea4ccSMatthew Dillon 			break;
2183e2ea4ccSMatthew Dillon 		case 'k':
2193e2ea4ccSMatthew Dillon 			rc = process_jailspec(cmd, optarg);
2203e2ea4ccSMatthew Dillon 			if (rc)
2213e2ea4ccSMatthew Dillon 				goto failed;
2223e2ea4ccSMatthew Dillon 			break;
2233e2ea4ccSMatthew Dillon 		case 'T':
2243e2ea4ccSMatthew Dillon 			sreplace(&cmd->proctitle, optarg);
2253e2ea4ccSMatthew Dillon 			break;
2263e2ea4ccSMatthew Dillon 		case 'F':
2273e2ea4ccSMatthew Dillon 			cmd->restart_per = 60;
2283e2ea4ccSMatthew Dillon 			if (sscanf(optarg, "%d:%d",
2293e2ea4ccSMatthew Dillon 				   &cmd->restart_count,
2303e2ea4ccSMatthew Dillon 				   &cmd->restart_per) < 1) {
2313e2ea4ccSMatthew Dillon 				fprintf(fp, "bad restart specification: %s\n",
2323e2ea4ccSMatthew Dillon 					optarg);
2333e2ea4ccSMatthew Dillon 				goto failed;
2343e2ea4ccSMatthew Dillon 			}
2353e2ea4ccSMatthew Dillon 			break;
2363e2ea4ccSMatthew Dillon 		default:
2373e2ea4ccSMatthew Dillon 			fprintf(fp, "Unknown option %c\n", ch);
2383e2ea4ccSMatthew Dillon 			goto failed;
2393e2ea4ccSMatthew Dillon 		}
2403e2ea4ccSMatthew Dillon 	}
2413e2ea4ccSMatthew Dillon 
2423e2ea4ccSMatthew Dillon 	/*
2433e2ea4ccSMatthew Dillon 	 * directive [label] [...additional args]
244ee6361d4SMatthew Dillon 	 *
245ee6361d4SMatthew Dillon 	 * If 'all' is specified the label field is left NULL (ensure that
246ee6361d4SMatthew Dillon 	 * it is NULL), and empty_label is still cleared so safety code works.
2473e2ea4ccSMatthew Dillon 	 */
2483e2ea4ccSMatthew Dillon 	i = optind;
2493e2ea4ccSMatthew Dillon 	if (av[i]) {
2503e2ea4ccSMatthew Dillon 		cmd->directive = strdup(av[i]);
2513e2ea4ccSMatthew Dillon 		++i;
2523e2ea4ccSMatthew Dillon 		if (av[i]) {
253ee6361d4SMatthew Dillon 			cmd->empty_label = 0;
254ee6361d4SMatthew Dillon 			if (strcmp(av[i], "all") == 0)
255ee6361d4SMatthew Dillon 				sfree(&cmd->label);
256ee6361d4SMatthew Dillon 			else
2573e2ea4ccSMatthew Dillon 				cmd->label = strdup(av[i]);
2583e2ea4ccSMatthew Dillon 			++i;
2593e2ea4ccSMatthew Dillon 			cmd->ext_av = av + i;
2603e2ea4ccSMatthew Dillon 			cmd->ext_ac = ac - i;
2613e2ea4ccSMatthew Dillon 			adup(&cmd->ext_av);
2623e2ea4ccSMatthew Dillon 		}
2633e2ea4ccSMatthew Dillon 	} else {
2643e2ea4ccSMatthew Dillon 		fprintf(fp, "No directive specified\n");
2653e2ea4ccSMatthew Dillon 		goto failed;
2663e2ea4ccSMatthew Dillon 	}
2673e2ea4ccSMatthew Dillon 	rc = 0;
2683e2ea4ccSMatthew Dillon failed:
2693e2ea4ccSMatthew Dillon 	endgrent();
2703e2ea4ccSMatthew Dillon 	endpwent();
2713e2ea4ccSMatthew Dillon 
2723e2ea4ccSMatthew Dillon 	return rc;
2733e2ea4ccSMatthew Dillon }
2743e2ea4ccSMatthew Dillon 
2753e2ea4ccSMatthew Dillon int
execute_cmd(command_t * cmd)2763e2ea4ccSMatthew Dillon execute_cmd(command_t *cmd)
2773e2ea4ccSMatthew Dillon {
2783e2ea4ccSMatthew Dillon 	const char *directive;
2793e2ea4ccSMatthew Dillon 	int rc;
2803e2ea4ccSMatthew Dillon 
2813e2ea4ccSMatthew Dillon 	directive = cmd->directive;
2823e2ea4ccSMatthew Dillon 
283ee6361d4SMatthew Dillon 	/*
284ee6361d4SMatthew Dillon 	 * Safely, require a label for directives that do not match
285ee6361d4SMatthew Dillon 	 * this list, or 'all'.  Do not default to all if no label
286ee6361d4SMatthew Dillon 	 * is specified.  e.g. things like 'kill' or 'exit' could
287ee6361d4SMatthew Dillon 	 * blow up the system.
288ee6361d4SMatthew Dillon 	 */
289ee6361d4SMatthew Dillon 	if (cmd->empty_label) {
290ee6361d4SMatthew Dillon 		if (strcmp(directive, "status") != 0 &&
291ee6361d4SMatthew Dillon 		    strcmp(directive, "list") != 0 &&
292ee6361d4SMatthew Dillon 		    strcmp(directive, "log") != 0 &&
293ee6361d4SMatthew Dillon 		    strcmp(directive, "logf") != 0 &&
294ee6361d4SMatthew Dillon 		    strcmp(directive, "help") != 0 &&
295ee6361d4SMatthew Dillon 		    strcmp(directive, "tailf") != 0)  {
296ee6361d4SMatthew Dillon 			fprintf(cmd->fp,
297ee6361d4SMatthew Dillon 				"Directive requires a label or 'all': %s\n",
298ee6361d4SMatthew Dillon 				directive);
299ee6361d4SMatthew Dillon 			rc = 1;
300ee6361d4SMatthew Dillon 			return rc;
301ee6361d4SMatthew Dillon 		}
302ee6361d4SMatthew Dillon 	}
303ee6361d4SMatthew Dillon 
304ee6361d4SMatthew Dillon 	/*
305ee6361d4SMatthew Dillon 	 * Process directives.  If we are on the remote already the
306ee6361d4SMatthew Dillon 	 * execute_remote() function will simply chain to the passed-in
307ee6361d4SMatthew Dillon 	 * function.
308ee6361d4SMatthew Dillon 	 */
3093e2ea4ccSMatthew Dillon 	if (strcmp(directive, "init") == 0) {
3103e2ea4ccSMatthew Dillon 		rc = execute_init(cmd);
311ee6361d4SMatthew Dillon 	} else if (strcmp(directive, "help") == 0) {
312ee6361d4SMatthew Dillon 		rc = execute_help(cmd);
3133e2ea4ccSMatthew Dillon 	} else if (strcmp(directive, "start") == 0) {
3143e2ea4ccSMatthew Dillon 		rc = execute_remote(cmd, execute_start);
3153e2ea4ccSMatthew Dillon 	} else if (strcmp(directive, "stop") == 0) {
3163e2ea4ccSMatthew Dillon 		rc = execute_remote(cmd, execute_stop);
3173e2ea4ccSMatthew Dillon 	} else if (strcmp(directive, "stopall") == 0) {
3183e2ea4ccSMatthew Dillon 		cmd->restart_some = 0;
3193e2ea4ccSMatthew Dillon 		cmd->restart_all = 1;
3203e2ea4ccSMatthew Dillon 		rc = execute_remote(cmd, execute_stop);
3213e2ea4ccSMatthew Dillon 	} else if (strcmp(directive, "restart") == 0) {
3223e2ea4ccSMatthew Dillon 		rc = execute_remote(cmd, execute_restart);
3233e2ea4ccSMatthew Dillon 	} else if (strcmp(directive, "exit") == 0) {
3243e2ea4ccSMatthew Dillon 		cmd->restart_some = 0;
3253e2ea4ccSMatthew Dillon 		cmd->restart_all = 1;		/* stop everything */
326ee6361d4SMatthew Dillon 		cmd->force_remove_files = 1;
3273e2ea4ccSMatthew Dillon 		rc = execute_remote(cmd, execute_exit);
3283e2ea4ccSMatthew Dillon 	} else if (strcmp(directive, "kill") == 0) {
3293e2ea4ccSMatthew Dillon 		cmd->restart_some = 0;
3303e2ea4ccSMatthew Dillon 		cmd->restart_all = 1;		/* stop everything */
3313e2ea4ccSMatthew Dillon 		cmd->termkill_timo = 0;		/* force immediate SIGKILL */
332ee6361d4SMatthew Dillon 		cmd->force_remove_files = 1;
3333e2ea4ccSMatthew Dillon 		rc = execute_remote(cmd, execute_exit);
3343e2ea4ccSMatthew Dillon 	} else if (strcmp(directive, "list") == 0) {
3353e2ea4ccSMatthew Dillon 		rc = execute_remote(cmd, execute_list);
3363e2ea4ccSMatthew Dillon 	} else if (strcmp(directive, "status") == 0) {
3373e2ea4ccSMatthew Dillon 		rc = execute_remote(cmd, execute_status);
3383e2ea4ccSMatthew Dillon 	} else if (strcmp(directive, "log") == 0) {
3393e2ea4ccSMatthew Dillon 		rc = execute_remote(cmd, execute_log);
3403e2ea4ccSMatthew Dillon 	} else if (strcmp(directive, "logf") == 0) {
3413e2ea4ccSMatthew Dillon 		cmd->tail_mode = 1;
3423e2ea4ccSMatthew Dillon 		rc = execute_remote(cmd, execute_log);
3433e2ea4ccSMatthew Dillon 	} else if (strcmp(directive, "tailf") == 0) {
3443e2ea4ccSMatthew Dillon 		cmd->tail_mode = 2;
3453e2ea4ccSMatthew Dillon 		rc = execute_remote(cmd, execute_log);
3463e2ea4ccSMatthew Dillon 	} else if (strcmp(directive, "logfile") == 0) {
3473e2ea4ccSMatthew Dillon 		rc = execute_remote(cmd, execute_logfile);
3483e2ea4ccSMatthew Dillon 	} else {
349*f9993810SSascha Wildner 		fprintf(cmd->fp, "Unknown directive: %s\n", directive);
3503e2ea4ccSMatthew Dillon 		rc = 1;
3513e2ea4ccSMatthew Dillon 	}
3523e2ea4ccSMatthew Dillon 	return rc;
3533e2ea4ccSMatthew Dillon }
3543e2ea4ccSMatthew Dillon 
3553e2ea4ccSMatthew Dillon static
3563e2ea4ccSMatthew Dillon int
execute_remote(command_t * cmd,int (* func)(command_t * cmd))3573e2ea4ccSMatthew Dillon execute_remote(command_t *cmd, int (*func)(command_t *cmd))
3583e2ea4ccSMatthew Dillon {
3593e2ea4ccSMatthew Dillon 	DIR *dir;
3603e2ea4ccSMatthew Dillon 	struct dirent *den;
3613e2ea4ccSMatthew Dillon 	const char *p1;
3623e2ea4ccSMatthew Dillon 	const char *p2;
3633e2ea4ccSMatthew Dillon 	char *plab;
3643e2ea4ccSMatthew Dillon 	size_t cmdlen;
3653e2ea4ccSMatthew Dillon 	size_t len;
3663e2ea4ccSMatthew Dillon 	int rc;
3673e2ea4ccSMatthew Dillon 
3683e2ea4ccSMatthew Dillon 	/*
3693e2ea4ccSMatthew Dillon 	 * If already on the remote service just execute the operation
3703e2ea4ccSMatthew Dillon 	 * as requested.
3713e2ea4ccSMatthew Dillon 	 */
3723e2ea4ccSMatthew Dillon 	if (cmd->cmdline == 0) {
3733e2ea4ccSMatthew Dillon 		return (func(cmd));
3743e2ea4ccSMatthew Dillon 	}
3753e2ea4ccSMatthew Dillon 
3763e2ea4ccSMatthew Dillon 	/*
3773e2ea4ccSMatthew Dillon 	 * Look for label(s).  If no exact match or label is NULL, scan
3783e2ea4ccSMatthew Dillon 	 * piddir for matches.
3793e2ea4ccSMatthew Dillon 	 */
3803e2ea4ccSMatthew Dillon 	if ((dir = opendir(cmd->piddir)) == NULL) {
3813e2ea4ccSMatthew Dillon 		fprintf(cmd->fp, "Unable to scan \"%s\"\n", cmd->piddir);
3823e2ea4ccSMatthew Dillon 		return 1;
3833e2ea4ccSMatthew Dillon 	}
3843e2ea4ccSMatthew Dillon 
3853e2ea4ccSMatthew Dillon 	rc = 0;
3863e2ea4ccSMatthew Dillon 	cmdlen = (cmd->label ? strlen(cmd->label) : 0);
3873e2ea4ccSMatthew Dillon 
3883e2ea4ccSMatthew Dillon 	while ((den = readdir(dir)) != NULL) {
3893e2ea4ccSMatthew Dillon 		/*
3903e2ea4ccSMatthew Dillon 		 * service. prefix.
3913e2ea4ccSMatthew Dillon 		 */
3923e2ea4ccSMatthew Dillon 		if (strncmp(den->d_name, "service.", 8) != 0)
3933e2ea4ccSMatthew Dillon 			continue;
3943e2ea4ccSMatthew Dillon 
3953e2ea4ccSMatthew Dillon 		/*
3963e2ea4ccSMatthew Dillon 		 * .sk suffix
3973e2ea4ccSMatthew Dillon 		 */
3983e2ea4ccSMatthew Dillon 		p1 = den->d_name + 8;
3993e2ea4ccSMatthew Dillon 		p2 = strrchr(p1, '.');
4003e2ea4ccSMatthew Dillon 		if (p2 == NULL || p2 < p1 || strcmp(p2, ".sk") != 0)
4013e2ea4ccSMatthew Dillon 			continue;
4023e2ea4ccSMatthew Dillon 
4033e2ea4ccSMatthew Dillon 		/*
4043e2ea4ccSMatthew Dillon 		 * Extract the label from the service.<label>.sk name.
4053e2ea4ccSMatthew Dillon 		 */
40640e10befSMatthew Dillon 		len = p2 - p1;
4073e2ea4ccSMatthew Dillon 		plab = strdup(p1);
4083e2ea4ccSMatthew Dillon 		*strrchr(plab, '.') = 0;
4093e2ea4ccSMatthew Dillon 
4103e2ea4ccSMatthew Dillon 		/*
4113e2ea4ccSMatthew Dillon 		 * Start remote execution (in parallel) for all matching
4123e2ea4ccSMatthew Dillon 		 * labels.  This will generally create some asynchronous
4133e2ea4ccSMatthew Dillon 		 * threads.
4143e2ea4ccSMatthew Dillon 		 */
4153e2ea4ccSMatthew Dillon 		if (cmdlen == 0 ||
4163e2ea4ccSMatthew Dillon 		    (cmdlen <= len && strncmp(cmd->label, plab, cmdlen) == 0)) {
4173e2ea4ccSMatthew Dillon 			remote_execute(cmd, plab);
4183e2ea4ccSMatthew Dillon 		}
4193e2ea4ccSMatthew Dillon 		free(plab);
4203e2ea4ccSMatthew Dillon 	}
4213e2ea4ccSMatthew Dillon 	closedir(dir);
4223e2ea4ccSMatthew Dillon 
4233e2ea4ccSMatthew Dillon 	/*
4243e2ea4ccSMatthew Dillon 	 * Wait for completion of remote commands and dump output.
4253e2ea4ccSMatthew Dillon 	 */
4263e2ea4ccSMatthew Dillon 	rc = remote_wait();
4273e2ea4ccSMatthew Dillon 
4283e2ea4ccSMatthew Dillon 	return rc;
4293e2ea4ccSMatthew Dillon }
4303e2ea4ccSMatthew Dillon 
4313e2ea4ccSMatthew Dillon void
free_cmd(command_t * cmd)4323e2ea4ccSMatthew Dillon free_cmd(command_t *cmd)
4333e2ea4ccSMatthew Dillon {
4343e2ea4ccSMatthew Dillon 	sfree(&cmd->piddir);
4353e2ea4ccSMatthew Dillon 
4363e2ea4ccSMatthew Dillon 	sfree(&cmd->pwent.pw_name);
4373e2ea4ccSMatthew Dillon 	sfree(&cmd->pwent.pw_passwd);
4383e2ea4ccSMatthew Dillon 	sfree(&cmd->pwent.pw_class);
4393e2ea4ccSMatthew Dillon 	sfree(&cmd->pwent.pw_gecos);
4403e2ea4ccSMatthew Dillon 	sfree(&cmd->pwent.pw_dir);
4413e2ea4ccSMatthew Dillon 	sfree(&cmd->pwent.pw_shell);
4423e2ea4ccSMatthew Dillon 
4433e2ea4ccSMatthew Dillon 	sfree(&cmd->grent.gr_name);
4443e2ea4ccSMatthew Dillon 	sfree(&cmd->grent.gr_passwd);
4453e2ea4ccSMatthew Dillon 	afree(&cmd->grent.gr_mem);
4463e2ea4ccSMatthew Dillon 
4473e2ea4ccSMatthew Dillon 	sfree(&cmd->logfile);
4483e2ea4ccSMatthew Dillon 	sfree(&cmd->rootdir);
4493e2ea4ccSMatthew Dillon 	sfree(&cmd->jaildir);
4503e2ea4ccSMatthew Dillon 	sfree(&cmd->proctitle);
4513e2ea4ccSMatthew Dillon 	sfree(&cmd->directive);
4523e2ea4ccSMatthew Dillon 	sfree(&cmd->label);
4533e2ea4ccSMatthew Dillon 	afree(&cmd->ext_av);
4543e2ea4ccSMatthew Dillon 
45582f39527SMatthew Dillon 	if (cmd->logfd >= 0) {
45682f39527SMatthew Dillon 		close(cmd->logfd);
45782f39527SMatthew Dillon 		cmd->logfd = -1;
45882f39527SMatthew Dillon 	}
45982f39527SMatthew Dillon 
4603e2ea4ccSMatthew Dillon 	bzero(cmd, sizeof(*cmd));
4613e2ea4ccSMatthew Dillon }
4623e2ea4ccSMatthew Dillon 
4633e2ea4ccSMatthew Dillon static
4643e2ea4ccSMatthew Dillon int
process_jailspec(command_t * cmd,const char * spec)4653e2ea4ccSMatthew Dillon process_jailspec(command_t *cmd, const char *spec)
4663e2ea4ccSMatthew Dillon {
4673e2ea4ccSMatthew Dillon 	char *cpy = strdup(spec);
4683e2ea4ccSMatthew Dillon 	char *ptr;
4693e2ea4ccSMatthew Dillon 	int rc = 0;
4703e2ea4ccSMatthew Dillon 
4713e2ea4ccSMatthew Dillon 	ptr = strtok(cpy, ",");
4723e2ea4ccSMatthew Dillon 	while (ptr) {
4733e2ea4ccSMatthew Dillon 		if (strcmp(ptr, "clean") == 0) {
4743e2ea4ccSMatthew Dillon 			cmd->jail_clean = 1;
4753e2ea4ccSMatthew Dillon 		} else if (strncmp(ptr, "ip=", 3) == 0) {
4763e2ea4ccSMatthew Dillon 			assert(0); /* XXX TODO */
4773e2ea4ccSMatthew Dillon 		} else {
4783e2ea4ccSMatthew Dillon 			fprintf(cmd->fp, "jail-spec '%s' not understood\n",
4793e2ea4ccSMatthew Dillon 				ptr);
4803e2ea4ccSMatthew Dillon 			rc = 1;
4813e2ea4ccSMatthew Dillon 		}
4823e2ea4ccSMatthew Dillon 		ptr = strtok(NULL, ",");
4833e2ea4ccSMatthew Dillon 	}
4843e2ea4ccSMatthew Dillon 	free(cpy);
4853e2ea4ccSMatthew Dillon 
4863e2ea4ccSMatthew Dillon 	return rc;
4873e2ea4ccSMatthew Dillon }
488