32f382d3c141ce364d45d110c18c92b85fe68302
[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 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);
56
57 int
58 execute_init(command_t *cmd)
59 {
60         char buf[32];
61         pid_t pid;
62         pid_t stoppingpid = -1;
63         pthread_t logtd;
64         time_t nextstop = 0;
65         int lfd;        /* unix domain listen socket */
66         int pfd;        /* pid file */
67         int xfd;
68         int rc;
69         int fds[2];
70         char c;
71
72         if (cmd->label == NULL || cmd->ext_ac == 0) {
73                 fprintf(cmd->fp, "init requires a label and command\n");
74                 return 1;
75         }
76         fprintf(cmd->fp, "initializing new service: %s\n", cmd->label);
77
78         if ((xfd = open("/dev/null", O_RDWR)) < 0) {
79                 fprintf(cmd->fp, "Unable to open /dev/null: %s\n",
80                         strerror(errno));
81                 return 1;
82         }
83
84         /*
85          * Setup pidfile and unix domain listen socket and lock the
86          * pidfile.
87          */
88         rc = setup_pid_and_socket(cmd, &lfd, &pfd);
89         if (rc)
90                 return rc;
91
92         /*
93          * Detach the service
94          */
95         if (cmd->foreground) {
96                 /*
97                  * Stay in foreground.
98                  */
99                 fds[0] = -1;
100                 fds[1] = -1;
101                 pid = 0;
102         } else {
103                 if (pipe(fds) < 0) {
104                         fprintf(cmd->fp, "Unable to create pipe: %s\n",
105                                 strerror(errno));
106                         close(lfd);
107                         close(pfd);
108                         remove_pid_and_socket(cmd, cmd->label);
109                         return 1;
110                 }
111                 pid = fork();
112         }
113
114         if (pid != 0) {
115                 /*
116                  * Parent
117                  */
118                 close(fds[1]);
119                 if (pid < 0) {
120                         fprintf(cmd->fp, "fork failed: %s\n", strerror(errno));
121                         close(lfd);
122                         close(pfd);
123                         close(fds[0]);
124                         close(fds[1]);
125                         remove_pid_and_socket(cmd, cmd->label);
126                         return 1;
127                 } else {
128                         /*
129                          * Fill-in pfd before returning.
130                          */
131                         snprintf(buf, sizeof(buf), "%d\n", (int)pid);
132                         write(pfd, buf, strlen(buf));
133                 }
134                 close(lfd);
135                 close(pfd);
136
137                 /*
138                  * Wait for child to completely detach from the tty
139                  * before returning.
140                  */
141                 read(fds[0], &c, 1);
142                 close(fds[0]);
143
144                 return 0;
145         }
146
147         /*
148          * Forked child is now the service demon.
149          *
150          * Detach from terminal, scrap tty, set process title.
151          */
152         if (cmd->proctitle) {
153                 setproctitle("%s - %s", cmd->label, cmd->proctitle);
154         } else {
155                 setproctitle("%s", cmd->label);
156         }
157         if (cmd->mountdev) {
158                 if (cmd->jaildir)
159                         setup_devfs(cmd, cmd->jaildir, 1);
160                 else if (cmd->rootdir)
161                         setup_devfs(cmd, cmd->rootdir, 1);
162         }
163
164         if (cmd->foreground == 0) {
165                 close(fds[0]);
166                 fds[0] = -1;
167         }
168
169         if (xfd != 0)                           /* scrap tty inputs */
170                 dup2(xfd, 0);
171         if (cmd->foreground == 0) {
172                 int tfd;
173
174                 if (xfd != 1)                   /* scrap tty outputs */
175                         dup2(xfd, 1);
176                 if (xfd != 2)
177                         dup2(xfd, 2);
178
179                 if ((tfd = open("/dev/tty", O_RDWR)) >= 0) {
180                         ioctl(tfd, TIOCNOTTY, 0);       /* no controlling tty */
181                         close(tfd);
182                 }
183                 setsid();                               /* new session */
184         }
185
186         /*
187          * Setup log file.  The log file must not use descriptors 0, 1, or 2.
188          */
189         if (cmd->logfile && strcmp(cmd->logfile, "/dev/null") == 0)
190                 cmd->logfd = -1;
191         else if (cmd->logfile)
192                 cmd->logfd = open(cmd->logfile, O_WRONLY|O_CREAT|O_APPEND, 0640);
193         else if (cmd->foreground)
194                 cmd->logfd = dup(1);
195         else
196                 cmd->logfd = -1;
197
198         /*
199          * Signal parent that we are completely detached now.
200          */
201         c = 1;
202         if (cmd->foreground == 0) {
203                 write(fds[1], &c, 1);
204                 close(fds[1]);
205                 fds[1] = -1;
206         }
207         InitCmd = cmd;
208
209         /*
210          * Setup log pipe.  The logger thread copies the pipe to a buffer
211          * for the 'log' directive and also writes it to logfd.
212          */
213         pipe(cmd->logfds);
214         if (cmd->fp != stdout)
215                 fclose(cmd->fp);
216         cmd->fp = fdopen(cmd->logfds[1], "w");
217
218         if (xfd > 2) {
219                 close(xfd);
220                 xfd = -1;
221         }
222
223         pthread_cond_init(&cmd->logcond, NULL);
224
225         /*
226          * Start accept thread for unix domain listen socket.
227          */
228         pthread_mutex_lock(&serial_mtx);
229         pthread_create(&logtd, NULL, logger_thread, cmd);
230         remote_listener(cmd, lfd);
231
232         /*
233          * Become the reaper for all children recursively.
234          */
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");
238                 fflush(cmd->fp);
239         }
240
241         /*
242          * Initial service start
243          */
244         execute_start(cmd);
245
246         /*
247          * Main loop is the reaper
248          */
249         for (;;) {
250                 union reaper_info info;
251                 struct timespec ts;
252                 int status;
253                 int dt;
254                 pid_t usepid;
255
256                 /*
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.
260                  */
261                 fflush(cmd->fp);
262                 if (RunState == RS_STARTED) {
263                         pthread_mutex_unlock(&serial_mtx);
264                         pid = wait3(&status, 0, NULL);
265                         pthread_mutex_lock(&serial_mtx);
266                 } else {
267                         pid = wait3(&status, WNOHANG, NULL);
268                 }
269                 clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
270
271                 if (pid > 0) {
272                         if (pid == DirectPid) {
273                                 fprintf(cmd->fp,
274                                         "svc %s: lost direct child %d\n",
275                                         cmd->label, pid);
276                                 fflush(cmd->fp);
277                                 DirectPid = -1;
278                                 if (cmd->restart_some) {
279                                         setstate_stopped(cmd, &ts);
280                                 } /* else still considered normal run state */
281                         } else if (cmd->debug) {
282                                 /*
283                                  * Reap random disconnected child, but don't
284                                  * spew to the log unless debugging is
285                                  * enabled.
286                                  */
287                                 fprintf(cmd->fp,
288                                         "svc %s: reap indirect child %d\n",
289                                         cmd->label,
290                                         (int)pid);
291                         }
292                 }
293
294                 /*
295                  * Calculate the pid to potentially act on and/or
296                  * determine if any children still exist.
297                  */
298                 if (DirectPid >= 0) {
299                         usepid = DirectPid;
300                 } else if (procctl(P_PID, getpid(),
301                                    PROC_REAP_STATUS, &info) == 0) {
302                         usepid = info.status.pid_head;
303                 } else {
304                         usepid = -1;
305                 }
306                 if (cmd->debug) {
307                         fprintf(stderr, "svc %s: usepid %d\n",
308                                 cmd->label, usepid);
309                         fflush(stderr);
310                 }
311
312                 /*
313                  * If stoppingpid changes we have to reset the TERM->KILL
314                  * timer.
315                  */
316                 if (usepid < 0) {
317                         setstate_stopped(cmd, &ts);
318                 } else if (stoppingpid != usepid &&
319                            (RunState == RS_STOPPING2 ||
320                             RunState == RS_STOPPING3)) {
321                         RunState = RS_STOPPING1;
322                 }
323                 stoppingpid = usepid;
324
325                 /*
326                  * State machine
327                  */
328                 switch(RunState) {
329                 case RS_STARTED:
330                         if (usepid < 0)
331                                 setstate_stopped(cmd, &ts);
332                         break;
333                 case RS_STOPPED:
334                         dt = (int)(ts.tv_sec - LastStop);
335
336                         if (cmd->exit_mode) {
337                                 /*
338                                  * Service demon was told to exit on service
339                                  * stop (-x passed to init).
340                                  */
341                                 fprintf(cmd->fp,
342                                         "svc %s: service demon exiting\n",
343                                         cmd->label);
344                                 remove_pid_and_socket(cmd, cmd->label);
345                                 goto exitloop;
346                         } else if (cmd->manual_stop) {
347                                 /*
348                                  * Service demon was told to stop via
349                                  * commanded (not automatic) action.  We
350                                  * do not auto-restart the service in
351                                  * this situation.
352                                  */
353                                 pthread_mutex_unlock(&serial_mtx);
354                                 if (dt < 0 || dt > 60)
355                                         sleep(60);
356                                 else
357                                         sleep(1);
358                                 pthread_mutex_lock(&serial_mtx);
359                         } else if (cmd->restart_some || cmd->restart_all) {
360                                 /*
361                                  * Handle automatic restarts
362                                  */
363                                 if (dt > cmd->restart_timo) {
364                                         execute_start(cmd);
365                                 } else {
366                                         pthread_mutex_unlock(&serial_mtx);
367                                         sleep(1);
368                                         pthread_mutex_lock(&serial_mtx);
369                                 }
370                         } else {
371                                 /*
372                                  * No automatic restart was configured,
373                                  * wait for commanded action.
374                                  */
375                                 pthread_mutex_unlock(&serial_mtx);
376                                 if (dt < 0 || dt > 60)
377                                         sleep(60);
378                                 else
379                                         sleep(1);
380                                 pthread_mutex_lock(&serial_mtx);
381                         }
382                         break;
383                 case RS_STOPPING1:
384                         /*
385                          * Reset TERM->KILL timer
386                          */
387                         nextstop = ts.tv_sec;
388                         RunState = RS_STOPPING2;
389                         /* fall through */
390                 case RS_STOPPING2:
391                         if (cmd->termkill_timo == 0) {
392                                 nextstop = ts.tv_sec - 1;
393                         } else {
394                                 kill(stoppingpid, SIGTERM);
395                                 fprintf(cmd->fp, "svc %s: sigterm %d\n",
396                                         cmd->label, stoppingpid);
397                                 sleep(1);
398                         }
399                         RunState = RS_STOPPING3;
400                         /* fall through */
401                 case 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);
407                         }
408                         sleep(1);
409                         break;
410                 }
411         }
412 exitloop:
413         pthread_mutex_unlock(&serial_mtx);
414         if (cmd->mountdev) {
415                 if (cmd->jaildir)
416                         setup_devfs(cmd, cmd->jaildir, 0);
417                 else if (cmd->rootdir)
418                         setup_devfs(cmd, cmd->rootdir, 0);
419         }
420         exit(0);
421         /* does not return */
422 }
423
424 int
425 execute_start(command_t *cmd)
426 {
427         struct timespec ts;
428         int maxwait = 60;
429
430         while (RunState == RS_STOPPING1 ||
431                RunState == RS_STOPPING2 ||
432                RunState == RS_STOPPING3) {
433                 fprintf(cmd->fp,
434                         "svc %s: Waiting for previous action to complete\n",
435                         cmd->label);
436                 fflush(cmd->fp);
437                 pthread_mutex_unlock(&serial_mtx);
438                 sleep(1);
439                 pthread_mutex_lock(&serial_mtx);
440                 if (--maxwait == 0) {
441                         fprintf(cmd->fp,
442                                 "svc %s: Giving up waiting for action\n",
443                                 cmd->label);
444                         fflush(cmd->fp);
445                         break;
446                 }
447         }
448         if (RunState == RS_STARTED) {
449                 fprintf(cmd->fp, "svc %s: Already started pid %d\n",
450                         cmd->label, DirectPid);
451                 fflush(cmd->fp);
452                 return 0;
453         }
454
455         clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
456         if ((DirectPid = fork()) == 0) {
457                 fflush(InitCmd->fp);
458                                                 /* leave stdin /dev/null */
459                 dup2(fileno(InitCmd->fp), 1);   /* setup stdout */
460                 dup2(fileno(InitCmd->fp), 2);   /* setup stderr */
461                 closefrom(3);
462
463                 if (cmd->jaildir)               /* jail or chroot */
464                         setup_jail(cmd);
465                 else if (cmd->rootdir)
466                         setup_chroot(cmd);
467
468                 setup_gid(cmd);
469                 setup_uid(cmd);
470                 execvp(InitCmd->ext_av[0], InitCmd->ext_av);
471                 exit(99);
472         }
473         if (DirectPid >= 0) {
474                 RunState = RS_STARTED;
475                 LastStart = ts.tv_sec;
476         } else {
477                 setstate_stopped(InitCmd, &ts);
478         }
479         InitCmd->manual_stop = 0;
480         fprintf(cmd->fp, "svc %s: Starting pid %d\n", cmd->label, DirectPid);
481         fflush(cmd->fp);
482
483         return 0;
484 }
485
486 int
487 execute_restart(command_t *cmd)
488 {
489         int rc;
490
491         rc = execute_stop(cmd) + execute_start(cmd);
492         return rc;
493 }
494
495 int
496 execute_stop(command_t *cmd)
497 {
498         union reaper_info info;
499         struct timespec ts;
500         int save_restart_some;
501         int save_restart_all;
502         int maxwait = 60;
503
504         save_restart_some = InitCmd->restart_some;
505         save_restart_all = InitCmd->restart_all;
506         if (cmd->commanded)
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;
511         }
512         fprintf(cmd->fp, "svc %s: Stopping\n", cmd->label);
513         fflush(cmd->fp);
514
515         /*
516          * Start the kill chain going so the master loop's wait3 wakes up.
517          */
518         if (DirectPid >= 0) {
519                 kill(DirectPid, SIGTERM);
520         } else {
521                 if (procctl(P_PID, getpid(), PROC_REAP_STATUS, &info) == 0 &&
522                     info.status.pid_head > 0) {
523                         kill(info.status.pid_head, SIGTERM);
524                 }
525         }
526
527         clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
528         LastStop = ts.tv_sec;
529         RunState = RS_STOPPING1;
530
531         /*
532          * If commanded (verses automatic), we are running remote in our
533          * own thread and we need to wait for the action to complete.
534          */
535         if (cmd->commanded) {
536                 while (RunState == RS_STOPPING1 ||
537                        RunState == RS_STOPPING2 ||
538                        RunState == RS_STOPPING3) {
539                         fprintf(cmd->fp,
540                                 "svc %s: Waiting for service to stop\n",
541                                 cmd->label);
542                         fflush(cmd->fp);
543                         pthread_mutex_unlock(&serial_mtx);
544                         sleep(1);
545                         pthread_mutex_lock(&serial_mtx);
546                         if (--maxwait == 0) {
547                                 fprintf(cmd->fp,
548                                         "svc %s: Giving up waiting for stop\n",
549                                         cmd->label);
550                                 fflush(cmd->fp);
551                                 break;
552                         }
553                 }
554                 if (cmd->restart_some || cmd->restart_all) {
555                         InitCmd->restart_some = save_restart_some;
556                         InitCmd->restart_all = save_restart_all;
557                 }
558         }
559
560         return 0;
561 }
562
563 int
564 execute_exit(command_t *cmd)
565 {
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 */
570         } else {
571                 cmd->exit_mode = 1;
572         }
573         fprintf(cmd->fp, "svc %s: Stopping and Exiting\n", cmd->label);
574         execute_stop(cmd);
575
576         return 0;
577 }
578
579 int
580 execute_list(command_t *cmd)
581 {
582         fprintf(cmd->fp, "%-16s\n", cmd->label);
583
584         return 0;
585 }
586
587 int
588 execute_status(command_t *cmd)
589 {
590         const char *state;
591
592         switch(RunState) {
593         case RS_STOPPED:
594                 if (InitCmd && InitCmd->exit_mode)
595                         state = "stopped (exiting)";
596                 else if (InitCmd && InitCmd->manual_stop)
597                         state = "stopped (manual)";
598                 else
599                         state = "stopped";
600                 break;
601         case RS_STARTED:
602                 state = "running";
603                 break;
604         case RS_STOPPING1:
605         case RS_STOPPING2:
606         case RS_STOPPING3:
607                 state = "killing";
608                 break;
609         default:
610                 state = "unknown";
611                 break;
612         }
613
614         fprintf(cmd->fp, "%-16s %s\n", cmd->label, state);
615
616         return 0;
617 }
618
619 int
620 execute_log(command_t *cmd)
621 {
622         int lbsize = (int)sizeof(cmd->logbuf);
623         int lbmask = lbsize - 1;
624         int windex;
625         int n;
626         int lastnl;
627         int dotstate;
628         char buf[LOGCHUNK];
629
630         assert(InitCmd);
631
632         /*
633          * mode 0 - Dump everything then exit
634          * mode 1 - Dump everything then block/loop
635          * mode 2 - Skeep to end then block/loop
636          */
637         if (cmd->tail_mode == 2)
638                 windex = InitCmd->logwindex;
639         else
640                 windex = InitCmd->logwindex - InitCmd->logcount;
641         lastnl = 1;
642         dotstate = 0;   /* 0=start-of-line 1=middle-of-line 2=dot */
643
644         for (;;) {
645                 /*
646                  * Calculate the amount of data we missed and determine
647                  * if some data was lost.
648                  */
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);
655                         continue;
656                 }
657
658                 /*
659                  * Circular buffer and copy size limitations.  If no
660                  * data ready, wait for some.
661                  */
662                 if (n > lbsize - (windex & lbmask))
663                         n = lbsize - (windex & lbmask);
664                 if (n > LOGCHUNK)
665                         n = LOGCHUNK;
666                 if (n == 0) {
667                         if (cmd->tail_mode == 0)
668                                 break;
669                         pthread_cond_wait(&InitCmd->logcond, &serial_mtx);
670                         continue;
671                 }
672                 bcopy(InitCmd->logbuf + (windex & lbmask), buf, n);
673
674                 /*
675                  * Dump log output, escape any '.' on a line by itself.
676                  */
677                 pthread_mutex_unlock(&serial_mtx);
678                 n = escapewrite(cmd->fp, buf, n, &dotstate);
679                 fflush(cmd->fp);
680                 if (n > 0)
681                         lastnl = (buf[n-1] == '\n');
682                 pthread_mutex_lock(&serial_mtx);
683
684                 if (n < 0)
685                         break;
686                 windex += n;
687         }
688         if (lastnl == 0) {
689                 pthread_mutex_unlock(&serial_mtx);
690                 fprintf(cmd->fp, "\n");
691                 pthread_mutex_lock(&serial_mtx);
692         }
693         return 0;
694 }
695
696 /*
697  * Change or reopen logfile.
698  */
699 int
700 execute_logfile(command_t *cmd)
701 {
702         char *logfile;
703         int fd;
704         int rc;
705
706         assert(InitCmd);
707
708         logfile = cmd->logfile;
709         if (cmd->ext_av && cmd->ext_av[0])
710                 logfile = cmd->ext_av[0];
711         if (logfile == NULL)
712                 logfile = InitCmd->logfile;
713
714         rc = 0;
715         if (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);
720                 } else {
721                         fprintf(cmd->fp, "svc %s: Change logfile to %s\n",
722                                 cmd->label, logfile);
723                 }
724                 if (InitCmd->logfd >= 0) {
725                         close(InitCmd->logfd);
726                         InitCmd->logfd = -1;
727                 }
728                 if (strcmp(logfile, "/dev/null") == 0) {
729                         sreplace(&InitCmd->logfile, logfile);
730                 } else {
731                         fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0640);
732                         if (fd >= 0) {
733                                 InitCmd->logfd = fd;
734                                 sreplace(&InitCmd->logfile, logfile);
735                         } else {
736                                 fprintf(cmd->fp,
737                                         "svc %s: Unable to open/create "
738                                         "\"%s\": %s\n",
739                                         cmd->label,
740                                         logfile, strerror(errno));
741                                 rc = 1;
742                         }
743                 }
744         }
745         return rc;
746 }
747
748 int
749 execute_help(command_t *cmd)
750 {
751         fprintf(cmd->fp,
752                 "svc [options] directive [label [additional_args]]\n"
753                 "\n"
754                 "Directives: init start stop stopall restart exit\n"
755                 "            kill list status log logf tailf logfile\n"
756                 "            help\n"
757         );
758         return 0;
759 }
760
761 static
762 void *
763 logger_thread(void *arg)
764 {
765         command_t *cmd = arg;
766         int lbsize = (int)sizeof(cmd->logbuf);
767         int lbmask = lbsize - 1;
768         int windex;
769         int n;
770
771         pthread_detach(pthread_self());
772         pthread_mutex_lock(&serial_mtx);
773         for (;;) {
774                 /*
775                  * slip circular buffer to make room for new data.
776                  */
777                 n = cmd->logcount - (lbsize - LOGCHUNK);
778                 if (n > 0) {
779                         cmd->logcount -= n;
780                         cmd->logwindex += n;
781                 }
782                 windex = cmd->logwindex & lbmask;
783                 n = lbsize - windex;
784                 if (n > LOGCHUNK)
785                         n = LOGCHUNK;
786                 pthread_mutex_unlock(&serial_mtx);
787                 n = read(cmd->logfds[0], cmd->logbuf + windex, n);
788                 pthread_mutex_lock(&serial_mtx);
789                 if (n > 0) {
790                         if (cmd->logfd >= 0)
791                                 write(cmd->logfd, cmd->logbuf + windex, n);
792                         cmd->logcount += n;
793                         cmd->logwindex += n;
794                         pthread_cond_signal(&cmd->logcond);
795                 }
796                 if (n == 0 || (n < 0 && errno != EINTR))
797                         break;
798         }
799         pthread_mutex_unlock(&serial_mtx);
800         return NULL;
801 }
802
803 /*
804  * Put us in the STOPPED state if we are not already there, and
805  * handle post-stop options (aka sync).
806  */
807 static
808 void
809 setstate_stopped(command_t *cmd, struct timespec *ts)
810 {
811         if (RunState != RS_STOPPED) {
812                 RunState = RS_STOPPED;
813                 LastStop = ts->tv_sec;
814                 if (cmd->sync_mode)     /* support -s option */
815                         sync();
816         }
817 }
818
819 static
820 int
821 setup_gid(command_t *cmd)
822 {
823         int i;
824
825         if (cmd->gid_mode &&
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));
829                 return 1;
830         }
831
832         /*
833          * -G overrides all group ids.
834          */
835         if (cmd->ngroups) {
836                 if (setgroups(cmd->ngroups, cmd->groups) < 0) {
837                         fprintf(cmd->fp, "unable to setgroups to (");
838                         for (i = 0; i < cmd->ngroups; ++i) {
839                                 if (i)
840                                         fprintf(cmd->fp, ", ");
841                                 fprintf(cmd->fp, "%d", cmd->groups[i]);
842                         }
843                         fprintf(cmd->fp, "): %s\n", strerror(errno));
844                         return 1;
845                 }
846         }
847         return 0;
848 }
849
850 static
851 int
852 setup_uid(command_t *cmd)
853 {
854         fprintf(stderr, "UIDMODE %d %d\n", cmd->uid_mode, cmd->pwent.pw_uid);
855         if (cmd->uid_mode &&
856             cmd->gid_mode == 0 &&
857             cmd->ngroups == 0 &&
858             setgid(cmd->pwent.pw_gid) < 0) {
859                 fprintf(cmd->fp, "unable to setgid for user \"%s\": %s\n",
860                         cmd->pwent.pw_name,
861                         strerror(errno));
862                 return 1;
863         }
864         if (cmd->uid_mode &&
865             setuid(cmd->pwent.pw_uid) < 0) {
866                 fprintf(cmd->fp, "unable to setuid for user \"%s\": %s\n",
867                         cmd->pwent.pw_name,
868                         strerror(errno));
869                 return 1;
870         }
871         return 0;
872 }
873
874 static
875 int
876 setup_jail(command_t *cmd)
877 {
878         struct jail info;
879         char hostbuf[256];
880
881         if (gethostname(hostbuf, sizeof(hostbuf) - 1) < 0) {
882                 fprintf(cmd->fp, "gethostname() failed: %s\n", strerror(errno));
883                 return 1;
884         }
885         /* make sure it is zero terminated */
886         hostbuf[sizeof(hostbuf) -1] = 0;
887
888         bzero(&info, sizeof(info));
889         info.version = 1;
890         info.path = cmd->jaildir;
891         info.hostname = hostbuf;
892         /* info.n_ips, sockaddr_storage ips[] */
893
894         if (jail(&info) < 0) {
895                 fprintf(cmd->fp, "unable to create jail \"%s\": %s\n",
896                         cmd->rootdir,
897                         strerror(errno));
898                 return 1;
899         }
900         return 0;
901 }
902
903 static
904 int
905 setup_chroot(command_t *cmd)
906 {
907         if (chroot(cmd->rootdir) < 0) {
908                 fprintf(cmd->fp, "unable to chroot to \"%s\": %s\n",
909                         cmd->rootdir,
910                         strerror(errno));
911                 return 1;
912         }
913         return 0;
914 }
915
916 static
917 int
918 setup_devfs(command_t *cmd, const char *dir, int domount)
919 {
920         struct devfs_mount_info info;
921         struct statfs fs;
922         int rc = 0;
923         char *path;
924
925         bzero(&info, sizeof(info));
926         info.flags = 0;
927         asprintf(&path, "%s/dev", dir);
928
929         if (domount) {
930                 if (statfs(path, &fs) == 0 &&
931                     strcmp(fs.f_fstypename, "devfs") == 0) {
932                         fprintf(cmd->fp, "devfs already mounted\n");
933                 } else
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));
937                         rc = 1;
938                 }
939         } else {
940                 if (statfs(path, &fs) < 0 ||
941                     strcmp(fs.f_fstypename, "devfs") != 0) {
942                         fprintf(cmd->fp, "devfs already unmounted\n");
943                 } else
944                 if (unmount(path, 0) < 0) {
945                         fprintf(cmd->fp, "cannot unmount devfs from %s: %s\n",
946                                 path, strerror(errno));
947                         rc = 1;
948                 }
949         }
950         free(path);
951         return rc;
952 }
953
954 /*
955  * Escape writes.  A '.' on a line by itself must be escaped to '..'.
956  */
957 static
958 int
959 escapewrite(FILE *fp, char *buf, int n, int *statep)
960 {
961         int b;
962         int i;
963         int r;
964         char c;
965
966         b = 0;
967         r = 0;
968         while (i < n) {
969                 for (i = b; i < n; ++i) {
970                         c = buf[i];
971
972                         switch(*statep) {
973                         case 0:
974                                 /*
975                                  * beginning of line
976                                  */
977                                 if (c == '.')
978                                         *statep = 2;
979                                 else if (c != '\n')
980                                         *statep = 1;
981                                 break;
982                         case 1:
983                                 /*
984                                  * middle of line
985                                  */
986                                 if (c == '\n')
987                                         *statep = 0;
988                                 break;
989                         case 2:
990                                 /*
991                                  * dot was output at beginning of line
992                                  */
993                                 if (c == '\n')
994                                         *statep = 3;
995                                 else
996                                         *statep = 1;
997                                 break;
998                         default:
999                                 break;
1000                         }
1001                         if (*statep == 3)       /* flush with escape */
1002                                 break;
1003                 }
1004                 if (i != b) {
1005                         n = fwrite(buf, 1, i - b, fp);
1006                         if (n > 0)
1007                                 r += n;
1008                         if (n < 0)
1009                                 r = -1;
1010                 }
1011                 if (*statep == 3) {             /* added escape */
1012                         n = fwrite(".", 1, 1, fp);
1013                         /* escapes not counted in r */
1014                         *statep = 1;
1015                         if (n < 0)
1016                                 r = -1;
1017                 }
1018                 if (r < 0)
1019                         break;
1020         }
1021         return r;
1022 }