diff options
author | Marc-André Lureau <marcandre.lureau@redhat.com> | 2018-08-31 16:53:12 +0200 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2018-10-02 18:47:55 +0200 |
commit | 9e6bdef224f700c057462a7d5e9b4a2770e04569 (patch) | |
tree | a9328d62c36923ef04d2896fda9f05f348d03587 /qga | |
parent | 71bb4ce1b5592cdc03abc48cdf4ecb15b2db81a0 (diff) |
util: add qemu_write_pidfile()
There are variants of qemu_create_pidfile() in qemu-pr-helper and
qemu-ga. Let's have a common implementation in libqemuutil.
The code is initially based from pr-helper write_pidfile(), with
various improvements and suggestions from Daniel Berrangé:
QEMU will leave the pidfile existing on disk when it exits which
initially made me think it avoids the deletion race. The app
managing QEMU, however, may well delete the pidfile after it has
seen QEMU exit, and even if the app locks the pidfile before
deleting it, there is still a race.
eg consider the following sequence
QEMU 1 libvirtd QEMU 2
1. lock(pidfile)
2. exit()
3. open(pidfile)
4. lock(pidfile)
5. open(pidfile)
6. unlink(pidfile)
7. close(pidfile)
8. lock(pidfile)
IOW, at step 8 the new QEMU has successfully acquired the lock, but
the pidfile no longer exists on disk because it was deleted after
the original QEMU exited.
While we could just say no external app should ever delete the
pidfile, I don't think that is satisfactory as people don't read
docs, and admins don't like stale pidfiles being left around on
disk.
To make this robust, I think we might want to copy libvirt's
approach to pidfile acquisition which runs in a loop and checks that
the file on disk /after/ acquiring the lock matches the file that
was locked. Then we could in fact safely let QEMU delete its own
pidfiles on clean exit..
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20180831145314.14736-2-marcandre.lureau@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'qga')
-rw-r--r-- | qga/main.c | 54 |
1 files changed, 10 insertions, 44 deletions
diff --git a/qga/main.c b/qga/main.c index 6d70242d05..c399320d3c 100644 --- a/qga/main.c +++ b/qga/main.c @@ -340,46 +340,6 @@ static FILE *ga_open_logfile(const char *logfile) return f; } -#ifndef _WIN32 -static bool ga_open_pidfile(const char *pidfile) -{ - int pidfd; - char pidstr[32]; - - pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); - if (pidfd == -1 || lockf(pidfd, F_TLOCK, 0)) { - g_critical("Cannot lock pid file, %s", strerror(errno)); - if (pidfd != -1) { - close(pidfd); - } - return false; - } - - if (ftruncate(pidfd, 0)) { - g_critical("Failed to truncate pid file"); - goto fail; - } - snprintf(pidstr, sizeof(pidstr), "%d\n", getpid()); - if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) { - g_critical("Failed to write pid file"); - goto fail; - } - - /* keep pidfile open & locked forever */ - return true; - -fail: - unlink(pidfile); - close(pidfd); - return false; -} -#else /* _WIN32 */ -static bool ga_open_pidfile(const char *pidfile) -{ - return true; -} -#endif - static gint ga_strcmp(gconstpointer str1, gconstpointer str2) { return strcmp(str1, str2); @@ -479,8 +439,11 @@ void ga_unset_frozen(GAState *s) ga_enable_logging(s); g_warning("logging re-enabled due to filesystem unfreeze"); if (s->deferred_options.pid_filepath) { - if (!ga_open_pidfile(s->deferred_options.pid_filepath)) { - g_warning("failed to create/open pid file"); + Error *err = NULL; + + if (!qemu_write_pidfile(s->deferred_options.pid_filepath, &err)) { + g_warning("%s", error_get_pretty(err)); + error_free(err); } s->deferred_options.pid_filepath = NULL; } @@ -515,8 +478,11 @@ static void become_daemon(const char *pidfile) } if (pidfile) { - if (!ga_open_pidfile(pidfile)) { - g_critical("failed to create pidfile"); + Error *err = NULL; + + if (!qemu_write_pidfile(pidfile, &err)) { + g_critical("%s", error_get_pretty(err)); + error_free(err); exit(EXIT_FAILURE); } } |