1 /*
2 * virdaemon.c: shared daemon setup code
3 *
4 * Copyright (C) 2020 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; If not, see
18 * <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #ifndef WIN32
26 # include <sys/wait.h>
27 #endif
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <stdbool.h>
31
32 #include "virdaemon.h"
33 #include "virutil.h"
34 #include "virfile.h"
35 #include "virlog.h"
36 #include "viralloc.h"
37
38 #include "configmake.h"
39
40 #ifndef WIN32
41
42 int
virDaemonForkIntoBackground(const char * argv0)43 virDaemonForkIntoBackground(const char *argv0)
44 {
45 int statuspipe[2];
46 pid_t pid;
47
48 if (virPipeQuiet(statuspipe) < 0)
49 return -1;
50
51 pid = fork();
52 switch (pid) {
53 case 0:
54 {
55 /* intermediate child */
56 int stdinfd = -1;
57 int stdoutfd = -1;
58 int nextpid;
59
60 VIR_FORCE_CLOSE(statuspipe[0]);
61
62 if ((stdinfd = open("/dev/null", O_RDONLY)) < 0)
63 goto cleanup;
64 if ((stdoutfd = open("/dev/null", O_WRONLY)) < 0)
65 goto cleanup;
66 if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO)
67 goto cleanup;
68 if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO)
69 goto cleanup;
70 if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO)
71 goto cleanup;
72 if (VIR_CLOSE(stdinfd) < 0)
73 goto cleanup;
74 if (VIR_CLOSE(stdoutfd) < 0)
75 goto cleanup;
76
77 if (setsid() < 0)
78 goto cleanup;
79
80 nextpid = fork();
81 switch (nextpid) {
82 case 0: /* grandchild */
83 return statuspipe[1];
84 case -1: /* error */
85 goto cleanup;
86 default: /* intermediate child succeeded */
87 _exit(EXIT_SUCCESS);
88 }
89
90 cleanup:
91 VIR_FORCE_CLOSE(stdoutfd);
92 VIR_FORCE_CLOSE(stdinfd);
93 VIR_FORCE_CLOSE(statuspipe[1]);
94 _exit(EXIT_FAILURE);
95
96 }
97
98 case -1: /* error in parent */
99 goto error;
100
101 default:
102 {
103 /* parent */
104 int got, exitstatus = 0;
105 int ret;
106 char status;
107
108 VIR_FORCE_CLOSE(statuspipe[1]);
109
110 /* We wait to make sure the first child forked successfully */
111 if ((got = waitpid(pid, &exitstatus, 0)) < 0 ||
112 got != pid ||
113 exitstatus != 0) {
114 goto error;
115 }
116
117 /* If we got here, then the grandchild was spawned, so we
118 * must exit. Block until the second child initializes
119 * successfully */
120 ret = saferead(statuspipe[0], &status, 1);
121
122 VIR_FORCE_CLOSE(statuspipe[0]);
123
124 if (ret != 1) {
125 fprintf(stderr,
126 _("%s: error: unable to determine if daemon is "
127 "running: %s\n"), argv0,
128 g_strerror(errno));
129 exit(EXIT_FAILURE);
130 } else if (status != 0) {
131 fprintf(stderr,
132 _("%s: error: %s. Check /var/log/messages or run without "
133 "--daemon for more info.\n"), argv0,
134 virDaemonErrTypeToString(status));
135 exit(EXIT_FAILURE);
136 }
137 _exit(EXIT_SUCCESS);
138 }
139 }
140
141 error:
142 VIR_FORCE_CLOSE(statuspipe[0]);
143 VIR_FORCE_CLOSE(statuspipe[1]);
144 return -1;
145 }
146
147
148 /*
149 * Set up the logging environment
150 * By default if daemonized all errors go to the logfile libvirtd.log,
151 * but if verbose or error debugging is asked for then also output
152 * informational and debug messages. Default size if 64 kB.
153 */
154 void
virDaemonSetupLogging(const char * daemon_name,unsigned int log_level,char * log_filters,char * log_outputs,bool privileged,bool verbose,bool godaemon)155 virDaemonSetupLogging(const char *daemon_name,
156 unsigned int log_level,
157 char *log_filters,
158 char *log_outputs,
159 bool privileged,
160 bool verbose,
161 bool godaemon)
162 {
163 virLogReset();
164
165 /*
166 * Libvirtd's order of precedence is:
167 * cmdline > environment > config
168 *
169 * Given the precedence, we must process the variables in the opposite
170 * order, each one overriding the previous.
171 */
172 if (log_level != 0)
173 virLogSetDefaultPriority(log_level);
174
175 /* In case the config is empty, both filters and outputs will become empty,
176 * however we can't start with empty outputs, thus we'll need to define and
177 * setup a default one.
178 */
179 ignore_value(virLogSetFilters(log_filters));
180 ignore_value(virLogSetOutputs(log_outputs));
181
182 /* If there are some environment variables defined, use those instead */
183 virLogSetFromEnv();
184
185 /*
186 * Command line override for --verbose
187 */
188 if ((verbose) && (virLogGetDefaultPriority() > VIR_LOG_INFO))
189 virLogSetDefaultPriority(VIR_LOG_INFO);
190
191 /* Define the default output. This is only applied if there was no setting
192 * from either the config or the environment.
193 */
194 virLogSetDefaultOutput(daemon_name, godaemon, privileged);
195
196 if (virLogGetNbOutputs() == 0)
197 virLogSetOutputs(virLogGetDefaultOutput());
198 }
199
200
201 int
virDaemonUnixSocketPaths(const char * sock_prefix,bool privileged,char * unix_sock_dir,char ** sockfile,char ** rosockfile,char ** admsockfile)202 virDaemonUnixSocketPaths(const char *sock_prefix,
203 bool privileged,
204 char *unix_sock_dir,
205 char **sockfile,
206 char **rosockfile,
207 char **admsockfile)
208 {
209 int ret = -1;
210 char *rundir = NULL;
211
212 if (unix_sock_dir) {
213 if (sockfile)
214 *sockfile = g_strdup_printf("%s/%s-sock", unix_sock_dir, sock_prefix);
215
216 if (privileged) {
217 if (rosockfile)
218 *rosockfile = g_strdup_printf("%s/%s-sock-ro",
219 unix_sock_dir, sock_prefix);
220 if (admsockfile)
221 *admsockfile = g_strdup_printf("%s/%s-admin-sock",
222 unix_sock_dir, sock_prefix);
223 }
224 } else {
225 if (privileged) {
226 if (sockfile)
227 *sockfile = g_strdup_printf("%s/libvirt/%s-sock",
228 RUNSTATEDIR, sock_prefix);
229 if (rosockfile)
230 *rosockfile = g_strdup_printf("%s/libvirt/%s-sock-ro",
231 RUNSTATEDIR, sock_prefix);
232 if (admsockfile)
233 *admsockfile = g_strdup_printf("%s/libvirt/%s-admin-sock",
234 RUNSTATEDIR, sock_prefix);
235 } else {
236 mode_t old_umask;
237
238 rundir = virGetUserRuntimeDirectory();
239
240 old_umask = umask(077);
241 if (g_mkdir_with_parents(rundir, 0777) < 0) {
242 umask(old_umask);
243 goto cleanup;
244 }
245 umask(old_umask);
246
247 if (sockfile)
248 *sockfile = g_strdup_printf("%s/%s-sock", rundir, sock_prefix);
249 if (admsockfile)
250 *admsockfile = g_strdup_printf("%s/%s-admin-sock", rundir, sock_prefix);
251 }
252 }
253
254 ret = 0;
255 cleanup:
256 VIR_FREE(rundir);
257 return ret;
258 }
259
260 #else /* WIN32 */
261
virDaemonForkIntoBackground(const char * argv0 G_GNUC_UNUSED)262 int virDaemonForkIntoBackground(const char *argv0 G_GNUC_UNUSED)
263 {
264 errno = ENOTSUP;
265 return -1;
266 }
267
virDaemonSetupLogging(const char * daemon_name G_GNUC_UNUSED,unsigned int log_level G_GNUC_UNUSED,char * log_filters G_GNUC_UNUSED,char * log_outputs G_GNUC_UNUSED,bool privileged G_GNUC_UNUSED,bool verbose G_GNUC_UNUSED,bool godaemon G_GNUC_UNUSED)268 void virDaemonSetupLogging(const char *daemon_name G_GNUC_UNUSED,
269 unsigned int log_level G_GNUC_UNUSED,
270 char *log_filters G_GNUC_UNUSED,
271 char *log_outputs G_GNUC_UNUSED,
272 bool privileged G_GNUC_UNUSED,
273 bool verbose G_GNUC_UNUSED,
274 bool godaemon G_GNUC_UNUSED)
275 {
276 /* NOOP */
277 errno = ENOTSUP;
278 return;
279 }
280
virDaemonUnixSocketPaths(const char * sock_prefix G_GNUC_UNUSED,bool privileged G_GNUC_UNUSED,char * unix_sock_dir G_GNUC_UNUSED,char ** sockfile G_GNUC_UNUSED,char ** rosockfile G_GNUC_UNUSED,char ** adminSockfile G_GNUC_UNUSED)281 int virDaemonUnixSocketPaths(const char *sock_prefix G_GNUC_UNUSED,
282 bool privileged G_GNUC_UNUSED,
283 char *unix_sock_dir G_GNUC_UNUSED,
284 char **sockfile G_GNUC_UNUSED,
285 char **rosockfile G_GNUC_UNUSED,
286 char **adminSockfile G_GNUC_UNUSED)
287 {
288 errno = ENOTSUP;
289 return -1;
290 }
291
292 #endif /* WIN32 */
293