2 * Copyright (c) 2014 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * This program builds an environment to run a service in and provides
38 * numerous options for naming, tracking, and management. It uses
39 * reapctl(2) to corral the processes under management.
44 static int execute_remote(command_t *cmd, int (*func)(command_t *cmd));
45 static int process_jailspec(command_t *cmd, const char *spec);
48 main(int ac, char **av)
53 signal(SIGPIPE, SIG_IGN);
55 rc = process_cmd(&cmd, stdout, ac, av);
56 cmd.cmdline = 1; /* commanded from front-end */
57 cmd.commanded = 1; /* commanded action (vs automatic) */
59 rc = execute_cmd(&cmd);
66 process_cmd(command_t *cmd, FILE *fp, int ac, char **av)
68 const char *optstr = "dfp:r:R:xst:u:g:G:l:c:C:j:J:k:T:F:";
77 bzero(cmd, sizeof(*cmd));
78 cmd->fp = fp; /* error and output reporting */
79 sreplace(&cmd->piddir, "/var/run"); /* must not be NULL */
80 cmd->termkill_timo = -1; /* will use default value */
88 while ((ch = getopt(ac, av, optstr)) != -1) {
98 sreplace(&cmd->piddir, optarg);
101 cmd->restart_some = 1;
102 cmd->restart_all = 0;
103 cmd->restart_timo = strtol(optarg, NULL, 0);
106 cmd->restart_some = 0;
107 cmd->restart_all = 1;
108 cmd->restart_timo = strtol(optarg, NULL, 0);
117 cmd->termkill_timo = strtoul(optarg, NULL, 0);
120 if (isdigit(optarg[0])) {
121 pwent = getpwnam(optarg);
123 pwent = getpwuid(strtol(optarg, NULL, 0));
126 fprintf(fp, "Cannot find user %s: %s\n",
131 sfree(&cmd->pwent.pw_name);
132 sfree(&cmd->pwent.pw_passwd);
133 sfree(&cmd->pwent.pw_class);
134 sfree(&cmd->pwent.pw_gecos);
135 sfree(&cmd->pwent.pw_dir);
136 sfree(&cmd->pwent.pw_shell);
138 sdup(&cmd->pwent.pw_name);
139 sdup(&cmd->pwent.pw_passwd);
140 sdup(&cmd->pwent.pw_class);
141 sdup(&cmd->pwent.pw_gecos);
142 sdup(&cmd->pwent.pw_dir);
143 sdup(&cmd->pwent.pw_shell);
147 if (isdigit(optarg[0])) {
148 grent = getgrnam(optarg);
150 grent = getgrgid(strtol(optarg, NULL, 0));
153 fprintf(fp, "Cannot find group %s: %s\n",
158 sfree(&cmd->grent.gr_name);
159 sfree(&cmd->grent.gr_passwd);
160 afree(&cmd->grent.gr_mem);
162 sdup(&cmd->grent.gr_name);
163 sdup(&cmd->grent.gr_passwd);
164 adup(&cmd->grent.gr_mem);
168 cpy = strdup(optarg);
169 sub = strtok(cpy, ",");
172 if (isdigit(optarg[0])) {
173 grent = getgrnam(optarg);
175 grent = getgrgid(strtol(optarg,
180 "Cannot find group %s: %s\n",
187 "Too many groups specified, "
188 "max %d\n", NGROUPS);
192 cmd->groups[i++] = grent->gr_gid;
193 sub = strtok(NULL, ",");
201 sreplace(&cmd->logfile, optarg);
207 sreplace(&cmd->rootdir, optarg);
213 sreplace(&cmd->jaildir, optarg);
216 rc = process_jailspec(cmd, optarg);
221 sreplace(&cmd->proctitle, optarg);
224 cmd->restart_per = 60;
225 if (sscanf(optarg, "%d:%d",
227 &cmd->restart_per) < 1) {
228 fprintf(fp, "bad restart specification: %s\n",
234 fprintf(fp, "Unknown option %c\n", ch);
240 * directive [label] [...additional args]
244 cmd->directive = strdup(av[i]);
247 cmd->label = strdup(av[i]);
249 cmd->ext_av = av + i;
250 cmd->ext_ac = ac - i;
254 fprintf(fp, "No directive specified\n");
266 execute_cmd(command_t *cmd)
268 const char *directive;
271 directive = cmd->directive;
273 if (strcmp(directive, "init") == 0) {
274 rc = execute_init(cmd);
275 } else if (strcmp(directive, "start") == 0) {
276 rc = execute_remote(cmd, execute_start);
277 } else if (strcmp(directive, "stop") == 0) {
278 rc = execute_remote(cmd, execute_stop);
279 } else if (strcmp(directive, "stopall") == 0) {
280 cmd->restart_some = 0;
281 cmd->restart_all = 1;
282 rc = execute_remote(cmd, execute_stop);
283 } else if (strcmp(directive, "restart") == 0) {
284 rc = execute_remote(cmd, execute_restart);
285 } else if (strcmp(directive, "exit") == 0) {
286 cmd->restart_some = 0;
287 cmd->restart_all = 1; /* stop everything */
288 rc = execute_remote(cmd, execute_exit);
289 } else if (strcmp(directive, "kill") == 0) {
290 cmd->restart_some = 0;
291 cmd->restart_all = 1; /* stop everything */
292 cmd->termkill_timo = 0; /* force immediate SIGKILL */
293 rc = execute_remote(cmd, execute_exit);
294 } else if (strcmp(directive, "list") == 0) {
295 rc = execute_remote(cmd, execute_list);
296 } else if (strcmp(directive, "status") == 0) {
297 rc = execute_remote(cmd, execute_status);
298 } else if (strcmp(directive, "log") == 0) {
299 rc = execute_remote(cmd, execute_log);
300 } else if (strcmp(directive, "logf") == 0) {
302 rc = execute_remote(cmd, execute_log);
303 } else if (strcmp(directive, "tailf") == 0) {
305 rc = execute_remote(cmd, execute_log);
306 } else if (strcmp(directive, "logfile") == 0) {
307 rc = execute_remote(cmd, execute_logfile);
309 fprintf(cmd->fp, "Uknown directive: %s\n", directive);
317 execute_remote(command_t *cmd, int (*func)(command_t *cmd))
329 * If already on the remote service just execute the operation
332 if (cmd->cmdline == 0) {
337 * Look for label(s). If no exact match or label is NULL, scan
338 * piddir for matches.
340 if ((dir = opendir(cmd->piddir)) == NULL) {
341 fprintf(cmd->fp, "Unable to scan \"%s\"\n", cmd->piddir);
346 cmdlen = (cmd->label ? strlen(cmd->label) : 0);
348 while ((den = readdir(dir)) != NULL) {
352 if (strncmp(den->d_name, "service.", 8) != 0)
358 p1 = den->d_name + 8;
359 p2 = strrchr(p1, '.');
360 if (p2 == NULL || p2 < p1 || strcmp(p2, ".sk") != 0)
364 * Extract the label from the service.<label>.sk name.
368 *strrchr(plab, '.') = 0;
371 * Start remote execution (in parallel) for all matching
372 * labels. This will generally create some asynchronous
376 (cmdlen <= len && strncmp(cmd->label, plab, cmdlen) == 0)) {
377 remote_execute(cmd, plab);
384 * Wait for completion of remote commands and dump output.
392 free_cmd(command_t *cmd)
396 sfree(&cmd->pwent.pw_name);
397 sfree(&cmd->pwent.pw_passwd);
398 sfree(&cmd->pwent.pw_class);
399 sfree(&cmd->pwent.pw_gecos);
400 sfree(&cmd->pwent.pw_dir);
401 sfree(&cmd->pwent.pw_shell);
403 sfree(&cmd->grent.gr_name);
404 sfree(&cmd->grent.gr_passwd);
405 afree(&cmd->grent.gr_mem);
407 sfree(&cmd->logfile);
408 sfree(&cmd->rootdir);
409 sfree(&cmd->jaildir);
410 sfree(&cmd->proctitle);
411 sfree(&cmd->directive);
415 bzero(cmd, sizeof(*cmd));
420 process_jailspec(command_t *cmd, const char *spec)
422 char *cpy = strdup(spec);
426 ptr = strtok(cpy, ",");
428 if (strcmp(ptr, "clean") == 0) {
430 } else if (strncmp(ptr, "ip=", 3) == 0) {
431 assert(0); /* XXX TODO */
433 fprintf(cmd->fp, "jail-spec '%s' not understood\n",
437 ptr = strtok(NULL, ",");