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>
This commit is contained in:
parent
71bb4ce1b5
commit
9e6bdef224
@ -448,7 +448,8 @@ bool qemu_has_ofd_lock(void);
|
||||
#define FMT_pid "%d"
|
||||
#endif
|
||||
|
||||
int qemu_create_pidfile(const char *filename);
|
||||
bool qemu_write_pidfile(const char *pidfile, Error **errp);
|
||||
|
||||
int qemu_get_thread_id(void);
|
||||
|
||||
#ifndef CONFIG_IOVEC
|
||||
|
24
os-posix.c
24
os-posix.c
@ -344,30 +344,6 @@ void os_set_line_buffering(void)
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
}
|
||||
|
||||
int qemu_create_pidfile(const char *filename)
|
||||
{
|
||||
char buffer[128];
|
||||
int len;
|
||||
int fd;
|
||||
|
||||
fd = qemu_open(filename, O_RDWR | O_CREAT, 0600);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (lockf(fd, F_TLOCK, 0) == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
len = snprintf(buffer, sizeof(buffer), FMT_pid "\n", getpid());
|
||||
if (write(fd, buffer, len) != len) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* keep pidfile open & locked forever */
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_daemonized(void)
|
||||
{
|
||||
return daemonize;
|
||||
|
25
os-win32.c
25
os-win32.c
@ -97,28 +97,3 @@ int os_parse_cmd_args(int index, const char *optarg)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int qemu_create_pidfile(const char *filename)
|
||||
{
|
||||
char buffer[128];
|
||||
int len;
|
||||
HANDLE file;
|
||||
OVERLAPPED overlap;
|
||||
BOOL ret;
|
||||
memset(&overlap, 0, sizeof(overlap));
|
||||
|
||||
file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
||||
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
len = snprintf(buffer, sizeof(buffer), "%d\n", getpid());
|
||||
ret = WriteFile(file, (LPCVOID)buffer, (DWORD)len,
|
||||
NULL, &overlap);
|
||||
CloseHandle(file);
|
||||
if (ret == 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
54
qga/main.c
54
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);
|
||||
}
|
||||
}
|
||||
|
@ -117,39 +117,6 @@ QEMU_COPYRIGHT "\n"
|
||||
, name);
|
||||
}
|
||||
|
||||
static void write_pidfile(void)
|
||||
{
|
||||
int pidfd;
|
||||
char pidstr[32];
|
||||
|
||||
pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
|
||||
if (pidfd == -1) {
|
||||
error_report("Cannot open pid file, %s", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (lockf(pidfd, F_TLOCK, 0)) {
|
||||
error_report("Cannot lock pid file, %s", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
if (ftruncate(pidfd, 0)) {
|
||||
error_report("Failed to truncate pid file");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
|
||||
if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
|
||||
error_report("Failed to write pid file");
|
||||
goto fail;
|
||||
}
|
||||
return;
|
||||
|
||||
fail:
|
||||
unlink(pidfile);
|
||||
close(pidfd);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* SG_IO support */
|
||||
|
||||
typedef struct PRHelperSGIOData {
|
||||
@ -1080,8 +1047,11 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (daemonize || pidfile_specified)
|
||||
write_pidfile();
|
||||
if ((daemonize || pidfile_specified) &&
|
||||
!qemu_write_pidfile(pidfile, &local_err)) {
|
||||
error_report_err(local_err);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LIBCAP
|
||||
if (drop_privileges() < 0) {
|
||||
|
@ -88,6 +88,74 @@ int qemu_daemon(int nochdir, int noclose)
|
||||
return daemon(nochdir, noclose);
|
||||
}
|
||||
|
||||
bool qemu_write_pidfile(const char *path, Error **errp)
|
||||
{
|
||||
int fd;
|
||||
char pidstr[32];
|
||||
|
||||
while (1) {
|
||||
struct stat a, b;
|
||||
|
||||
fd = qemu_open(path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1) {
|
||||
error_setg_errno(errp, errno, "Cannot open pid file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fstat(fd, &b) < 0) {
|
||||
error_setg_errno(errp, errno, "Cannot stat file");
|
||||
goto fail_close;
|
||||
}
|
||||
|
||||
if (lockf(fd, F_TLOCK, 0) < 0) {
|
||||
error_setg_errno(errp, errno, "Cannot lock pid file");
|
||||
goto fail_close;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now make sure the path we locked is the same one that now
|
||||
* exists on the filesystem.
|
||||
*/
|
||||
if (stat(path, &a) < 0) {
|
||||
/*
|
||||
* PID file disappeared, someone else must be racing with
|
||||
* us, so try again.
|
||||
*/
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a.st_ino == b.st_ino) {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* PID file was recreated, someone else must be racing with
|
||||
* us, so try again.
|
||||
*/
|
||||
close(fd);
|
||||
}
|
||||
|
||||
if (ftruncate(fd, 0) < 0) {
|
||||
error_setg_errno(errp, errno, "Failed to truncate pid file");
|
||||
goto fail_unlink;
|
||||
}
|
||||
|
||||
snprintf(pidstr, sizeof(pidstr), FMT_pid "\n", getpid());
|
||||
if (write(fd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
|
||||
error_setg(errp, "Failed to write pid file");
|
||||
goto fail_unlink;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
fail_unlink:
|
||||
unlink(path);
|
||||
fail_close:
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
void *qemu_oom_check(void *ptr)
|
||||
{
|
||||
if (ptr == NULL) {
|
||||
|
@ -776,3 +776,30 @@ ssize_t qemu_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags,
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool qemu_write_pidfile(const char *filename, Error **errp)
|
||||
{
|
||||
char buffer[128];
|
||||
int len;
|
||||
HANDLE file;
|
||||
OVERLAPPED overlap;
|
||||
BOOL ret;
|
||||
memset(&overlap, 0, sizeof(overlap));
|
||||
|
||||
file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
||||
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
error_setg(errp, "Failed to create PID file");
|
||||
return false;
|
||||
}
|
||||
len = snprintf(buffer, sizeof(buffer), FMT_pid "\n", (pid_t)getpid());
|
||||
ret = WriteFile(file, (LPCVOID)buffer, (DWORD)len,
|
||||
NULL, &overlap);
|
||||
CloseHandle(file);
|
||||
if (ret == 0) {
|
||||
error_setg(errp, "Failed to write PID file");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
4
vl.c
4
vl.c
@ -3906,8 +3906,8 @@ int main(int argc, char **argv, char **envp)
|
||||
os_daemonize();
|
||||
rcu_disable_atfork();
|
||||
|
||||
if (pid_file && qemu_create_pidfile(pid_file) != 0) {
|
||||
error_report("could not acquire pid file: %s", strerror(errno));
|
||||
if (pid_file && !qemu_write_pidfile(pid_file, &err)) {
|
||||
error_reportf_err(err, "cannot create PID file: ");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user