1/* $NetBSD: rumpuser_daemonize.c,v 1.10 2024/04/04 21:19:25 riastradh Exp $ */ 2 3/* 4 * Copyright (c) 2010 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include "rumpuser_port.h" 29 30#if !defined(lint) 31__RCSID("$NetBSD: rumpuser_daemonize.c,v 1.10 2024/04/04 21:19:25 riastradh Exp $"); 32#endif /* !lint */ 33 34#include <sys/types.h> 35#include <sys/socket.h> 36 37#include <errno.h> 38#include <fcntl.h> 39#include <stdint.h> 40#include <stdio.h> 41#include <unistd.h> 42 43#include "rumpuser_int.h" 44 45#if defined(HAVE_PATHS_H) 46#include <paths.h> 47#else 48#define _PATH_DEVNULL "/dev/null" 49#endif 50 51static int isdaemonizing; 52static int daemonpipe[2]; 53 54#include <rump/rumpuser.h> 55 56static int 57openstdoutstderr(void) 58{ 59 char path[PATH_MAX]; 60 int fd; 61 62 if (getenv_r("RUMP_STDOUT", path, sizeof(path)) == 0) { 63 if ((fd = open(path, O_WRONLY|O_CREAT)) == -1) 64 return -1; 65 dup2(fd, STDOUT_FILENO); 66 (void)close(fd); 67 } 68 if (getenv_r("RUMP_STDERR", path, sizeof(path)) == 0) { 69 if ((fd = open(path, O_WRONLY|O_CREAT)) == -1) 70 return -1; 71 dup2(fd, STDERR_FILENO); 72 (void)close(fd); 73 } 74 return 0; 75} 76 77int 78rumpuser_daemonize_begin(void) 79{ 80 ssize_t n; 81 int error; 82 int rv; 83 84 if (isdaemonizing) { 85 rv = EINPROGRESS; 86 goto out; 87 } 88 isdaemonizing = 1; 89 90 /* 91 * For daemons we need to fork. However, since we can't fork 92 * after rump_init (which creates threads), do it now. Add 93 * a little pipe trickery to make sure we don't exit until the 94 * service is fully inited (i.e. interlocked daemonization). 95 * Actually, use socketpair since that allows to easily steer 96 * clear of the dreaded sigpipe. 97 * 98 * Note: We do *NOT* host chdir("/"). It's up to the caller to 99 * take care of that or not. 100 */ 101 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, daemonpipe) == -1) { 102 rv = errno; 103 goto out; 104 } 105 106 if (openstdoutstderr() == -1) { 107 rv = errno; 108 (void)close(daemonpipe[0]); 109 (void)close(daemonpipe[1]); 110 goto out; 111 } 112 113 switch (fork()) { 114 case 0: 115 if (setsid() == -1) { 116 rumpuser_daemonize_done(errno); 117 } 118 rv = 0; 119 break; 120 case -1: 121 rv = errno; 122 break; 123 default: 124 close(daemonpipe[1]); 125 n = recv(daemonpipe[0], &error, sizeof(error), MSG_NOSIGNAL); 126 if (n == -1) 127 error = errno; 128 else if (n != sizeof(error)) 129 error = ESRCH; 130 _exit(error); 131 /*NOTREACHED*/ 132 } 133 134 out: 135 ET(rv); 136} 137 138int 139rumpuser_daemonize_done(int error) 140{ 141 ssize_t n; 142 int fd, rv = 0; 143 144 if (!isdaemonizing) { 145 rv = ENOENT; 146 goto outout; 147 } 148 149 if (error == 0) { 150 fd = open(_PATH_DEVNULL, O_RDWR); 151 if (fd == -1) { 152 error = errno; 153 goto out; 154 } 155 dup2(fd, STDIN_FILENO); 156 if (getenv("RUMP_STDOUT") == NULL) 157 dup2(fd, STDOUT_FILENO); 158 if (getenv("RUMP_STDERR") == NULL) 159 dup2(fd, STDERR_FILENO); 160 if (fd > STDERR_FILENO) 161 close(fd); 162 } 163 164 fflush(stdout); 165 fflush(stderr); 166 167 out: 168 n = send(daemonpipe[1], &error, sizeof(error), MSG_NOSIGNAL); 169 if (n != sizeof(error)) { 170 rv = EPIPE; 171 } else if (n == -1) { 172 rv = errno; 173 } else { 174 close(daemonpipe[0]); 175 close(daemonpipe[1]); 176 } 177 178 outout: 179 ET(rv); 180} 181