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;
|
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;
|
NMHostnameManagerPrivate *priv;
|
||||||
|
const char *hostname;
|
||||||
gs_free char *hostname_eol = NULL;
|
gs_free char *hostname_eol = NULL;
|
||||||
gboolean ret;
|
gboolean ret;
|
||||||
gs_free_error GError *error = NULL;
|
gs_free_error GError *error = NULL;
|
||||||
@@ -337,23 +357,15 @@ nm_hostname_manager_write_hostname(NMHostnameManager *self, const char *hostname
|
|||||||
char *fcon_prev = NULL;
|
char *fcon_prev = NULL;
|
||||||
#endif
|
#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);
|
priv = NM_HOSTNAME_MANAGER_GET_PRIVATE(self);
|
||||||
|
|
||||||
if (priv->hostnamed_proxy) {
|
nm_assert(!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);
|
|
||||||
|
|
||||||
return !error;
|
hostname = g_task_get_task_data(task);
|
||||||
}
|
|
||||||
|
|
||||||
/* If the hostname file is a symbolic link, follow it to find where the
|
/* 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
|
* 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
|
#endif
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
_LOGW("could not save hostname to %s: %s", file, error->message);
|
g_task_return_new_error(task,
|
||||||
return FALSE;
|
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);
|
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,
|
void nm_hostname_manager_set_transient_hostname(NMHostnameManager *self,
|
||||||
const char *hostname,
|
const char *hostname,
|
||||||
|
@@ -376,6 +376,8 @@ typedef struct {
|
|||||||
|
|
||||||
GHashTable *sce_idx;
|
GHashTable *sce_idx;
|
||||||
|
|
||||||
|
GCancellable *shutdown_cancellable;
|
||||||
|
|
||||||
CList sce_dirty_lst_head;
|
CList sce_dirty_lst_head;
|
||||||
|
|
||||||
CList connections_lst_head;
|
CList connections_lst_head;
|
||||||
@@ -3466,46 +3468,93 @@ load_plugins(NMSettings *self, const char *const *plugins, GError **error)
|
|||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static void
|
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);
|
NMSettings *self = NM_SETTINGS(user_data);
|
||||||
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self);
|
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self);
|
||||||
NMAuthCallResult result;
|
NMAuthCallResult result;
|
||||||
GError *error = NULL;
|
gs_free char *hostname = NULL;
|
||||||
const char *hostname;
|
|
||||||
|
|
||||||
nm_assert(G_IS_DBUS_METHOD_INVOCATION(context));
|
nm_assert(G_IS_DBUS_METHOD_INVOCATION(context));
|
||||||
|
|
||||||
c_list_unlink(nm_auth_chain_parent_lst_list(chain));
|
c_list_unlink(nm_auth_chain_parent_lst_list(chain));
|
||||||
|
|
||||||
result = nm_auth_chain_get_result(chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME);
|
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) {
|
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,
|
||||||
NM_SETTINGS_ERROR_PERMISSION_DENIED,
|
hostname ?: "",
|
||||||
NM_UTILS_ERROR_MSG_INSUFF_PRIV);
|
FALSE,
|
||||||
goto done;
|
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);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nm_hostname_manager_write_hostname(priv->hostname_manager, hostname)) {
|
if (!priv->shutdown_cancellable) {
|
||||||
error = g_error_new_literal(NM_SETTINGS_ERROR,
|
/* we only keep a weak pointer on the cancellable, so we can
|
||||||
NM_SETTINGS_ERROR_FAILED,
|
* wrap it up after use. We almost never require this, because
|
||||||
"Saving the hostname failed.");
|
* 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_hostname_manager_write_hostname(
|
||||||
nm_audit_log_control_op(NM_AUDIT_OP_HOSTNAME_SAVE,
|
priv->hostname_manager,
|
||||||
hostname,
|
hostname,
|
||||||
!error,
|
priv->shutdown_cancellable,
|
||||||
nm_auth_chain_get_subject(chain),
|
_save_hostname_write_cb,
|
||||||
error ? error->message : NULL);
|
nm_utils_user_data_pack(self,
|
||||||
|
context,
|
||||||
if (error)
|
g_object_ref(nm_auth_chain_get_subject(chain)),
|
||||||
g_dbus_method_invocation_take_error(context, error);
|
hostname,
|
||||||
else
|
g_object_ref(priv->shutdown_cancellable)));
|
||||||
g_dbus_method_invocation_return_value(context, NULL);
|
g_steal_pointer(&hostname);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -3533,7 +3582,7 @@ impl_settings_save_hostname(NMDBusObject *obj,
|
|||||||
goto err;
|
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) {
|
if (!chain) {
|
||||||
error_code = NM_SETTINGS_ERROR_PERMISSION_DENIED;
|
error_code = NM_SETTINGS_ERROR_PERMISSION_DENIED;
|
||||||
error_reason = NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED;
|
error_reason = NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED;
|
||||||
@@ -4126,6 +4175,12 @@ dispose(GObject *object)
|
|||||||
g_clear_object(&priv->session_monitor);
|
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);
|
G_OBJECT_CLASS(nm_settings_parent_class)->dispose(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user