core/hostname: avoid blocking calls in NMHostnameManager setting static hostname
Of course, blocking and synchronous code is much simpler. But it's also fundamentally wrong to block while we talk to systemd-hostnamed. Refactor to use async operations.
This commit is contained in:
@@ -321,10 +321,30 @@ nm_hostname_manager_get_transient_hostname(NMHostnameManager *self, char **hostn
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_hostname_manager_write_hostname(NMHostnameManager *self, const char *hostname)
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
_write_hostname_dbus_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||
{
|
||||
gs_unref_object GTask *task = G_TASK(user_data);
|
||||
gs_unref_variant GVariant *res = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
res = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), result, &error);
|
||||
if (!res) {
|
||||
g_task_return_error(task, error);
|
||||
return;
|
||||
}
|
||||
g_task_return_boolean(task, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
_write_hostname_on_idle_cb(gpointer user_data, GCancellable *cancellable)
|
||||
{
|
||||
gs_unref_object GTask *task = G_TASK(user_data);
|
||||
NMHostnameManager *self;
|
||||
NMHostnameManagerPrivate *priv;
|
||||
const char *hostname;
|
||||
gs_free char *hostname_eol = NULL;
|
||||
gboolean ret;
|
||||
gs_free_error GError *error = NULL;
|
||||
@@ -337,23 +357,15 @@ nm_hostname_manager_write_hostname(NMHostnameManager *self, const char *hostname
|
||||
char *fcon_prev = NULL;
|
||||
#endif
|
||||
|
||||
g_return_val_if_fail(NM_IS_HOSTNAME_MANAGER(self), FALSE);
|
||||
if (g_task_return_error_if_cancelled(task))
|
||||
return;
|
||||
|
||||
self = g_task_get_source_object(task);
|
||||
priv = NM_HOSTNAME_MANAGER_GET_PRIVATE(self);
|
||||
|
||||
if (priv->hostnamed_proxy) {
|
||||
var = g_dbus_proxy_call_sync(priv->hostnamed_proxy,
|
||||
"SetStaticHostname",
|
||||
g_variant_new("(sb)", hostname ?: "", FALSE),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
&error);
|
||||
if (error)
|
||||
_LOGW("could not set hostname: %s", error->message);
|
||||
nm_assert(!priv->hostnamed_proxy);
|
||||
|
||||
return !error;
|
||||
}
|
||||
hostname = g_task_get_task_data(task);
|
||||
|
||||
/* If the hostname file is a symbolic link, follow it to find where the
|
||||
* real file is located, otherwise g_file_set_contents will attempt to
|
||||
@@ -409,11 +421,62 @@ nm_hostname_manager_write_hostname(NMHostnameManager *self, const char *hostname
|
||||
#endif
|
||||
|
||||
if (!ret) {
|
||||
_LOGW("could not save hostname to %s: %s", file, error->message);
|
||||
return FALSE;
|
||||
g_task_return_new_error(task,
|
||||
NM_UTILS_ERROR,
|
||||
NM_UTILS_ERROR_UNKNOWN,
|
||||
"could not save hostname to %s: %s",
|
||||
file,
|
||||
error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
g_task_return_boolean(task, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
nm_hostname_manager_write_hostname(NMHostnameManager *self,
|
||||
const char *hostname,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
NMHostnameManagerPrivate *priv;
|
||||
GTask *task;
|
||||
|
||||
g_return_if_fail(NM_IS_HOSTNAME_MANAGER(self));
|
||||
|
||||
priv = NM_HOSTNAME_MANAGER_GET_PRIVATE(self);
|
||||
|
||||
task =
|
||||
nm_g_task_new(self, cancellable, nm_hostname_manager_write_hostname, callback, user_data);
|
||||
|
||||
g_task_set_task_data(task, g_strdup(hostname), g_free);
|
||||
|
||||
if (priv->hostnamed_proxy) {
|
||||
g_dbus_proxy_call(priv->hostnamed_proxy,
|
||||
"SetStaticHostname",
|
||||
g_variant_new("(sb)", hostname ?: "", FALSE),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
15000,
|
||||
cancellable,
|
||||
_write_hostname_dbus_cb,
|
||||
task);
|
||||
return;
|
||||
}
|
||||
|
||||
nm_utils_invoke_on_idle(cancellable, _write_hostname_on_idle_cb, task);
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_hostname_manager_write_hostname_finish(NMHostnameManager *self,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail(NM_IS_HOSTNAME_MANAGER(self), FALSE);
|
||||
g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_hostname_manager_write_hostname),
|
||||
FALSE);
|
||||
|
||||
return g_task_propagate_boolean(G_TASK(result), error);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@@ -36,7 +36,15 @@ NMHostnameManager *nm_hostname_manager_get(void);
|
||||
|
||||
const char *nm_hostname_manager_get_static_hostname(NMHostnameManager *self);
|
||||
|
||||
gboolean nm_hostname_manager_write_hostname(NMHostnameManager *self, const char *hostname);
|
||||
void nm_hostname_manager_write_hostname(NMHostnameManager *self,
|
||||
const char *hostname,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean nm_hostname_manager_write_hostname_finish(NMHostnameManager *self,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
void nm_hostname_manager_set_transient_hostname(NMHostnameManager *self,
|
||||
const char *hostname,
|
||||
|
@@ -376,6 +376,8 @@ typedef struct {
|
||||
|
||||
GHashTable *sce_idx;
|
||||
|
||||
GCancellable *shutdown_cancellable;
|
||||
|
||||
CList sce_dirty_lst_head;
|
||||
|
||||
CList connections_lst_head;
|
||||
@@ -3466,46 +3468,93 @@ load_plugins(NMSettings *self, const char *const *plugins, GError **error)
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
pk_hostname_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data)
|
||||
_save_hostname_write_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||
{
|
||||
NMSettings *self;
|
||||
GDBusMethodInvocation *context;
|
||||
gs_free char *hostname = NULL;
|
||||
gs_unref_object NMAuthSubject *auth_subject = NULL;
|
||||
gs_unref_object GCancellable *cancellable = NULL;
|
||||
gs_free_error GError *error = NULL;
|
||||
|
||||
nm_utils_user_data_unpack(user_data, &self, &context, &auth_subject, &hostname, &cancellable);
|
||||
|
||||
nm_hostname_manager_write_hostname_finish(NM_HOSTNAME_MANAGER(source), result, &error);
|
||||
|
||||
nm_audit_log_control_op(NM_AUDIT_OP_HOSTNAME_SAVE,
|
||||
hostname ?: "",
|
||||
!error,
|
||||
auth_subject,
|
||||
error ? error->message : NULL);
|
||||
|
||||
if (nm_utils_error_is_cancelled(error)) {
|
||||
g_dbus_method_invocation_return_error_literal(context,
|
||||
NM_SETTINGS_ERROR,
|
||||
NM_SETTINGS_ERROR_FAILED,
|
||||
"NetworkManager is shutting down");
|
||||
return;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
g_dbus_method_invocation_take_error(context,
|
||||
g_error_new(NM_SETTINGS_ERROR,
|
||||
NM_SETTINGS_ERROR_FAILED,
|
||||
"Saving the hostname failed: %s",
|
||||
error->message));
|
||||
return;
|
||||
}
|
||||
|
||||
g_dbus_method_invocation_return_value(context, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
_save_hostname_pk_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data)
|
||||
{
|
||||
NMSettings *self = NM_SETTINGS(user_data);
|
||||
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self);
|
||||
NMAuthCallResult result;
|
||||
GError *error = NULL;
|
||||
const char *hostname;
|
||||
gs_free char *hostname = NULL;
|
||||
|
||||
nm_assert(G_IS_DBUS_METHOD_INVOCATION(context));
|
||||
|
||||
c_list_unlink(nm_auth_chain_parent_lst_list(chain));
|
||||
|
||||
result = nm_auth_chain_get_result(chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME);
|
||||
hostname = nm_auth_chain_get_data(chain, "hostname");
|
||||
hostname = nm_auth_chain_steal_data(chain, "hostname");
|
||||
|
||||
/* If our NMSettingsConnection is already gone, do nothing */
|
||||
if (result != NM_AUTH_CALL_RESULT_YES) {
|
||||
error = g_error_new_literal(NM_SETTINGS_ERROR,
|
||||
nm_audit_log_control_op(NM_AUDIT_OP_HOSTNAME_SAVE,
|
||||
hostname ?: "",
|
||||
FALSE,
|
||||
nm_auth_chain_get_subject(chain),
|
||||
NM_UTILS_ERROR_MSG_INSUFF_PRIV);
|
||||
g_dbus_method_invocation_return_error_literal(context,
|
||||
NM_SETTINGS_ERROR,
|
||||
NM_SETTINGS_ERROR_PERMISSION_DENIED,
|
||||
NM_UTILS_ERROR_MSG_INSUFF_PRIV);
|
||||
goto done;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nm_hostname_manager_write_hostname(priv->hostname_manager, hostname)) {
|
||||
error = g_error_new_literal(NM_SETTINGS_ERROR,
|
||||
NM_SETTINGS_ERROR_FAILED,
|
||||
"Saving the hostname failed.");
|
||||
if (!priv->shutdown_cancellable) {
|
||||
/* we only keep a weak pointer on the cancellable, so we can
|
||||
* wrap it up after use. We almost never require this, because
|
||||
* SaveHostname is almost never called. */
|
||||
priv->shutdown_cancellable = g_cancellable_new();
|
||||
g_object_add_weak_pointer(G_OBJECT(priv->shutdown_cancellable),
|
||||
(gpointer *) &priv->shutdown_cancellable);
|
||||
}
|
||||
|
||||
done:
|
||||
nm_audit_log_control_op(NM_AUDIT_OP_HOSTNAME_SAVE,
|
||||
nm_hostname_manager_write_hostname(
|
||||
priv->hostname_manager,
|
||||
hostname,
|
||||
!error,
|
||||
nm_auth_chain_get_subject(chain),
|
||||
error ? error->message : NULL);
|
||||
|
||||
if (error)
|
||||
g_dbus_method_invocation_take_error(context, error);
|
||||
else
|
||||
g_dbus_method_invocation_return_value(context, NULL);
|
||||
priv->shutdown_cancellable,
|
||||
_save_hostname_write_cb,
|
||||
nm_utils_user_data_pack(self,
|
||||
context,
|
||||
g_object_ref(nm_auth_chain_get_subject(chain)),
|
||||
hostname,
|
||||
g_object_ref(priv->shutdown_cancellable)));
|
||||
g_steal_pointer(&hostname);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -3533,7 +3582,7 @@ impl_settings_save_hostname(NMDBusObject *obj,
|
||||
goto err;
|
||||
}
|
||||
|
||||
chain = nm_auth_chain_new_context(invocation, pk_hostname_cb, self);
|
||||
chain = nm_auth_chain_new_context(invocation, _save_hostname_pk_cb, self);
|
||||
if (!chain) {
|
||||
error_code = NM_SETTINGS_ERROR_PERMISSION_DENIED;
|
||||
error_reason = NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED;
|
||||
@@ -4126,6 +4175,12 @@ dispose(GObject *object)
|
||||
g_clear_object(&priv->session_monitor);
|
||||
}
|
||||
|
||||
if (priv->shutdown_cancellable) {
|
||||
g_object_remove_weak_pointer(G_OBJECT(priv->shutdown_cancellable),
|
||||
(gpointer *) &priv->shutdown_cancellable);
|
||||
g_cancellable_cancel(g_steal_pointer(&priv->shutdown_cancellable));
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS(nm_settings_parent_class)->dispose(object);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user