Commit fe090c34 authored by Aleksander Morgado's avatar Aleksander Morgado Committed by Dan Williams

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]
parent ec61601a
...@@ -433,6 +433,50 @@ deactivate (NMDevice *device) ...@@ -433,6 +433,50 @@ deactivate (NMDevice *device)
nm_modem_deactivate (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, 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 static NMActStageReturn
act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
{ {
...@@ -711,6 +755,8 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass) ...@@ -711,6 +755,8 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
device_class->check_connection_compatible = check_connection_compatible; device_class->check_connection_compatible = check_connection_compatible;
device_class->check_connection_available = check_connection_available; device_class->check_connection_available = check_connection_available;
device_class->complete_connection = complete_connection; 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->deactivate = deactivate;
device_class->act_stage1_prepare = act_stage1_prepare; device_class->act_stage1_prepare = act_stage1_prepare;
device_class->act_stage2_config = act_stage2_config; device_class->act_stage2_config = act_stage2_config;
......
...@@ -928,6 +928,8 @@ disconnect (NMModem *self, ...@@ -928,6 +928,8 @@ disconnect (NMModem *self,
return; 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 ( mm_modem_simple_disconnect (
ctx->self->priv->simple_iface, ctx->self->priv->simple_iface,
NULL, /* bearer path; if NULL given ALL get disconnected */ NULL, /* bearer path; if NULL given ALL get disconnected */
...@@ -939,7 +941,7 @@ disconnect (NMModem *self, ...@@ -939,7 +941,7 @@ disconnect (NMModem *self,
/*****************************************************************************/ /*****************************************************************************/
static void static void
deactivate (NMModem *_self, NMDevice *device) deactivate_cleanup (NMModem *_self, NMDevice *device)
{ {
NMModemBroadband *self = NM_MODEM_BROADBAND (_self); NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
...@@ -953,7 +955,7 @@ deactivate (NMModem *_self, NMDevice *device) ...@@ -953,7 +955,7 @@ deactivate (NMModem *_self, NMDevice *device)
self->priv->pin_tries = 0; self->priv->pin_tries = 0;
/* Chain up parent's */ /* 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) ...@@ -1183,7 +1185,7 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass)
modem_class->stage3_ip6_config_request = stage3_ip6_config_request; modem_class->stage3_ip6_config_request = stage3_ip6_config_request;
modem_class->disconnect = disconnect; modem_class->disconnect = disconnect;
modem_class->disconnect_finish = disconnect_finish; 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->set_mm_enabled = set_mm_enabled;
modem_class->get_user_pass = get_user_pass; modem_class->get_user_pass = get_user_pass;
modem_class->check_connection_compatible = check_connection_compatible; modem_class->check_connection_compatible = check_connection_compatible;
......
...@@ -839,7 +839,7 @@ nm_modem_complete_connection (NMModem *self, ...@@ -839,7 +839,7 @@ nm_modem_complete_connection (NMModem *self,
/*****************************************************************************/ /*****************************************************************************/
static void static void
deactivate (NMModem *self, NMDevice *device) deactivate_cleanup (NMModem *self, NMDevice *device)
{ {
NMModemPrivate *priv; NMModemPrivate *priv;
int ifindex; int ifindex;
...@@ -864,15 +864,17 @@ deactivate (NMModem *self, NMDevice *device) ...@@ -864,15 +864,17 @@ deactivate (NMModem *self, NMDevice *device)
priv->ppp_manager = NULL; priv->ppp_manager = NULL;
} }
if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC || if (device) {
priv->ip4_method == NM_MODEM_IP_METHOD_AUTO || if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC ||
priv->ip6_method == NM_MODEM_IP_METHOD_STATIC || priv->ip4_method == NM_MODEM_IP_METHOD_AUTO ||
priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) { priv->ip6_method == NM_MODEM_IP_METHOD_STATIC ||
ifindex = nm_device_get_ip_ifindex (device); priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) {
if (ifindex > 0) { ifindex = nm_device_get_ip_ifindex (device);
nm_platform_route_flush (ifindex); if (ifindex > 0) {
nm_platform_address_flush (ifindex); nm_platform_route_flush (ifindex);
nm_platform_link_set_down (ifindex); nm_platform_address_flush (ifindex);
nm_platform_link_set_down (ifindex);
}
} }
} }
priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN; priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
...@@ -884,10 +886,176 @@ deactivate (NMModem *self, NMDevice *device) ...@@ -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 void
nm_modem_deactivate (NMModem *self, NMDevice *device) 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, ...@@ -912,7 +1080,6 @@ nm_modem_device_state_changed (NMModem *self,
switch (new_state) { switch (new_state) {
case NM_DEVICE_STATE_UNMANAGED: case NM_DEVICE_STATE_UNMANAGED:
case NM_DEVICE_STATE_UNAVAILABLE: case NM_DEVICE_STATE_UNAVAILABLE:
case NM_DEVICE_STATE_DISCONNECTED:
case NM_DEVICE_STATE_FAILED: case NM_DEVICE_STATE_FAILED:
if (priv->act_request) { if (priv->act_request) {
cancel_get_secrets (self); cancel_get_secrets (self);
...@@ -924,6 +1091,8 @@ nm_modem_device_state_changed (NMModem *self, ...@@ -924,6 +1091,8 @@ nm_modem_device_state_changed (NMModem *self,
/* Don't bother warning on FAILED since the modem is already gone */ /* Don't bother warning on FAILED since the modem is already gone */
if (new_state == NM_DEVICE_STATE_FAILED) if (new_state == NM_DEVICE_STATE_FAILED)
warn = FALSE; warn = FALSE;
/* First cleanup */
NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, NULL);
NM_MODEM_GET_CLASS (self)->disconnect (self, warn, NULL, NULL, NULL); NM_MODEM_GET_CLASS (self)->disconnect (self, warn, NULL, NULL, NULL);
} }
break; break;
...@@ -1209,7 +1378,7 @@ nm_modem_class_init (NMModemClass *klass) ...@@ -1209,7 +1378,7 @@ nm_modem_class_init (NMModemClass *klass)
klass->act_stage1_prepare = act_stage1_prepare; klass->act_stage1_prepare = act_stage1_prepare;
klass->stage3_ip6_config_request = stage3_ip6_config_request; klass->stage3_ip6_config_request = stage3_ip6_config_request;
klass->deactivate = deactivate; klass->deactivate_cleanup = deactivate_cleanup;
/* Properties */ /* Properties */
......
...@@ -152,7 +152,7 @@ typedef struct { ...@@ -152,7 +152,7 @@ typedef struct {
GAsyncResult *res, GAsyncResult *res,
GError **error); GError **error);
void (*deactivate) (NMModem *self, NMDevice *device); void (*deactivate_cleanup) (NMModem *self, NMDevice *device);
gboolean (*owns_port) (NMModem *self, const char *iface); gboolean (*owns_port) (NMModem *self, const char *iface);
...@@ -225,6 +225,15 @@ gboolean nm_modem_get_secrets (NMModem *modem, ...@@ -225,6 +225,15 @@ gboolean nm_modem_get_secrets (NMModem *modem,
void nm_modem_deactivate (NMModem *modem, NMDevice *device); 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, void nm_modem_device_state_changed (NMModem *modem,
NMDeviceState new_state, NMDeviceState new_state,
NMDeviceState old_state, NMDeviceState old_state,
......
...@@ -5,6 +5,8 @@ global: ...@@ -5,6 +5,8 @@ global:
nm_modem_check_connection_compatible; nm_modem_check_connection_compatible;
nm_modem_complete_connection; nm_modem_complete_connection;
nm_modem_deactivate; nm_modem_deactivate;
nm_modem_deactivate_async;
nm_modem_deactivate_async_finish;
nm_modem_device_state_changed; nm_modem_device_state_changed;
nm_modem_get_capabilities; nm_modem_get_capabilities;
nm_modem_get_control_port; nm_modem_get_control_port;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment