From 9cf5f8f7ff7b93473eabf21e8482bb8feace039a Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 14 Apr 2021 20:03:55 +0100 Subject: [PATCH] Use an OFD lock to prevent more than one fwupdtool process Fixes https://github.com/fwupd/fwupd/issues/3019 --- meson.build | 3 +++ src/fu-tool.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/meson.build b/meson.build index 48655a42f..c3e574683 100644 --- a/meson.build +++ b/meson.build @@ -349,6 +349,9 @@ endif if cc.has_header_symbol('locale.h', 'LC_MESSAGES') conf.set('HAVE_LC_MESSAGES', '1') endif +if cc.has_header_symbol('fcntl.h', 'F_WRLCK') + conf.set('HAVE_WRLCK', '1') +endif if cc.has_function('pwrite', args : '-D_XOPEN_SOURCE') conf.set('HAVE_PWRITE', '1') endif diff --git a/src/fu-tool.c b/src/fu-tool.c index be319cce1..55607deda 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -65,6 +65,7 @@ struct FuUtilPrivate { FwupdInstallFlags flags; gboolean show_all; gboolean disable_ssl_strict; + gint lock_fd; /* only valid in update and downgrade */ FuUtilOperation current_operation; FwupdDevice *current_device; @@ -262,6 +263,8 @@ fu_util_private_free (FuUtilPrivate *priv) g_object_unref (priv->progressbar); if (priv->context != NULL) g_option_context_free (priv->context); + if (priv->lock_fd != 0) + g_close (priv->lock_fd, NULL); g_free (priv->current_message); g_free (priv); } @@ -2781,6 +2784,48 @@ fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error) return fu_util_prompt_complete (priv->completion_flags, TRUE, error); } +static gboolean +fu_util_lock (FuUtilPrivate *priv, GError **error) +{ +#ifdef HAVE_WRLCK + struct flock lockp = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + }; + g_autofree gchar *localstatedir = NULL; + g_autofree gchar *lockfn = NULL; + + /* open file */ + localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR); + lockfn = g_build_filename (localstatedir, "run", "fwupdtool", NULL); + if (!fu_common_mkdir_parent (lockfn, error)) + return FALSE; + priv->lock_fd = g_open (lockfn, O_RDWR | O_CREAT, S_IRWXU); + if (priv->lock_fd < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to open %s", + lockfn); + return FALSE; + } + + /* write lock */ + if (fcntl (priv->lock_fd, F_OFD_SETLK, &lockp) < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "another instance has locked %s", + lockfn); + return FALSE; + } + + /* success */ + g_debug ("locked %s", lockfn); +#endif + return TRUE; +} + int main (int argc, char *argv[]) { @@ -2873,6 +2918,13 @@ main (int argc, char *argv[]) bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); + /* ensure single instance */ + if (!fu_util_lock (priv, &error)) { + /* TRANSLATORS: another fwupdtool instance is already running */ + g_print ("%s: %s\n", _("Failed to lock"), error->message); + return EXIT_FAILURE; + } + #ifdef HAVE_GETUID /* ensure root user */ if (interactive && (getuid () != 0 || geteuid () != 0))