xref: /netbsd-src/lib/librumpuser/rumpuser_daemonize.c (revision 561072c7c1874035652bf4084d6cb47b85da0e92)
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 
51 static int isdaemonizing;
52 static int daemonpipe[2];
53 
54 #include <rump/rumpuser.h>
55 
56 static int
openstdoutstderr(void)57 openstdoutstderr(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 
77 int
rumpuser_daemonize_begin(void)78 rumpuser_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 
138 int
rumpuser_daemonize_done(int error)139 rumpuser_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