/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * 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. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2009 - 2010 Red Hat, Inc. * Copyright (C) 2009 Novell, Inc. */ #include #include "nm-modem.h" #include "nm-system.h" #include "nm-dbus-manager.h" #include "nm-setting-connection.h" #include "nm-marshal.h" #include "nm-properties-changed-signal.h" #include "nm-modem-types.h" #include "nm-logging.h" #include "NetworkManagerUtils.h" #include "nm-device-private.h" #include "nm-device-interface.h" #include "nm-dbus-glib-types.h" #include "nm-serial-device-glue.h" G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT) #define NM_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM, NMModemPrivate)) enum { PROP_0, PROP_DEVICE, PROP_IFACE, PROP_PATH, PROP_IP_METHOD, PROP_ENABLED, LAST_PROP }; typedef struct { NMDBusManager *dbus_mgr; DBusGProxy *proxy; DBusGProxy *props_proxy; char *path; NMPPPManager *ppp_manager; NMIP4Config *pending_ip4_config; guint32 ip_method; char *device; char *iface; guint32 secrets_tries; DBusGProxyCall *call; gboolean mm_enabled; /* PPP stats */ guint32 in_bytes; guint32 out_bytes; } NMModemPrivate; enum { PPP_STATS, PPP_FAILED, PREPARE_RESULT, IP4_CONFIG_RESULT, NEED_AUTH, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; NMPPPManager * nm_modem_get_ppp_manager (NMModem *self) { g_return_val_if_fail (NM_IS_MODEM (self), NULL); return NM_MODEM_GET_PRIVATE (self)->ppp_manager; } gboolean nm_modem_get_mm_enabled (NMModem *self) { g_return_val_if_fail (NM_IS_MODEM (self), TRUE); return NM_MODEM_GET_PRIVATE (self)->mm_enabled; } DBusGProxy * nm_modem_get_proxy (NMModem *self, const char *interface) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); const char *current_iface; g_return_val_if_fail (NM_IS_MODEM (self), NULL); /* Default to the default interface. */ if (interface == NULL) interface = MM_DBUS_INTERFACE_MODEM; if (interface && !strcmp (interface, DBUS_INTERFACE_PROPERTIES)) return priv->props_proxy; current_iface = dbus_g_proxy_get_interface (priv->proxy); if (!current_iface || strcmp (current_iface, interface)) dbus_g_proxy_set_interface (priv->proxy, interface); return priv->proxy; } static void merge_ip4_config (NMActRequest *req, NMIP4Config *config) { NMConnection *connection; NMSettingIP4Config *s_ip4; /* Merge user-defined overrides into the IP4Config to be applied */ connection = nm_act_request_get_connection (req); g_assert (connection); s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); if (s_ip4) nm_utils_merge_ip4_config (config, s_ip4); } /*****************************************************************************/ /* IP method PPP */ static void ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data) { switch (status) { case NM_PPP_STATUS_DISCONNECT: g_signal_emit (NM_MODEM (user_data), signals[PPP_FAILED], 0, NM_DEVICE_STATE_REASON_PPP_DISCONNECT); break; case NM_PPP_STATUS_DEAD: g_signal_emit (NM_MODEM (user_data), signals[PPP_FAILED], 0, NM_DEVICE_STATE_REASON_PPP_FAILED); break; default: break; } } static void ppp_ip4_config (NMPPPManager *ppp_manager, const char *iface, NMIP4Config *config, gpointer user_data) { NMModem *self = NM_MODEM (user_data); NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); guint32 i, num; guint32 bad_dns1 = htonl (0x0A0B0C0D); guint32 good_dns1 = htonl (0x04020201); /* GTE nameserver */ guint32 bad_dns2 = htonl (0x0A0B0C0E); guint32 good_dns2 = htonl (0x04020202); /* GTE nameserver */ gboolean dns_workaround = FALSE; /* Work around a PPP bug (#1732) which causes many mobile broadband * providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers. * Apparently fixed in ppp-2.4.5 but we've had some reports that this is * not the case. * * http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=2e09ef6886bbf00bc5a9a641110f801e372ffde6 * http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=f8191bf07df374f119a07910a79217c7618f113e */ num = nm_ip4_config_get_num_nameservers (config); if (num == 2) { gboolean found1 = FALSE, found2 = FALSE; for (i = 0; i < num; i++) { guint32 ns = nm_ip4_config_get_nameserver (config, i); if (ns == bad_dns1) found1 = TRUE; else if (ns == bad_dns2) found2 = TRUE; } /* Be somewhat conservative about substitutions; the "bad" nameservers * could actually be valid in some cases, so only substitute if ppp * returns *only* the two bad nameservers. */ dns_workaround = (found1 && found2); } if (!num || dns_workaround) { nm_log_warn (LOGD_PPP, "compensating for invalid PPP-provided nameservers"); nm_ip4_config_reset_nameservers (config); nm_ip4_config_add_nameserver (config, good_dns1); nm_ip4_config_add_nameserver (config, good_dns2); } priv->pending_ip4_config = g_object_ref (config); g_signal_emit (self, signals[IP4_CONFIG_RESULT], 0, iface, config, NULL); } static void ppp_stats (NMPPPManager *ppp_manager, guint32 in_bytes, guint32 out_bytes, gpointer user_data) { NMModem *self = NM_MODEM (user_data); NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); if (priv->in_bytes != in_bytes || priv->out_bytes != out_bytes) { priv->in_bytes = in_bytes; priv->out_bytes = out_bytes; g_signal_emit (self, signals[PPP_STATS], 0, in_bytes, out_bytes); } } static NMActStageReturn ppp_stage3_ip4_config_start (NMModem *self, NMActRequest *req, NMDeviceStateReason *reason) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); const char *ppp_name = NULL; GError *error = NULL; NMActStageReturn ret; g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); if (NM_MODEM_GET_CLASS (self)->get_user_pass) { NMConnection *connection = nm_act_request_get_connection (req); g_assert (connection); if (!NM_MODEM_GET_CLASS (self)->get_user_pass (self, connection, &ppp_name, NULL)) return NM_ACT_STAGE_RETURN_FAILURE; } priv->ppp_manager = nm_ppp_manager_new (priv->iface); if (nm_ppp_manager_start (priv->ppp_manager, req, ppp_name, 20, &error)) { g_signal_connect (priv->ppp_manager, "state-changed", G_CALLBACK (ppp_state_changed), self); g_signal_connect (priv->ppp_manager, "ip4-config", G_CALLBACK (ppp_ip4_config), self); g_signal_connect (priv->ppp_manager, "stats", G_CALLBACK (ppp_stats), self); ret = NM_ACT_STAGE_RETURN_POSTPONE; } else { nm_log_err (LOGD_PPP, "error starting PPP: (%d) %s", error ? error->code : -1, error && error->message ? error->message : "(unknown)"); g_error_free (error); g_object_unref (priv->ppp_manager); priv->ppp_manager = NULL; *reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED; ret = NM_ACT_STAGE_RETURN_FAILURE; } return ret; } static NMActStageReturn ppp_stage4 (NMModem *self, NMActRequest *req, NMIP4Config **config, NMDeviceStateReason *reason) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); *config = priv->pending_ip4_config; priv->pending_ip4_config = NULL; merge_ip4_config (req, *config); return NM_ACT_STAGE_RETURN_SUCCESS; } /*****************************************************************************/ /* IP method static */ static void static_stage3_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) { NMModem *self = NM_MODEM (user_data); NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); GValueArray *ret_array = NULL; GError *error = NULL; NMIP4Config *config = NULL; priv->call = NULL; if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_VALUE_ARRAY, &ret_array, G_TYPE_INVALID)) { NMIP4Address *addr; int i; config = nm_ip4_config_new (); addr = nm_ip4_address_new (); nm_ip4_address_set_address (addr, g_value_get_uint (g_value_array_get_nth (ret_array, 0))); nm_ip4_address_set_prefix (addr, 32); nm_ip4_config_take_address (config, addr); for (i = 0; i < ret_array->n_values; i++) { GValue *value = g_value_array_get_nth (ret_array, i); nm_ip4_config_add_nameserver (config, g_value_get_uint (value)); } g_value_array_free (ret_array); priv->pending_ip4_config = g_object_ref (config); g_signal_emit (self, signals[IP4_CONFIG_RESULT], 0, NULL, config, NULL); } g_signal_emit (self, signals[IP4_CONFIG_RESULT], 0, NULL, config, error); g_clear_error (&error); } static NMActStageReturn static_stage3_ip4_config_start (NMModem *self, NMActRequest *req, NMDeviceStateReason *reason) { NMModemPrivate *priv; g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); priv = NM_MODEM_GET_PRIVATE (self); priv->call = dbus_g_proxy_begin_call (nm_modem_get_proxy (self, MM_DBUS_INTERFACE_MODEM), "GetIP4Config", static_stage3_done, self, NULL, G_TYPE_INVALID); return NM_ACT_STAGE_RETURN_POSTPONE; } static NMActStageReturn static_stage4 (NMModem *self, NMActRequest *req, NMDevice *device, NMIP4Config **config, NMDeviceStateReason *reason) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); gboolean no_firmware = FALSE; if (!nm_device_hw_bring_up (device, TRUE, &no_firmware)) { if (no_firmware) *reason = NM_DEVICE_STATE_REASON_FIRMWARE_MISSING; else *reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED; return NM_ACT_STAGE_RETURN_FAILURE; } *config = priv->pending_ip4_config; priv->pending_ip4_config = NULL; merge_ip4_config (req, *config); return NM_ACT_STAGE_RETURN_SUCCESS; } /*****************************************************************************/ NMActStageReturn nm_modem_stage3_ip4_config_start (NMModem *self, NMDevice *device, NMDeviceClass *device_class, NMDeviceStateReason *reason) { NMModemPrivate *priv; NMActRequest *req; NMActStageReturn ret; g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (device != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_DEVICE (device), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (device_class != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_DEVICE_CLASS (device_class), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); req = nm_device_get_act_request (device); g_assert (req); priv = NM_MODEM_GET_PRIVATE (self); switch (priv->ip_method) { case MM_MODEM_IP_METHOD_PPP: ret = ppp_stage3_ip4_config_start (self, req, reason); break; case MM_MODEM_IP_METHOD_STATIC: ret = static_stage3_ip4_config_start (self, req, reason); break; case MM_MODEM_IP_METHOD_DHCP: ret = device_class->act_stage3_ip4_config_start (device, reason); break; default: nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method); ret = NM_ACT_STAGE_RETURN_FAILURE; break; } return ret; } NMActStageReturn nm_modem_stage4_get_ip4_config (NMModem *self, NMDevice *device, NMDeviceClass *device_class, NMIP4Config **config, NMDeviceStateReason *reason) { NMModemPrivate *priv; NMActRequest *req; NMActStageReturn ret; g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (device != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_DEVICE (device), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (device_class != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (NM_IS_DEVICE_CLASS (device_class), NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); req = nm_device_get_act_request (device); g_assert (req); priv = NM_MODEM_GET_PRIVATE (self); switch (priv->ip_method) { case MM_MODEM_IP_METHOD_PPP: ret = ppp_stage4 (self, req, config, reason); break; case MM_MODEM_IP_METHOD_STATIC: ret = static_stage4 (self, req, device, config, reason); break; case MM_MODEM_IP_METHOD_DHCP: ret = device_class->act_stage4_get_ip4_config (device, config, reason); break; default: nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method); ret = NM_ACT_STAGE_RETURN_FAILURE; break; } return ret; } gboolean nm_modem_connection_secrets_updated (NMModem *self, NMActRequest *req, NMConnection *connection, GSList *updated_settings, RequestSecretsCaller caller) { NMModemPrivate *priv; gboolean found = FALSE; const char *setting_name; GSList *iter; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_MODEM (self), FALSE); g_return_val_if_fail (req != NULL, FALSE); g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); g_return_val_if_fail (connection != NULL, FALSE); g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); priv = NM_MODEM_GET_PRIVATE (self); if (caller == SECRETS_CALLER_PPP) { const char *user = NULL; const char *pass = NULL; g_return_val_if_fail (priv->ppp_manager != NULL, FALSE); if (!NM_MODEM_GET_CLASS (self)->get_user_pass (self, connection, &user, &pass)) { /* Shouldn't ever happen */ nm_ppp_manager_update_secrets (priv->ppp_manager, priv->iface, NULL, NULL, "missing GSM/CDMA setting; no secrets could be found."); } else { nm_ppp_manager_update_secrets (priv->ppp_manager, priv->iface, user ? user : "", pass ? pass : "", NULL); } return TRUE; } g_return_val_if_fail (caller == SECRETS_CALLER_MOBILE_BROADBAND, FALSE); g_assert (NM_MODEM_GET_CLASS (self)->get_setting_name); setting_name = NM_MODEM_GET_CLASS (self)->get_setting_name (self); for (iter = updated_settings; iter; iter = g_slist_next (iter)) { const char *candidate_setting_name = (const char *) iter->data; if (!strcmp (candidate_setting_name, setting_name)) found = TRUE; else { nm_log_warn (LOGD_MB, "ignoring updated secrets for setting '%s'.", candidate_setting_name); } } return found; } static NMActStageReturn real_act_stage1_prepare (NMModem *modem, NMActRequest *req, GPtrArray **out_hints, const char **out_setting_name, NMDeviceStateReason *reason) { *reason = NM_DEVICE_STATE_REASON_UNKNOWN; return NM_ACT_STAGE_RETURN_FAILURE; } NMActStageReturn nm_modem_act_stage1_prepare (NMModem *self, NMActRequest *req, NMDeviceStateReason *reason) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); NMActStageReturn ret; GPtrArray *hints = NULL; const char *setting_name = NULL; ret = NM_MODEM_GET_CLASS (self)->act_stage1_prepare (self, req, &hints, &setting_name, reason); if ((ret == NM_ACT_STAGE_RETURN_POSTPONE) && setting_name) { const char *hint1 = NULL, *hint2 = NULL; /* Need some secrets */ if (hints) { if (hints->len > 0) hint1 = g_ptr_array_index (hints, 0); if (hints->len > 1) hint2 = g_ptr_array_index (hints, 1); } g_signal_emit (self, signals[NEED_AUTH], 0, setting_name, priv->secrets_tries++ ? TRUE : FALSE, SECRETS_CALLER_MOBILE_BROADBAND, hint1, hint2); if (hints) g_ptr_array_free (hints, TRUE); } return ret; } NMActStageReturn nm_modem_act_stage2_config (NMModem *self, NMActRequest *req, NMDeviceStateReason *reason) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); /* Clear secrets tries counter since secrets were successfully used * already if we get here. */ priv->secrets_tries = 0; return NM_ACT_STAGE_RETURN_SUCCESS; } NMConnection * nm_modem_get_best_auto_connection (NMModem *self, GSList *connections, char **specific_object) { if (NM_MODEM_GET_CLASS (self)->get_best_auto_connection) return NM_MODEM_GET_CLASS (self)->get_best_auto_connection (self, connections, specific_object); return NULL; } gboolean nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection, GError **error) { if (NM_MODEM_GET_CLASS (self)->check_connection_compatible) return NM_MODEM_GET_CLASS (self)->check_connection_compatible (self, connection, error); return FALSE; } static void real_deactivate_quickly (NMModem *self, NMDevice *device) { NMModemPrivate *priv; const char *iface; g_return_if_fail (self != NULL); g_return_if_fail (NM_IS_MODEM (self)); g_return_if_fail (device != NULL); g_return_if_fail (NM_IS_DEVICE (device)); priv = NM_MODEM_GET_PRIVATE (self); priv->secrets_tries = 0; if (priv->call) { dbus_g_proxy_cancel_call (priv->proxy, priv->call); priv->call = NULL; } if (priv->pending_ip4_config) { g_object_unref (priv->pending_ip4_config); priv->pending_ip4_config = NULL; } priv->in_bytes = priv->out_bytes = 0; switch (priv->ip_method) { case MM_MODEM_IP_METHOD_PPP: if (priv->ppp_manager) { g_object_unref (priv->ppp_manager); priv->ppp_manager = NULL; } break; case MM_MODEM_IP_METHOD_STATIC: case MM_MODEM_IP_METHOD_DHCP: iface = nm_device_get_ip_iface (device); /* FIXME: use AF_UNSPEC here when we have IPv6 support */ nm_system_device_flush_routes_with_iface (iface, AF_INET); nm_system_device_flush_addresses_with_iface (iface); nm_system_device_set_up_down_with_iface (iface, FALSE, NULL); break; default: nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method); break; } } void nm_modem_deactivate_quickly (NMModem *self, NMDevice *device) { NM_MODEM_GET_CLASS (self)->deactivate_quickly (self, device); } static void disconnect_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) { GError *error = NULL; if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) { nm_log_info (LOGD_MB, "disconnect failed: (%d) %s", error ? error->code : -1, error && error->message ? error->message : "(unknown)"); } } void nm_modem_device_state_changed (NMModem *self, NMDeviceState new_state, NMDeviceState old_state, NMDeviceStateReason reason) { gboolean was_connected = FALSE; g_return_if_fail (self != NULL); g_return_if_fail (NM_IS_MODEM (self)); if (IS_ACTIVATING_STATE (old_state) || (old_state == NM_DEVICE_STATE_ACTIVATED)) was_connected = TRUE; /* Make sure we don't leave the serial device open */ switch (new_state) { case NM_DEVICE_STATE_NEED_AUTH: if (NM_MODEM_GET_PRIVATE (self)->ppp_manager) break; /* else fall through */ case NM_DEVICE_STATE_UNMANAGED: case NM_DEVICE_STATE_UNAVAILABLE: case NM_DEVICE_STATE_FAILED: case NM_DEVICE_STATE_DISCONNECTED: if (was_connected) { dbus_g_proxy_begin_call (nm_modem_get_proxy (self, MM_DBUS_INTERFACE_MODEM), "Disconnect", disconnect_done, self, NULL, G_TYPE_INVALID); } break; default: break; } } gboolean nm_modem_hw_is_up (NMModem *self, NMDevice *device) { guint32 ip_method = NM_MODEM_GET_PRIVATE (self)->ip_method; if (ip_method == MM_MODEM_IP_METHOD_STATIC || ip_method == MM_MODEM_IP_METHOD_DHCP) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); NMDeviceState state; state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); if (priv->pending_ip4_config || state == NM_DEVICE_STATE_IP_CONFIG || state == NM_DEVICE_STATE_ACTIVATED) return nm_system_device_is_up (device); } return TRUE; } gboolean nm_modem_hw_bring_up (NMModem *self, NMDevice *device, gboolean *no_firmware) { guint32 ip_method = NM_MODEM_GET_PRIVATE (self)->ip_method; if (ip_method == MM_MODEM_IP_METHOD_STATIC || ip_method == MM_MODEM_IP_METHOD_DHCP) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); NMDeviceState state; state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); if (priv->pending_ip4_config || state == NM_DEVICE_STATE_IP_CONFIG || state == NM_DEVICE_STATE_ACTIVATED) return nm_system_device_set_up_down (device, TRUE, no_firmware); } return TRUE; } const char * nm_modem_get_iface (NMModem *self) { g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_MODEM (self), NULL); return NM_MODEM_GET_PRIVATE (self)->iface; } const char * nm_modem_get_path (NMModem *self) { g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_MODEM (self), NULL); return NM_MODEM_GET_PRIVATE (self)->path; } static void get_mm_enabled_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) { NMModem *self = NM_MODEM (user_data); GError *error = NULL; GValue value = { 0, }; if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_VALUE, &value, G_TYPE_INVALID)) { nm_log_warn (LOGD_MB, "failed get modem enabled state: (%d) %s", error ? error->code : -1, error && error->message ? error->message : "(unknown)"); return; } if (G_VALUE_HOLDS_BOOLEAN (&value)) { NM_MODEM_GET_PRIVATE (self)->mm_enabled = g_value_get_boolean (&value); g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED); } else nm_log_warn (LOGD_MB, "failed get modem enabled state: unexpected reply type"); g_value_unset (&value); } static void query_mm_enabled (NMModem *self) { dbus_g_proxy_begin_call (NM_MODEM_GET_PRIVATE (self)->props_proxy, "Get", get_mm_enabled_done, self, NULL, G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, G_TYPE_STRING, "Enabled", G_TYPE_INVALID); } static void set_mm_enabled_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) { GError *error = NULL; if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) { nm_log_warn (LOGD_MB, "failed to enable/disable modem: (%d) %s", error ? error->code : -1, error && error->message ? error->message : "(unknown)"); } /* Update enabled/disabled state again */ query_mm_enabled (NM_MODEM (user_data)); } void nm_modem_set_mm_enabled (NMModem *self, gboolean enabled) { NMModemPrivate *priv; g_return_if_fail (self != NULL); g_return_if_fail (NM_IS_MODEM (self)); priv = NM_MODEM_GET_PRIVATE (self); /* FIXME: For now this just toggles the ModemManager enabled state. In the * future we want to tie this into rfkill state instead so that the user can * toggle rfkill status of the WWAN modem. */ if (priv->mm_enabled != enabled) { DBusGProxy *proxy; proxy = nm_modem_get_proxy (self, MM_DBUS_INTERFACE_MODEM); dbus_g_proxy_begin_call (proxy, "Enable", set_mm_enabled_done, self, NULL, G_TYPE_BOOLEAN, enabled, G_TYPE_INVALID); } } static void modem_properties_changed (DBusGProxy *proxy, const char *interface, GHashTable *props, gpointer user_data) { NMModem *self = NM_MODEM (user_data); NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); GValue *value; if (strcmp (interface, MM_DBUS_INTERFACE_MODEM)) return; value = g_hash_table_lookup (props, "Enabled"); if (value && G_VALUE_HOLDS_BOOLEAN (value)) { priv->mm_enabled = g_value_get_boolean (value); g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED); } value = g_hash_table_lookup (props, "IpMethod"); if (value && G_VALUE_HOLDS_UINT (value)) { priv->ip_method = g_value_get_uint (value); g_object_notify (G_OBJECT (self), NM_MODEM_IP_METHOD); } } /*****************************************************************************/ static void nm_modem_init (NMModem *self) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); priv->dbus_mgr = nm_dbus_manager_get (); } static GObject* constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params) { GObject *object; NMModemPrivate *priv; DBusGConnection *bus; object = G_OBJECT_CLASS (nm_modem_parent_class)->constructor (type, n_construct_params, construct_params); if (!object) return NULL; priv = NM_MODEM_GET_PRIVATE (object); if (!priv->device) { nm_log_err (LOGD_HW, "modem parent device not provided"); goto err; } if (!priv->device) { nm_log_err (LOGD_HW, "modem command interface not provided"); goto err; } if (!priv->path) { nm_log_err (LOGD_HW, "D-Bus path not provided"); goto err; } bus = nm_dbus_manager_get_connection (priv->dbus_mgr); priv->proxy = dbus_g_proxy_new_for_name (bus, MM_DBUS_SERVICE, priv->path, MM_DBUS_INTERFACE_MODEM); priv->props_proxy = dbus_g_proxy_new_for_name (bus, MM_DBUS_SERVICE, priv->path, DBUS_INTERFACE_PROPERTIES); dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED, G_TYPE_NONE, G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID); dbus_g_proxy_add_signal (priv->props_proxy, "MmPropertiesChanged", G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->props_proxy, "MmPropertiesChanged", G_CALLBACK (modem_properties_changed), object, NULL); query_mm_enabled (NM_MODEM (object)); return object; err: g_object_unref (object); return NULL; } static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); switch (prop_id) { case PROP_PATH: g_value_set_string (value, priv->path); break; case PROP_DEVICE: g_value_set_string (value, priv->device); break; case PROP_IFACE: g_value_set_string (value, priv->iface); break; case PROP_IP_METHOD: g_value_set_uint (value, priv->ip_method); break; case PROP_ENABLED: g_value_set_boolean (value, priv->mm_enabled); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); switch (prop_id) { case PROP_PATH: /* Construct only */ priv->path = g_value_dup_string (value); break; case PROP_DEVICE: /* Construct only */ priv->device = g_value_dup_string (value); break; case PROP_IFACE: /* Construct only */ priv->iface = g_value_dup_string (value); break; case PROP_IP_METHOD: /* Construct only */ priv->ip_method = g_value_get_uint (value); break; case PROP_ENABLED: break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void finalize (GObject *object) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); if (priv->proxy) g_object_unref (priv->proxy); if (priv->props_proxy) g_object_unref (priv->props_proxy); g_object_unref (priv->dbus_mgr); g_free (priv->iface); g_free (priv->path); g_free (priv->device); G_OBJECT_CLASS (nm_modem_parent_class)->finalize (object); } static void nm_modem_class_init (NMModemClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (NMModemPrivate)); /* Virtual methods */ object_class->constructor = constructor; object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; klass->act_stage1_prepare = real_act_stage1_prepare; klass->deactivate_quickly = real_deactivate_quickly; /* Properties */ g_object_class_install_property (object_class, PROP_PATH, g_param_spec_string (NM_MODEM_PATH, "DBus path", "DBus path", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_DEVICE, g_param_spec_string (NM_MODEM_DEVICE, "Device", "Master modem parent device", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_IFACE, g_param_spec_string (NM_MODEM_IFACE, "Interface", "Modem command interface", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_IP_METHOD, g_param_spec_uint (NM_MODEM_IP_METHOD, "IP method", "IP method", MM_MODEM_IP_METHOD_PPP, MM_MODEM_IP_METHOD_DHCP, MM_MODEM_IP_METHOD_PPP, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_ENABLED, g_param_spec_boolean (NM_MODEM_ENABLED, "Enabled", "Enabled", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); /* Signals */ signals[PPP_STATS] = g_signal_new ("ppp-stats", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMModemClass, ppp_stats), NULL, NULL, _nm_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); signals[PPP_FAILED] = g_signal_new ("ppp-failed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMModemClass, ppp_failed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[IP4_CONFIG_RESULT] = g_signal_new (NM_MODEM_IP4_CONFIG_RESULT, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMModemClass, ip4_config_result), NULL, NULL, _nm_marshal_VOID__STRING_OBJECT_POINTER, G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_POINTER); signals[PREPARE_RESULT] = g_signal_new (NM_MODEM_PREPARE_RESULT, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMModemClass, prepare_result), NULL, NULL, _nm_marshal_VOID__BOOLEAN_UINT, G_TYPE_NONE, 2, G_TYPE_BOOLEAN, G_TYPE_UINT); signals[NEED_AUTH] = g_signal_new (NM_MODEM_NEED_AUTH, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMModemClass, need_auth), NULL, NULL, _nm_marshal_VOID__STRING_BOOLEAN_UINT_STRING_STRING, G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); } const DBusGObjectInfo * nm_modem_get_serial_dbus_info (void) { return &dbus_glib_nm_serial_device_object_info; }