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;
49 execute_init(command_t *cmd)
53 pid_t stoppingpid = -1;
55 int lfd; /* unix domain listen socket */
56 int pfd; /* pid file */
62 if (cmd->label == NULL || cmd->ext_ac == 0) {
63 fprintf(cmd->fp, "init requires a label and command\n");
66 fprintf(cmd->fp, "initializing new service: %s\n", cmd->label);
69 fprintf(cmd->fp, "Unable to create pipe: %s\n",
73 if ((xfd = open("/dev/null", O_RDWR)) < 0) {
74 fprintf(cmd->fp, "Unable to open /dev/null: %s\n",
80 * Setup pidfile and unix domain listen socket and lock the
83 rc = setup_pid_and_socket(cmd, &lfd, &pfd);
93 if ((pid = fork()) != 0) {
99 fprintf(cmd->fp, "fork failed: %s\n", strerror(errno));
102 * Fill-in pfd before returning.
104 snprintf(buf, sizeof(buf), "%d\n", (int)pid);
105 write(pfd, buf, strlen(buf));
111 * Wait for child to completely detach from the tty
121 * Service demon is now running.
123 * Detach from terminal, setup logfile.
128 cmd->fp = fopen(cmd->logfile, "a");
130 cmd->fp = fdopen(dup(xfd), "a");
135 dup2(fileno(cmd->fp), 1);
136 dup2(fileno(cmd->fp), 2);
137 if ((xfd = open("/dev/tty", O_RDWR)) >= 0) {
138 ioctl(xfd, TIOCNOTTY, 0); /* no controlling tty */
141 setsid(); /* new session */
144 * Signal parent that we are completely detached now.
147 write(fds[1], &c, 1);
152 * Start accept thread for unix domain listen socket.
154 remote_listener(cmd, lfd);
155 pthread_mutex_lock(&serial_mtx);
158 * Become the reaper for all children recursively.
160 if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) < 0) {
161 fprintf(cmd->fp, "svc is unable to become the "
162 "reaper for its children\n");
167 * Initial service start
172 * Main loop is the reaper
175 union reaper_info info;
182 * If we are running just block doing normal reaping,
183 * if we are stopping we have to poll for reaping while
184 * we handle stopping.
187 if (RunState == RS_STARTED) {
188 pthread_mutex_unlock(&serial_mtx);
189 pid = wait3(&status, 0, NULL);
190 pthread_mutex_lock(&serial_mtx);
192 pid = wait3(&status, WNOHANG, NULL);
194 clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
197 if (pid == DirectPid) {
199 "svc %s: lost direct child %d\n",
203 if (cmd->restart_some) {
204 RunState = RS_STOPPED;
205 LastStop = ts.tv_sec;
206 } /* else still considered normal run state */
207 } else if (cmd->debug) {
209 * Reap random disconnected child, but don't
210 * spew to the log unless debugging is
214 "svc %s: reap indirect child %d\n",
221 * Calculate the pid to potentially act on and/or
222 * determine if any children still exist.
224 if (DirectPid >= 0) {
226 } else if (procctl(P_PID, getpid(),
227 PROC_REAP_STATUS, &info) == 0) {
228 usepid = info.status.pid_head;
233 fprintf(stderr, "svc %s: usepid %d\n",
239 * If stoppingpid changes we have to reset the TERM->KILL
243 if (RunState != RS_STOPPED) {
244 RunState = RS_STOPPED;
245 LastStop = ts.tv_sec;
247 } else if (stoppingpid != usepid &&
248 (RunState == RS_STOPPING2 ||
249 RunState == RS_STOPPING3)) {
250 RunState = RS_STOPPING1;
252 stoppingpid = usepid;
260 RunState = RS_STOPPED;
261 LastStop = ts.tv_sec;
265 dt = (int)(ts.tv_sec - LastStop);
267 if (cmd->exit_mode) {
269 * Service demon was told to exit on service
270 * stop (-x passed to init).
273 "svc %s: service demon exiting\n",
275 remove_pid_and_socket(cmd, cmd->label);
277 } else if (cmd->manual_stop) {
279 * Service demon was told to stop via
280 * commanded (not automatic) action. We
281 * do not auto-restart the service in
284 pthread_mutex_unlock(&serial_mtx);
285 if (dt < 0 || dt > 60)
289 pthread_mutex_lock(&serial_mtx);
290 } else if (cmd->restart_some || cmd->restart_all) {
292 * Handle automatic restarts
294 if (dt > cmd->restart_timo) {
297 pthread_mutex_unlock(&serial_mtx);
299 pthread_mutex_lock(&serial_mtx);
303 * No automatic restart was configured,
304 * wait for commanded action.
306 pthread_mutex_unlock(&serial_mtx);
307 if (dt < 0 || dt > 60)
311 pthread_mutex_lock(&serial_mtx);
316 * Reset TERM->KILL timer
318 nextstop = ts.tv_sec;
319 RunState = RS_STOPPING2;
322 if (cmd->termkill_timo == 0) {
323 nextstop = ts.tv_sec - 1;
325 kill(stoppingpid, SIGTERM);
326 fprintf(cmd->fp, "svc %s: sigterm %d\n",
327 cmd->label, stoppingpid);
330 RunState = RS_STOPPING3;
333 dt = (int)(ts.tv_sec - nextstop);
334 if (dt > cmd->termkill_timo) {
335 fprintf(cmd->fp, "svc %s: sigkill %d\n",
336 cmd->label, stoppingpid);
337 kill(stoppingpid, SIGKILL);
343 pthread_mutex_unlock(&serial_mtx);
345 /* does not return */
349 execute_start(command_t *cmd)
354 while (RunState == RS_STOPPING1 ||
355 RunState == RS_STOPPING2 ||
356 RunState == RS_STOPPING3) {
358 "svc %s: Waiting for previous action to complete\n",
361 pthread_mutex_unlock(&serial_mtx);
363 pthread_mutex_lock(&serial_mtx);
364 if (--maxwait == 0) {
366 "svc %s: Giving up waiting for action\n",
372 if (RunState == RS_STARTED) {
373 fprintf(cmd->fp, "svc %s: Already started pid %d\n",
374 cmd->label, DirectPid);
379 clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
380 if ((DirectPid = fork()) == 0) {
382 execvp(InitCmd->ext_av[0], InitCmd->ext_av);
385 if (DirectPid >= 0) {
386 RunState = RS_STARTED;
387 LastStart = ts.tv_sec;
389 RunState = RS_STOPPED;
390 LastStop = ts.tv_sec;
392 InitCmd->manual_stop = 0;
393 fprintf(cmd->fp, "svc %s: Starting pid %d\n", cmd->label, DirectPid);
400 execute_restart(command_t *cmd)
404 rc = execute_stop(cmd) + execute_start(cmd);
409 execute_stop(command_t *cmd)
411 union reaper_info info;
413 int save_restart_some;
414 int save_restart_all;
417 save_restart_some = InitCmd->restart_some;
418 save_restart_all = InitCmd->restart_all;
420 InitCmd->manual_stop = 1;
421 if (cmd->commanded && (cmd->restart_some || cmd->restart_all)) {
422 InitCmd->restart_some = cmd->restart_some;
423 InitCmd->restart_all = cmd->restart_all;
425 fprintf(cmd->fp, "svc %s: Stopping\n", cmd->label);
429 * Start the kill chain going so the master loop's wait3 wakes up.
431 if (DirectPid >= 0) {
432 kill(DirectPid, SIGTERM);
434 if (procctl(P_PID, getpid(), PROC_REAP_STATUS, &info) == 0 &&
435 info.status.pid_head > 0) {
436 kill(info.status.pid_head, SIGTERM);
440 clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
441 LastStop = ts.tv_sec;
442 RunState = RS_STOPPING1;
445 * If commanded (verses automatic), we are running remote in our
446 * own thread and we need to wait for the action to complete.
448 if (cmd->commanded) {
449 while (RunState == RS_STOPPING1 ||
450 RunState == RS_STOPPING2 ||
451 RunState == RS_STOPPING3) {
453 "svc %s: Waiting for service to stop\n",
456 pthread_mutex_unlock(&serial_mtx);
458 pthread_mutex_lock(&serial_mtx);
459 if (--maxwait == 0) {
461 "svc %s: Giving up waiting for stop\n",
467 if (cmd->restart_some || cmd->restart_all) {
468 InitCmd->restart_some = save_restart_some;
469 InitCmd->restart_all = save_restart_all;
477 execute_exit(command_t *cmd)
479 if (cmd->commanded) {
480 InitCmd->restart_some = 0;
481 InitCmd->restart_all = 1; /* kill all children */
482 InitCmd->exit_mode = 1; /* exit after stop */
484 fprintf(cmd->fp, "svc %s: Stopping and Exiting\n", cmd->label);
491 execute_list(command_t *cmd)
493 fprintf(cmd->fp, "%-16s\n", cmd->label);
499 execute_status(command_t *cmd)
505 if (InitCmd && InitCmd->exit_mode)
506 state = "stopped (exiting)";
507 else if (InitCmd && InitCmd->manual_stop)
508 state = "stopped (manual)";
525 fprintf(cmd->fp, "%-16s %s\n", cmd->label, state);
531 execute_log(command_t *cmd __unused)
537 execute_logfile(command_t *cmd)
543 logfile = cmd->logfile;
544 if (cmd->ext_av && cmd->ext_av[0])
545 logfile = cmd->ext_av[0];
546 if (logfile == NULL && InitCmd)
547 logfile = InitCmd->logfile;
550 if (InitCmd && logfile) {
551 if (InitCmd->logfile &&
552 strcmp(InitCmd->logfile, logfile) == 0) {
553 fprintf(cmd->fp, "svc %s: Reopen logfile %s\n",
554 cmd->label, logfile);
556 fprintf(cmd->fp, "svc %s: Change logfile to %s\n",
557 cmd->label, logfile);
559 fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0640);
563 dup2(fd, fileno(stdout));
564 dup2(fd, fileno(stderr));
565 sreplace(&InitCmd->logfile, logfile);
569 "svc %s: Unable to open/create \"%s\": %s\n",
570 cmd->label, logfile, strerror(errno));
578 execute_help(command_t *cmd)
581 "svc [options] directive [label [additional_args]]\n"
583 "Directives: init start stop stopall restart exit\n"
584 " kill list status log logf tailf logfile\n"