Just when we request to cancel the connection we need to set the bearer as being disconnected, so that we do not end up overwriting connection cancellation requests. Reported by: Thieu Le <thieule@chromium.org>
885 lines
28 KiB
C
885 lines
28 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details:
|
|
*
|
|
* Copyright (C) 2008 - 2009 Novell, Inc.
|
|
* Copyright (C) 2009 - 2011 Red Hat, Inc.
|
|
* Copyright (C) 2011 Google, Inc.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include <ModemManager.h>
|
|
#include <libmm-common.h>
|
|
|
|
#include "mm-daemon-enums-types.h"
|
|
#include "mm-iface-modem.h"
|
|
#include "mm-bearer.h"
|
|
#include "mm-base-modem-at.h"
|
|
#include "mm-base-modem.h"
|
|
#include "mm-utils.h"
|
|
#include "mm-log.h"
|
|
#include "mm-modem-helpers.h"
|
|
|
|
/* We require up to 20s to get a proper IP when using PPP */
|
|
#define MM_BEARER_IP_TIMEOUT_DEFAULT 20
|
|
|
|
G_DEFINE_TYPE (MMBearer, mm_bearer, MM_GDBUS_TYPE_BEARER_SKELETON);
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_PATH,
|
|
PROP_CONNECTION,
|
|
PROP_MODEM,
|
|
PROP_STATUS,
|
|
PROP_CONFIG,
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *properties[PROP_LAST];
|
|
|
|
struct _MMBearerPrivate {
|
|
/* The connection to the system bus */
|
|
GDBusConnection *connection;
|
|
/* The modem which owns this BEARER */
|
|
MMBaseModem *modem;
|
|
/* The path where the BEARER object is exported */
|
|
gchar *path;
|
|
/* Status of this bearer */
|
|
MMBearerStatus status;
|
|
/* Configuration of the bearer */
|
|
MMBearerProperties *config;
|
|
|
|
/* Cancellable for connect() */
|
|
GCancellable *connect_cancellable;
|
|
/* handler id for the disconnect + cancel connect request */
|
|
gulong disconnect_signal_handler;
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
mm_bearer_export (MMBearer *self)
|
|
{
|
|
static guint id = 0;
|
|
gchar *path;
|
|
|
|
path = g_strdup_printf (MM_DBUS_BEARER_PREFIX "/%d", id++);
|
|
g_object_set (self,
|
|
MM_BEARER_PATH, path,
|
|
NULL);
|
|
g_free (path);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
bearer_reset_interface_status (MMBearer *self)
|
|
{
|
|
mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE);
|
|
mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
|
|
mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL);
|
|
mm_gdbus_bearer_set_ip4_config (
|
|
MM_GDBUS_BEARER (self),
|
|
mm_bearer_ip_config_get_dictionary (NULL));
|
|
mm_gdbus_bearer_set_ip6_config (
|
|
MM_GDBUS_BEARER (self),
|
|
mm_bearer_ip_config_get_dictionary (NULL));
|
|
}
|
|
|
|
static void
|
|
bearer_update_status (MMBearer *self,
|
|
MMBearerStatus status)
|
|
{
|
|
/* NOTE: we do allow status 'CONNECTED' here; it may happen if we go into
|
|
* DISCONNECTING and we cannot disconnect */
|
|
|
|
/* Update the property value */
|
|
self->priv->status = status;
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]);
|
|
|
|
/* Ensure that we don't expose any connection related data in the
|
|
* interface when going into disconnected state. */
|
|
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTED)
|
|
bearer_reset_interface_status (self);
|
|
}
|
|
|
|
static void
|
|
bearer_update_status_connected (MMBearer *self,
|
|
const gchar *interface,
|
|
MMBearerIpConfig *ipv4_config,
|
|
MMBearerIpConfig *ipv6_config)
|
|
{
|
|
mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), TRUE);
|
|
mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
|
|
mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), interface);
|
|
mm_gdbus_bearer_set_ip4_config (
|
|
MM_GDBUS_BEARER (self),
|
|
mm_bearer_ip_config_get_dictionary (ipv4_config));
|
|
mm_gdbus_bearer_set_ip6_config (
|
|
MM_GDBUS_BEARER (self),
|
|
mm_bearer_ip_config_get_dictionary (ipv6_config));
|
|
|
|
/* Update the property value */
|
|
self->priv->status = MM_BEARER_STATUS_CONNECTED;
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* CONNECT */
|
|
|
|
gboolean
|
|
mm_bearer_connect_finish (MMBearer *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
|
|
}
|
|
|
|
static void
|
|
disconnect_after_cancel_ready (MMBearer *self,
|
|
GAsyncResult *res)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!MM_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
|
|
mm_warn ("Error disconnecting bearer '%s': '%s'. "
|
|
"Will assume disconnected anyway.",
|
|
self->priv->path,
|
|
error->message);
|
|
g_error_free (error);
|
|
}
|
|
else
|
|
mm_dbg ("Disconnected bearer '%s'", self->priv->path);
|
|
|
|
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
|
|
}
|
|
|
|
static void
|
|
connect_ready (MMBearer *self,
|
|
GAsyncResult *res,
|
|
GSimpleAsyncResult *simple)
|
|
{
|
|
GError *error = NULL;
|
|
gboolean launch_disconnect = FALSE;
|
|
MMPort *data = NULL;
|
|
MMBearerIpConfig *ipv4_config = NULL;
|
|
MMBearerIpConfig *ipv6_config = NULL;
|
|
|
|
/* NOTE: connect() implementations *MUST* handle cancellations themselves */
|
|
if (!MM_BEARER_GET_CLASS (self)->connect_finish (self,
|
|
res,
|
|
&data,
|
|
&ipv4_config,
|
|
&ipv6_config,
|
|
&error)) {
|
|
mm_dbg ("Couldn't connect bearer '%s': '%s'",
|
|
self->priv->path,
|
|
error->message);
|
|
if (g_error_matches (error,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_CANCELLED)) {
|
|
/* Will launch disconnection */
|
|
launch_disconnect = TRUE;
|
|
} else
|
|
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
|
|
|
|
g_simple_async_result_take_error (simple, error);
|
|
}
|
|
/* Handle cancellations detected after successful connection */
|
|
else if (g_cancellable_is_cancelled (self->priv->connect_cancellable)) {
|
|
mm_dbg ("Connected bearer '%s', but need to disconnect", self->priv->path);
|
|
|
|
g_clear_object (&data);
|
|
g_clear_object (&ipv4_config);
|
|
g_clear_object (&ipv6_config);
|
|
|
|
g_simple_async_result_set_error (
|
|
simple,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_CANCELLED,
|
|
"Bearer got connected, but had to disconnect after cancellation request");
|
|
launch_disconnect = TRUE;
|
|
}
|
|
else {
|
|
mm_dbg ("Connected bearer '%s'", self->priv->path);
|
|
|
|
/* Update bearer and interface status */
|
|
bearer_update_status_connected (self,
|
|
mm_port_get_device (data),
|
|
ipv4_config,
|
|
ipv6_config);
|
|
|
|
g_clear_object (&data);
|
|
g_clear_object (&ipv4_config);
|
|
g_clear_object (&ipv6_config);
|
|
|
|
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
|
|
}
|
|
|
|
if (launch_disconnect) {
|
|
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING);
|
|
MM_BEARER_GET_CLASS (self)->disconnect (
|
|
self,
|
|
(GAsyncReadyCallback)disconnect_after_cancel_ready,
|
|
NULL);
|
|
}
|
|
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
void
|
|
mm_bearer_connect (MMBearer *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *result;
|
|
|
|
g_assert (MM_BEARER_GET_CLASS (self)->connect != NULL);
|
|
g_assert (MM_BEARER_GET_CLASS (self)->connect_finish != NULL);
|
|
|
|
/* If already connecting, return error, don't allow a second request. */
|
|
if (self->priv->status == MM_BEARER_STATUS_CONNECTING) {
|
|
g_simple_async_report_error_in_idle (
|
|
G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_IN_PROGRESS,
|
|
"Bearer already being connected");
|
|
return;
|
|
}
|
|
|
|
/* If currently disconnecting, return error, previous operation should
|
|
* finish before allowing to connect again. */
|
|
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING) {
|
|
g_simple_async_report_error_in_idle (
|
|
G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_FAILED,
|
|
"Bearer currently being disconnected");
|
|
return;
|
|
}
|
|
|
|
result = g_simple_async_result_new (G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
mm_bearer_connect);
|
|
|
|
/* If already connected, done */
|
|
if (self->priv->status == MM_BEARER_STATUS_CONNECTED) {
|
|
g_simple_async_result_set_op_res_gboolean (result, TRUE);
|
|
g_simple_async_result_complete_in_idle (result);
|
|
g_object_unref (result);
|
|
return;
|
|
}
|
|
|
|
/* Connecting! */
|
|
mm_dbg ("Connecting bearer '%s'", self->priv->path);
|
|
self->priv->connect_cancellable = g_cancellable_new ();
|
|
bearer_update_status (self, MM_BEARER_STATUS_CONNECTING);
|
|
MM_BEARER_GET_CLASS (self)->connect (
|
|
self,
|
|
self->priv->connect_cancellable,
|
|
(GAsyncReadyCallback)connect_ready,
|
|
result);
|
|
}
|
|
|
|
typedef struct {
|
|
MMBearer *self;
|
|
MMBaseModem *modem;
|
|
GDBusMethodInvocation *invocation;
|
|
} HandleConnectContext;
|
|
|
|
static void
|
|
handle_connect_context_free (HandleConnectContext *ctx)
|
|
{
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->modem);
|
|
g_object_unref (ctx->self);
|
|
g_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_connect_ready (MMBearer *self,
|
|
GAsyncResult *res,
|
|
HandleConnectContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_bearer_connect_finish (self, res, &error))
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else
|
|
mm_gdbus_bearer_complete_connect (MM_GDBUS_BEARER (self), ctx->invocation);
|
|
|
|
handle_connect_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_connect_auth_ready (MMBaseModem *modem,
|
|
GAsyncResult *res,
|
|
HandleConnectContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_modem_authorize_finish (modem, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_connect_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
mm_bearer_connect (ctx->self,
|
|
(GAsyncReadyCallback)handle_connect_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_connect (MMBearer *self,
|
|
GDBusMethodInvocation *invocation)
|
|
{
|
|
HandleConnectContext *ctx;
|
|
|
|
ctx = g_new0 (HandleConnectContext, 1);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
g_object_get (self,
|
|
MM_BEARER_MODEM, &ctx->modem,
|
|
NULL);
|
|
|
|
mm_base_modem_authorize (ctx->modem,
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_connect_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* DISCONNECT */
|
|
|
|
gboolean
|
|
mm_bearer_disconnect_finish (MMBearer *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
|
|
}
|
|
|
|
static void
|
|
disconnect_ready (MMBearer *self,
|
|
GAsyncResult *res,
|
|
GSimpleAsyncResult *simple)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!MM_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
|
|
mm_dbg ("Couldn't disconnect bearer '%s'", self->priv->path);
|
|
bearer_update_status (self, MM_BEARER_STATUS_CONNECTED);
|
|
g_simple_async_result_take_error (simple, error);
|
|
}
|
|
else {
|
|
mm_dbg ("Disconnected bearer '%s'", self->priv->path);
|
|
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
|
|
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
|
|
}
|
|
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
static void
|
|
status_changed_complete_disconnect (MMBearer *self,
|
|
GParamSpec *pspec,
|
|
GSimpleAsyncResult *simple)
|
|
{
|
|
/* We may get other states here before DISCONNECTED, like DISCONNECTING or
|
|
* even CONNECTED. */
|
|
if (self->priv->status != MM_BEARER_STATUS_DISCONNECTED)
|
|
return;
|
|
|
|
mm_dbg ("Disconnected bearer '%s' after cancelling previous connect request",
|
|
self->priv->path);
|
|
g_signal_handler_disconnect (self,
|
|
self->priv->disconnect_signal_handler);
|
|
self->priv->disconnect_signal_handler = 0;
|
|
|
|
/* Note: interface state is updated when the DISCONNECTED state is set */
|
|
|
|
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
|
|
g_simple_async_result_complete (simple);
|
|
g_object_unref (simple);
|
|
}
|
|
|
|
void
|
|
mm_bearer_disconnect (MMBearer *self,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *simple;
|
|
|
|
g_assert (MM_BEARER_GET_CLASS (self)->disconnect != NULL);
|
|
g_assert (MM_BEARER_GET_CLASS (self)->disconnect_finish != NULL);
|
|
|
|
simple = g_simple_async_result_new (G_OBJECT (self),
|
|
callback,
|
|
user_data,
|
|
mm_bearer_disconnect);
|
|
|
|
/* If already disconnected, done */
|
|
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTED) {
|
|
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
|
|
g_simple_async_result_complete_in_idle (simple);
|
|
g_object_unref (simple);
|
|
return;
|
|
}
|
|
|
|
/* If already disconnecting, return error, don't allow a second request. */
|
|
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING) {
|
|
g_simple_async_result_set_error (
|
|
simple,
|
|
MM_CORE_ERROR,
|
|
MM_CORE_ERROR_IN_PROGRESS,
|
|
"Bearer already being disconnected");
|
|
g_simple_async_result_complete_in_idle (simple);
|
|
g_object_unref (simple);
|
|
return;
|
|
}
|
|
|
|
mm_dbg ("Disconnecting bearer '%s'", self->priv->path);
|
|
|
|
/* If currently connecting, try to cancel that operation, and wait to get
|
|
* disconnected. */
|
|
if (self->priv->status == MM_BEARER_STATUS_CONNECTING) {
|
|
/* Set ourselves as disconnecting */
|
|
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING);
|
|
|
|
/* We MUST ensure that we get to DISCONNECTED */
|
|
g_cancellable_cancel (self->priv->connect_cancellable);
|
|
/* Note that we only allow to remove disconnected bearers, so should
|
|
* be safe to assume that we'll get the signal handler called properly
|
|
*/
|
|
self->priv->disconnect_signal_handler =
|
|
g_signal_connect (self,
|
|
"notify::" MM_BEARER_STATUS,
|
|
(GCallback)status_changed_complete_disconnect,
|
|
simple); /* takes ownership */
|
|
|
|
return;
|
|
}
|
|
|
|
/* Disconnecting! */
|
|
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING);
|
|
MM_BEARER_GET_CLASS (self)->disconnect (
|
|
self,
|
|
(GAsyncReadyCallback)disconnect_ready,
|
|
simple); /* takes ownership */
|
|
}
|
|
|
|
typedef struct {
|
|
MMBearer *self;
|
|
MMBaseModem *modem;
|
|
GDBusMethodInvocation *invocation;
|
|
} HandleDisconnectContext;
|
|
|
|
static void
|
|
handle_disconnect_context_free (HandleDisconnectContext *ctx)
|
|
{
|
|
g_object_unref (ctx->invocation);
|
|
g_object_unref (ctx->modem);
|
|
g_object_unref (ctx->self);
|
|
g_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_disconnect_ready (MMBearer *self,
|
|
GAsyncResult *res,
|
|
HandleDisconnectContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_bearer_disconnect_finish (self, res, &error))
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
else
|
|
mm_gdbus_bearer_complete_disconnect (MM_GDBUS_BEARER (self), ctx->invocation);
|
|
|
|
handle_disconnect_context_free (ctx);
|
|
}
|
|
|
|
static void
|
|
handle_disconnect_auth_ready (MMBaseModem *modem,
|
|
GAsyncResult *res,
|
|
HandleDisconnectContext *ctx)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!mm_base_modem_authorize_finish (modem, res, &error)) {
|
|
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
|
handle_disconnect_context_free (ctx);
|
|
return;
|
|
}
|
|
|
|
mm_bearer_disconnect (ctx->self,
|
|
(GAsyncReadyCallback)handle_disconnect_ready,
|
|
ctx);
|
|
}
|
|
|
|
static gboolean
|
|
handle_disconnect (MMBearer *self,
|
|
GDBusMethodInvocation *invocation)
|
|
{
|
|
HandleDisconnectContext *ctx;
|
|
|
|
ctx = g_new0 (HandleDisconnectContext, 1);
|
|
ctx->self = g_object_ref (self);
|
|
ctx->invocation = g_object_ref (invocation);
|
|
g_object_get (self,
|
|
MM_BEARER_MODEM, &ctx->modem,
|
|
NULL);
|
|
|
|
mm_base_modem_authorize (ctx->modem,
|
|
invocation,
|
|
MM_AUTHORIZATION_DEVICE_CONTROL,
|
|
(GAsyncReadyCallback)handle_disconnect_auth_ready,
|
|
ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
mm_bearer_dbus_export (MMBearer *self)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
/* Handle method invocations */
|
|
g_signal_connect (self,
|
|
"handle-connect",
|
|
G_CALLBACK (handle_connect),
|
|
NULL);
|
|
g_signal_connect (self,
|
|
"handle-disconnect",
|
|
G_CALLBACK (handle_disconnect),
|
|
NULL);
|
|
|
|
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self),
|
|
self->priv->connection,
|
|
self->priv->path,
|
|
&error)) {
|
|
mm_warn ("couldn't export BEARER at '%s': '%s'",
|
|
self->priv->path,
|
|
error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
static void
|
|
mm_bearer_dbus_unexport (MMBearer *self)
|
|
{
|
|
const gchar *path;
|
|
|
|
path = g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self));
|
|
/* Only unexport if currently exported */
|
|
if (path) {
|
|
mm_dbg ("Removing from DBus bearer at '%s'", path);
|
|
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self));
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
MMBearerStatus
|
|
mm_bearer_get_status (MMBearer *self)
|
|
{
|
|
return self->priv->status;
|
|
}
|
|
|
|
const gchar *
|
|
mm_bearer_get_path (MMBearer *self)
|
|
{
|
|
return self->priv->path;
|
|
}
|
|
|
|
MMBearerProperties *
|
|
mm_bearer_peek_config (MMBearer *self)
|
|
{
|
|
return self->priv->config;
|
|
}
|
|
|
|
MMBearerProperties *
|
|
mm_bearer_get_config (MMBearer *self)
|
|
{
|
|
return (self->priv->config ?
|
|
g_object_ref (self->priv->config) :
|
|
NULL);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
disconnect_force_ready (MMBearer *self,
|
|
GAsyncResult *res)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!MM_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
|
|
mm_warn ("Error disconnecting bearer '%s': '%s'. "
|
|
"Will assume disconnected anyway.",
|
|
self->priv->path,
|
|
error->message);
|
|
g_error_free (error);
|
|
}
|
|
else
|
|
mm_dbg ("Disconnected bearer '%s'", self->priv->path);
|
|
|
|
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
|
|
}
|
|
|
|
void
|
|
mm_bearer_disconnect_force (MMBearer *self)
|
|
{
|
|
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING ||
|
|
self->priv->status == MM_BEARER_STATUS_DISCONNECTED)
|
|
return;
|
|
|
|
mm_dbg ("Forcing disconnection of bearer '%s'", self->priv->path);
|
|
|
|
/* If currently connecting, try to cancel that operation. */
|
|
if (self->priv->status == MM_BEARER_STATUS_CONNECTING) {
|
|
g_cancellable_cancel (self->priv->connect_cancellable);
|
|
return;
|
|
}
|
|
|
|
/* Disconnecting! */
|
|
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING);
|
|
MM_BEARER_GET_CLASS (self)->disconnect (
|
|
self,
|
|
(GAsyncReadyCallback)disconnect_force_ready,
|
|
NULL);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
report_disconnection (MMBearer *self)
|
|
{
|
|
/* In the generic bearer implementation we just need to reset the
|
|
* interface status */
|
|
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
|
|
}
|
|
|
|
void
|
|
mm_bearer_report_disconnection (MMBearer *self)
|
|
{
|
|
return MM_BEARER_GET_CLASS (self)->report_disconnection (self);
|
|
}
|
|
|
|
static void
|
|
set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MMBearer *self = MM_BEARER (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_PATH:
|
|
g_free (self->priv->path);
|
|
self->priv->path = g_value_dup_string (value);
|
|
|
|
/* Export when we get a DBus connection AND we have a path */
|
|
if (self->priv->path &&
|
|
self->priv->connection)
|
|
mm_bearer_dbus_export (self);
|
|
break;
|
|
case PROP_CONNECTION:
|
|
g_clear_object (&self->priv->connection);
|
|
self->priv->connection = g_value_dup_object (value);
|
|
|
|
/* Export when we get a DBus connection AND we have a path */
|
|
if (!self->priv->connection)
|
|
mm_bearer_dbus_unexport (self);
|
|
else if (self->priv->path)
|
|
mm_bearer_dbus_export (self);
|
|
break;
|
|
case PROP_MODEM:
|
|
g_clear_object (&self->priv->modem);
|
|
self->priv->modem = g_value_dup_object (value);
|
|
if (self->priv->modem)
|
|
/* Bind the modem's connection (which is set when it is exported,
|
|
* and unset when unexported) to the BEARER's connection */
|
|
g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION,
|
|
self, MM_BEARER_CONNECTION,
|
|
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
|
|
break;
|
|
case PROP_STATUS:
|
|
/* We don't allow g_object_set()-ing the status property */
|
|
g_assert_not_reached ();
|
|
break;
|
|
case PROP_CONFIG: {
|
|
GVariant *dictionary;
|
|
|
|
g_clear_object (&self->priv->config);
|
|
self->priv->config = g_value_dup_object (value);
|
|
/* Also expose the properties */
|
|
dictionary = mm_bearer_properties_get_dictionary (self->priv->config);
|
|
mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), dictionary);
|
|
if (dictionary)
|
|
g_variant_unref (dictionary);
|
|
break;
|
|
}
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MMBearer *self = MM_BEARER (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_PATH:
|
|
g_value_set_string (value, self->priv->path);
|
|
break;
|
|
case PROP_CONNECTION:
|
|
g_value_set_object (value, self->priv->connection);
|
|
break;
|
|
case PROP_MODEM:
|
|
g_value_set_object (value, self->priv->modem);
|
|
break;
|
|
case PROP_STATUS:
|
|
g_value_set_enum (value, self->priv->status);
|
|
break;
|
|
case PROP_CONFIG:
|
|
g_value_set_object (value, self->priv->config);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
mm_bearer_init (MMBearer *self)
|
|
{
|
|
/* Initialize private data */
|
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
|
|
MM_TYPE_BEARER,
|
|
MMBearerPrivate);
|
|
self->priv->status = MM_BEARER_STATUS_DISCONNECTED;
|
|
|
|
/* Set defaults */
|
|
mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL);
|
|
mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE);
|
|
mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
|
|
mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), NULL);
|
|
mm_gdbus_bearer_set_ip_timeout (MM_GDBUS_BEARER (self), MM_BEARER_IP_TIMEOUT_DEFAULT);
|
|
mm_gdbus_bearer_set_ip4_config (MM_GDBUS_BEARER (self),
|
|
mm_bearer_ip_config_get_dictionary (NULL));
|
|
mm_gdbus_bearer_set_ip6_config (MM_GDBUS_BEARER (self),
|
|
mm_bearer_ip_config_get_dictionary (NULL));
|
|
}
|
|
|
|
static void
|
|
finalize (GObject *object)
|
|
{
|
|
MMBearer *self = MM_BEARER (object);
|
|
|
|
g_free (self->priv->path);
|
|
|
|
G_OBJECT_CLASS (mm_bearer_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
dispose (GObject *object)
|
|
{
|
|
MMBearer *self = MM_BEARER (object);
|
|
|
|
if (self->priv->connection) {
|
|
mm_bearer_dbus_unexport (self);
|
|
g_clear_object (&self->priv->connection);
|
|
}
|
|
|
|
g_clear_object (&self->priv->modem);
|
|
g_clear_object (&self->priv->config);
|
|
|
|
G_OBJECT_CLASS (mm_bearer_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
mm_bearer_class_init (MMBearerClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
g_type_class_add_private (object_class, sizeof (MMBearerPrivate));
|
|
|
|
/* Virtual methods */
|
|
object_class->get_property = get_property;
|
|
object_class->set_property = set_property;
|
|
object_class->finalize = finalize;
|
|
object_class->dispose = dispose;
|
|
|
|
klass->report_disconnection = report_disconnection;
|
|
|
|
properties[PROP_CONNECTION] =
|
|
g_param_spec_object (MM_BEARER_CONNECTION,
|
|
"Connection",
|
|
"GDBus connection to the system bus.",
|
|
G_TYPE_DBUS_CONNECTION,
|
|
G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]);
|
|
|
|
properties[PROP_PATH] =
|
|
g_param_spec_string (MM_BEARER_PATH,
|
|
"Path",
|
|
"DBus path of the Bearer",
|
|
NULL,
|
|
G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_PATH, properties[PROP_PATH]);
|
|
|
|
properties[PROP_MODEM] =
|
|
g_param_spec_object (MM_BEARER_MODEM,
|
|
"Modem",
|
|
"The Modem which owns this Bearer",
|
|
MM_TYPE_BASE_MODEM,
|
|
G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]);
|
|
|
|
properties[PROP_STATUS] =
|
|
g_param_spec_enum (MM_BEARER_STATUS,
|
|
"Bearer status",
|
|
"Status of the bearer",
|
|
MM_TYPE_BEARER_STATUS,
|
|
MM_BEARER_STATUS_DISCONNECTED,
|
|
G_PARAM_READABLE);
|
|
g_object_class_install_property (object_class, PROP_STATUS, properties[PROP_STATUS]);
|
|
|
|
properties[PROP_CONFIG] =
|
|
g_param_spec_object (MM_BEARER_CONFIG,
|
|
"Bearer configuration",
|
|
"List of user provided properties",
|
|
MM_TYPE_BEARER_PROPERTIES,
|
|
G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_CONFIG, properties[PROP_CONFIG]);
|
|
}
|