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
35 * Handle remote listen/connect and parsing operations.
40 pthread_mutex_t serial_mtx;
41 time_t LastStart; /* uptime */
42 time_t LastStop; /* uptime */
44 runstate_t RunState = RS_STOPPED;
48 static void *logger_thread(void *arg);
49 static void setstate_stopped(command_t *cmd, struct timespec *ts);
50 static int setup_gid(command_t *cmd);
51 static int setup_uid(command_t *cmd);
52 static int setup_jail(command_t *cmd);
53 static int setup_chroot(command_t *cmd);
54 static int setup_devfs(command_t *cmd, const char *dir, int domount);
55 static int escapewrite(FILE *fp, char *buf, int n, int *statep);
58 execute_init(command_t *cmd)
62 pid_t stoppingpid = -1;
65 int lfd; /* unix domain listen socket */
66 int pfd; /* pid file */
72 if (cmd->label == NULL || cmd->ext_ac == 0) {
73 fprintf(cmd->fp, "init requires a label and command\n");
76 fprintf(cmd->fp, "initializing new service: %s\n", cmd->label);
78 if ((xfd = open("/dev/null", O_RDWR)) < 0) {
79 fprintf(cmd->fp, "Unable to open /dev/null: %s\n",
85 * Setup pidfile and unix domain listen socket and lock the
88 rc = setup_pid_and_socket(cmd, &lfd, &pfd);
95 if (cmd->foreground) {
104 fprintf(cmd->fp, "Unable to create pipe: %s\n",
108 remove_pid_and_socket(cmd, cmd->label);
120 fprintf(cmd->fp, "fork failed: %s\n", strerror(errno));
125 remove_pid_and_socket(cmd, cmd->label);
129 * Fill-in pfd before returning.
131 snprintf(buf, sizeof(buf), "%d\n", (int)pid);
132 write(pfd, buf, strlen(buf));
138 * Wait for child to completely detach from the tty
148 * Forked child is now the service demon.
150 * Detach from terminal, scrap tty, set process title.
152 if (cmd->proctitle) {
153 setproctitle("%s - %s", cmd->label, cmd->proctitle);
155 setproctitle("%s", cmd->label);
159 setup_devfs(cmd, cmd->jaildir, 1);
160 else if (cmd->rootdir)
161 setup_devfs(cmd, cmd->rootdir, 1);
164 if (cmd->foreground == 0) {
169 if (xfd != 0) /* scrap tty inputs */
171 if (cmd->foreground == 0) {
174 if (xfd != 1) /* scrap tty outputs */
179 if ((tfd = open("/dev/tty", O_RDWR)) >= 0) {
180 ioctl(tfd, TIOCNOTTY, 0); /* no controlling tty */
183 setsid(); /* new session */
187 * Setup log file. The log file must not use descriptors 0, 1, or 2.
189 if (cmd->logfile && strcmp(cmd->logfile, "/dev/null") == 0)
191 else if (cmd->logfile)
192 cmd->logfd = open(cmd->logfile, O_WRONLY|O_CREAT|O_APPEND, 0640);
193 else if (cmd->foreground)
199 * Signal parent that we are completely detached now.
202 if (cmd->foreground == 0) {
203 write(fds[1], &c, 1);
210 * Setup log pipe. The logger thread copies the pipe to a buffer
211 * for the 'log' directive and also writes it to logfd.
214 if (cmd->fp != stdout)
216 cmd->fp = fdopen(cmd->logfds[1], "w");
223 pthread_cond_init(&cmd->logcond, NULL);
226 * Start accept thread for unix domain listen socket.
228 pthread_mutex_lock(&serial_mtx);
229 pthread_create(&logtd, NULL, logger_thread, cmd);
230 remote_listener(cmd, lfd);
233 * Become the reaper for all children recursively.
235 if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) < 0) {
236 fprintf(cmd->fp, "svc is unable to become the "
237 "reaper for its children\n");
242 * Initial service start
247 * Main loop is the reaper
250 union reaper_info info;
257 * If we are running just block doing normal reaping,
258 * if we are stopping we have to poll for reaping while
259 * we handle stopping.
262 if (RunState == RS_STARTED) {
263 pthread_mutex_unlock(&serial_mtx);
264 pid = wait3(&status, 0, NULL);
265 pthread_mutex_lock(&serial_mtx);
267 pid = wait3(&status, WNOHANG, NULL);
269 clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
272 if (pid == DirectPid) {
274 "svc %s: lost direct child %d\n",
278 if (cmd->restart_some) {
279 setstate_stopped(cmd, &ts);
280 } /* else still considered normal run state */
281 } else if (cmd->debug) {
283 * Reap random disconnected child, but don't
284 * spew to the log unless debugging is
288 "svc %s: reap indirect child %d\n",
295 * Calculate the pid to potentially act on and/or
296 * determine if any children still exist.
298 if (DirectPid >= 0) {
300 } else if (procctl(P_PID, getpid(),
301 PROC_REAP_STATUS, &info) == 0) {
302 usepid = info.status.pid_head;
307 fprintf(stderr, "svc %s: usepid %d\n",
313 * If stoppingpid changes we have to reset the TERM->KILL
317 setstate_stopped(cmd, &ts);
318 } else if (stoppingpid != usepid &&
319 (RunState == RS_STOPPING2 ||
320 RunState == RS_STOPPING3)) {
321 RunState = RS_STOPPING1;
323 stoppingpid = usepid;
331 setstate_stopped(cmd, &ts);
334 dt = (int)(ts.tv_sec - LastStop);
336 if (cmd->exit_mode) {
338 * Service demon was told to exit on service
339 * stop (-x passed to init).
342 "svc %s: service demon exiting\n",
344 remove_pid_and_socket(cmd, cmd->label);
346 } else if (cmd->manual_stop) {
348 * Service demon was told to stop via
349 * commanded (not automatic) action. We
350 * do not auto-restart the service in
353 pthread_mutex_unlock(&serial_mtx);
354 if (dt < 0 || dt > 60)
358 pthread_mutex_lock(&serial_mtx);
359 } else if (cmd->restart_some || cmd->restart_all) {
361 * Handle automatic restarts
363 if (dt > cmd->restart_timo) {
366 pthread_mutex_unlock(&serial_mtx);
368 pthread_mutex_lock(&serial_mtx);
372 * No automatic restart was configured,
373 * wait for commanded action.
375 pthread_mutex_unlock(&serial_mtx);
376 if (dt < 0 || dt > 60)
380 pthread_mutex_lock(&serial_mtx);
385 * Reset TERM->KILL timer
387 nextstop = ts.tv_sec;
388 RunState = RS_STOPPING2;
391 if (cmd->termkill_timo == 0) {
392 nextstop = ts.tv_sec - 1;
394 kill(stoppingpid, SIGTERM);
395 fprintf(cmd->fp, "svc %s: sigterm %d\n",
396 cmd->label, stoppingpid);
399 RunState = RS_STOPPING3;
402 dt = (int)(ts.tv_sec - nextstop);
403 if (dt > cmd->termkill_timo) {
404 fprintf(cmd->fp, "svc %s: sigkill %d\n",
405 cmd->label, stoppingpid);
406 kill(stoppingpid, SIGKILL);
413 pthread_mutex_unlock(&serial_mtx);
416 setup_devfs(cmd, cmd->jaildir, 0);
417 else if (cmd->rootdir)
418 setup_devfs(cmd, cmd->rootdir, 0);
421 /* does not return */
425 execute_start(command_t *cmd)
430 while (RunState == RS_STOPPING1 ||
431 RunState == RS_STOPPING2 ||
432 RunState == RS_STOPPING3) {
434 "svc %s: Waiting for previous action to complete\n",
437 pthread_mutex_unlock(&serial_mtx);
439 pthread_mutex_lock(&serial_mtx);
440 if (--maxwait == 0) {
442 "svc %s: Giving up waiting for action\n",
448 if (RunState == RS_STARTED) {
449 fprintf(cmd->fp, "svc %s: Already started pid %d\n",
450 cmd->label, DirectPid);
455 clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
456 if ((DirectPid = fork()) == 0) {
458 /* leave stdin /dev/null */
459 dup2(fileno(InitCmd->fp), 1); /* setup stdout */
460 dup2(fileno(InitCmd->fp), 2); /* setup stderr */
463 if (cmd->jaildir) /* jail or chroot */
465 else if (cmd->rootdir)
470 execvp(InitCmd->ext_av[0], InitCmd->ext_av);
473 if (DirectPid >= 0) {
474 RunState = RS_STARTED;
475 LastStart = ts.tv_sec;
477 setstate_stopped(InitCmd, &ts);
479 InitCmd->manual_stop = 0;
480 fprintf(cmd->fp, "svc %s: Starting pid %d\n", cmd->label, DirectPid);
487 execute_restart(command_t *cmd)
491 rc = execute_stop(cmd) + execute_start(cmd);
496 execute_stop(command_t *cmd)
498 union reaper_info info;
500 int save_restart_some;
501 int save_restart_all;
504 save_restart_some = InitCmd->restart_some;
505 save_restart_all = InitCmd->restart_all;
507 InitCmd->manual_stop = 1;
508 if (cmd->commanded && (cmd->restart_some || cmd->restart_all)) {
509 InitCmd->restart_some = cmd->restart_some;
510 InitCmd->restart_all = cmd->restart_all;
512 fprintf(cmd->fp, "svc %s: Stopping\n", cmd->label);
516 * Start the kill chain going so the master loop's wait3 wakes up.
518 if (DirectPid >= 0) {
519 kill(DirectPid, SIGTERM);
521 if (procctl(P_PID, getpid(), PROC_REAP_STATUS, &info) == 0 &&
522 info.status.pid_head > 0) {
523 kill(info.status.pid_head, SIGTERM);
527 clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
528 LastStop = ts.tv_sec;
529 RunState = RS_STOPPING1;
532 * If commanded (verses automatic), we are running remote in our
533 * own thread and we need to wait for the action to complete.
535 if (cmd->commanded) {
536 while (RunState == RS_STOPPING1 ||
537 RunState == RS_STOPPING2 ||
538 RunState == RS_STOPPING3) {
540 "svc %s: Waiting for service to stop\n",
543 pthread_mutex_unlock(&serial_mtx);
545 pthread_mutex_lock(&serial_mtx);
546 if (--maxwait == 0) {
548 "svc %s: Giving up waiting for stop\n",
554 if (cmd->restart_some || cmd->restart_all) {
555 InitCmd->restart_some = save_restart_some;
556 InitCmd->restart_all = save_restart_all;
564 execute_exit(command_t *cmd)
566 if (cmd->commanded) {
567 InitCmd->restart_some = 0;
568 InitCmd->restart_all = 1; /* kill all children */
569 InitCmd->exit_mode = 1; /* exit after stop */
573 fprintf(cmd->fp, "svc %s: Stopping and Exiting\n", cmd->label);
580 execute_list(command_t *cmd)
582 fprintf(cmd->fp, "%-16s\n", cmd->label);
588 execute_status(command_t *cmd)
594 if (InitCmd && InitCmd->exit_mode)
595 state = "stopped (exiting)";
596 else if (InitCmd && InitCmd->manual_stop)
597 state = "stopped (manual)";
614 fprintf(cmd->fp, "%-16s %s\n", cmd->label, state);
620 execute_log(command_t *cmd)
622 int lbsize = (int)sizeof(cmd->logbuf);
623 int lbmask = lbsize - 1;
633 * mode 0 - Dump everything then exit
634 * mode 1 - Dump everything then block/loop
635 * mode 2 - Skeep to end then block/loop
637 if (cmd->tail_mode == 2)
638 windex = InitCmd->logwindex;
640 windex = InitCmd->logwindex - InitCmd->logcount;
642 dotstate = 0; /* 0=start-of-line 1=middle-of-line 2=dot */
646 * Calculate the amount of data we missed and determine
647 * if some data was lost.
649 n = InitCmd->logwindex - windex;
650 if (n < 0 || n > InitCmd->logcount) {
651 windex = InitCmd->logwindex - InitCmd->logcount;
652 pthread_mutex_unlock(&serial_mtx);
653 fprintf(cmd->fp, "\n(LOG DATA LOST)\n");
654 pthread_mutex_lock(&serial_mtx);
659 * Circular buffer and copy size limitations. If no
660 * data ready, wait for some.
662 if (n > lbsize - (windex & lbmask))
663 n = lbsize - (windex & lbmask);
667 if (cmd->tail_mode == 0)
669 pthread_cond_wait(&InitCmd->logcond, &serial_mtx);
672 bcopy(InitCmd->logbuf + (windex & lbmask), buf, n);
675 * Dump log output, escape any '.' on a line by itself.
677 pthread_mutex_unlock(&serial_mtx);
678 n = escapewrite(cmd->fp, buf, n, &dotstate);
681 lastnl = (buf[n-1] == '\n');
682 pthread_mutex_lock(&serial_mtx);
689 pthread_mutex_unlock(&serial_mtx);
690 fprintf(cmd->fp, "\n");
691 pthread_mutex_lock(&serial_mtx);
697 * Change or reopen logfile.
700 execute_logfile(command_t *cmd)
708 logfile = cmd->logfile;
709 if (cmd->ext_av && cmd->ext_av[0])
710 logfile = cmd->ext_av[0];
712 logfile = InitCmd->logfile;
716 if (InitCmd->logfile &&
717 strcmp(InitCmd->logfile, logfile) == 0) {
718 fprintf(cmd->fp, "svc %s: Reopen logfile %s\n",
719 cmd->label, logfile);
721 fprintf(cmd->fp, "svc %s: Change logfile to %s\n",
722 cmd->label, logfile);
724 if (InitCmd->logfd >= 0) {
725 close(InitCmd->logfd);
728 if (strcmp(logfile, "/dev/null") == 0) {
729 sreplace(&InitCmd->logfile, logfile);
731 fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0640);
734 sreplace(&InitCmd->logfile, logfile);
737 "svc %s: Unable to open/create "
740 logfile, strerror(errno));
749 execute_help(command_t *cmd)
752 "svc [options] directive [label [additional_args]]\n"
754 "Directives: init start stop stopall restart exit\n"
755 " kill list status log logf tailf logfile\n"
763 logger_thread(void *arg)
765 command_t *cmd = arg;
766 int lbsize = (int)sizeof(cmd->logbuf);
767 int lbmask = lbsize - 1;
771 pthread_detach(pthread_self());
772 pthread_mutex_lock(&serial_mtx);
775 * slip circular buffer to make room for new data.
777 n = cmd->logcount - (lbsize - LOGCHUNK);
782 windex = cmd->logwindex & lbmask;
786 pthread_mutex_unlock(&serial_mtx);
787 n = read(cmd->logfds[0], cmd->logbuf + windex, n);
788 pthread_mutex_lock(&serial_mtx);
791 write(cmd->logfd, cmd->logbuf + windex, n);
794 pthread_cond_signal(&cmd->logcond);
796 if (n == 0 || (n < 0 && errno != EINTR))
799 pthread_mutex_unlock(&serial_mtx);
804 * Put us in the STOPPED state if we are not already there, and
805 * handle post-stop options (aka sync).
809 setstate_stopped(command_t *cmd, struct timespec *ts)
811 if (RunState != RS_STOPPED) {
812 RunState = RS_STOPPED;
813 LastStop = ts->tv_sec;
814 if (cmd->sync_mode) /* support -s option */
821 setup_gid(command_t *cmd)
826 setgid(cmd->grent.gr_gid) < 0) {
827 fprintf(cmd->fp, "unable to setgid to \"%s\": %s\n",
828 cmd->grent.gr_name, strerror(errno));
833 * -G overrides all group ids.
836 if (setgroups(cmd->ngroups, cmd->groups) < 0) {
837 fprintf(cmd->fp, "unable to setgroups to (");
838 for (i = 0; i < cmd->ngroups; ++i) {
840 fprintf(cmd->fp, ", ");
841 fprintf(cmd->fp, "%d", cmd->groups[i]);
843 fprintf(cmd->fp, "): %s\n", strerror(errno));
852 setup_uid(command_t *cmd)
854 fprintf(stderr, "UIDMODE %d %d\n", cmd->uid_mode, cmd->pwent.pw_uid);
856 cmd->gid_mode == 0 &&
858 setgid(cmd->pwent.pw_gid) < 0) {
859 fprintf(cmd->fp, "unable to setgid for user \"%s\": %s\n",
865 setuid(cmd->pwent.pw_uid) < 0) {
866 fprintf(cmd->fp, "unable to setuid for user \"%s\": %s\n",
876 setup_jail(command_t *cmd)
881 if (gethostname(hostbuf, sizeof(hostbuf) - 1) < 0) {
882 fprintf(cmd->fp, "gethostname() failed: %s\n", strerror(errno));
885 /* make sure it is zero terminated */
886 hostbuf[sizeof(hostbuf) -1] = 0;
888 bzero(&info, sizeof(info));
890 info.path = cmd->jaildir;
891 info.hostname = hostbuf;
892 /* info.n_ips, sockaddr_storage ips[] */
894 if (jail(&info) < 0) {
895 fprintf(cmd->fp, "unable to create jail \"%s\": %s\n",
905 setup_chroot(command_t *cmd)
907 if (chroot(cmd->rootdir) < 0) {
908 fprintf(cmd->fp, "unable to chroot to \"%s\": %s\n",
918 setup_devfs(command_t *cmd, const char *dir, int domount)
920 struct devfs_mount_info info;
925 bzero(&info, sizeof(info));
927 asprintf(&path, "%s/dev", dir);
930 if (statfs(path, &fs) == 0 &&
931 strcmp(fs.f_fstypename, "devfs") == 0) {
932 fprintf(cmd->fp, "devfs already mounted\n");
934 if (mount("devfs", path, MNT_NOEXEC|MNT_NOSUID, &info) < 0) {
935 fprintf(cmd->fp, "cannot mount devfs on %s: %s\n",
936 path, strerror(errno));
940 if (statfs(path, &fs) < 0 ||
941 strcmp(fs.f_fstypename, "devfs") != 0) {
942 fprintf(cmd->fp, "devfs already unmounted\n");
944 if (unmount(path, 0) < 0) {
945 fprintf(cmd->fp, "cannot unmount devfs from %s: %s\n",
946 path, strerror(errno));
955 * Escape writes. A '.' on a line by itself must be escaped to '..'.
959 escapewrite(FILE *fp, char *buf, int n, int *statep)
970 for (i = b; i < n; ++i) {
992 * dot was output at beginning of line
1002 if (*statep == 3) /* flush with escape */
1006 n = fwrite(buf, 1, i - b, fp);
1012 if (*statep == 3) { /* added escape */
1013 n = fwrite(".", 1, 1, fp);
1014 /* escapes not counted in r */