diff --git a/doc/ostree-sections.txt b/doc/ostree-sections.txt index b1b86f01..2cfc8fa6 100644 --- a/doc/ostree-sections.txt +++ b/doc/ostree-sections.txt @@ -368,6 +368,9 @@ ostree_sysroot_new_default ostree_sysroot_get_path ostree_sysroot_load ostree_sysroot_lock +ostree_sysroot_try_lock +ostree_sysroot_lock_async +ostree_sysroot_lock_finish ostree_sysroot_unlock ostree_sysroot_get_fd ostree_sysroot_ensure_initialized diff --git a/libglnx b/libglnx index cf8ae27b..900b25f7 160000 --- a/libglnx +++ b/libglnx @@ -1 +1 @@ -Subproject commit cf8ae27bab717621f1edf8ab6ae3dd89da4d8d4f +Subproject commit 900b25f7018878ab64bd04751d8f15c6d83ba823 diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index bdd67917..ebcb6329 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -1170,6 +1170,55 @@ ostree_sysroot_lock (OstreeSysroot *self, LOCK_EX, &self->lock, error); } +/** + * ostree_sysroot_try_lock: + * @self: Self + * @out_acquired: (out): Whether or not the lock has been acquired + * @error: Error + * + * Try to acquire an exclusive multi-process write lock for @self. If + * another process holds the lock, this function will return + * immediately, setting @out_acquired to %FALSE, and returning %TRUE + * (and no error). + * + * Release the lock with ostree_sysroot_unlock(). The lock will also + * be released if @self is deallocated. + */ +gboolean +ostree_sysroot_try_lock (OstreeSysroot *self, + gboolean *out_acquired, + GError **error) +{ + gboolean ret = FALSE; + GError *local_error = NULL; + + if (!ensure_sysroot_fd (self, error)) + goto out; + + /* Note use of LOCK_NB */ + if (!glnx_make_lock_file (self->sysroot_fd, OSTREE_SYSROOT_LOCKFILE, + LOCK_EX | LOCK_NB, &self->lock, &local_error)) + { + if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) + { + *out_acquired = FALSE; + } + else + { + g_propagate_error (error, local_error); + goto out; + } + } + else + { + *out_acquired = TRUE; + } + + ret = TRUE; + out: + return ret; +} + /** * ostree_sysroot_unlock: * @self: Self @@ -1184,6 +1233,64 @@ ostree_sysroot_unlock (OstreeSysroot *self) glnx_release_lock_file (&self->lock); } +static void +lock_in_thread (GTask *task, + gpointer source, + gpointer task_data, + GCancellable *cancellable) +{ + GError *local_error = NULL; + OstreeSysroot *self = source; + + if (!ostree_sysroot_lock (self, &local_error)) + goto out; + + if (g_cancellable_set_error_if_cancelled (cancellable, &local_error)) + ostree_sysroot_unlock (self); + + out: + if (local_error) + g_task_return_error (task, local_error); + else + g_task_return_boolean (task, TRUE); +} + +/** + * ostree_sysroot_lock_async: + * @self: Self + * @cancellable: Cancellable + * @callback: Callback + * @user_data: User data + * + * An asynchronous version of ostree_sysroot_lock(). + */ +void +ostree_sysroot_lock_async (OstreeSysroot *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data); + g_task_run_in_thread (task, lock_in_thread); +} + +/** + * ostree_sysroot_lock_finish: + * @self: Self + * @result: Result + * @error: Error + * + * Call when ostree_sysroot_lock_async() is ready. + */ +gboolean +ostree_sysroot_lock_finish (OstreeSysroot *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + return g_task_propagate_boolean ((GTask*)result, error); +} + /** * ostree_sysroot_simple_write_deployment: * @sysroot: Sysroot diff --git a/src/libostree/ostree-sysroot.h b/src/libostree/ostree-sysroot.h index e7f6e482..ce128bba 100644 --- a/src/libostree/ostree-sysroot.h +++ b/src/libostree/ostree-sysroot.h @@ -63,6 +63,16 @@ char *ostree_sysroot_get_deployment_dirpath (OstreeSysroot *self, GFile * ostree_sysroot_get_deployment_origin_path (GFile *deployment_path); gboolean ostree_sysroot_lock (OstreeSysroot *self, GError **error); +gboolean ostree_sysroot_try_lock (OstreeSysroot *self, + gboolean *out_acquired, + GError **error); +void ostree_sysroot_lock_async (OstreeSysroot *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean ostree_sysroot_lock_finish (OstreeSysroot *self, + GAsyncResult *result, + GError **error); void ostree_sysroot_unlock (OstreeSysroot *self); gboolean ostree_sysroot_cleanup (OstreeSysroot *self, diff --git a/src/ostree/ot-admin-builtin-deploy.c b/src/ostree/ot-admin-builtin-deploy.c index b18b3fd3..78d60bb5 100644 --- a/src/ostree/ot-admin-builtin-deploy.c +++ b/src/ostree/ot-admin-builtin-deploy.c @@ -79,7 +79,7 @@ ot_admin_builtin_deploy (int argc, char **argv, GCancellable *cancellable, GErro refspec = argv[1]; - if (!ostree_sysroot_lock (sysroot, error)) + if (!ot_admin_sysroot_lock (sysroot, error)) goto out; if (!ostree_sysroot_load (sysroot, cancellable, error)) diff --git a/src/ostree/ot-admin-functions.c b/src/ostree/ot-admin-functions.c index a29d1558..c818a00c 100644 --- a/src/ostree/ot-admin-functions.c +++ b/src/ostree/ot-admin-functions.c @@ -96,3 +96,62 @@ ot_admin_get_indexed_deployment (OstreeSysroot *sysroot, return g_object_ref (current_deployments->pdata[index]); } + +struct ContextState { + GMainContext *mainctx; + gboolean running; +}; + +static gboolean +on_sysroot_lock_timeout (gpointer user_data) +{ + g_print ("Waiting for sysroot lock...\n"); + return TRUE; +} + +static void +on_sysroot_lock_acquired (OstreeSysroot *sysroot, + GAsyncResult *result, + struct ContextState *state) +{ + state->running = FALSE; + g_main_context_wakeup (state->mainctx); +} + +gboolean +ot_admin_sysroot_lock (OstreeSysroot *sysroot, + GError **error) +{ + gboolean ret = FALSE; + gboolean acquired; + struct ContextState state = { + .mainctx = g_main_context_new (), + .running = TRUE, + }; + + g_main_context_push_thread_default (state.mainctx); + + if (!ostree_sysroot_try_lock (sysroot, &acquired, error)) + goto out; + + if (!acquired) + { + GSource *timeout_src = g_timeout_source_new_seconds (3); + g_source_set_callback (timeout_src, (GSourceFunc)on_sysroot_lock_timeout, &state, NULL); + g_source_attach (timeout_src, state.mainctx); + g_source_unref (timeout_src); + + on_sysroot_lock_timeout (&state); + + ostree_sysroot_lock_async (sysroot, NULL, (GAsyncReadyCallback)on_sysroot_lock_acquired, &state); + + while (state.running) + g_main_context_iteration (state.mainctx, TRUE); + } + + ret = TRUE; + out: + g_main_context_pop_thread_default (state.mainctx); + g_main_context_unref (state.mainctx); + return ret; +} diff --git a/src/ostree/ot-admin-functions.h b/src/ostree/ot-admin-functions.h index 49b7039d..67164ca5 100644 --- a/src/ostree/ot-admin-functions.h +++ b/src/ostree/ot-admin-functions.h @@ -41,5 +41,9 @@ ot_admin_get_indexed_deployment (OstreeSysroot *sysroot, int index, GError **error); +gboolean +ot_admin_sysroot_lock (OstreeSysroot *sysroot, + GError **error); + G_END_DECLS