#include "svc.h"
static int execute_remote(command_t *cmd, int (*func)(command_t *cmd));
static int process_jailspec(command_t *cmd, const char *spec);
int
main(int ac, char **av)
{
command_t cmd;
int rc;
signal(SIGPIPE, SIG_IGN);
rc = process_cmd(&cmd, stdout, ac, av);
cmd.cmdline = 1;
cmd.commanded = 1;
if (rc == 0)
rc = execute_cmd(&cmd);
free_cmd(&cmd);
return rc;
}
int
process_cmd(command_t *cmd, FILE *fp, int ac, char **av)
{
const char *optstr = "dfhp:r:R:xst:u:g:G:l:c:mj:k:T:F:";
struct group *grent;
struct passwd *pwent;
char *sub;
char *cpy;
int rc = 1;
int ch;
int i;
bzero(cmd, sizeof(*cmd));
cmd->fp = fp;
cmd->logfd = -1;
sreplace(&cmd->piddir, "/var/run");
cmd->termkill_timo = -1;
cmd->orig_ac = ac;
cmd->orig_av = av;
cmd->empty_label = 1;
optind = 1;
opterr = 1;
optreset = 1;
while ((ch = getopt(ac, av, optstr)) != -1) {
switch(ch) {
case 'd':
cmd->debug = 1;
cmd->foreground = 1;
break;
case 'f':
cmd->foreground = 1;
break;
case 'h':
execute_help(cmd);
exit(0);
break;
case 'p':
sreplace(&cmd->piddir, optarg);
break;
case 'r':
cmd->restart_some = 1;
cmd->restart_all = 0;
cmd->restart_timo = strtol(optarg, NULL, 0);
break;
case 'R':
cmd->restart_some = 0;
cmd->restart_all = 1;
cmd->restart_timo = strtol(optarg, NULL, 0);
break;
case 'x':
cmd->exit_mode = 1;
break;
case 's':
cmd->sync_mode = 1;
break;
case 't':
cmd->termkill_timo = strtoul(optarg, NULL, 0);
break;
case 'u':
if (isdigit(optarg[0])) {
pwent = getpwuid(strtol(optarg, NULL, 0));
} else {
pwent = getpwnam(optarg);
}
if (pwent == NULL) {
fprintf(fp, "Cannot find user %s: %s\n",
optarg,
strerror(errno));
goto failed;
}
cmd->uid_mode = 1;
sfree(&cmd->pwent.pw_name);
sfree(&cmd->pwent.pw_passwd);
sfree(&cmd->pwent.pw_class);
sfree(&cmd->pwent.pw_gecos);
sfree(&cmd->pwent.pw_dir);
sfree(&cmd->pwent.pw_shell);
cmd->pwent = *pwent;
sdup(&cmd->pwent.pw_name);
sdup(&cmd->pwent.pw_passwd);
sdup(&cmd->pwent.pw_class);
sdup(&cmd->pwent.pw_gecos);
sdup(&cmd->pwent.pw_dir);
sdup(&cmd->pwent.pw_shell);
break;
case 'g':
setgroupent(1);
if (isdigit(optarg[0])) {
grent = getgrgid(strtol(optarg, NULL, 0));
} else {
grent = getgrnam(optarg);
}
if (grent == NULL) {
fprintf(fp, "Cannot find group %s: %s\n",
optarg,
strerror(errno));
goto failed;
}
cmd->gid_mode = 1;
sfree(&cmd->grent.gr_name);
sfree(&cmd->grent.gr_passwd);
afree(&cmd->grent.gr_mem);
cmd->grent = *grent;
sdup(&cmd->grent.gr_name);
sdup(&cmd->grent.gr_passwd);
adup(&cmd->grent.gr_mem);
break;
case 'G':
setgroupent(1);
cpy = strdup(optarg);
sub = strtok(cpy, ",");
i = 0;
while (sub) {
if (isdigit(sub[0])) {
grent = getgrgid(strtol(sub, NULL, 0));
} else {
grent = getgrnam(sub);
}
if (grent == NULL) {
fprintf(fp,
"Cannot find group %s: %s\n",
sub, strerror(errno));
i = -1;
}
if (i == NGROUPS) {
fprintf(fp,
"Too many groups specified, "
"max %d\n", NGROUPS);
i = -1;
}
if (i >= 0)
cmd->groups[i++] = grent->gr_gid;
sub = strtok(NULL, ",");
}
free(cpy);
if (i < 0)
goto failed;
cmd->ngroups = i;
break;
case 'l':
sreplace(&cmd->logfile, optarg);
break;
case 'c':
sreplace(&cmd->rootdir, optarg);
break;
case 'm':
cmd->mountdev = 1;
break;
case 'j':
sreplace(&cmd->jaildir, optarg);
break;
case 'k':
rc = process_jailspec(cmd, optarg);
if (rc)
goto failed;
break;
case 'T':
sreplace(&cmd->proctitle, optarg);
break;
case 'F':
cmd->restart_per = 60;
if (sscanf(optarg, "%d:%d",
&cmd->restart_count,
&cmd->restart_per) < 1) {
fprintf(fp, "bad restart specification: %s\n",
optarg);
goto failed;
}
break;
default:
fprintf(fp, "Unknown option %c\n", ch);
goto failed;
}
}
i = optind;
if (av[i]) {
cmd->directive = strdup(av[i]);
++i;
if (av[i]) {
cmd->empty_label = 0;
if (strcmp(av[i], "all") == 0)
sfree(&cmd->label);
else
cmd->label = strdup(av[i]);
++i;
cmd->ext_av = av + i;
cmd->ext_ac = ac - i;
adup(&cmd->ext_av);
}
} else {
fprintf(fp, "No directive specified\n");
goto failed;
}
rc = 0;
failed:
endgrent();
endpwent();
return rc;
}
int
execute_cmd(command_t *cmd)
{
const char *directive;
int rc;
directive = cmd->directive;
if (cmd->empty_label) {
if (strcmp(directive, "status") != 0 &&
strcmp(directive, "list") != 0 &&
strcmp(directive, "log") != 0 &&
strcmp(directive, "logf") != 0 &&
strcmp(directive, "help") != 0 &&
strcmp(directive, "tailf") != 0) {
fprintf(cmd->fp,
"Directive requires a label or 'all': %s\n",
directive);
rc = 1;
return rc;
}
}
if (strcmp(directive, "init") == 0) {
rc = execute_init(cmd);
} else if (strcmp(directive, "help") == 0) {
rc = execute_help(cmd);
} else if (strcmp(directive, "start") == 0) {
rc = execute_remote(cmd, execute_start);
} else if (strcmp(directive, "stop") == 0) {
rc = execute_remote(cmd, execute_stop);
} else if (strcmp(directive, "stopall") == 0) {
cmd->restart_some = 0;
cmd->restart_all = 1;
rc = execute_remote(cmd, execute_stop);
} else if (strcmp(directive, "restart") == 0) {
rc = execute_remote(cmd, execute_restart);
} else if (strcmp(directive, "exit") == 0) {
cmd->restart_some = 0;
cmd->restart_all = 1;
cmd->force_remove_files = 1;
rc = execute_remote(cmd, execute_exit);
} else if (strcmp(directive, "kill") == 0) {
cmd->restart_some = 0;
cmd->restart_all = 1;
cmd->termkill_timo = 0;
cmd->force_remove_files = 1;
rc = execute_remote(cmd, execute_exit);
} else if (strcmp(directive, "list") == 0) {
rc = execute_remote(cmd, execute_list);
} else if (strcmp(directive, "status") == 0) {
rc = execute_remote(cmd, execute_status);
} else if (strcmp(directive, "log") == 0) {
rc = execute_remote(cmd, execute_log);
} else if (strcmp(directive, "logf") == 0) {
cmd->tail_mode = 1;
rc = execute_remote(cmd, execute_log);
} else if (strcmp(directive, "tailf") == 0) {
cmd->tail_mode = 2;
rc = execute_remote(cmd, execute_log);
} else if (strcmp(directive, "logfile") == 0) {
rc = execute_remote(cmd, execute_logfile);
} else {
fprintf(cmd->fp, "Unknown directive: %s\n", directive);
rc = 1;
}
return rc;
}
static
int
execute_remote(command_t *cmd, int (*func)(command_t *cmd))
{
DIR *dir;
struct dirent *den;
const char *p1;
const char *p2;
char *plab;
size_t cmdlen;
size_t len;
int rc;
if (cmd->cmdline == 0) {
return (func(cmd));
}
if ((dir = opendir(cmd->piddir)) == NULL) {
fprintf(cmd->fp, "Unable to scan \"%s\"\n", cmd->piddir);
return 1;
}
rc = 0;
cmdlen = (cmd->label ? strlen(cmd->label) : 0);
while ((den = readdir(dir)) != NULL) {
if (strncmp(den->d_name, "service.", 8) != 0)
continue;
p1 = den->d_name + 8;
p2 = strrchr(p1, '.');
if (p2 == NULL || p2 < p1 || strcmp(p2, ".sk") != 0)
continue;
len = p2 - p1;
plab = strdup(p1);
*strrchr(plab, '.') = 0;
if (cmdlen == 0 ||
(cmdlen <= len && strncmp(cmd->label, plab, cmdlen) == 0)) {
remote_execute(cmd, plab);
}
free(plab);
}
closedir(dir);
rc = remote_wait();
return rc;
}
void
free_cmd(command_t *cmd)
{
sfree(&cmd->piddir);
sfree(&cmd->pwent.pw_name);
sfree(&cmd->pwent.pw_passwd);
sfree(&cmd->pwent.pw_class);
sfree(&cmd->pwent.pw_gecos);
sfree(&cmd->pwent.pw_dir);
sfree(&cmd->pwent.pw_shell);
sfree(&cmd->grent.gr_name);
sfree(&cmd->grent.gr_passwd);
afree(&cmd->grent.gr_mem);
sfree(&cmd->logfile);
sfree(&cmd->rootdir);
sfree(&cmd->jaildir);
sfree(&cmd->proctitle);
sfree(&cmd->directive);
sfree(&cmd->label);
afree(&cmd->ext_av);
if (cmd->logfd >= 0) {
close(cmd->logfd);
cmd->logfd = -1;
}
bzero(cmd, sizeof(*cmd));
}
static
int
process_jailspec(command_t *cmd, const char *spec)
{
char *cpy = strdup(spec);
char *ptr;
int rc = 0;
ptr = strtok(cpy, ",");
while (ptr) {
if (strcmp(ptr, "clean") == 0) {
cmd->jail_clean = 1;
} else if (strncmp(ptr, "ip=", 3) == 0) {
assert(0);
} else {
fprintf(cmd->fp, "jail-spec '%s' not understood\n",
ptr);
rc = 1;
}
ptr = strtok(NULL, ",");
}
free(cpy);
return rc;
}