summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2014-08-06 11:24:33 +0200
committerDan Williams <dcbw@redhat.com>2015-01-21 18:36:00 -0600
commitfe090c34b7247116a1424d956148548cd0237c4b (patch)
treea6bc3524a6303cb7489b78459bf1b07ef1a26a67
parentec61601a2234bc57fdd0aaeb090a0dd550521c58 (diff)
wwan: wait for pppd to exit before relaying the port to ModemManager
ModemManager needs to have CLOCAL set in the TTY termios configuration, in order to notify the kernel that modem control lines are not in effect (e.g. so that a transition to LOW in the DCD input control line doesn't trigger a hangup in the TTY). pppd in the other hand, needs CLOCAL unset in order to have proper modem control lines in effect during the PPP session. So, when pppd starts it will store the original termios settings, and before exiting it will restore the original settings in the TTY. In other words, if CLOCAL was set before launching pppd, CLOCAL will be also set after pppd exits. Now, in order for this sequence to work correctly, NetworkManager also needs to make sure that ModemManager is notified about the disconnection only after pppd has really finished re-configuring the TTY. https://bugzilla.gnome.org/show_bug.cgi?id=734347 ---------------------- Once the patch is applied, we will be making sure that ModemManager is only notified about the disconnection AFTER pppd has fully exited: NetworkManager[27589]: <info> (ttyUSB2): device state change: activated -> deactivating (reason 'user-requested') [100 110 39] Terminating on signal 15 nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 10 / phase 'terminate' nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 8 / phase 'network' Connect time 0.3 minutes. Sent 56 bytes, received 0 bytes. nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 5 / phase 'establish' nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 11 / phase 'disconnect' Connection terminated. nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 1 / phase 'dead' nm-pppd-plugin-Message: nm-ppp-plugin: (nm_exit_notify): cleaning up NetworkManager[27589]: <warn> pppd pid 27617 exited with error: pppd received a signal NetworkManager[27589]: <info> (ttyUSB2): modem state changed, 'connected' --> 'disconnecting' (reason: user-requested) NetworkManager[27589]: <info> (ttyUSB2): modem state changed, 'disconnecting' --> 'registered' (reason: user-requested) NetworkManager[27589]: <info> (ttyUSB2) modem deactivation finished NetworkManager[27589]: <info> (ttyUSB2): device state change: deactivating -> disconnected (reason 'user-requested') [110 30 39] NetworkManager[27589]: <info> (ttyUSB2): deactivating device (reason 'user-requested') [39]
-rw-r--r--src/devices/wwan/nm-device-modem.c46
-rw-r--r--src/devices/wwan/nm-modem-broadband.c8
-rw-r--r--src/devices/wwan/nm-modem.c195
-rw-r--r--src/devices/wwan/nm-modem.h11
-rw-r--r--src/devices/wwan/wwan-exports.ver2
5 files changed, 245 insertions, 17 deletions
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 fcab416ef6..31d09e1c7f 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -928,6 +928,8 @@ disconnect (NMModem *self,
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 */
@@ -939,7 +941,7 @@ disconnect (NMModem *self,
/*****************************************************************************/
static void
-deactivate (NMModem *_self, NMDevice *device)
+deactivate_cleanup (NMModem *_self, NMDevice *device)
{
NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
@@ -953,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);
}
/*****************************************************************************/
@@ -1183,7 +1185,7 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass)
modem_class->stage3_ip6_config_request = stage3_ip6_config_request;
modem_class->disconnect = disconnect;
modem_class->disconnect_finish = disconnect_finish;
- modem_class->deactivate = deactivate;
+ 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 04fc798ccc..9b913cbb48 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -839,7 +839,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 +864,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 +886,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 +1080,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,6 +1091,8 @@ 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;
+ /* First cleanup */
+ NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, NULL);
NM_MODEM_GET_CLASS (self)->disconnect (self, warn, NULL, NULL, NULL);
}
break;
@@ -1209,7 +1378,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 a4c6702c52..ae757580e9 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -152,7 +152,7 @@ typedef struct {
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);
@@ -225,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;