svc - bug fixes
[dragonfly.git] / sbin / svc / execute.c
1 /*
2  * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
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
16  *    distribution.
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.
20  *
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
32  * SUCH DAMAGE.
33  */
34 /*
35  * Handle remote listen/connect and parsing operations.
36  */
37
38 #include "svc.h"
39
40 pthread_mutex_t serial_mtx;
41 time_t LastStart;       /* uptime */
42 time_t LastStop;        /* uptime */
43 pid_t DirectPid = -1;
44 runstate_t RunState = RS_STOPPED;
45 command_t *InitCmd;
46 int RestartCounter;
47
48 int
49 execute_init(command_t *cmd)
50 {
51         char buf[32];
52         pid_t pid;
53         pid_t stoppingpid = -1;
54         time_t nextstop = 0;
55         int lfd;        /* unix domain listen socket */
56         int pfd;        /* pid file */
57         int xfd;
58         int rc;
59         int fds[2];
60         char c;
61
62         if (cmd->label == NULL || cmd->ext_ac == 0) {
63                 fprintf(cmd->fp, "init requires a label and command\n");
64                 return 1;
65         }
66         fprintf(cmd->fp, "initializing new service: %s\n", cmd->label);
67
68         if (pipe(fds) < 0) {
69                 fprintf(cmd->fp, "Unable to create pipe: %s\n",
70                         strerror(errno));
71                 return 1;
72         }
73         if ((xfd = open("/dev/null", O_RDWR)) < 0) {
74                 fprintf(cmd->fp, "Unable to open /dev/null: %s\n",
75                         strerror(errno));
76                 return 1;
77         }
78
79         /*
80          * Setup pidfile and unix domain listen socket and lock the
81          * pidfile.
82          */
83         rc = setup_pid_and_socket(cmd, &lfd, &pfd);
84         if (rc) {
85                 close(fds[0]);
86                 close(fds[1]);
87                 return rc;
88         }
89
90         /*
91          * Detach the service
92          */
93         if ((pid = fork()) != 0) {
94                 /*
95                  * Parent
96                  */
97                 close(fds[1]);
98                 if (pid < 0) {
99                         fprintf(cmd->fp, "fork failed: %s\n", strerror(errno));
100                 } else {
101                         /*
102                          * Fill-in pfd before returning.
103                          */
104                         snprintf(buf, sizeof(buf), "%d\n", (int)pid);
105                         write(pfd, buf, strlen(buf));
106                 }
107                 close(lfd);
108                 close(pfd);
109
110                 /*
111                  * Wait for child to completely detach from the tty
112                  * before returning.
113                  */
114                 read(fds[0], &c, 1);
115                 close(fds[0]);
116
117                 return 0;
118         }
119
120         /*
121          * Service demon is now running.
122          *
123          * Detach from terminal, setup logfile.
124          */
125         close(fds[0]);
126         fclose(cmd->fp);
127         if (cmd->logfile)
128                 cmd->fp = fopen(cmd->logfile, "a");
129         else
130                 cmd->fp = fdopen(dup(xfd), "a");
131         if (xfd != 0) {
132                 dup2(xfd, 0);
133                 close(xfd);
134         }
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 */
139                 close(xfd);
140         }
141         setsid();                               /* new session */
142
143         /*
144          * Signal parent that we are completely detached now.
145          */
146         c = 1;
147         write(fds[1], &c, 1);
148         close(fds[1]);
149         InitCmd = cmd;
150
151         /*
152          * Start accept thread for unix domain listen socket.
153          */
154         remote_listener(cmd, lfd);
155         pthread_mutex_lock(&serial_mtx);
156
157         /*
158          * Become the reaper for all children recursively.
159          */
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");
163                 fflush(cmd->fp);
164         }
165
166         /*
167          * Initial service start
168          */
169         execute_start(cmd);
170
171         /*
172          * Main loop is the reaper
173          */
174         for (;;) {
175                 union reaper_info info;
176                 struct timespec ts;
177                 int status;
178                 int dt;
179                 pid_t usepid;
180
181                 /*
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.
185                  */
186                 fflush(cmd->fp);
187                 if (RunState == RS_STARTED) {
188                         pthread_mutex_unlock(&serial_mtx);
189                         pid = wait3(&status, 0, NULL);
190                         pthread_mutex_lock(&serial_mtx);
191                 } else {
192                         pid = wait3(&status, WNOHANG, NULL);
193                 }
194                 clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
195
196                 if (pid > 0) {
197                         if (pid == DirectPid) {
198                                 fprintf(cmd->fp,
199                                         "svc %s: lost direct child %d\n",
200                                         cmd->label, pid);
201                                 fflush(cmd->fp);
202                                 DirectPid = -1;
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) {
208                                 /*
209                                  * Reap random disconnected child, but don't
210                                  * spew to the log unless debugging is
211                                  * enabled.
212                                  */
213                                 fprintf(cmd->fp,
214                                         "svc %s: reap indirect child %d\n",
215                                         cmd->label,
216                                         (int)pid);
217                         }
218                 }
219
220                 /*
221                  * Calculate the pid to potentially act on and/or
222                  * determine if any children still exist.
223                  */
224                 if (DirectPid >= 0) {
225                         usepid = DirectPid;
226                 } else if (procctl(P_PID, getpid(),
227                                    PROC_REAP_STATUS, &info) == 0) {
228                         usepid = info.status.pid_head;
229                 } else {
230                         usepid = -1;
231                 }
232                 if (cmd->debug) {
233                         fprintf(stderr, "svc %s: usepid %d\n",
234                                 cmd->label, usepid);
235                         fflush(stderr);
236                 }
237
238                 /*
239                  * If stoppingpid changes we have to reset the TERM->KILL
240                  * timer.
241                  */
242                 if (usepid < 0) {
243                         if (RunState != RS_STOPPED) {
244                                 RunState = RS_STOPPED;
245                                 LastStop = ts.tv_sec;
246                         }
247                 } else if (stoppingpid != usepid &&
248                            (RunState == RS_STOPPING2 ||
249                             RunState == RS_STOPPING3)) {
250                         RunState = RS_STOPPING1;
251                 }
252                 stoppingpid = usepid;
253
254                 /*
255                  * State machine
256                  */
257                 switch(RunState) {
258                 case RS_STARTED:
259                         if (usepid < 0) {
260                                 RunState = RS_STOPPED;
261                                 LastStop = ts.tv_sec;
262                         }
263                         break;
264                 case RS_STOPPED:
265                         dt = (int)(ts.tv_sec - LastStop);
266
267                         if (cmd->exit_mode) {
268                                 /*
269                                  * Service demon was told to exit on service
270                                  * stop (-x passed to init).
271                                  */
272                                 fprintf(cmd->fp,
273                                         "svc %s: service demon exiting\n",
274                                         cmd->label);
275                                 remove_pid_and_socket(cmd, cmd->label);
276                                 exit(0);
277                         } else if (cmd->manual_stop) {
278                                 /*
279                                  * Service demon was told to stop via
280                                  * commanded (not automatic) action.  We
281                                  * do not auto-restart the service in
282                                  * this situation.
283                                  */
284                                 pthread_mutex_unlock(&serial_mtx);
285                                 if (dt < 0 || dt > 60)
286                                         sleep(60);
287                                 else
288                                         sleep(1);
289                                 pthread_mutex_lock(&serial_mtx);
290                         } else if (cmd->restart_some || cmd->restart_all) {
291                                 /*
292                                  * Handle automatic restarts
293                                  */
294                                 if (dt > cmd->restart_timo) {
295                                         execute_start(cmd);
296                                 } else {
297                                         pthread_mutex_unlock(&serial_mtx);
298                                         sleep(1);
299                                         pthread_mutex_lock(&serial_mtx);
300                                 }
301                         } else {
302                                 /*
303                                  * No automatic restart was configured,
304                                  * wait for commanded action.
305                                  */
306                                 pthread_mutex_unlock(&serial_mtx);
307                                 if (dt < 0 || dt > 60)
308                                         sleep(60);
309                                 else
310                                         sleep(1);
311                                 pthread_mutex_lock(&serial_mtx);
312                         }
313                         break;
314                 case RS_STOPPING1:
315                         /*
316                          * Reset TERM->KILL timer
317                          */
318                         nextstop = ts.tv_sec;
319                         RunState = RS_STOPPING2;
320                         /* fall through */
321                 case RS_STOPPING2:
322                         if (cmd->termkill_timo == 0) {
323                                 nextstop = ts.tv_sec - 1;
324                         } else {
325                                 kill(stoppingpid, SIGTERM);
326                                 fprintf(cmd->fp, "svc %s: sigterm %d\n",
327                                         cmd->label, stoppingpid);
328                                 sleep(1);
329                         }
330                         RunState = RS_STOPPING3;
331                         /* fall through */
332                 case 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);
338                         }
339                         sleep(1);
340                         break;
341                 }
342         }
343         pthread_mutex_unlock(&serial_mtx);
344         exit(0);
345         /* does not return */
346 }
347
348 int
349 execute_start(command_t *cmd)
350 {
351         struct timespec ts;
352         int maxwait = 60;
353
354         while (RunState == RS_STOPPING1 ||
355                RunState == RS_STOPPING2 ||
356                RunState == RS_STOPPING3) {
357                 fprintf(cmd->fp,
358                         "svc %s: Waiting for previous action to complete\n",
359                         cmd->label);
360                 fflush(cmd->fp);
361                 pthread_mutex_unlock(&serial_mtx);
362                 sleep(1);
363                 pthread_mutex_lock(&serial_mtx);
364                 if (--maxwait == 0) {
365                         fprintf(cmd->fp,
366                                 "svc %s: Giving up waiting for action\n",
367                                 cmd->label);
368                         fflush(cmd->fp);
369                         break;
370                 }
371         }
372         if (RunState == RS_STARTED) {
373                 fprintf(cmd->fp, "svc %s: Already started pid %d\n",
374                         cmd->label, DirectPid);
375                 fflush(cmd->fp);
376                 return 0;
377         }
378
379         clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
380         if ((DirectPid = fork()) == 0) {
381                 closefrom(3);
382                 execvp(InitCmd->ext_av[0], InitCmd->ext_av);
383                 exit(99);
384         }
385         if (DirectPid >= 0) {
386                 RunState = RS_STARTED;
387                 LastStart = ts.tv_sec;
388         } else {
389                 RunState = RS_STOPPED;
390                 LastStop = ts.tv_sec;
391         }
392         InitCmd->manual_stop = 0;
393         fprintf(cmd->fp, "svc %s: Starting pid %d\n", cmd->label, DirectPid);
394         fflush(cmd->fp);
395
396         return 0;
397 }
398
399 int
400 execute_restart(command_t *cmd)
401 {
402         int rc;
403
404         rc = execute_stop(cmd) + execute_start(cmd);
405         return rc;
406 }
407
408 int
409 execute_stop(command_t *cmd)
410 {
411         union reaper_info info;
412         struct timespec ts;
413         int save_restart_some;
414         int save_restart_all;
415         int maxwait = 60;
416
417         save_restart_some = InitCmd->restart_some;
418         save_restart_all = InitCmd->restart_all;
419         if (cmd->commanded)
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;
424         }
425         fprintf(cmd->fp, "svc %s: Stopping\n", cmd->label);
426         fflush(cmd->fp);
427
428         /*
429          * Start the kill chain going so the master loop's wait3 wakes up.
430          */
431         if (DirectPid >= 0) {
432                 kill(DirectPid, SIGTERM);
433         } else {
434                 if (procctl(P_PID, getpid(), PROC_REAP_STATUS, &info) == 0 &&
435                     info.status.pid_head > 0) {
436                         kill(info.status.pid_head, SIGTERM);
437                 }
438         }
439
440         clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
441         LastStop = ts.tv_sec;
442         RunState = RS_STOPPING1;
443
444         /*
445          * If commanded (verses automatic), we are running remote in our
446          * own thread and we need to wait for the action to complete.
447          */
448         if (cmd->commanded) {
449                 while (RunState == RS_STOPPING1 ||
450                        RunState == RS_STOPPING2 ||
451                        RunState == RS_STOPPING3) {
452                         fprintf(cmd->fp,
453                                 "svc %s: Waiting for service to stop\n",
454                                 cmd->label);
455                         fflush(cmd->fp);
456                         pthread_mutex_unlock(&serial_mtx);
457                         sleep(1);
458                         pthread_mutex_lock(&serial_mtx);
459                         if (--maxwait == 0) {
460                                 fprintf(cmd->fp,
461                                         "svc %s: Giving up waiting for stop\n",
462                                         cmd->label);
463                                 fflush(cmd->fp);
464                                 break;
465                         }
466                 }
467                 if (cmd->restart_some || cmd->restart_all) {
468                         InitCmd->restart_some = save_restart_some;
469                         InitCmd->restart_all = save_restart_all;
470                 }
471         }
472
473         return 0;
474 }
475
476 int
477 execute_exit(command_t *cmd)
478 {
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 */
483         }
484         fprintf(cmd->fp, "svc %s: Stopping and Exiting\n", cmd->label);
485         execute_stop(cmd);
486
487         exit(0);
488 }
489
490 int
491 execute_list(command_t *cmd)
492 {
493         fprintf(cmd->fp, "%-16s\n", cmd->label);
494
495         return 0;
496 }
497
498 int
499 execute_status(command_t *cmd)
500 {
501         const char *state;
502
503         switch(RunState) {
504         case RS_STOPPED:
505                 if (InitCmd && InitCmd->exit_mode)
506                         state = "stopped (exiting)";
507                 else if (InitCmd && InitCmd->manual_stop)
508                         state = "stopped (manual)";
509                 else
510                         state = "stopped";
511                 break;
512         case RS_STARTED:
513                 state = "running";
514                 break;
515         case RS_STOPPING1:
516         case RS_STOPPING2:
517         case RS_STOPPING3:
518                 state = "killing";
519                 break;
520         default:
521                 state = "unknown";
522                 break;
523         }
524
525         fprintf(cmd->fp, "%-16s %s\n", cmd->label, state);
526
527         return 0;
528 }
529
530 int
531 execute_log(command_t *cmd __unused)
532 {
533         return 0;
534 }
535
536 int
537 execute_logfile(command_t *cmd)
538 {
539         char *logfile;
540         int fd;
541         int rc;
542
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;
548
549         rc = 0;
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);
555                 } else {
556                         fprintf(cmd->fp, "svc %s: Change logfile to %s\n",
557                                 cmd->label, logfile);
558                 }
559                 fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0640);
560                 if (fd >= 0) {
561                         fflush(stdout);
562                         fflush(stderr);
563                         dup2(fd, fileno(stdout));
564                         dup2(fd, fileno(stderr));
565                         sreplace(&InitCmd->logfile, logfile);
566                         close(fd);
567                 } else {
568                         fprintf(cmd->fp,
569                                 "svc %s: Unable to open/create \"%s\": %s\n",
570                                 cmd->label, logfile, strerror(errno));
571                         rc = 1;
572                 }
573         }
574         return rc;
575 }
576
577 int
578 execute_help(command_t *cmd)
579 {
580         fprintf(cmd->fp,
581                 "svc [options] directive [label [additional_args]]\n"
582                 "\n"
583                 "Directives: init start stop stopall restart exit\n"
584                 "            kill list status log logf tailf logfile\n"
585                 "            help\n"
586         );
587         return 0;
588 }