[libvirt] [PATCH 10/21] Don't let parent of daemon exit until basic initialization is done
Cole Robinson
crobinso at redhat.com
Mon Nov 2 22:05:55 UTC 2009
On 10/23/2009 09:05 AM, Daniel P. Berrange wrote:
> The daemonizing code lets the parent exit almost immediately. This
> means that it may think it has successfully started even when
> important failures occur like not being able to acquire the PID
> file. It also means network sockets are not yet open.
>
> To address this when daemonizing the parent passes an open pipe
> file descriptor to the child. The child does its basic initialization
> and then writes a status code to the pipe indicating either success,
> or failure. This ensures that when daemonizing, the parent does not
> exit until the pidfile is acquired & basic network sockets are open.
>
> Initialization of the libvirt drivers is still done asynchronously
> since this may take a very long time.
>
> * daemon/libvirtd.c: Force parent to stay around until basic config
> file, pidfile & network socket init is completed
> ---
> daemon/libvirtd.c | 120 +++++++++++++++++++++++++++++++++++++++++++++--------
> 1 files changed, 102 insertions(+), 18 deletions(-)
>
> diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
> index 4e268a2..b118da8 100644
> --- a/daemon/libvirtd.c
> +++ b/daemon/libvirtd.c
> @@ -185,6 +185,30 @@ static int max_client_requests = 5;
> static sig_atomic_t sig_errors = 0;
> static int sig_lasterrno = 0;
>
> +enum {
> + VIR_DAEMON_ERR_NONE = 0,
> + VIR_DAEMON_ERR_PIDFILE,
> + VIR_DAEMON_ERR_RUNDIR,
> + VIR_DAEMON_ERR_INIT,
> + VIR_DAEMON_ERR_SIGNAL,
> + VIR_DAEMON_ERR_PRIVS,
> + VIR_DAEMON_ERR_NETWORK,
> + VIR_DAEMON_ERR_CONFIG,
> +
> + VIR_DAEMON_ERR_LAST
> +};
> +
> +VIR_ENUM_DECL(virDaemonErr)
> +VIR_ENUM_IMPL(virDaemonErr, VIR_DAEMON_ERR_LAST,
> + "Initialization successful",
> + "Unable to obtain pidfile",
> + "Unable to create rundir",
> + "Unable to initialize libvirt",
> + "Unable to setup signal handlers",
> + "Unable to drop privileges",
> + "Unable to initialize network sockets",
> + "Unable to load configuration file")
> +
> static void sig_handler(int sig, siginfo_t * siginfo,
> void* context ATTRIBUTE_UNUSED) {
> int origerrno;
> @@ -375,7 +399,11 @@ qemudDispatchSignalEvent(int watch ATTRIBUTE_UNUSED,
> }
>
>
> -static int qemudGoDaemon(void) {
> +static int daemonForkIntoBackground(void) {
> + int statuspipe[2];
> + if (pipe(statuspipe) < 0)
> + return -1;
> +
> int pid = fork();
> switch (pid) {
> case 0:
> @@ -384,6 +412,8 @@ static int qemudGoDaemon(void) {
> int stdoutfd = -1;
> int nextpid;
>
> + close(statuspipe[0]);
> +
> if ((stdinfd = open("/dev/null", O_RDONLY)) < 0)
> goto cleanup;
> if ((stdoutfd = open("/dev/null", O_WRONLY)) < 0)
> @@ -407,7 +437,7 @@ static int qemudGoDaemon(void) {
> nextpid = fork();
> switch (nextpid) {
> case 0:
> - return 0;
> + return statuspipe[1];
> case -1:
> return -1;
> default:
> @@ -428,15 +458,29 @@ static int qemudGoDaemon(void) {
>
> default:
> {
> - int got, status = 0;
> - /* We wait to make sure the next child forked
> - successfully */
> - if ((got = waitpid(pid, &status, 0)) < 0 ||
> + int got, exitstatus = 0;
> + int ret;
> + char status;
> +
> + close(statuspipe[1]);
> +
> + /* We wait to make sure the first child forked successfully */
> + if ((got = waitpid(pid, &exitstatus, 0)) < 0 ||
> got != pid ||
> status != 0) {
> return -1;
> }
> - _exit(0);
> +
> + /* Now block until the second child initializes successfully */
> + again:
> + ret = read(statuspipe[0], &status, 1);
> + if (ret == -1 && errno == EINTR)
> + goto again;
> +
> + if (ret == 1 && status != 0) {
> + fprintf(stderr, "error: %s\n", virDaemonErrTypeToString(status));
> + }
> + _exit(ret == 1 && status == 0 ? 0 : 1);
> }
> }
> }
> @@ -871,8 +915,6 @@ static struct qemud_server *qemudInitialize(void) {
> virEventUpdateTimeoutImpl,
> virEventRemoveTimeoutImpl);
>
> - virStateInitialize(server->privileged);
> -
> return server;
> }
>
> @@ -2902,6 +2944,7 @@ int main(int argc, char **argv) {
> struct qemud_server *server = NULL;
> const char *pid_file = NULL;
> const char *remote_config_file = NULL;
> + int statuswrite = -1;
> int ret = 1;
>
> struct option opts[] = {
> @@ -2985,7 +3028,7 @@ int main(int argc, char **argv) {
>
> if (godaemon) {
> char ebuf[1024];
> - if (qemudGoDaemon() < 0) {
> + if ((statuswrite = daemonForkIntoBackground()) < 0) {
> VIR_ERROR(_("Failed to fork as daemon: %s"),
> virStrerror(errno, ebuf, sizeof ebuf));
> goto error;
> @@ -3000,8 +3043,11 @@ int main(int argc, char **argv) {
>
> /* If we have a pidfile set, claim it now, exiting if already taken */
> if (pid_file != NULL &&
> - qemudWritePidFile (pid_file) < 0)
> + qemudWritePidFile (pid_file) < 0) {
> + pid_file = NULL; /* Prevent unlinking of someone else's pid ! */
> + ret = VIR_DAEMON_ERR_PIDFILE;
> goto error;
> + }
>
> /* Ensure the rundir exists (on tmpfs on some systems) */
> if (geteuid() == 0) {
> @@ -3010,7 +3056,8 @@ int main(int argc, char **argv) {
> if (mkdir (rundir, 0755)) {
> if (errno != EEXIST) {
> VIR_ERROR0 (_("unable to create rundir"));
> - return -1;
> + ret = VIR_DAEMON_ERR_RUNDIR;
> + goto error;
> }
> }
> }
> @@ -3021,34 +3068,71 @@ int main(int argc, char **argv) {
> * which is also passed into all libvirt stateful
> * drivers
> */
> - if (qemudSetupPrivs() < 0)
> + if (qemudSetupPrivs() < 0) {
> + ret = VIR_DAEMON_ERR_PRIVS;
> goto error;
> + }
>
> if (!(server = qemudInitialize())) {
> - ret = 2;
> + ret = VIR_DAEMON_ERR_INIT;
> goto error;
> }
>
> - if ((daemonSetupSignals(server)) < 0)
> + if ((daemonSetupSignals(server)) < 0) {
> + ret = VIR_DAEMON_ERR_SIGNAL;
> goto error;
> + }
>
> /* Read the config file (if it exists). */
> - if (remoteReadConfigFile (server, remote_config_file) < 0)
> + if (remoteReadConfigFile (server, remote_config_file) < 0) {
> + ret = VIR_DAEMON_ERR_CONFIG;
> goto error;
> + }
>
> /* Disable error func, now logging is setup */
> virSetErrorFunc(NULL, virshErrorHandler);
>
> if (qemudNetworkInit(server) < 0) {
> - ret = 2;
> + ret = VIR_DAEMON_ERR_NETWORK;
> goto error;
> }
>
> - qemudRunLoop(server);
> + /* Tell parent of daemon that basic initialization is complete
> + * In particular we're ready to accept net connections & have
> + * written the pidfile
> + */
> + if (statuswrite != -1) {
> + char status = 0;
> + while (write(statuswrite, &status, 1) == -1 &&
> + errno == EINTR)
> + ;
> + close(statuswrite);
> + statuswrite = -1;
> + }
> +
> + /* Start the stateful HV drivers
> + * This is delibrately done after telling the parent process
> + * we're ready, since it can take a long time and this will
> + * seriously delay OS bootup process */
> + if (virStateInitialize(server->privileged) < 0) {
> + VIR_ERROR0("Driver state initialization failed");
> + goto error;
> + }
>
This breaks qemu:///session for me.
Starting libvirtd by hand as a regular user, virStateInitialize tries to
init lxc, but lxc explicitly denies non-root driver startup in
lxc_driver.c:lxcStartup:
/* Check that the user is root */
if (!privileged) {
return -1;
}
Not sure what the proper fix is.
- Cole
More information about the libvir-list
mailing list