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 * Handle remote listen/connect and parsing operations.
363e2ea4ccSMatthew Dillon */
373e2ea4ccSMatthew Dillon
383e2ea4ccSMatthew Dillon #include "svc.h"
393e2ea4ccSMatthew Dillon
403e2ea4ccSMatthew Dillon pthread_mutex_t serial_mtx;
413e2ea4ccSMatthew Dillon time_t LastStart; /* uptime */
423e2ea4ccSMatthew Dillon time_t LastStop; /* uptime */
433e2ea4ccSMatthew Dillon pid_t DirectPid = -1;
443e2ea4ccSMatthew Dillon runstate_t RunState = RS_STOPPED;
453e2ea4ccSMatthew Dillon command_t *InitCmd;
463e2ea4ccSMatthew Dillon int RestartCounter;
473e2ea4ccSMatthew Dillon
4882f39527SMatthew Dillon static void *logger_thread(void *arg);
4982f39527SMatthew Dillon static void setstate_stopped(command_t *cmd, struct timespec *ts);
5021537d46SMatthew Dillon static int setup_gid(command_t *cmd);
5121537d46SMatthew Dillon static int setup_uid(command_t *cmd);
5221537d46SMatthew Dillon static int setup_jail(command_t *cmd);
5321537d46SMatthew Dillon static int setup_chroot(command_t *cmd);
5421537d46SMatthew Dillon static int setup_devfs(command_t *cmd, const char *dir, int domount);
5582f39527SMatthew Dillon static int escapewrite(FILE *fp, char *buf, int n, int *statep);
5682f39527SMatthew Dillon
573e2ea4ccSMatthew Dillon int
execute_init(command_t * cmd)583e2ea4ccSMatthew Dillon execute_init(command_t *cmd)
593e2ea4ccSMatthew Dillon {
603e2ea4ccSMatthew Dillon char buf[32];
613e2ea4ccSMatthew Dillon pid_t pid;
623e2ea4ccSMatthew Dillon pid_t stoppingpid = -1;
6382f39527SMatthew Dillon pthread_t logtd;
643e2ea4ccSMatthew Dillon time_t nextstop = 0;
653e2ea4ccSMatthew Dillon int lfd; /* unix domain listen socket */
663e2ea4ccSMatthew Dillon int pfd; /* pid file */
673e2ea4ccSMatthew Dillon int xfd;
683e2ea4ccSMatthew Dillon int rc;
693e2ea4ccSMatthew Dillon int fds[2];
703e2ea4ccSMatthew Dillon char c;
713e2ea4ccSMatthew Dillon
723e2ea4ccSMatthew Dillon if (cmd->label == NULL || cmd->ext_ac == 0) {
733e2ea4ccSMatthew Dillon fprintf(cmd->fp, "init requires a label and command\n");
743e2ea4ccSMatthew Dillon return 1;
753e2ea4ccSMatthew Dillon }
763e2ea4ccSMatthew Dillon fprintf(cmd->fp, "initializing new service: %s\n", cmd->label);
773e2ea4ccSMatthew Dillon
783e2ea4ccSMatthew Dillon if ((xfd = open("/dev/null", O_RDWR)) < 0) {
793e2ea4ccSMatthew Dillon fprintf(cmd->fp, "Unable to open /dev/null: %s\n",
803e2ea4ccSMatthew Dillon strerror(errno));
813e2ea4ccSMatthew Dillon return 1;
823e2ea4ccSMatthew Dillon }
833e2ea4ccSMatthew Dillon
843e2ea4ccSMatthew Dillon /*
853e2ea4ccSMatthew Dillon * Setup pidfile and unix domain listen socket and lock the
863e2ea4ccSMatthew Dillon * pidfile.
873e2ea4ccSMatthew Dillon */
883e2ea4ccSMatthew Dillon rc = setup_pid_and_socket(cmd, &lfd, &pfd);
8982f39527SMatthew Dillon if (rc)
903e2ea4ccSMatthew Dillon return rc;
913e2ea4ccSMatthew Dillon
923e2ea4ccSMatthew Dillon /*
933e2ea4ccSMatthew Dillon * Detach the service
943e2ea4ccSMatthew Dillon */
9582f39527SMatthew Dillon if (cmd->foreground) {
9682f39527SMatthew Dillon /*
9782f39527SMatthew Dillon * Stay in foreground.
9882f39527SMatthew Dillon */
9982f39527SMatthew Dillon fds[0] = -1;
10082f39527SMatthew Dillon fds[1] = -1;
10182f39527SMatthew Dillon pid = 0;
10282f39527SMatthew Dillon } else {
10382f39527SMatthew Dillon if (pipe(fds) < 0) {
10482f39527SMatthew Dillon fprintf(cmd->fp, "Unable to create pipe: %s\n",
10582f39527SMatthew Dillon strerror(errno));
10682f39527SMatthew Dillon close(lfd);
10782f39527SMatthew Dillon close(pfd);
10882f39527SMatthew Dillon remove_pid_and_socket(cmd, cmd->label);
10982f39527SMatthew Dillon return 1;
11082f39527SMatthew Dillon }
11182f39527SMatthew Dillon pid = fork();
11282f39527SMatthew Dillon }
11382f39527SMatthew Dillon
11482f39527SMatthew Dillon if (pid != 0) {
1153e2ea4ccSMatthew Dillon /*
1163e2ea4ccSMatthew Dillon * Parent
1173e2ea4ccSMatthew Dillon */
1183e2ea4ccSMatthew Dillon close(fds[1]);
1193e2ea4ccSMatthew Dillon if (pid < 0) {
1203e2ea4ccSMatthew Dillon fprintf(cmd->fp, "fork failed: %s\n", strerror(errno));
12182f39527SMatthew Dillon close(lfd);
12282f39527SMatthew Dillon close(pfd);
12382f39527SMatthew Dillon close(fds[0]);
12482f39527SMatthew Dillon close(fds[1]);
12582f39527SMatthew Dillon remove_pid_and_socket(cmd, cmd->label);
12682f39527SMatthew Dillon return 1;
1273e2ea4ccSMatthew Dillon } else {
1283e2ea4ccSMatthew Dillon /*
1293e2ea4ccSMatthew Dillon * Fill-in pfd before returning.
1303e2ea4ccSMatthew Dillon */
1313e2ea4ccSMatthew Dillon snprintf(buf, sizeof(buf), "%d\n", (int)pid);
1323e2ea4ccSMatthew Dillon write(pfd, buf, strlen(buf));
1333e2ea4ccSMatthew Dillon }
1343e2ea4ccSMatthew Dillon close(lfd);
1353e2ea4ccSMatthew Dillon close(pfd);
1363e2ea4ccSMatthew Dillon
1373e2ea4ccSMatthew Dillon /*
1383e2ea4ccSMatthew Dillon * Wait for child to completely detach from the tty
1393e2ea4ccSMatthew Dillon * before returning.
1403e2ea4ccSMatthew Dillon */
1413e2ea4ccSMatthew Dillon read(fds[0], &c, 1);
1423e2ea4ccSMatthew Dillon close(fds[0]);
1433e2ea4ccSMatthew Dillon
1443e2ea4ccSMatthew Dillon return 0;
1453e2ea4ccSMatthew Dillon }
1463e2ea4ccSMatthew Dillon
1473e2ea4ccSMatthew Dillon /*
14882f39527SMatthew Dillon * Forked child is now the service demon.
1493e2ea4ccSMatthew Dillon *
15021537d46SMatthew Dillon * Detach from terminal, scrap tty, set process title.
1513e2ea4ccSMatthew Dillon */
15221537d46SMatthew Dillon if (cmd->proctitle) {
15321537d46SMatthew Dillon setproctitle("%s - %s", cmd->label, cmd->proctitle);
15421537d46SMatthew Dillon } else {
15521537d46SMatthew Dillon setproctitle("%s", cmd->label);
15621537d46SMatthew Dillon }
15721537d46SMatthew Dillon if (cmd->mountdev) {
15821537d46SMatthew Dillon if (cmd->jaildir)
15921537d46SMatthew Dillon setup_devfs(cmd, cmd->jaildir, 1);
16021537d46SMatthew Dillon else if (cmd->rootdir)
16121537d46SMatthew Dillon setup_devfs(cmd, cmd->rootdir, 1);
16221537d46SMatthew Dillon }
16321537d46SMatthew Dillon
16482f39527SMatthew Dillon if (cmd->foreground == 0) {
1653e2ea4ccSMatthew Dillon close(fds[0]);
16682f39527SMatthew Dillon fds[0] = -1;
1673e2ea4ccSMatthew Dillon }
16882f39527SMatthew Dillon
16982f39527SMatthew Dillon if (xfd != 0) /* scrap tty inputs */
17082f39527SMatthew Dillon dup2(xfd, 0);
17182f39527SMatthew Dillon if (cmd->foreground == 0) {
17282f39527SMatthew Dillon int tfd;
17382f39527SMatthew Dillon
17482f39527SMatthew Dillon if (xfd != 1) /* scrap tty outputs */
17582f39527SMatthew Dillon dup2(xfd, 1);
17682f39527SMatthew Dillon if (xfd != 2)
17782f39527SMatthew Dillon dup2(xfd, 2);
17882f39527SMatthew Dillon
17982f39527SMatthew Dillon if ((tfd = open("/dev/tty", O_RDWR)) >= 0) {
18082f39527SMatthew Dillon ioctl(tfd, TIOCNOTTY, 0); /* no controlling tty */
18182f39527SMatthew Dillon close(tfd);
1823e2ea4ccSMatthew Dillon }
1833e2ea4ccSMatthew Dillon setsid(); /* new session */
18482f39527SMatthew Dillon }
18582f39527SMatthew Dillon
18682f39527SMatthew Dillon /*
18782f39527SMatthew Dillon * Setup log file. The log file must not use descriptors 0, 1, or 2.
18882f39527SMatthew Dillon */
18982f39527SMatthew Dillon if (cmd->logfile && strcmp(cmd->logfile, "/dev/null") == 0)
19082f39527SMatthew Dillon cmd->logfd = -1;
19182f39527SMatthew Dillon else if (cmd->logfile)
19282f39527SMatthew Dillon cmd->logfd = open(cmd->logfile, O_WRONLY|O_CREAT|O_APPEND, 0640);
19382f39527SMatthew Dillon else if (cmd->foreground)
19482f39527SMatthew Dillon cmd->logfd = dup(1);
19582f39527SMatthew Dillon else
19682f39527SMatthew Dillon cmd->logfd = -1;
1973e2ea4ccSMatthew Dillon
1983e2ea4ccSMatthew Dillon /*
1993e2ea4ccSMatthew Dillon * Signal parent that we are completely detached now.
2003e2ea4ccSMatthew Dillon */
2013e2ea4ccSMatthew Dillon c = 1;
20282f39527SMatthew Dillon if (cmd->foreground == 0) {
2033e2ea4ccSMatthew Dillon write(fds[1], &c, 1);
2043e2ea4ccSMatthew Dillon close(fds[1]);
20582f39527SMatthew Dillon fds[1] = -1;
20682f39527SMatthew Dillon }
2073e2ea4ccSMatthew Dillon InitCmd = cmd;
2083e2ea4ccSMatthew Dillon
2093e2ea4ccSMatthew Dillon /*
21082f39527SMatthew Dillon * Setup log pipe. The logger thread copies the pipe to a buffer
21182f39527SMatthew Dillon * for the 'log' directive and also writes it to logfd.
21282f39527SMatthew Dillon */
21382f39527SMatthew Dillon pipe(cmd->logfds);
21482f39527SMatthew Dillon if (cmd->fp != stdout)
21582f39527SMatthew Dillon fclose(cmd->fp);
21682f39527SMatthew Dillon cmd->fp = fdopen(cmd->logfds[1], "w");
21782f39527SMatthew Dillon
21882f39527SMatthew Dillon if (xfd > 2) {
21982f39527SMatthew Dillon close(xfd);
22082f39527SMatthew Dillon xfd = -1;
22182f39527SMatthew Dillon }
22282f39527SMatthew Dillon
22382f39527SMatthew Dillon pthread_cond_init(&cmd->logcond, NULL);
22482f39527SMatthew Dillon
22582f39527SMatthew Dillon /*
2263e2ea4ccSMatthew Dillon * Start accept thread for unix domain listen socket.
2273e2ea4ccSMatthew Dillon */
2283e2ea4ccSMatthew Dillon pthread_mutex_lock(&serial_mtx);
22982f39527SMatthew Dillon pthread_create(&logtd, NULL, logger_thread, cmd);
23082f39527SMatthew Dillon remote_listener(cmd, lfd);
2313e2ea4ccSMatthew Dillon
2323e2ea4ccSMatthew Dillon /*
2333e2ea4ccSMatthew Dillon * Become the reaper for all children recursively.
2343e2ea4ccSMatthew Dillon */
2353e2ea4ccSMatthew Dillon if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) < 0) {
2363e2ea4ccSMatthew Dillon fprintf(cmd->fp, "svc is unable to become the "
2373e2ea4ccSMatthew Dillon "reaper for its children\n");
2383e2ea4ccSMatthew Dillon fflush(cmd->fp);
2393e2ea4ccSMatthew Dillon }
2403e2ea4ccSMatthew Dillon
2413e2ea4ccSMatthew Dillon /*
24240e10befSMatthew Dillon * Initial service start
24340e10befSMatthew Dillon */
24440e10befSMatthew Dillon execute_start(cmd);
24540e10befSMatthew Dillon
24640e10befSMatthew Dillon /*
2473e2ea4ccSMatthew Dillon * Main loop is the reaper
2483e2ea4ccSMatthew Dillon */
2493e2ea4ccSMatthew Dillon for (;;) {
2503e2ea4ccSMatthew Dillon union reaper_info info;
2513e2ea4ccSMatthew Dillon struct timespec ts;
2523e2ea4ccSMatthew Dillon int status;
2533e2ea4ccSMatthew Dillon int dt;
2543e2ea4ccSMatthew Dillon pid_t usepid;
2553e2ea4ccSMatthew Dillon
2563e2ea4ccSMatthew Dillon /*
2573e2ea4ccSMatthew Dillon * If we are running just block doing normal reaping,
2583e2ea4ccSMatthew Dillon * if we are stopping we have to poll for reaping while
2593e2ea4ccSMatthew Dillon * we handle stopping.
2603e2ea4ccSMatthew Dillon */
26140e10befSMatthew Dillon fflush(cmd->fp);
2623e2ea4ccSMatthew Dillon if (RunState == RS_STARTED) {
2633e2ea4ccSMatthew Dillon pthread_mutex_unlock(&serial_mtx);
2643e2ea4ccSMatthew Dillon pid = wait3(&status, 0, NULL);
2653e2ea4ccSMatthew Dillon pthread_mutex_lock(&serial_mtx);
2663e2ea4ccSMatthew Dillon } else {
2673e2ea4ccSMatthew Dillon pid = wait3(&status, WNOHANG, NULL);
2683e2ea4ccSMatthew Dillon }
2693e2ea4ccSMatthew Dillon clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
2703e2ea4ccSMatthew Dillon
2713e2ea4ccSMatthew Dillon if (pid > 0) {
2723e2ea4ccSMatthew Dillon if (pid == DirectPid) {
2733e2ea4ccSMatthew Dillon fprintf(cmd->fp,
2743e2ea4ccSMatthew Dillon "svc %s: lost direct child %d\n",
2753e2ea4ccSMatthew Dillon cmd->label, pid);
2763e2ea4ccSMatthew Dillon fflush(cmd->fp);
2773e2ea4ccSMatthew Dillon DirectPid = -1;
2783e2ea4ccSMatthew Dillon if (cmd->restart_some) {
27982f39527SMatthew Dillon setstate_stopped(cmd, &ts);
2803e2ea4ccSMatthew Dillon } /* else still considered normal run state */
28140e10befSMatthew Dillon } else if (cmd->debug) {
28240e10befSMatthew Dillon /*
28340e10befSMatthew Dillon * Reap random disconnected child, but don't
28440e10befSMatthew Dillon * spew to the log unless debugging is
28540e10befSMatthew Dillon * enabled.
28640e10befSMatthew Dillon */
2873e2ea4ccSMatthew Dillon fprintf(cmd->fp,
2883e2ea4ccSMatthew Dillon "svc %s: reap indirect child %d\n",
2893e2ea4ccSMatthew Dillon cmd->label,
2903e2ea4ccSMatthew Dillon (int)pid);
2913e2ea4ccSMatthew Dillon }
2923e2ea4ccSMatthew Dillon }
2933e2ea4ccSMatthew Dillon
2943e2ea4ccSMatthew Dillon /*
2953e2ea4ccSMatthew Dillon * Calculate the pid to potentially act on and/or
2963e2ea4ccSMatthew Dillon * determine if any children still exist.
2973e2ea4ccSMatthew Dillon */
2983e2ea4ccSMatthew Dillon if (DirectPid >= 0) {
2993e2ea4ccSMatthew Dillon usepid = DirectPid;
3003e2ea4ccSMatthew Dillon } else if (procctl(P_PID, getpid(),
3013e2ea4ccSMatthew Dillon PROC_REAP_STATUS, &info) == 0) {
3023e2ea4ccSMatthew Dillon usepid = info.status.pid_head;
3033e2ea4ccSMatthew Dillon } else {
3043e2ea4ccSMatthew Dillon usepid = -1;
3053e2ea4ccSMatthew Dillon }
30640e10befSMatthew Dillon if (cmd->debug) {
30740e10befSMatthew Dillon fprintf(stderr, "svc %s: usepid %d\n",
30840e10befSMatthew Dillon cmd->label, usepid);
30940e10befSMatthew Dillon fflush(stderr);
31040e10befSMatthew Dillon }
3113e2ea4ccSMatthew Dillon
3123e2ea4ccSMatthew Dillon /*
3133e2ea4ccSMatthew Dillon * If stoppingpid changes we have to reset the TERM->KILL
3143e2ea4ccSMatthew Dillon * timer.
3153e2ea4ccSMatthew Dillon */
3163e2ea4ccSMatthew Dillon if (usepid < 0) {
31782f39527SMatthew Dillon setstate_stopped(cmd, &ts);
3183e2ea4ccSMatthew Dillon } else if (stoppingpid != usepid &&
3193e2ea4ccSMatthew Dillon (RunState == RS_STOPPING2 ||
3203e2ea4ccSMatthew Dillon RunState == RS_STOPPING3)) {
3213e2ea4ccSMatthew Dillon RunState = RS_STOPPING1;
3223e2ea4ccSMatthew Dillon }
3233e2ea4ccSMatthew Dillon stoppingpid = usepid;
3243e2ea4ccSMatthew Dillon
3253e2ea4ccSMatthew Dillon /*
3263e2ea4ccSMatthew Dillon * State machine
3273e2ea4ccSMatthew Dillon */
3283e2ea4ccSMatthew Dillon switch(RunState) {
3293e2ea4ccSMatthew Dillon case RS_STARTED:
33082f39527SMatthew Dillon if (usepid < 0)
33182f39527SMatthew Dillon setstate_stopped(cmd, &ts);
3323e2ea4ccSMatthew Dillon break;
3333e2ea4ccSMatthew Dillon case RS_STOPPED:
3343e2ea4ccSMatthew Dillon dt = (int)(ts.tv_sec - LastStop);
3353e2ea4ccSMatthew Dillon
3363e2ea4ccSMatthew Dillon if (cmd->exit_mode) {
3373e2ea4ccSMatthew Dillon /*
3383e2ea4ccSMatthew Dillon * Service demon was told to exit on service
3393e2ea4ccSMatthew Dillon * stop (-x passed to init).
3403e2ea4ccSMatthew Dillon */
3413e2ea4ccSMatthew Dillon fprintf(cmd->fp,
3423e2ea4ccSMatthew Dillon "svc %s: service demon exiting\n",
3433e2ea4ccSMatthew Dillon cmd->label);
344ee6361d4SMatthew Dillon remove_pid_and_socket(cmd, cmd->label);
34521537d46SMatthew Dillon goto exitloop;
3463e2ea4ccSMatthew Dillon } else if (cmd->manual_stop) {
3473e2ea4ccSMatthew Dillon /*
3483e2ea4ccSMatthew Dillon * Service demon was told to stop via
3493e2ea4ccSMatthew Dillon * commanded (not automatic) action. We
3503e2ea4ccSMatthew Dillon * do not auto-restart the service in
3513e2ea4ccSMatthew Dillon * this situation.
3523e2ea4ccSMatthew Dillon */
3533e2ea4ccSMatthew Dillon pthread_mutex_unlock(&serial_mtx);
3543e2ea4ccSMatthew Dillon if (dt < 0 || dt > 60)
3553e2ea4ccSMatthew Dillon sleep(60);
3563e2ea4ccSMatthew Dillon else
3573e2ea4ccSMatthew Dillon sleep(1);
3583e2ea4ccSMatthew Dillon pthread_mutex_lock(&serial_mtx);
3593e2ea4ccSMatthew Dillon } else if (cmd->restart_some || cmd->restart_all) {
3603e2ea4ccSMatthew Dillon /*
3613e2ea4ccSMatthew Dillon * Handle automatic restarts
3623e2ea4ccSMatthew Dillon */
3633e2ea4ccSMatthew Dillon if (dt > cmd->restart_timo) {
3643e2ea4ccSMatthew Dillon execute_start(cmd);
3653e2ea4ccSMatthew Dillon } else {
3663e2ea4ccSMatthew Dillon pthread_mutex_unlock(&serial_mtx);
3673e2ea4ccSMatthew Dillon sleep(1);
3683e2ea4ccSMatthew Dillon pthread_mutex_lock(&serial_mtx);
3693e2ea4ccSMatthew Dillon }
3703e2ea4ccSMatthew Dillon } else {
3713e2ea4ccSMatthew Dillon /*
3723e2ea4ccSMatthew Dillon * No automatic restart was configured,
3733e2ea4ccSMatthew Dillon * wait for commanded action.
3743e2ea4ccSMatthew Dillon */
3753e2ea4ccSMatthew Dillon pthread_mutex_unlock(&serial_mtx);
3763e2ea4ccSMatthew Dillon if (dt < 0 || dt > 60)
3773e2ea4ccSMatthew Dillon sleep(60);
3783e2ea4ccSMatthew Dillon else
3793e2ea4ccSMatthew Dillon sleep(1);
3803e2ea4ccSMatthew Dillon pthread_mutex_lock(&serial_mtx);
3813e2ea4ccSMatthew Dillon }
3823e2ea4ccSMatthew Dillon break;
3833e2ea4ccSMatthew Dillon case RS_STOPPING1:
3843e2ea4ccSMatthew Dillon /*
3853e2ea4ccSMatthew Dillon * Reset TERM->KILL timer
3863e2ea4ccSMatthew Dillon */
3873e2ea4ccSMatthew Dillon nextstop = ts.tv_sec;
3883e2ea4ccSMatthew Dillon RunState = RS_STOPPING2;
3893e2ea4ccSMatthew Dillon /* fall through */
3903e2ea4ccSMatthew Dillon case RS_STOPPING2:
3913e2ea4ccSMatthew Dillon if (cmd->termkill_timo == 0) {
3923e2ea4ccSMatthew Dillon nextstop = ts.tv_sec - 1;
3933e2ea4ccSMatthew Dillon } else {
3943e2ea4ccSMatthew Dillon kill(stoppingpid, SIGTERM);
3953e2ea4ccSMatthew Dillon fprintf(cmd->fp, "svc %s: sigterm %d\n",
3963e2ea4ccSMatthew Dillon cmd->label, stoppingpid);
3973e2ea4ccSMatthew Dillon sleep(1);
3983e2ea4ccSMatthew Dillon }
3993e2ea4ccSMatthew Dillon RunState = RS_STOPPING3;
4003e2ea4ccSMatthew Dillon /* fall through */
4013e2ea4ccSMatthew Dillon case RS_STOPPING3:
4023e2ea4ccSMatthew Dillon dt = (int)(ts.tv_sec - nextstop);
4033e2ea4ccSMatthew Dillon if (dt > cmd->termkill_timo) {
4043e2ea4ccSMatthew Dillon fprintf(cmd->fp, "svc %s: sigkill %d\n",
4053e2ea4ccSMatthew Dillon cmd->label, stoppingpid);
4063e2ea4ccSMatthew Dillon kill(stoppingpid, SIGKILL);
4073e2ea4ccSMatthew Dillon }
4083e2ea4ccSMatthew Dillon sleep(1);
4093e2ea4ccSMatthew Dillon break;
4103e2ea4ccSMatthew Dillon }
4113e2ea4ccSMatthew Dillon }
41221537d46SMatthew Dillon exitloop:
4133e2ea4ccSMatthew Dillon pthread_mutex_unlock(&serial_mtx);
41421537d46SMatthew Dillon if (cmd->mountdev) {
41521537d46SMatthew Dillon if (cmd->jaildir)
41621537d46SMatthew Dillon setup_devfs(cmd, cmd->jaildir, 0);
41721537d46SMatthew Dillon else if (cmd->rootdir)
41821537d46SMatthew Dillon setup_devfs(cmd, cmd->rootdir, 0);
41921537d46SMatthew Dillon }
4203e2ea4ccSMatthew Dillon exit(0);
4213e2ea4ccSMatthew Dillon /* does not return */
4223e2ea4ccSMatthew Dillon }
4233e2ea4ccSMatthew Dillon
4243e2ea4ccSMatthew Dillon int
execute_start(command_t * cmd)4253e2ea4ccSMatthew Dillon execute_start(command_t *cmd)
4263e2ea4ccSMatthew Dillon {
4273e2ea4ccSMatthew Dillon struct timespec ts;
4283e2ea4ccSMatthew Dillon int maxwait = 60;
4293e2ea4ccSMatthew Dillon
4303e2ea4ccSMatthew Dillon while (RunState == RS_STOPPING1 ||
4313e2ea4ccSMatthew Dillon RunState == RS_STOPPING2 ||
4323e2ea4ccSMatthew Dillon RunState == RS_STOPPING3) {
4333e2ea4ccSMatthew Dillon fprintf(cmd->fp,
4343e2ea4ccSMatthew Dillon "svc %s: Waiting for previous action to complete\n",
4353e2ea4ccSMatthew Dillon cmd->label);
4363e2ea4ccSMatthew Dillon fflush(cmd->fp);
4373e2ea4ccSMatthew Dillon pthread_mutex_unlock(&serial_mtx);
4383e2ea4ccSMatthew Dillon sleep(1);
4393e2ea4ccSMatthew Dillon pthread_mutex_lock(&serial_mtx);
4403e2ea4ccSMatthew Dillon if (--maxwait == 0) {
4413e2ea4ccSMatthew Dillon fprintf(cmd->fp,
4423e2ea4ccSMatthew Dillon "svc %s: Giving up waiting for action\n",
4433e2ea4ccSMatthew Dillon cmd->label);
4443e2ea4ccSMatthew Dillon fflush(cmd->fp);
4453e2ea4ccSMatthew Dillon break;
4463e2ea4ccSMatthew Dillon }
4473e2ea4ccSMatthew Dillon }
4483e2ea4ccSMatthew Dillon if (RunState == RS_STARTED) {
4493e2ea4ccSMatthew Dillon fprintf(cmd->fp, "svc %s: Already started pid %d\n",
4503e2ea4ccSMatthew Dillon cmd->label, DirectPid);
4513e2ea4ccSMatthew Dillon fflush(cmd->fp);
4523e2ea4ccSMatthew Dillon return 0;
4533e2ea4ccSMatthew Dillon }
4543e2ea4ccSMatthew Dillon
4553e2ea4ccSMatthew Dillon clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
4563e2ea4ccSMatthew Dillon if ((DirectPid = fork()) == 0) {
45782f39527SMatthew Dillon fflush(InitCmd->fp);
45882f39527SMatthew Dillon /* leave stdin /dev/null */
45982f39527SMatthew Dillon dup2(fileno(InitCmd->fp), 1); /* setup stdout */
46082f39527SMatthew Dillon dup2(fileno(InitCmd->fp), 2); /* setup stderr */
4613e2ea4ccSMatthew Dillon closefrom(3);
46221537d46SMatthew Dillon
46321537d46SMatthew Dillon if (cmd->jaildir) /* jail or chroot */
46421537d46SMatthew Dillon setup_jail(cmd);
46521537d46SMatthew Dillon else if (cmd->rootdir)
46621537d46SMatthew Dillon setup_chroot(cmd);
46721537d46SMatthew Dillon
46821537d46SMatthew Dillon setup_gid(cmd);
46921537d46SMatthew Dillon setup_uid(cmd);
4703e2ea4ccSMatthew Dillon execvp(InitCmd->ext_av[0], InitCmd->ext_av);
4713e2ea4ccSMatthew Dillon exit(99);
4723e2ea4ccSMatthew Dillon }
4733e2ea4ccSMatthew Dillon if (DirectPid >= 0) {
4743e2ea4ccSMatthew Dillon RunState = RS_STARTED;
4753e2ea4ccSMatthew Dillon LastStart = ts.tv_sec;
4763e2ea4ccSMatthew Dillon } else {
47782f39527SMatthew Dillon setstate_stopped(InitCmd, &ts);
4783e2ea4ccSMatthew Dillon }
4793e2ea4ccSMatthew Dillon InitCmd->manual_stop = 0;
4803e2ea4ccSMatthew Dillon fprintf(cmd->fp, "svc %s: Starting pid %d\n", cmd->label, DirectPid);
4813e2ea4ccSMatthew Dillon fflush(cmd->fp);
4823e2ea4ccSMatthew Dillon
4833e2ea4ccSMatthew Dillon return 0;
4843e2ea4ccSMatthew Dillon }
4853e2ea4ccSMatthew Dillon
4863e2ea4ccSMatthew Dillon int
execute_restart(command_t * cmd)4873e2ea4ccSMatthew Dillon execute_restart(command_t *cmd)
4883e2ea4ccSMatthew Dillon {
4893e2ea4ccSMatthew Dillon int rc;
4903e2ea4ccSMatthew Dillon
4913e2ea4ccSMatthew Dillon rc = execute_stop(cmd) + execute_start(cmd);
4923e2ea4ccSMatthew Dillon return rc;
4933e2ea4ccSMatthew Dillon }
4943e2ea4ccSMatthew Dillon
4953e2ea4ccSMatthew Dillon int
execute_stop(command_t * cmd)4963e2ea4ccSMatthew Dillon execute_stop(command_t *cmd)
4973e2ea4ccSMatthew Dillon {
49840e10befSMatthew Dillon union reaper_info info;
4993e2ea4ccSMatthew Dillon struct timespec ts;
5003e2ea4ccSMatthew Dillon int save_restart_some;
5013e2ea4ccSMatthew Dillon int save_restart_all;
5023e2ea4ccSMatthew Dillon int maxwait = 60;
5033e2ea4ccSMatthew Dillon
5043e2ea4ccSMatthew Dillon save_restart_some = InitCmd->restart_some;
5053e2ea4ccSMatthew Dillon save_restart_all = InitCmd->restart_all;
5063e2ea4ccSMatthew Dillon if (cmd->commanded)
5073e2ea4ccSMatthew Dillon InitCmd->manual_stop = 1;
5083e2ea4ccSMatthew Dillon if (cmd->commanded && (cmd->restart_some || cmd->restart_all)) {
5093e2ea4ccSMatthew Dillon InitCmd->restart_some = cmd->restart_some;
5103e2ea4ccSMatthew Dillon InitCmd->restart_all = cmd->restart_all;
5113e2ea4ccSMatthew Dillon }
5123e2ea4ccSMatthew Dillon fprintf(cmd->fp, "svc %s: Stopping\n", cmd->label);
5133e2ea4ccSMatthew Dillon fflush(cmd->fp);
5143e2ea4ccSMatthew Dillon
51540e10befSMatthew Dillon /*
51640e10befSMatthew Dillon * Start the kill chain going so the master loop's wait3 wakes up.
51740e10befSMatthew Dillon */
5183e2ea4ccSMatthew Dillon if (DirectPid >= 0) {
5193e2ea4ccSMatthew Dillon kill(DirectPid, SIGTERM);
52040e10befSMatthew Dillon } else {
52140e10befSMatthew Dillon if (procctl(P_PID, getpid(), PROC_REAP_STATUS, &info) == 0 &&
52240e10befSMatthew Dillon info.status.pid_head > 0) {
52340e10befSMatthew Dillon kill(info.status.pid_head, SIGTERM);
52440e10befSMatthew Dillon }
5253e2ea4ccSMatthew Dillon }
5263e2ea4ccSMatthew Dillon
5273e2ea4ccSMatthew Dillon clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
5283e2ea4ccSMatthew Dillon LastStop = ts.tv_sec;
5293e2ea4ccSMatthew Dillon RunState = RS_STOPPING1;
5303e2ea4ccSMatthew Dillon
5313e2ea4ccSMatthew Dillon /*
5323e2ea4ccSMatthew Dillon * If commanded (verses automatic), we are running remote in our
5333e2ea4ccSMatthew Dillon * own thread and we need to wait for the action to complete.
5343e2ea4ccSMatthew Dillon */
5353e2ea4ccSMatthew Dillon if (cmd->commanded) {
5363e2ea4ccSMatthew Dillon while (RunState == RS_STOPPING1 ||
5373e2ea4ccSMatthew Dillon RunState == RS_STOPPING2 ||
5383e2ea4ccSMatthew Dillon RunState == RS_STOPPING3) {
5393e2ea4ccSMatthew Dillon fprintf(cmd->fp,
5403e2ea4ccSMatthew Dillon "svc %s: Waiting for service to stop\n",
5413e2ea4ccSMatthew Dillon cmd->label);
5423e2ea4ccSMatthew Dillon fflush(cmd->fp);
5433e2ea4ccSMatthew Dillon pthread_mutex_unlock(&serial_mtx);
5443e2ea4ccSMatthew Dillon sleep(1);
5453e2ea4ccSMatthew Dillon pthread_mutex_lock(&serial_mtx);
5463e2ea4ccSMatthew Dillon if (--maxwait == 0) {
5473e2ea4ccSMatthew Dillon fprintf(cmd->fp,
5483e2ea4ccSMatthew Dillon "svc %s: Giving up waiting for stop\n",
5493e2ea4ccSMatthew Dillon cmd->label);
5503e2ea4ccSMatthew Dillon fflush(cmd->fp);
5513e2ea4ccSMatthew Dillon break;
5523e2ea4ccSMatthew Dillon }
5533e2ea4ccSMatthew Dillon }
5543e2ea4ccSMatthew Dillon if (cmd->restart_some || cmd->restart_all) {
5553e2ea4ccSMatthew Dillon InitCmd->restart_some = save_restart_some;
5563e2ea4ccSMatthew Dillon InitCmd->restart_all = save_restart_all;
5573e2ea4ccSMatthew Dillon }
5583e2ea4ccSMatthew Dillon }
5593e2ea4ccSMatthew Dillon
5603e2ea4ccSMatthew Dillon return 0;
5613e2ea4ccSMatthew Dillon }
5623e2ea4ccSMatthew Dillon
5633e2ea4ccSMatthew Dillon int
execute_exit(command_t * cmd)5643e2ea4ccSMatthew Dillon execute_exit(command_t *cmd)
5653e2ea4ccSMatthew Dillon {
566ee6361d4SMatthew Dillon if (cmd->commanded) {
567ee6361d4SMatthew Dillon InitCmd->restart_some = 0;
568ee6361d4SMatthew Dillon InitCmd->restart_all = 1; /* kill all children */
569ee6361d4SMatthew Dillon InitCmd->exit_mode = 1; /* exit after stop */
57021537d46SMatthew Dillon } else {
57121537d46SMatthew Dillon cmd->exit_mode = 1;
572ee6361d4SMatthew Dillon }
573ee6361d4SMatthew Dillon fprintf(cmd->fp, "svc %s: Stopping and Exiting\n", cmd->label);
5743e2ea4ccSMatthew Dillon execute_stop(cmd);
5753e2ea4ccSMatthew Dillon
57621537d46SMatthew Dillon return 0;
5773e2ea4ccSMatthew Dillon }
5783e2ea4ccSMatthew Dillon
5793e2ea4ccSMatthew Dillon int
execute_list(command_t * cmd)5803e2ea4ccSMatthew Dillon execute_list(command_t *cmd)
5813e2ea4ccSMatthew Dillon {
5823e2ea4ccSMatthew Dillon fprintf(cmd->fp, "%-16s\n", cmd->label);
5833e2ea4ccSMatthew Dillon
5843e2ea4ccSMatthew Dillon return 0;
5853e2ea4ccSMatthew Dillon }
5863e2ea4ccSMatthew Dillon
5873e2ea4ccSMatthew Dillon int
execute_status(command_t * cmd)5883e2ea4ccSMatthew Dillon execute_status(command_t *cmd)
5893e2ea4ccSMatthew Dillon {
5903e2ea4ccSMatthew Dillon const char *state;
5913e2ea4ccSMatthew Dillon
5923e2ea4ccSMatthew Dillon switch(RunState) {
5933e2ea4ccSMatthew Dillon case RS_STOPPED:
59440e10befSMatthew Dillon if (InitCmd && InitCmd->exit_mode)
59540e10befSMatthew Dillon state = "stopped (exiting)";
59640e10befSMatthew Dillon else if (InitCmd && InitCmd->manual_stop)
5973e2ea4ccSMatthew Dillon state = "stopped (manual)";
5983e2ea4ccSMatthew Dillon else
5993e2ea4ccSMatthew Dillon state = "stopped";
6003e2ea4ccSMatthew Dillon break;
6013e2ea4ccSMatthew Dillon case RS_STARTED:
6023e2ea4ccSMatthew Dillon state = "running";
6033e2ea4ccSMatthew Dillon break;
6043e2ea4ccSMatthew Dillon case RS_STOPPING1:
6053e2ea4ccSMatthew Dillon case RS_STOPPING2:
6063e2ea4ccSMatthew Dillon case RS_STOPPING3:
6073e2ea4ccSMatthew Dillon state = "killing";
6083e2ea4ccSMatthew Dillon break;
6093e2ea4ccSMatthew Dillon default:
6103e2ea4ccSMatthew Dillon state = "unknown";
6113e2ea4ccSMatthew Dillon break;
6123e2ea4ccSMatthew Dillon }
6133e2ea4ccSMatthew Dillon
6143e2ea4ccSMatthew Dillon fprintf(cmd->fp, "%-16s %s\n", cmd->label, state);
6153e2ea4ccSMatthew Dillon
6163e2ea4ccSMatthew Dillon return 0;
6173e2ea4ccSMatthew Dillon }
6183e2ea4ccSMatthew Dillon
6193e2ea4ccSMatthew Dillon int
execute_log(command_t * cmd)62082f39527SMatthew Dillon execute_log(command_t *cmd)
6213e2ea4ccSMatthew Dillon {
62282f39527SMatthew Dillon int lbsize = (int)sizeof(cmd->logbuf);
62382f39527SMatthew Dillon int lbmask = lbsize - 1;
62482f39527SMatthew Dillon int windex;
62582f39527SMatthew Dillon int n;
62682f39527SMatthew Dillon int lastnl;
62782f39527SMatthew Dillon int dotstate;
62882f39527SMatthew Dillon char buf[LOGCHUNK];
62982f39527SMatthew Dillon
63082f39527SMatthew Dillon assert(InitCmd);
63182f39527SMatthew Dillon
63282f39527SMatthew Dillon /*
63382f39527SMatthew Dillon * mode 0 - Dump everything then exit
63482f39527SMatthew Dillon * mode 1 - Dump everything then block/loop
63582f39527SMatthew Dillon * mode 2 - Skeep to end then block/loop
63682f39527SMatthew Dillon */
63782f39527SMatthew Dillon if (cmd->tail_mode == 2)
63882f39527SMatthew Dillon windex = InitCmd->logwindex;
63982f39527SMatthew Dillon else
64082f39527SMatthew Dillon windex = InitCmd->logwindex - InitCmd->logcount;
64182f39527SMatthew Dillon lastnl = 1;
64282f39527SMatthew Dillon dotstate = 0; /* 0=start-of-line 1=middle-of-line 2=dot */
64382f39527SMatthew Dillon
64482f39527SMatthew Dillon for (;;) {
64582f39527SMatthew Dillon /*
64682f39527SMatthew Dillon * Calculate the amount of data we missed and determine
64782f39527SMatthew Dillon * if some data was lost.
64882f39527SMatthew Dillon */
64982f39527SMatthew Dillon n = InitCmd->logwindex - windex;
65082f39527SMatthew Dillon if (n < 0 || n > InitCmd->logcount) {
65182f39527SMatthew Dillon windex = InitCmd->logwindex - InitCmd->logcount;
65282f39527SMatthew Dillon pthread_mutex_unlock(&serial_mtx);
65382f39527SMatthew Dillon fprintf(cmd->fp, "\n(LOG DATA LOST)\n");
65482f39527SMatthew Dillon pthread_mutex_lock(&serial_mtx);
65582f39527SMatthew Dillon continue;
65682f39527SMatthew Dillon }
65782f39527SMatthew Dillon
65882f39527SMatthew Dillon /*
65982f39527SMatthew Dillon * Circular buffer and copy size limitations. If no
66082f39527SMatthew Dillon * data ready, wait for some.
66182f39527SMatthew Dillon */
66282f39527SMatthew Dillon if (n > lbsize - (windex & lbmask))
66382f39527SMatthew Dillon n = lbsize - (windex & lbmask);
66482f39527SMatthew Dillon if (n > LOGCHUNK)
66582f39527SMatthew Dillon n = LOGCHUNK;
66682f39527SMatthew Dillon if (n == 0) {
66782f39527SMatthew Dillon if (cmd->tail_mode == 0)
66882f39527SMatthew Dillon break;
66982f39527SMatthew Dillon pthread_cond_wait(&InitCmd->logcond, &serial_mtx);
67082f39527SMatthew Dillon continue;
67182f39527SMatthew Dillon }
67282f39527SMatthew Dillon bcopy(InitCmd->logbuf + (windex & lbmask), buf, n);
67382f39527SMatthew Dillon
67482f39527SMatthew Dillon /*
67582f39527SMatthew Dillon * Dump log output, escape any '.' on a line by itself.
67682f39527SMatthew Dillon */
67782f39527SMatthew Dillon pthread_mutex_unlock(&serial_mtx);
67882f39527SMatthew Dillon n = escapewrite(cmd->fp, buf, n, &dotstate);
67982f39527SMatthew Dillon fflush(cmd->fp);
68082f39527SMatthew Dillon if (n > 0)
68182f39527SMatthew Dillon lastnl = (buf[n-1] == '\n');
68282f39527SMatthew Dillon pthread_mutex_lock(&serial_mtx);
68382f39527SMatthew Dillon
68482f39527SMatthew Dillon if (n < 0)
68582f39527SMatthew Dillon break;
68682f39527SMatthew Dillon windex += n;
68782f39527SMatthew Dillon }
68882f39527SMatthew Dillon if (lastnl == 0) {
68982f39527SMatthew Dillon pthread_mutex_unlock(&serial_mtx);
69082f39527SMatthew Dillon fprintf(cmd->fp, "\n");
69182f39527SMatthew Dillon pthread_mutex_lock(&serial_mtx);
69282f39527SMatthew Dillon }
6933e2ea4ccSMatthew Dillon return 0;
6943e2ea4ccSMatthew Dillon }
6953e2ea4ccSMatthew Dillon
69682f39527SMatthew Dillon /*
69782f39527SMatthew Dillon * Change or reopen logfile.
69882f39527SMatthew Dillon */
6993e2ea4ccSMatthew Dillon int
execute_logfile(command_t * cmd)7003e2ea4ccSMatthew Dillon execute_logfile(command_t *cmd)
7013e2ea4ccSMatthew Dillon {
7023e2ea4ccSMatthew Dillon char *logfile;
7033e2ea4ccSMatthew Dillon int fd;
7043e2ea4ccSMatthew Dillon int rc;
7053e2ea4ccSMatthew Dillon
70682f39527SMatthew Dillon assert(InitCmd);
70782f39527SMatthew Dillon
7083e2ea4ccSMatthew Dillon logfile = cmd->logfile;
7093e2ea4ccSMatthew Dillon if (cmd->ext_av && cmd->ext_av[0])
7103e2ea4ccSMatthew Dillon logfile = cmd->ext_av[0];
71182f39527SMatthew Dillon if (logfile == NULL)
7123e2ea4ccSMatthew Dillon logfile = InitCmd->logfile;
7133e2ea4ccSMatthew Dillon
7143e2ea4ccSMatthew Dillon rc = 0;
71582f39527SMatthew Dillon if (logfile) {
7163e2ea4ccSMatthew Dillon if (InitCmd->logfile &&
7173e2ea4ccSMatthew Dillon strcmp(InitCmd->logfile, logfile) == 0) {
7183e2ea4ccSMatthew Dillon fprintf(cmd->fp, "svc %s: Reopen logfile %s\n",
7193e2ea4ccSMatthew Dillon cmd->label, logfile);
7203e2ea4ccSMatthew Dillon } else {
7213e2ea4ccSMatthew Dillon fprintf(cmd->fp, "svc %s: Change logfile to %s\n",
7223e2ea4ccSMatthew Dillon cmd->label, logfile);
7233e2ea4ccSMatthew Dillon }
72482f39527SMatthew Dillon if (InitCmd->logfd >= 0) {
72582f39527SMatthew Dillon close(InitCmd->logfd);
72682f39527SMatthew Dillon InitCmd->logfd = -1;
72782f39527SMatthew Dillon }
72882f39527SMatthew Dillon if (strcmp(logfile, "/dev/null") == 0) {
72982f39527SMatthew Dillon sreplace(&InitCmd->logfile, logfile);
73082f39527SMatthew Dillon } else {
7313e2ea4ccSMatthew Dillon fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0640);
7323e2ea4ccSMatthew Dillon if (fd >= 0) {
73382f39527SMatthew Dillon InitCmd->logfd = fd;
7343e2ea4ccSMatthew Dillon sreplace(&InitCmd->logfile, logfile);
7353e2ea4ccSMatthew Dillon } else {
7363e2ea4ccSMatthew Dillon fprintf(cmd->fp,
73782f39527SMatthew Dillon "svc %s: Unable to open/create "
73882f39527SMatthew Dillon "\"%s\": %s\n",
73982f39527SMatthew Dillon cmd->label,
74082f39527SMatthew Dillon logfile, strerror(errno));
7413e2ea4ccSMatthew Dillon rc = 1;
7423e2ea4ccSMatthew Dillon }
7433e2ea4ccSMatthew Dillon }
74482f39527SMatthew Dillon }
7453e2ea4ccSMatthew Dillon return rc;
7463e2ea4ccSMatthew Dillon }
747ee6361d4SMatthew Dillon
748ee6361d4SMatthew Dillon int
execute_help(command_t * cmd)749ee6361d4SMatthew Dillon execute_help(command_t *cmd)
750ee6361d4SMatthew Dillon {
751ee6361d4SMatthew Dillon fprintf(cmd->fp,
752ee6361d4SMatthew Dillon "svc [options] directive [label [additional_args]]\n"
753ee6361d4SMatthew Dillon "\n"
754ee6361d4SMatthew Dillon "Directives: init start stop stopall restart exit\n"
755ee6361d4SMatthew Dillon " kill list status log logf tailf logfile\n"
756ee6361d4SMatthew Dillon " help\n"
757ee6361d4SMatthew Dillon );
758ee6361d4SMatthew Dillon return 0;
759ee6361d4SMatthew Dillon }
76082f39527SMatthew Dillon
76182f39527SMatthew Dillon static
76282f39527SMatthew Dillon void *
logger_thread(void * arg)76382f39527SMatthew Dillon logger_thread(void *arg)
76482f39527SMatthew Dillon {
76582f39527SMatthew Dillon command_t *cmd = arg;
76682f39527SMatthew Dillon int lbsize = (int)sizeof(cmd->logbuf);
76782f39527SMatthew Dillon int lbmask = lbsize - 1;
76882f39527SMatthew Dillon int windex;
76982f39527SMatthew Dillon int n;
77082f39527SMatthew Dillon
77182f39527SMatthew Dillon pthread_detach(pthread_self());
77282f39527SMatthew Dillon pthread_mutex_lock(&serial_mtx);
77382f39527SMatthew Dillon for (;;) {
77482f39527SMatthew Dillon /*
77582f39527SMatthew Dillon * slip circular buffer to make room for new data.
77682f39527SMatthew Dillon */
77782f39527SMatthew Dillon n = cmd->logcount - (lbsize - LOGCHUNK);
77882f39527SMatthew Dillon if (n > 0) {
77982f39527SMatthew Dillon cmd->logcount -= n;
78082f39527SMatthew Dillon cmd->logwindex += n;
78182f39527SMatthew Dillon }
78282f39527SMatthew Dillon windex = cmd->logwindex & lbmask;
78382f39527SMatthew Dillon n = lbsize - windex;
78482f39527SMatthew Dillon if (n > LOGCHUNK)
78582f39527SMatthew Dillon n = LOGCHUNK;
78682f39527SMatthew Dillon pthread_mutex_unlock(&serial_mtx);
78782f39527SMatthew Dillon n = read(cmd->logfds[0], cmd->logbuf + windex, n);
78882f39527SMatthew Dillon pthread_mutex_lock(&serial_mtx);
78982f39527SMatthew Dillon if (n > 0) {
79082f39527SMatthew Dillon if (cmd->logfd >= 0)
79182f39527SMatthew Dillon write(cmd->logfd, cmd->logbuf + windex, n);
79282f39527SMatthew Dillon cmd->logcount += n;
79382f39527SMatthew Dillon cmd->logwindex += n;
79482f39527SMatthew Dillon pthread_cond_signal(&cmd->logcond);
79582f39527SMatthew Dillon }
79682f39527SMatthew Dillon if (n == 0 || (n < 0 && errno != EINTR))
79782f39527SMatthew Dillon break;
79882f39527SMatthew Dillon }
79982f39527SMatthew Dillon pthread_mutex_unlock(&serial_mtx);
80082f39527SMatthew Dillon return NULL;
80182f39527SMatthew Dillon }
80282f39527SMatthew Dillon
80321537d46SMatthew Dillon /*
80421537d46SMatthew Dillon * Put us in the STOPPED state if we are not already there, and
80521537d46SMatthew Dillon * handle post-stop options (aka sync).
80621537d46SMatthew Dillon */
80782f39527SMatthew Dillon static
80882f39527SMatthew Dillon void
setstate_stopped(command_t * cmd,struct timespec * ts)80982f39527SMatthew Dillon setstate_stopped(command_t *cmd, struct timespec *ts)
81082f39527SMatthew Dillon {
81182f39527SMatthew Dillon if (RunState != RS_STOPPED) {
81282f39527SMatthew Dillon RunState = RS_STOPPED;
81382f39527SMatthew Dillon LastStop = ts->tv_sec;
81482f39527SMatthew Dillon if (cmd->sync_mode) /* support -s option */
81582f39527SMatthew Dillon sync();
81682f39527SMatthew Dillon }
81782f39527SMatthew Dillon }
81882f39527SMatthew Dillon
81982f39527SMatthew Dillon static
82082f39527SMatthew Dillon int
setup_gid(command_t * cmd)82121537d46SMatthew Dillon setup_gid(command_t *cmd)
82221537d46SMatthew Dillon {
82321537d46SMatthew Dillon int i;
82421537d46SMatthew Dillon
82521537d46SMatthew Dillon if (cmd->gid_mode &&
82621537d46SMatthew Dillon setgid(cmd->grent.gr_gid) < 0) {
82721537d46SMatthew Dillon fprintf(cmd->fp, "unable to setgid to \"%s\": %s\n",
82821537d46SMatthew Dillon cmd->grent.gr_name, strerror(errno));
82921537d46SMatthew Dillon return 1;
83021537d46SMatthew Dillon }
83121537d46SMatthew Dillon
83221537d46SMatthew Dillon /*
83321537d46SMatthew Dillon * -G overrides all group ids.
83421537d46SMatthew Dillon */
83521537d46SMatthew Dillon if (cmd->ngroups) {
83621537d46SMatthew Dillon if (setgroups(cmd->ngroups, cmd->groups) < 0) {
83721537d46SMatthew Dillon fprintf(cmd->fp, "unable to setgroups to (");
83821537d46SMatthew Dillon for (i = 0; i < cmd->ngroups; ++i) {
83921537d46SMatthew Dillon if (i)
84021537d46SMatthew Dillon fprintf(cmd->fp, ", ");
84121537d46SMatthew Dillon fprintf(cmd->fp, "%d", cmd->groups[i]);
84221537d46SMatthew Dillon }
84321537d46SMatthew Dillon fprintf(cmd->fp, "): %s\n", strerror(errno));
84421537d46SMatthew Dillon return 1;
84521537d46SMatthew Dillon }
84621537d46SMatthew Dillon }
84721537d46SMatthew Dillon return 0;
84821537d46SMatthew Dillon }
84921537d46SMatthew Dillon
85021537d46SMatthew Dillon static
85121537d46SMatthew Dillon int
setup_uid(command_t * cmd)85221537d46SMatthew Dillon setup_uid(command_t *cmd)
85321537d46SMatthew Dillon {
85421537d46SMatthew Dillon fprintf(stderr, "UIDMODE %d %d\n", cmd->uid_mode, cmd->pwent.pw_uid);
85521537d46SMatthew Dillon if (cmd->uid_mode &&
85621537d46SMatthew Dillon cmd->gid_mode == 0 &&
85721537d46SMatthew Dillon cmd->ngroups == 0 &&
85821537d46SMatthew Dillon setgid(cmd->pwent.pw_gid) < 0) {
85921537d46SMatthew Dillon fprintf(cmd->fp, "unable to setgid for user \"%s\": %s\n",
86021537d46SMatthew Dillon cmd->pwent.pw_name,
86121537d46SMatthew Dillon strerror(errno));
86221537d46SMatthew Dillon return 1;
86321537d46SMatthew Dillon }
86421537d46SMatthew Dillon if (cmd->uid_mode &&
86521537d46SMatthew Dillon setuid(cmd->pwent.pw_uid) < 0) {
86621537d46SMatthew Dillon fprintf(cmd->fp, "unable to setuid for user \"%s\": %s\n",
86721537d46SMatthew Dillon cmd->pwent.pw_name,
86821537d46SMatthew Dillon strerror(errno));
86921537d46SMatthew Dillon return 1;
87021537d46SMatthew Dillon }
87121537d46SMatthew Dillon return 0;
87221537d46SMatthew Dillon }
87321537d46SMatthew Dillon
87421537d46SMatthew Dillon static
87521537d46SMatthew Dillon int
setup_jail(command_t * cmd)87621537d46SMatthew Dillon setup_jail(command_t *cmd)
87721537d46SMatthew Dillon {
87821537d46SMatthew Dillon struct jail info;
87921537d46SMatthew Dillon char hostbuf[256];
88021537d46SMatthew Dillon
88121537d46SMatthew Dillon if (gethostname(hostbuf, sizeof(hostbuf) - 1) < 0) {
88221537d46SMatthew Dillon fprintf(cmd->fp, "gethostname() failed: %s\n", strerror(errno));
88321537d46SMatthew Dillon return 1;
88421537d46SMatthew Dillon }
88521537d46SMatthew Dillon /* make sure it is zero terminated */
88621537d46SMatthew Dillon hostbuf[sizeof(hostbuf) -1] = 0;
88721537d46SMatthew Dillon
88821537d46SMatthew Dillon bzero(&info, sizeof(info));
88921537d46SMatthew Dillon info.version = 1;
89021537d46SMatthew Dillon info.path = cmd->jaildir;
89121537d46SMatthew Dillon info.hostname = hostbuf;
89221537d46SMatthew Dillon /* info.n_ips, sockaddr_storage ips[] */
89321537d46SMatthew Dillon
89421537d46SMatthew Dillon if (jail(&info) < 0) {
89521537d46SMatthew Dillon fprintf(cmd->fp, "unable to create jail \"%s\": %s\n",
89621537d46SMatthew Dillon cmd->rootdir,
89721537d46SMatthew Dillon strerror(errno));
89821537d46SMatthew Dillon return 1;
89921537d46SMatthew Dillon }
90021537d46SMatthew Dillon return 0;
90121537d46SMatthew Dillon }
90221537d46SMatthew Dillon
90321537d46SMatthew Dillon static
90421537d46SMatthew Dillon int
setup_chroot(command_t * cmd)90521537d46SMatthew Dillon setup_chroot(command_t *cmd)
90621537d46SMatthew Dillon {
90721537d46SMatthew Dillon if (chroot(cmd->rootdir) < 0) {
90821537d46SMatthew Dillon fprintf(cmd->fp, "unable to chroot to \"%s\": %s\n",
90921537d46SMatthew Dillon cmd->rootdir,
91021537d46SMatthew Dillon strerror(errno));
91121537d46SMatthew Dillon return 1;
91221537d46SMatthew Dillon }
91321537d46SMatthew Dillon return 0;
91421537d46SMatthew Dillon }
91521537d46SMatthew Dillon
91621537d46SMatthew Dillon static
91721537d46SMatthew Dillon int
setup_devfs(command_t * cmd,const char * dir,int domount)91821537d46SMatthew Dillon setup_devfs(command_t *cmd, const char *dir, int domount)
91921537d46SMatthew Dillon {
92021537d46SMatthew Dillon struct devfs_mount_info info;
92121537d46SMatthew Dillon struct statfs fs;
92221537d46SMatthew Dillon int rc = 0;
92321537d46SMatthew Dillon char *path;
92421537d46SMatthew Dillon
92521537d46SMatthew Dillon bzero(&info, sizeof(info));
92621537d46SMatthew Dillon info.flags = 0;
92721537d46SMatthew Dillon asprintf(&path, "%s/dev", dir);
92821537d46SMatthew Dillon
92921537d46SMatthew Dillon if (domount) {
93021537d46SMatthew Dillon if (statfs(path, &fs) == 0 &&
93121537d46SMatthew Dillon strcmp(fs.f_fstypename, "devfs") == 0) {
93221537d46SMatthew Dillon fprintf(cmd->fp, "devfs already mounted\n");
93321537d46SMatthew Dillon } else
93421537d46SMatthew Dillon if (mount("devfs", path, MNT_NOEXEC|MNT_NOSUID, &info) < 0) {
93521537d46SMatthew Dillon fprintf(cmd->fp, "cannot mount devfs on %s: %s\n",
93621537d46SMatthew Dillon path, strerror(errno));
93721537d46SMatthew Dillon rc = 1;
93821537d46SMatthew Dillon }
93921537d46SMatthew Dillon } else {
94021537d46SMatthew Dillon if (statfs(path, &fs) < 0 ||
94121537d46SMatthew Dillon strcmp(fs.f_fstypename, "devfs") != 0) {
94221537d46SMatthew Dillon fprintf(cmd->fp, "devfs already unmounted\n");
94321537d46SMatthew Dillon } else
94421537d46SMatthew Dillon if (unmount(path, 0) < 0) {
94521537d46SMatthew Dillon fprintf(cmd->fp, "cannot unmount devfs from %s: %s\n",
94621537d46SMatthew Dillon path, strerror(errno));
94721537d46SMatthew Dillon rc = 1;
94821537d46SMatthew Dillon }
94921537d46SMatthew Dillon }
95021537d46SMatthew Dillon free(path);
95121537d46SMatthew Dillon return rc;
95221537d46SMatthew Dillon }
95321537d46SMatthew Dillon
95421537d46SMatthew Dillon /*
95521537d46SMatthew Dillon * Escape writes. A '.' on a line by itself must be escaped to '..'.
95621537d46SMatthew Dillon */
95721537d46SMatthew Dillon static
95821537d46SMatthew Dillon int
escapewrite(FILE * fp,char * buf,int n,int * statep)95982f39527SMatthew Dillon escapewrite(FILE *fp, char *buf, int n, int *statep)
96082f39527SMatthew Dillon {
96182f39527SMatthew Dillon int b;
96282f39527SMatthew Dillon int i;
96382f39527SMatthew Dillon int r;
96482f39527SMatthew Dillon char c;
96582f39527SMatthew Dillon
96682f39527SMatthew Dillon b = 0;
96782f39527SMatthew Dillon r = 0;
968*e9609839SAntonio Huete Jimenez i = 0;
96982f39527SMatthew Dillon while (i < n) {
97082f39527SMatthew Dillon for (i = b; i < n; ++i) {
97182f39527SMatthew Dillon c = buf[i];
97282f39527SMatthew Dillon
97382f39527SMatthew Dillon switch(*statep) {
97482f39527SMatthew Dillon case 0:
97582f39527SMatthew Dillon /*
97682f39527SMatthew Dillon * beginning of line
97782f39527SMatthew Dillon */
97882f39527SMatthew Dillon if (c == '.')
97982f39527SMatthew Dillon *statep = 2;
98082f39527SMatthew Dillon else if (c != '\n')
98182f39527SMatthew Dillon *statep = 1;
98282f39527SMatthew Dillon break;
98382f39527SMatthew Dillon case 1:
98482f39527SMatthew Dillon /*
98582f39527SMatthew Dillon * middle of line
98682f39527SMatthew Dillon */
98782f39527SMatthew Dillon if (c == '\n')
98882f39527SMatthew Dillon *statep = 0;
98982f39527SMatthew Dillon break;
99082f39527SMatthew Dillon case 2:
99182f39527SMatthew Dillon /*
99282f39527SMatthew Dillon * dot was output at beginning of line
99382f39527SMatthew Dillon */
99482f39527SMatthew Dillon if (c == '\n')
99582f39527SMatthew Dillon *statep = 3;
99682f39527SMatthew Dillon else
99782f39527SMatthew Dillon *statep = 1;
99882f39527SMatthew Dillon break;
99982f39527SMatthew Dillon default:
100082f39527SMatthew Dillon break;
100182f39527SMatthew Dillon }
100282f39527SMatthew Dillon if (*statep == 3) /* flush with escape */
100382f39527SMatthew Dillon break;
100482f39527SMatthew Dillon }
100582f39527SMatthew Dillon if (i != b) {
100682f39527SMatthew Dillon n = fwrite(buf, 1, i - b, fp);
100782f39527SMatthew Dillon if (n > 0)
100882f39527SMatthew Dillon r += n;
100982f39527SMatthew Dillon if (n < 0)
101082f39527SMatthew Dillon r = -1;
101182f39527SMatthew Dillon }
101282f39527SMatthew Dillon if (*statep == 3) { /* added escape */
101382f39527SMatthew Dillon n = fwrite(".", 1, 1, fp);
101482f39527SMatthew Dillon /* escapes not counted in r */
101582f39527SMatthew Dillon *statep = 1;
101682f39527SMatthew Dillon if (n < 0)
101782f39527SMatthew Dillon r = -1;
101882f39527SMatthew Dillon }
101982f39527SMatthew Dillon if (r < 0)
102082f39527SMatthew Dillon break;
102182f39527SMatthew Dillon }
102282f39527SMatthew Dillon return r;
102382f39527SMatthew Dillon }
1024