diff options
author | Dan Williams <dcbw@redhat.com> | 2015-01-21 18:36:48 -0600 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2015-01-21 18:36:48 -0600 |
commit | 3d25613327b35595f219ae59af734eeb146269e3 (patch) | |
tree | a62822d3041b7d24c65a279aa810d9120d82bf86 | |
parent | 6bb0cffba32ec925743c4d491b75474d4e855198 (diff) | |
parent | a6fedb1a26ea9109d26c92f7366dcf2a9ecab01b (diff) | |
download | NetworkManager-3d25613327b35595f219ae59af734eeb146269e3.tar.gz |
merge: stop pppd before notifying ModemManager to disconnect (bgo #734347)
-rw-r--r-- | src/devices/nm-device.c | 71 | ||||
-rw-r--r-- | src/devices/nm-device.h | 11 | ||||
-rw-r--r-- | src/devices/wwan/nm-device-modem.c | 46 | ||||
-rw-r--r-- | src/devices/wwan/nm-modem-broadband.c | 95 | ||||
-rw-r--r-- | src/devices/wwan/nm-modem.c | 213 | ||||
-rw-r--r-- | src/devices/wwan/nm-modem.h | 20 | ||||
-rw-r--r-- | src/devices/wwan/wwan-exports.ver | 2 | ||||
-rw-r--r-- | src/ppp-manager/nm-ppp-manager.c | 111 | ||||
-rw-r--r-- | src/ppp-manager/nm-ppp-manager.h | 8 |
9 files changed, 523 insertions, 54 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 4c514b4a15..f11266556d 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -205,6 +205,8 @@ typedef struct { gboolean is_nm_owned; /* whether the device is a device owned and created by NM */ DeleteOnDeactivateData *delete_on_deactivate_data; /* data for scheduled cleanup when deleting link (g_idle_add) */ + GCancellable *deactivating_cancellable; + guint32 ip4_address; NMActRequest * queued_act_request; @@ -7389,6 +7391,69 @@ ip6_managed_setup (NMDevice *self) } static void +deactivate_async_ready (NMDevice *self, + GAsyncResult *res, + gpointer user_data) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDeviceStateReason reason = GPOINTER_TO_UINT (user_data); + GError *error = NULL; + + NM_DEVICE_GET_CLASS (self)->deactivate_async_finish (self, res, &error); + + /* If operation cancelled, just return */ + if ( g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) + || (priv->deactivating_cancellable && g_cancellable_is_cancelled (priv->deactivating_cancellable))) { + nm_log_warn (LOGD_DEVICE, "Deactivation (%s) cancelled", + nm_device_get_iface (self)); + } + /* In every other case, transition to the DISCONNECTED state */ + else { + if (error) + nm_log_warn (LOGD_DEVICE, "Deactivation (%s) failed: %s", + nm_device_get_iface (self), + error->message); + nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason); + } + + g_clear_object (&priv->deactivating_cancellable); + g_clear_error (&error); +} + +static void +deactivate_dispatcher_complete (guint call_id, gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDeviceStateReason reason; + + g_return_if_fail (call_id == priv->dispatcher.call_id); + g_return_if_fail (priv->dispatcher.post_state == NM_DEVICE_STATE_DISCONNECTED); + + reason = priv->dispatcher.post_state_reason; + + priv->dispatcher.call_id = 0; + priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; + priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; + + if (priv->deactivating_cancellable) { + g_warn_if_reached (); + g_cancellable_cancel (priv->deactivating_cancellable); + g_clear_object (&priv->deactivating_cancellable); + } + + if ( NM_DEVICE_GET_CLASS (self)->deactivate_async + && NM_DEVICE_GET_CLASS (self)->deactivate_async_finish) { + priv->deactivating_cancellable = g_cancellable_new (); + NM_DEVICE_GET_CLASS (self)->deactivate_async (self, + priv->deactivating_cancellable, + (GAsyncReadyCallback) deactivate_async_ready, + GUINT_TO_POINTER (reason)); + } else + nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason); +} + +static void _set_state_full (NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, @@ -7432,6 +7497,8 @@ _set_state_full (NMDevice *self, nm_device_queued_state_clear (self); dispatcher_cleanup (self); + if (priv->deactivating_cancellable) + g_cancellable_cancel (priv->deactivating_cancellable); /* Cache the activation request for the dispatcher */ req = priv->act_request ? g_object_ref (priv->act_request) : NULL; @@ -7556,11 +7623,11 @@ _set_state_full (NMDevice *self, if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_DOWN, nm_act_request_get_connection (req), self, - dispatcher_complete_proceed_state, + deactivate_dispatcher_complete, self, &priv->dispatcher.call_id)) { /* Just proceed on errors */ - dispatcher_complete_proceed_state (0, self); + deactivate_dispatcher_complete (0, self); } } break; diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index b3855c7446..e6f07f2ef3 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -23,6 +23,7 @@ #define __NETWORKMANAGER_DEVICE_H__ #include <glib-object.h> +#include <gio/gio.h> #include <dbus/dbus-glib.h> #include <netinet/in.h> @@ -174,6 +175,16 @@ typedef struct { void (* ip4_config_pre_commit) (NMDevice *self, NMIP4Config *config); void (* ip6_config_pre_commit) (NMDevice *self, NMIP6Config *config); + /* Async deactivating (in the DEACTIVATING phase) */ + void (* deactivate_async) (NMDevice *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* deactivate_async_finish) (NMDevice *self, + GAsyncResult *res, + GError **error); + + /* Sync deactivating (in the DISCONNECTED phase) */ void (* deactivate) (NMDevice *self); gboolean (* spec_match_list) (NMDevice *self, const GSList *specs); diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index f681e59ca1..e876259a43 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -433,6 +433,50 @@ deactivate (NMDevice *device) nm_modem_deactivate (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device); } +/***********************************************************/ + +static gboolean +deactivate_async_finish (NMDevice *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +modem_deactivate_async_ready (NMModem *modem, + GAsyncResult *res, + GSimpleAsyncResult *simple) +{ + GError *error = NULL; + + if (!nm_modem_deactivate_async_finish (modem, res, &error)) + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static void +deactivate_async (NMDevice *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + + simple = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + deactivate_async); + nm_modem_deactivate_async (NM_DEVICE_MODEM_GET_PRIVATE (self)->modem, + self, + cancellable, + (GAsyncReadyCallback) modem_deactivate_async_ready, + simple); +} + +/***********************************************************/ + static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) { @@ -711,6 +755,8 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass) device_class->check_connection_compatible = check_connection_compatible; device_class->check_connection_available = check_connection_available; device_class->complete_connection = complete_connection; + device_class->deactivate_async = deactivate_async; + device_class->deactivate_async_finish = deactivate_async_finish; device_class->deactivate = deactivate; device_class->act_stage1_prepare = act_stage1_prepare; device_class->act_stage2_config = act_stage2_config; diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index f8239b7990..97d05ee2cd 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -203,7 +203,7 @@ connect_ready (MMModemSimple *simple_iface, if (g_dbus_error_is_remote_error (error)) g_dbus_error_strip_remote_error (error); - nm_log_warn (LOGD_MB, "(%s) failed to connect modem: %s", + nm_log_warn (LOGD_MB, "(%s): failed to connect modem: %s", nm_modem_get_uid (NM_MODEM (self)), error && error->message ? error->message : "(unknown)"); g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error)); @@ -225,7 +225,7 @@ connect_ready (MMModemSimple *simple_iface, if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN && ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) { - nm_log_warn (LOGD_MB, "(%s) failed to connect modem: invalid bearer IP configuration", + nm_log_warn (LOGD_MB, "(%s): failed to connect modem: invalid bearer IP configuration", nm_modem_get_uid (NM_MODEM (self))); error = g_error_new_literal (NM_DEVICE_ERROR, @@ -365,7 +365,7 @@ act_stage1_prepare (NMModem *_self, else if (MODEM_CAPS_3GPP2 (caps)) self->priv->connect_properties = create_cdma_connect_properties (connection); else { - nm_log_warn (LOGD_MB, "(%s) not a mobile broadband modem", + nm_log_warn (LOGD_MB, "(%s): not a mobile broadband modem", nm_modem_get_uid (NM_MODEM (self))); *reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED; return NM_ACT_STAGE_RETURN_FAILURE; @@ -566,7 +566,7 @@ set_power_state_low_ready (MMModem *modem, if (!mm_modem_set_power_state_finish (modem, result, &error)) { /* Log but ignore errors; not all modems support low power state */ - nm_log_dbg (LOGD_MB, "(%s) failed to set modem low power state: %s", + nm_log_dbg (LOGD_MB, "(%s): failed to set modem low power state: %s", nm_modem_get_uid (NM_MODEM (self)), error && error->message ? error->message : "(unknown)"); g_clear_error (&error); @@ -591,7 +591,7 @@ modem_disable_ready (MMModem *modem_iface, (GAsyncReadyCallback) set_power_state_low_ready, g_object_ref (self)); } else { - nm_log_warn (LOGD_MB, "(%s) failed to disable modem: %s", + nm_log_warn (LOGD_MB, "(%s): failed to disable modem: %s", nm_modem_get_uid (NM_MODEM (self)), error && error->message ? error->message : "(unknown)"); nm_modem_set_prev_state (NM_MODEM (self), "disable failed"); @@ -844,20 +844,47 @@ stage3_ip6_config_request (NMModem *_self, NMDeviceStateReason *reason) typedef struct { NMModemBroadband *self; + GSimpleAsyncResult *result; + GCancellable *cancellable; gboolean warn; -} SimpleDisconnectContext; +} DisconnectContext; static void -simple_disconnect_context_free (SimpleDisconnectContext *ctx) +disconnect_context_complete (DisconnectContext *ctx) { + g_simple_async_result_complete_in_idle (ctx->result); + if (ctx->cancellable) + g_object_unref (ctx->cancellable); + g_object_unref (ctx->result); g_object_unref (ctx->self); - g_slice_free (SimpleDisconnectContext, ctx); + g_slice_free (DisconnectContext, ctx); +} + +static gboolean +disconnect_context_complete_if_cancelled (DisconnectContext *ctx) +{ + GError *error = NULL; + + if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) { + g_simple_async_result_take_error (ctx->result, error); + disconnect_context_complete (ctx); + return TRUE; + } + return FALSE; +} + +static gboolean +disconnect_finish (NMModem *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); } static void simple_disconnect_ready (MMModemSimple *modem_iface, GAsyncResult *res, - SimpleDisconnectContext *ctx) + DisconnectContext *ctx) { GError *error = NULL; @@ -865,33 +892,48 @@ simple_disconnect_ready (MMModemSimple *modem_iface, if (ctx->warn) nm_log_warn (LOGD_MB, "(%s) failed to disconnect modem: %s", nm_modem_get_uid (NM_MODEM (ctx->self)), - error && error->message ? error->message : "(unknown)"); - g_clear_error (&error); + error->message); + g_simple_async_result_take_error (ctx->result, error); } - simple_disconnect_context_free (ctx); + disconnect_context_complete (ctx); } static void -disconnect (NMModem *modem, - gboolean warn) +disconnect (NMModem *self, + gboolean warn, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - NMModemBroadband *self = NM_MODEM_BROADBAND (modem); - SimpleDisconnectContext *ctx; - - if (!self->priv->simple_iface) - return; + DisconnectContext *ctx; - ctx = g_slice_new (SimpleDisconnectContext); + ctx = g_slice_new (DisconnectContext); ctx->self = g_object_ref (self); - + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + disconnect); /* Don't bother warning on FAILED since the modem is already gone */ ctx->warn = warn; + /* Setup cancellable */ + ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + if (disconnect_context_complete_if_cancelled (ctx)) + return; + + /* If no simple iface, we're done */ + if (!ctx->self->priv->simple_iface) { + disconnect_context_complete (ctx); + return; + } + + nm_log_dbg (LOGD_MB, "(%s): notifying ModemManager about the modem disconnection", + nm_modem_get_uid (NM_MODEM (ctx->self))); mm_modem_simple_disconnect ( ctx->self->priv->simple_iface, NULL, /* bearer path; if NULL given ALL get disconnected */ - NULL, /* cancellable */ + cancellable, (GAsyncReadyCallback)simple_disconnect_ready, ctx); } @@ -899,7 +941,7 @@ disconnect (NMModem *modem, /*****************************************************************************/ static void -deactivate (NMModem *_self, NMDevice *device) +deactivate_cleanup (NMModem *_self, NMDevice *device) { NMModemBroadband *self = NM_MODEM_BROADBAND (_self); @@ -913,7 +955,7 @@ deactivate (NMModem *_self, NMDevice *device) self->priv->pin_tries = 0; /* Chain up parent's */ - NM_MODEM_CLASS (nm_modem_broadband_parent_class)->deactivate (_self, device); + NM_MODEM_CLASS (nm_modem_broadband_parent_class)->deactivate_cleanup (_self, device); } /*****************************************************************************/ @@ -1027,7 +1069,7 @@ get_sim_ready (MMModem *modem, NULL); g_object_unref (new_sim); } else { - nm_log_warn (LOGD_MB, "(%s) failed to retrieve SIM object: %s", + nm_log_warn (LOGD_MB, "(%s): failed to retrieve SIM object: %s", nm_modem_get_uid (NM_MODEM (self)), error && error->message ? error->message : "(unknown)"); } @@ -1142,7 +1184,8 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass) modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start; modem_class->stage3_ip6_config_request = stage3_ip6_config_request; modem_class->disconnect = disconnect; - modem_class->deactivate = deactivate; + modem_class->disconnect_finish = disconnect_finish; + modem_class->deactivate_cleanup = deactivate_cleanup; modem_class->set_mm_enabled = set_mm_enabled; modem_class->get_user_pass = get_user_pass; modem_class->check_connection_compatible = check_connection_compatible; diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index ba1db8a4e0..e2f380ab31 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -175,23 +175,23 @@ nm_modem_set_mm_enabled (NMModem *self, NMModemState prev_state = priv->state; if (enabled && priv->state >= NM_MODEM_STATE_ENABLING) { - nm_log_dbg (LOGD_MB, "(%s) cannot enable modem: already enabled", + nm_log_dbg (LOGD_MB, "(%s): cannot enable modem: already enabled", nm_modem_get_uid (self)); return; } if (!enabled && priv->state <= NM_MODEM_STATE_DISABLING) { - nm_log_dbg (LOGD_MB, "(%s) cannot disable modem: already disabled", + nm_log_dbg (LOGD_MB, "(%s): cannot disable modem: already disabled", nm_modem_get_uid (self)); return; } if (priv->state <= NM_MODEM_STATE_INITIALIZING) { - nm_log_dbg (LOGD_MB, "(%s) cannot enable/disable modem: initializing or failed", + nm_log_dbg (LOGD_MB, "(%s): cannot enable/disable modem: initializing or failed", nm_modem_get_uid (self)); return; } else if (priv->state == NM_MODEM_STATE_LOCKED) { /* Don't try to enable if the modem is locked since that will fail */ - nm_log_warn (LOGD_MB, "(%s) cannot enable/disable modem: locked", + nm_log_warn (LOGD_MB, "(%s): cannot enable/disable modem: locked", nm_modem_get_uid (self)); /* Try to unlock the modem if it's being enabled */ @@ -464,7 +464,8 @@ ppp_stage3_ip_config_start (NMModem *self, /* Check if ModemManager requested a specific IP timeout to be used. If 0 reported, * use the default one (30s) */ if (priv->mm_ip_timeout > 0) { - nm_log_info (LOGD_PPP, "using modem-specified IP timeout: %u seconds", + nm_log_info (LOGD_PPP, "(%s): using modem-specified IP timeout: %u seconds", + nm_modem_get_uid (self), priv->mm_ip_timeout); ip_timeout = priv->mm_ip_timeout; } @@ -486,7 +487,8 @@ ppp_stage3_ip_config_start (NMModem *self, ret = NM_ACT_STAGE_RETURN_POSTPONE; } else { - nm_log_err (LOGD_PPP, "error starting PPP: (%d) %s", + nm_log_err (LOGD_PPP, "(%s): error starting PPP: (%d) %s", + nm_modem_get_uid (self), error ? error->code : -1, error && error->message ? error->message : "(unknown)"); g_error_free (error); @@ -705,7 +707,7 @@ modem_secrets_cb (NMActRequest *req, priv->secrets_id = 0; if (error) - nm_log_warn (LOGD_MB, "%s", error->message); + nm_log_warn (LOGD_MB, "(%s): %s", nm_modem_get_uid (self), error->message); g_signal_emit (self, signals[AUTH_RESULT], 0, error); } @@ -839,7 +841,7 @@ nm_modem_complete_connection (NMModem *self, /*****************************************************************************/ static void -deactivate (NMModem *self, NMDevice *device) +deactivate_cleanup (NMModem *self, NMDevice *device) { NMModemPrivate *priv; int ifindex; @@ -864,15 +866,17 @@ deactivate (NMModem *self, NMDevice *device) priv->ppp_manager = NULL; } - if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC || - priv->ip4_method == NM_MODEM_IP_METHOD_AUTO || - priv->ip6_method == NM_MODEM_IP_METHOD_STATIC || - priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) { - ifindex = nm_device_get_ip_ifindex (device); - if (ifindex > 0) { - nm_platform_route_flush (ifindex); - nm_platform_address_flush (ifindex); - nm_platform_link_set_down (ifindex); + if (device) { + if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC || + priv->ip4_method == NM_MODEM_IP_METHOD_AUTO || + priv->ip6_method == NM_MODEM_IP_METHOD_STATIC || + priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) { + ifindex = nm_device_get_ip_ifindex (device); + if (ifindex > 0) { + nm_platform_route_flush (ifindex); + nm_platform_address_flush (ifindex); + nm_platform_link_set_down (ifindex); + } } } priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN; @@ -884,10 +888,176 @@ deactivate (NMModem *self, NMDevice *device) /*****************************************************************************/ +typedef enum { + DEACTIVATE_CONTEXT_STEP_FIRST, + DEACTIVATE_CONTEXT_STEP_CLEANUP, + DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP, + DEACTIVATE_CONTEXT_STEP_MM_DISCONNECT, + DEACTIVATE_CONTEXT_STEP_LAST +} DeactivateContextStep; + +typedef struct { + NMModem *self; + NMDevice *device; + GCancellable *cancellable; + GSimpleAsyncResult *result; + DeactivateContextStep step; + NMPPPManager *ppp_manager; +} DeactivateContext; + +static void +deactivate_context_complete (DeactivateContext *ctx) +{ + if (ctx->ppp_manager) + g_object_unref (ctx->ppp_manager); + if (ctx->cancellable) + g_object_unref (ctx->cancellable); + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->result); + g_object_unref (ctx->device); + g_object_unref (ctx->self); + g_slice_free (DeactivateContext, ctx); +} + +gboolean +nm_modem_deactivate_async_finish (NMModem *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void deactivate_step (DeactivateContext *ctx); + +static void +disconnect_ready (NMModem *self, + GAsyncResult *res, + DeactivateContext *ctx) +{ + GError *error = NULL; + + if (!NM_MODEM_GET_CLASS (self)->disconnect_finish (self, res, &error)) { + g_simple_async_result_take_error (ctx->result, error); + deactivate_context_complete (ctx); + return; + } + + /* Go on */ + ctx->step++; + deactivate_step (ctx); +} + +static void +ppp_manager_stop_ready (NMPPPManager *ppp_manager, + GAsyncResult *res, + DeactivateContext *ctx) +{ + GError *error = NULL; + + if (!nm_ppp_manager_stop_finish (ppp_manager, res, &error)) { + nm_log_warn (LOGD_MB, "(%s): cannot stop PPP manager: %s", + nm_modem_get_uid (ctx->self), + error->message); + g_simple_async_result_take_error (ctx->result, error); + deactivate_context_complete (ctx); + return; + } + + /* Go on */ + ctx->step++; + deactivate_step (ctx); +} + +static void +deactivate_step (DeactivateContext *ctx) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (ctx->self); + GError *error = NULL; + + /* Check cancellable in each step */ + if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) { + g_simple_async_result_take_error (ctx->result, error); + deactivate_context_complete (ctx); + return; + } + + switch (ctx->step) { + case DEACTIVATE_CONTEXT_STEP_FIRST: + ctx->step++; + /* Fall down */ + + case DEACTIVATE_CONTEXT_STEP_CLEANUP: + /* Make sure we keep a ref to the PPP manager if there is one */ + if (priv->ppp_manager) + ctx->ppp_manager = g_object_ref (priv->ppp_manager); + /* Run cleanup */ + NM_MODEM_GET_CLASS (ctx->self)->deactivate_cleanup (ctx->self, ctx->device); + ctx->step++; + /* Fall down */ + + case DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP: + /* If we have a PPP manager, stop it */ + if (ctx->ppp_manager) { + nm_ppp_manager_stop (ctx->ppp_manager, + ctx->cancellable, + (GAsyncReadyCallback) ppp_manager_stop_ready, + ctx); + return; + } + ctx->step++; + /* Fall down */ + + case DEACTIVATE_CONTEXT_STEP_MM_DISCONNECT: + /* Disconnect asynchronously */ + NM_MODEM_GET_CLASS (ctx->self)->disconnect (ctx->self, + FALSE, + ctx->cancellable, + (GAsyncReadyCallback) disconnect_ready, + ctx); + return; + + case DEACTIVATE_CONTEXT_STEP_LAST: + nm_log_dbg (LOGD_MB, "(%s): modem deactivation finished", + nm_modem_get_uid (ctx->self)); + deactivate_context_complete (ctx); + return; + } + + g_assert_not_reached (); +} + +void +nm_modem_deactivate_async (NMModem *self, + NMDevice *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + DeactivateContext *ctx; + + ctx = g_slice_new0 (DeactivateContext); + ctx->self = g_object_ref (self); + ctx->device = g_object_ref (device); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + nm_modem_deactivate_async); + ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + + /* Start */ + ctx->step = DEACTIVATE_CONTEXT_STEP_FIRST; + deactivate_step (ctx); +} + +/*****************************************************************************/ + void nm_modem_deactivate (NMModem *self, NMDevice *device) { - NM_MODEM_GET_CLASS (self)->deactivate (self, device); + /* First cleanup */ + NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, device); + /* Then disconnect without waiting */ + NM_MODEM_GET_CLASS (self)->disconnect (self, FALSE, NULL, NULL, NULL); } /*****************************************************************************/ @@ -912,7 +1082,6 @@ nm_modem_device_state_changed (NMModem *self, switch (new_state) { case NM_DEVICE_STATE_UNMANAGED: case NM_DEVICE_STATE_UNAVAILABLE: - case NM_DEVICE_STATE_DISCONNECTED: case NM_DEVICE_STATE_FAILED: if (priv->act_request) { cancel_get_secrets (self); @@ -924,7 +1093,9 @@ nm_modem_device_state_changed (NMModem *self, /* Don't bother warning on FAILED since the modem is already gone */ if (new_state == NM_DEVICE_STATE_FAILED) warn = FALSE; - NM_MODEM_GET_CLASS (self)->disconnect (self, warn); + /* First cleanup */ + NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, NULL); + NM_MODEM_GET_CLASS (self)->disconnect (self, warn, NULL, NULL, NULL); } break; default: @@ -1209,7 +1380,7 @@ nm_modem_class_init (NMModemClass *klass) klass->act_stage1_prepare = act_stage1_prepare; klass->stage3_ip6_config_request = stage3_ip6_config_request; - klass->deactivate = deactivate; + klass->deactivate_cleanup = deactivate_cleanup; /* Properties */ diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index 2a4d917332..ae757580e9 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -143,9 +143,16 @@ typedef struct { void (*set_mm_enabled) (NMModem *self, gboolean enabled); - void (*disconnect) (NMModem *self, gboolean warn); + void (*disconnect) (NMModem *self, + gboolean warn, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*disconnect_finish) (NMModem *self, + GAsyncResult *res, + GError **error); - void (*deactivate) (NMModem *self, NMDevice *device); + void (*deactivate_cleanup) (NMModem *self, NMDevice *device); gboolean (*owns_port) (NMModem *self, const char *iface); @@ -218,6 +225,15 @@ gboolean nm_modem_get_secrets (NMModem *modem, void nm_modem_deactivate (NMModem *modem, NMDevice *device); +void nm_modem_deactivate_async (NMModem *self, + NMDevice *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean nm_modem_deactivate_async_finish (NMModem *self, + GAsyncResult *res, + GError **error); + void nm_modem_device_state_changed (NMModem *modem, NMDeviceState new_state, NMDeviceState old_state, diff --git a/src/devices/wwan/wwan-exports.ver b/src/devices/wwan/wwan-exports.ver index c23ab24b79..23412de627 100644 --- a/src/devices/wwan/wwan-exports.ver +++ b/src/devices/wwan/wwan-exports.ver @@ -5,6 +5,8 @@ global: nm_modem_check_connection_compatible; nm_modem_complete_connection; nm_modem_deactivate; + nm_modem_deactivate_async; + nm_modem_deactivate_async_finish; nm_modem_device_state_changed; nm_modem_get_capabilities; nm_modem_get_control_port; diff --git a/src/ppp-manager/nm-ppp-manager.c b/src/ppp-manager/nm-ppp-manager.c index b2d5d6c2a2..f0b8bbcb93 100644 --- a/src/ppp-manager/nm-ppp-manager.c +++ b/src/ppp-manager/nm-ppp-manager.c @@ -67,6 +67,7 @@ static gboolean impl_ppp_manager_set_ip6_config (NMPPPManager *manager, #include "nm-ppp-manager-glue.h" static void _ppp_cleanup (NMPPPManager *manager); +static void _ppp_kill (NMPPPManager *manager); #define NM_PPPD_PLUGIN PPPD_PLUGIN_DIR "/nm-pppd-plugin.so" #define PPP_MANAGER_SECRET_TRIES "ppp-manager-secret-tries" @@ -138,6 +139,7 @@ dispose (GObject *object) NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (object); _ppp_cleanup (NM_PPP_MANAGER (object)); + _ppp_kill (NM_PPP_MANAGER (object)); g_clear_object (&priv->act_req); @@ -835,6 +837,7 @@ pppd_timed_out (gpointer data) nm_log_warn (LOGD_PPP, "pppd timed out or didn't initialize our dbus module"); _ppp_cleanup (manager); + _ppp_kill (manager); g_signal_emit (manager, signals[STATE_CHANGED], 0, NM_PPP_STATUS_DEAD); @@ -1144,6 +1147,21 @@ out: } static void +_ppp_kill (NMPPPManager *manager) +{ + NMPPPManagerPrivate *priv; + + g_return_if_fail (NM_IS_PPP_MANAGER (manager)); + + priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + + if (priv->pid) { + nm_utils_kill_child_async (priv->pid, SIGTERM, LOGD_PPP, "pppd", 2000, NULL, NULL); + priv->pid = 0; + } +} + +static void _ppp_cleanup (NMPPPManager *manager) { NMPPPManagerPrivate *priv; @@ -1175,9 +1193,96 @@ _ppp_cleanup (NMPPPManager *manager) g_source_remove (priv->ppp_watch_id); priv->ppp_watch_id = 0; } +} - if (priv->pid) { - nm_utils_kill_child_async (priv->pid, SIGTERM, LOGD_PPP, "pppd", 2000, NULL, NULL); - priv->pid = 0; +/***********************************************************/ + +typedef struct { + NMPPPManager *manager; + GSimpleAsyncResult *result; + GCancellable *cancellable; +} StopContext; + +static void +stop_context_complete (StopContext *ctx) +{ + if (ctx->cancellable) + g_object_unref (ctx->cancellable); + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->result); + g_object_unref (ctx->manager); + g_slice_free (StopContext, ctx); +} + +static gboolean +stop_context_complete_if_cancelled (StopContext *ctx) +{ + GError *error = NULL; + + if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) { + g_simple_async_result_take_error (ctx->result, error); + stop_context_complete (ctx); + return TRUE; } + return FALSE; +} + +gboolean +nm_ppp_manager_stop_finish (NMPPPManager *manager, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +kill_child_ready (pid_t pid, + gboolean success, + int child_status, + StopContext *ctx) +{ + if (stop_context_complete_if_cancelled (ctx)) + return; + stop_context_complete (ctx); +} + +void +nm_ppp_manager_stop (NMPPPManager *manager, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); + StopContext *ctx; + + ctx = g_slice_new0 (StopContext); + ctx->manager = g_object_ref (manager); + ctx->result = g_simple_async_result_new (G_OBJECT (manager), + callback, + user_data, + nm_ppp_manager_stop); + + /* Setup cancellable */ + ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + if (stop_context_complete_if_cancelled (ctx)) + return; + + /* Cleanup internals */ + _ppp_cleanup (manager); + + /* If no pppd running, we're done */ + if (!priv->pid) { + stop_context_complete (ctx); + return; + } + + /* No cancellable operation, so just wait until it returns always */ + nm_utils_kill_child_async (priv->pid, + SIGTERM, + LOGD_PPP, + "pppd", + 2000, + (NMUtilsKillChildAsyncCb) kill_child_ready, + ctx); + priv->pid = 0; } diff --git a/src/ppp-manager/nm-ppp-manager.h b/src/ppp-manager/nm-ppp-manager.h index 84be76d044..f8cbda1365 100644 --- a/src/ppp-manager/nm-ppp-manager.h +++ b/src/ppp-manager/nm-ppp-manager.h @@ -24,6 +24,7 @@ #include <glib.h> #include <glib-object.h> +#include <gio/gio.h> #include "nm-ppp-status.h" #include "nm-activation-request.h" @@ -69,5 +70,12 @@ gboolean nm_ppp_manager_start (NMPPPManager *manager, guint32 timeout_secs, GError **err); +void nm_ppp_manager_stop (NMPPPManager *manager, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean nm_ppp_manager_stop_finish (NMPPPManager *manager, + GAsyncResult *res, + GError **error); #endif /* __NETWORKMANAGER_PPP_MANAGER_H__ */ |