summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2015-01-21 18:36:48 -0600
committerDan Williams <dcbw@redhat.com>2015-01-21 18:36:48 -0600
commit3d25613327b35595f219ae59af734eeb146269e3 (patch)
treea62822d3041b7d24c65a279aa810d9120d82bf86
parent6bb0cffba32ec925743c4d491b75474d4e855198 (diff)
parenta6fedb1a26ea9109d26c92f7366dcf2a9ecab01b (diff)
downloadNetworkManager-3d25613327b35595f219ae59af734eeb146269e3.tar.gz
merge: stop pppd before notifying ModemManager to disconnect (bgo #734347)
-rw-r--r--src/devices/nm-device.c71
-rw-r--r--src/devices/nm-device.h11
-rw-r--r--src/devices/wwan/nm-device-modem.c46
-rw-r--r--src/devices/wwan/nm-modem-broadband.c95
-rw-r--r--src/devices/wwan/nm-modem.c213
-rw-r--r--src/devices/wwan/nm-modem.h20
-rw-r--r--src/devices/wwan/wwan-exports.ver2
-rw-r--r--src/ppp-manager/nm-ppp-manager.c111
-rw-r--r--src/ppp-manager/nm-ppp-manager.h8
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__ */