VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp@ 98472

Last change on this file since 98472 was 98472, checked in by vboxsync, 16 months ago

Guest Library: Introduce VbglR3DaemonizeEx and VbglR3PidfileWait, bugref:10359.

VbglR3DaemonizeEx is a wrapper function for VbglR3Daemonize. In addition to the
original version it sets pidfile lock for the parent process and notifies caller
when child process terminates with exit status VBGLR3EXITCODERELOAD (currently
equal to 2) indicating that parent process should release its reference to vboxguest
kernel module and wait for SIGUSR2 in order to restart itself. This is a part of
the procedure to install and reload/restart Guest Additions kernel modules and
user services without requiring guest reboot. It is currently only has effect
on X11 guests.

VbglR3PidfileWait is a wrapper function for VbglR3Pidfile. In addition to the
original version, it waits specified amount of time until pidfile lock become
available or fails on timeout.

  • Property svn:eol-style set to native
  • Property svn:keyword set to Id
  • Property svn:keywords set to Id Revision
File size: 10.8 KB
Line 
1/** $Id: VBoxGuestR3LibDaemonize.cpp 98472 2023-02-03 18:56:59Z vboxsync $ */
2/** @file
3 * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, daemonize a process.
4 */
5
6/*
7 * Copyright (C) 2007-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#if defined(RT_OS_OS2)
42# define INCL_BASE
43# define INCL_ERRORS
44# include <os2.h>
45
46# include <iprt/alloca.h>
47# include <iprt/string.h>
48
49#elif defined(RT_OS_WINDOWS)
50# error "PORTME"
51
52#else /* the unices */
53# include <sys/types.h>
54# include <sys/stat.h>
55# include <sys/wait.h>
56# include <stdio.h>
57# include <fcntl.h>
58# include <stdlib.h>
59# include <unistd.h>
60# include <signal.h>
61# include <errno.h>
62#endif
63
64#include <iprt/process.h>
65#include <iprt/string.h>
66#include <VBox/err.h>
67#include <VBox/log.h>
68
69#include "VBoxGuestR3LibInternal.h"
70
71
72/**
73 * Daemonize the process for running in the background.
74 *
75 * This is supposed to do the same job as the BSD daemon() call.
76 *
77 * @returns 0 on success
78 *
79 * @param fNoChDir Pass false to change working directory to root.
80 * @param fNoClose Pass false to redirect standard file streams to /dev/null.
81 * @param fRespawn Restart the daemonised process after five seconds if it
82 * terminates abnormally.
83 * @param pcRespawn Where to store a count of how often we have respawned,
84 * intended for avoiding error spamming. Optional.
85 * @param fReturnOnUpdate If True, this function will return control to caller when
86 * child process will terminate with exit code of VBGLR3EXITCODERELOAD,
87 * indicating that Guest Additions update has been started and this running
88 * process will be asked to be restarted by arrival of the next SIGUSR1
89 * signal (caller should wait for SIGUSR1). If False, this functions will
90 * never return, but rather exit() when child process terminates with
91 * exit code 0.
92 * @param pfUpdateStarted A flag which passed to caller if fReturnOnUpdate is True (can be NULL).
93 * @param szPidfile Optional path to parent process' pidfile (can be NULL).
94 * @param phPidfile Optional path to parent process' pidfile handle (can not be NULL if
95 * szPidfile was specified).
96 *
97 * @todo Use RTProcDaemonize instead of this.
98 * @todo Implement fRespawn on OS/2.
99 * @todo Make the respawn interval configurable. But not until someone
100 * actually needs that.
101 */
102VBGLR3DECL(int) VbglR3DaemonizeEx(bool fNoChDir, bool fNoClose, bool fRespawn, unsigned *pcRespawn,
103 bool fReturnOnUpdate, bool *pfUpdateStarted, const char *szPidfile,
104 RTFILE *phPidfile)
105{
106#if defined(RT_OS_OS2)
107 PPIB pPib;
108 PTIB pTib;
109 DosGetInfoBlocks(&pTib, &pPib);
110
111 RT_NOREF(fReturnOnUpdate);
112 RT_NOREF(pfUpdateStarted);
113 RT_NOREF(szPidfile);
114 RT_NOREF(phPidfile);
115
116 AssertRelease(!fRespawn);
117 /* Get the full path to the executable. */
118 char szExe[CCHMAXPATH];
119 APIRET rc = DosQueryModuleName(pPib->pib_hmte, sizeof(szExe), szExe);
120 if (rc)
121 return RTErrConvertFromOS2(rc);
122
123 /* calc the length of the command line. */
124 char *pch = pPib->pib_pchcmd;
125 size_t cch0 = strlen(pch);
126 pch += cch0 + 1;
127 size_t cch1 = strlen(pch);
128 pch += cch1 + 1;
129 char *pchArgs;
130 if (cch1 && *pch)
131 {
132 do pch = strchr(pch, '\0') + 1;
133 while (*pch);
134
135 size_t cchTotal = pch - pPib->pib_pchcmd;
136 pchArgs = (char *)alloca(cchTotal + sizeof("--daemonized\0\0"));
137 memcpy(pchArgs, pPib->pib_pchcmd, cchTotal - 1);
138 memcpy(pchArgs + cchTotal - 1, "--daemonized\0\0", sizeof("--daemonized\0\0"));
139 }
140 else
141 {
142 size_t cchTotal = pch - pPib->pib_pchcmd + 1;
143 pchArgs = (char *)alloca(cchTotal + sizeof(" --daemonized "));
144 memcpy(pchArgs, pPib->pib_pchcmd, cch0 + 1);
145 pch = pchArgs + cch0 + 1;
146 memcpy(pch, " --daemonized ", sizeof(" --daemonized ") - 1);
147 pch += sizeof(" --daemonized ") - 1;
148 if (cch1)
149 memcpy(pch, pPib->pib_pchcmd + cch0 + 1, cch1 + 2);
150 else
151 pch[0] = pch[1] = '\0';
152 }
153
154 /* spawn a detach process */
155 char szObj[128];
156 RESULTCODES ResCodes = { 0, 0 };
157 szObj[0] = '\0';
158 rc = DosExecPgm(szObj, sizeof(szObj), EXEC_BACKGROUND, (PCSZ)pchArgs, NULL, &ResCodes, (PCSZ)szExe);
159 if (rc)
160 {
161 /** @todo Change this to some standard log/print error?? */
162 /* VBoxServiceError("DosExecPgm failed with rc=%d and szObj='%s'\n", rc, szObj); */
163 return RTErrConvertFromOS2(rc);
164 }
165 DosExit(EXIT_PROCESS, 0);
166 return VERR_GENERAL_FAILURE;
167
168#elif defined(RT_OS_WINDOWS)
169# error "PORTME"
170
171#else /* the unices */
172 /*
173 * Fork the child process in a new session and quit the parent.
174 *
175 * - fork once and create a new session (setsid). This will detach us
176 * from the controlling tty meaning that we won't receive the SIGHUP
177 * (or any other signal) sent to that session.
178 * - The SIGHUP signal is ignored because the session/parent may throw
179 * us one before we get to the setsid.
180 * - When the parent exit(0) we will become an orphan and re-parented to
181 * the init process.
182 * - Because of the Linux / System V semantics of assigning the controlling
183 * tty automagically when a session leader first opens a tty, we will
184 * fork() once more on Linux to get rid of the session leadership role.
185 */
186
187 struct sigaction OldSigAct;
188 struct sigaction SigAct;
189 RT_ZERO(SigAct);
190 SigAct.sa_handler = SIG_IGN;
191 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
192
193 pid_t pid = fork();
194 if (pid == -1)
195 return RTErrConvertFromErrno(errno);
196 if (pid != 0)
197 exit(0);
198
199 /*
200 * The orphaned child becomes is reparented to the init process.
201 * We create a new session for it (setsid), point the standard
202 * file descriptors to /dev/null, and change to the root directory.
203 */
204 pid_t newpgid = setsid();
205 int SavedErrno = errno;
206 if (rcSigAct != -1)
207 sigaction(SIGHUP, &OldSigAct, NULL);
208 if (newpgid == -1)
209 return RTErrConvertFromErrno(SavedErrno);
210
211 if (!fNoClose)
212 {
213 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
214 int fd = open("/dev/null", O_RDWR);
215 if (fd == -1) /* paranoia */
216 {
217 close(STDIN_FILENO);
218 close(STDOUT_FILENO);
219 close(STDERR_FILENO);
220 fd = open("/dev/null", O_RDWR);
221 }
222 if (fd != -1)
223 {
224 dup2(fd, STDIN_FILENO);
225 dup2(fd, STDOUT_FILENO);
226 dup2(fd, STDERR_FILENO);
227 if (fd > 2)
228 close(fd);
229 }
230 }
231
232 if (!fNoChDir)
233 {
234 int rcShutUpGcc = chdir("/");
235 RT_NOREF_PV(rcShutUpGcc);
236 }
237
238 /*
239 * Change the umask - this is non-standard daemon() behavior.
240 */
241 umask(027);
242
243# ifdef RT_OS_LINUX
244 /*
245 * And fork again to lose session leader status (non-standard daemon()
246 * behaviour).
247 */
248 pid = fork();
249 if (pid == -1)
250 return RTErrConvertFromErrno(errno);
251 if (pid != 0)
252 exit(0);
253
254 /* Check if another instance is already running. */
255 if (szPidfile != NULL)
256 {
257 if (phPidfile != NULL)
258 {
259 int rc = VbglR3PidfileWait(szPidfile, phPidfile, 5000);
260
261 /* Another instance of process is already running. */
262 if (rc == VERR_FILE_LOCK_VIOLATION)
263 {
264 LogRel(("cannot aquire pidfile %s, exitting\n", szPidfile));
265 exit(1);
266 }
267
268 /* Unable to lock on pidfile. */
269 if (RT_FAILURE(rc))
270 exit(1);
271 }
272 else
273 return VERR_INVALID_PARAMETER;
274 }
275# endif /* RT_OS_LINUX */
276
277 if (fRespawn)
278 {
279 /* We implement re-spawning as a third fork(), with the parent process
280 * monitoring the child and re-starting it after a delay if it exits
281 * abnormally. */
282 unsigned cRespawn = 0;
283 for (;;)
284 {
285 int iStatus, rcWait;
286
287 if (pcRespawn != NULL)
288 *pcRespawn = cRespawn;
289 pid = fork();
290 if (pid == -1)
291 return RTErrConvertFromErrno(errno);
292 if (pid == 0)
293 return VINF_SUCCESS;
294 do
295 rcWait = waitpid(pid, &iStatus, 0);
296 while (rcWait == -1 && errno == EINTR);
297 if (rcWait == -1)
298 exit(1);
299 if (WIFEXITED(iStatus))
300 {
301 if (WEXITSTATUS(iStatus) == 0)
302 exit(0);
303 else if (fReturnOnUpdate && WEXITSTATUS(iStatus) == VBGLR3EXITCODERELOAD)
304 {
305 /* Tell caller that update has been started. */
306 if (pfUpdateStarted != NULL)
307 *pfUpdateStarted = true;
308
309 return VINF_SUCCESS;
310 }
311 }
312 sleep(5);
313 ++cRespawn;
314 }
315 }
316 return VINF_SUCCESS;
317#endif
318}
319
320/**
321 * A wrapper function for VbglR3DaemonizeEx.
322 */
323VBGLR3DECL(int) VbglR3Daemonize(bool fNoChDir, bool fNoClose, bool fRespawn, unsigned *pcRespawn)
324{
325 return VbglR3DaemonizeEx(fNoChDir, fNoClose, fRespawn, pcRespawn, false, NULL, NULL, NULL);
326}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use