PageRenderTime 23ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/usr.sbin/daemon/daemon.c

https://gitlab.com/tlevine/DragonFlyBSD
C | 276 lines | 181 code | 21 blank | 74 comment | 52 complexity | 7b295615d770cea0b123bf79aa47c8b3 MD5 | raw file
  1. /*-
  2. * Copyright (c) 1999 Berkeley Software Design, Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. * 1. Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * 3. Berkeley Software Design Inc's name may not be used to endorse or
  13. * promote products derived from this software without specific prior
  14. * written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
  17. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
  20. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  22. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  23. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  26. * SUCH DAMAGE.
  27. *
  28. * From BSDI: daemon.c,v 1.2 1996/08/15 01:11:09 jch Exp
  29. * $FreeBSD: head/usr.sbin/daemon/daemon.c 255707 2013-09-19 18:00:05Z trociny $
  30. */
  31. #include <sys/param.h>
  32. #include <sys/mman.h>
  33. #include <sys/wait.h>
  34. #include <err.h>
  35. #include <errno.h>
  36. #include <libutil.h>
  37. #include <login_cap.h>
  38. #include <pwd.h>
  39. #include <signal.h>
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42. #include <unistd.h>
  43. static void dummy_sighandler(int);
  44. static void restrict_process(const char *);
  45. static int wait_child(pid_t pid, sigset_t *mask);
  46. static void usage(void);
  47. int
  48. main(int argc, char *argv[])
  49. {
  50. struct pidfh *ppfh, *pfh;
  51. sigset_t mask, oldmask;
  52. int ch, nochdir, noclose, restart, serrno;
  53. const char *pidfile, *ppidfile, *user;
  54. pid_t otherpid, pid;
  55. nochdir = noclose = 1;
  56. restart = 0;
  57. ppidfile = pidfile = user = NULL;
  58. while ((ch = getopt(argc, argv, "cfp:P:ru:")) != -1) {
  59. switch (ch) {
  60. case 'c':
  61. nochdir = 0;
  62. break;
  63. case 'f':
  64. noclose = 0;
  65. break;
  66. case 'p':
  67. pidfile = optarg;
  68. break;
  69. case 'P':
  70. ppidfile = optarg;
  71. break;
  72. case 'r':
  73. restart = 1;
  74. break;
  75. case 'u':
  76. user = optarg;
  77. break;
  78. default:
  79. usage();
  80. }
  81. }
  82. argc -= optind;
  83. argv += optind;
  84. if (argc == 0)
  85. usage();
  86. ppfh = pfh = NULL;
  87. /*
  88. * Try to open the pidfile before calling daemon(3),
  89. * to be able to report the error intelligently
  90. */
  91. if (pidfile != NULL) {
  92. pfh = pidfile_open(pidfile, 0600, &otherpid);
  93. if (pfh == NULL) {
  94. if (errno == EEXIST) {
  95. errx(3, "process already running, pid: %d",
  96. otherpid);
  97. }
  98. err(2, "pidfile ``%s''", pidfile);
  99. }
  100. }
  101. /* Do the same for actual daemon process. */
  102. if (ppidfile != NULL) {
  103. ppfh = pidfile_open(ppidfile, 0600, &otherpid);
  104. if (ppfh == NULL) {
  105. serrno = errno;
  106. pidfile_remove(pfh);
  107. errno = serrno;
  108. if (errno == EEXIST) {
  109. errx(3, "process already running, pid: %d",
  110. otherpid);
  111. }
  112. err(2, "ppidfile ``%s''", ppidfile);
  113. }
  114. }
  115. if (daemon(nochdir, noclose) == -1) {
  116. warn("daemon");
  117. goto exit;
  118. }
  119. /* Write out parent pidfile if needed. */
  120. pidfile_write(ppfh);
  121. /*
  122. * If the pidfile or restart option is specified the daemon
  123. * executes the command in a forked process and wait on child
  124. * exit to remove the pidfile or restart the command. Normally
  125. * we don't want the monitoring daemon to be terminated
  126. * leaving the running process and the stale pidfile, so we
  127. * catch SIGTERM and forward it to the children expecting to
  128. * get SIGCHLD eventually.
  129. */
  130. pid = -1;
  131. if (pidfile != NULL || restart) {
  132. /*
  133. * Restore default action for SIGTERM in case the
  134. * parent process decided to ignore it.
  135. */
  136. if (signal(SIGTERM, SIG_DFL) == SIG_ERR) {
  137. warn("signal");
  138. goto exit;
  139. }
  140. /*
  141. * Because SIGCHLD is ignored by default, setup dummy handler
  142. * for it, so we can mask it.
  143. */
  144. if (signal(SIGCHLD, dummy_sighandler) == SIG_ERR) {
  145. warn("signal");
  146. goto exit;
  147. }
  148. /*
  149. * Block interesting signals.
  150. */
  151. sigemptyset(&mask);
  152. sigaddset(&mask, SIGTERM);
  153. sigaddset(&mask, SIGCHLD);
  154. if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1) {
  155. warn("sigprocmask");
  156. goto exit;
  157. }
  158. /*
  159. * Try to protect against pageout kill. Ignore the
  160. * error, madvise(2) will fail only if a process does
  161. * not have superuser privileges.
  162. * MADV_PROTECT not supported on DragonFly
  163. * (void)madvise(NULL, 0, MADV_PROTECT);
  164. */
  165. restart:
  166. /*
  167. * Spawn a child to exec the command, so in the parent
  168. * we could wait for it to exit and remove pidfile.
  169. */
  170. pid = fork();
  171. if (pid == -1) {
  172. warn("fork");
  173. goto exit;
  174. }
  175. }
  176. if (pid <= 0) {
  177. if (pid == 0) {
  178. /* Restore old sigmask in the child. */
  179. if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1)
  180. err(1, "sigprocmask");
  181. }
  182. /* Now that we are the child, write out the pid. */
  183. pidfile_write(pfh);
  184. if (user != NULL)
  185. restrict_process(user);
  186. execvp(argv[0], argv);
  187. /*
  188. * execvp() failed -- report the error. The child is
  189. * now running, so the exit status doesn't matter.
  190. */
  191. err(1, "%s", argv[0]);
  192. }
  193. setproctitle("%s[%d]", argv[0], pid);
  194. if (wait_child(pid, &mask) == 0 && restart) {
  195. sleep(1);
  196. goto restart;
  197. }
  198. exit:
  199. pidfile_remove(pfh);
  200. pidfile_remove(ppfh);
  201. exit(1); /* If daemon(3) succeeded exit status does not matter. */
  202. }
  203. static void
  204. dummy_sighandler(int sig __unused)
  205. {
  206. /* Nothing to do. */
  207. }
  208. static void
  209. restrict_process(const char *user)
  210. {
  211. struct passwd *pw = NULL;
  212. pw = getpwnam(user);
  213. if (pw == NULL)
  214. errx(1, "unknown user: %s", user);
  215. if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0)
  216. errx(1, "failed to set user environment");
  217. }
  218. static int
  219. wait_child(pid_t pid, sigset_t *mask)
  220. {
  221. int terminate, signo;
  222. terminate = 0;
  223. for (;;) {
  224. if (sigwait(mask, &signo) == -1) {
  225. warn("sigwaitinfo");
  226. return (-1);
  227. }
  228. switch (signo) {
  229. case SIGCHLD:
  230. if (waitpid(pid, NULL, WNOHANG) == -1) {
  231. warn("waitpid");
  232. return (-1);
  233. }
  234. return (terminate);
  235. case SIGTERM:
  236. terminate = 1;
  237. if (kill(pid, signo) == -1) {
  238. warn("kill");
  239. return (-1);
  240. }
  241. continue;
  242. default:
  243. warnx("sigwaitinfo: invalid signal: %d", signo);
  244. return (-1);
  245. }
  246. }
  247. }
  248. static void
  249. usage(void)
  250. {
  251. (void)fprintf(stderr,
  252. "usage: daemon [-cfr] [-p child_pidfile] [-P supervisor_pidfile] "
  253. "[-u user]\n command arguments ...\n");
  254. exit(1);
  255. }