/* nmcli - command-line tool to control NetworkManager * * 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. * * (C) Copyright 2010 - 2012 Red Hat, Inc. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if WITH_WIMAX #include #endif #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "common.h" #include "settings.h" #include "connections.h" /* Available fields for 'con list' */ static NmcOutputField nmc_fields_con_list[] = { {"NAME", N_("NAME"), 25, NULL, 0}, /* 0 */ {"UUID", N_("UUID"), 38, NULL, 0}, /* 1 */ {"TYPE", N_("TYPE"), 17, NULL, 0}, /* 2 */ {"TIMESTAMP", N_("TIMESTAMP"), 12, NULL, 0}, /* 3 */ {"TIMESTAMP-REAL", N_("TIMESTAMP-REAL"), 34, NULL, 0}, /* 4 */ {"AUTOCONNECT", N_("AUTOCONNECT"), 13, NULL, 0}, /* 5 */ {"READONLY", N_("READONLY"), 10, NULL, 0}, /* 6 */ {"DBUS-PATH", N_("DBUS-PATH"), 42, NULL, 0}, /* 7 */ {NULL, NULL, 0, NULL, 0} }; #define NMC_FIELDS_CON_LIST_ALL "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,READONLY,DBUS-PATH" #define NMC_FIELDS_CON_LIST_COMMON "NAME,UUID,TYPE,TIMESTAMP-REAL" /* Helper macro to define fields */ #define SETTING_FIELD(setting, width) { setting, N_(setting), width, NULL, 0 } /* Available settings for 'con list id/uuid ' */ static NmcOutputField nmc_fields_settings_names[] = { SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME, 0), /* 0 */ SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME, 0), /* 1 */ SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME, 0), /* 2 */ SETTING_FIELD (NM_SETTING_WIRELESS_SETTING_NAME, 0), /* 3 */ SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, 0), /* 4 */ SETTING_FIELD (NM_SETTING_IP4_CONFIG_SETTING_NAME, 0), /* 5 */ SETTING_FIELD (NM_SETTING_IP6_CONFIG_SETTING_NAME, 0), /* 6 */ SETTING_FIELD (NM_SETTING_SERIAL_SETTING_NAME, 0), /* 7 */ SETTING_FIELD (NM_SETTING_PPP_SETTING_NAME, 0), /* 8 */ SETTING_FIELD (NM_SETTING_PPPOE_SETTING_NAME, 0), /* 9 */ SETTING_FIELD (NM_SETTING_GSM_SETTING_NAME, 0), /* 10 */ SETTING_FIELD (NM_SETTING_CDMA_SETTING_NAME, 0), /* 11 */ SETTING_FIELD (NM_SETTING_BLUETOOTH_SETTING_NAME, 0), /* 12 */ SETTING_FIELD (NM_SETTING_OLPC_MESH_SETTING_NAME, 0), /* 13 */ SETTING_FIELD (NM_SETTING_VPN_SETTING_NAME, 0), /* 14 */ SETTING_FIELD (NM_SETTING_WIMAX_SETTING_NAME, 0), /* 15 */ SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME, 0), /* 16 */ SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME, 0), /* 17 */ SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME, 0), /* 18 */ SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME, 0), /* 19 */ SETTING_FIELD (NM_SETTING_BRIDGE_SETTING_NAME, 0), /* 20 */ SETTING_FIELD (NM_SETTING_BRIDGE_PORT_SETTING_NAME, 0), /* 21 */ {NULL, NULL, 0, NULL, 0} }; #define NMC_FIELDS_SETTINGS_NAMES_ALL_X NM_SETTING_CONNECTION_SETTING_NAME","\ NM_SETTING_WIRED_SETTING_NAME","\ NM_SETTING_802_1X_SETTING_NAME","\ NM_SETTING_WIRELESS_SETTING_NAME","\ NM_SETTING_WIRELESS_SECURITY_SETTING_NAME","\ NM_SETTING_IP4_CONFIG_SETTING_NAME","\ NM_SETTING_IP6_CONFIG_SETTING_NAME","\ NM_SETTING_SERIAL_SETTING_NAME","\ NM_SETTING_PPP_SETTING_NAME","\ NM_SETTING_PPPOE_SETTING_NAME","\ NM_SETTING_ADSL_SETTING_NAME","\ NM_SETTING_GSM_SETTING_NAME","\ NM_SETTING_CDMA_SETTING_NAME","\ NM_SETTING_BLUETOOTH_SETTING_NAME","\ NM_SETTING_OLPC_MESH_SETTING_NAME","\ NM_SETTING_VPN_SETTING_NAME","\ NM_SETTING_INFINIBAND_SETTING_NAME","\ NM_SETTING_BOND_SETTING_NAME","\ NM_SETTING_VLAN_SETTING_NAME","\ NM_SETTING_BRIDGE_SETTING_NAME","\ NM_SETTING_BRIDGE_PORT_SETTING_NAME #if WITH_WIMAX #define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X","\ NM_SETTING_WIMAX_SETTING_NAME #else #define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X #endif /* Available fields for 'con status' */ static NmcOutputField nmc_fields_con_status[] = { {"GROUP", N_("GROUP"), 9, NULL, 0}, /* 0 */ /* used only for 'GENERAL' group listing */ {"NAME", N_("NAME"), 25, NULL, 0}, /* 1 */ {"UUID", N_("UUID"), 38, NULL, 0}, /* 2 */ {"DEVICES", N_("DEVICES"), 10, NULL, 0}, /* 3 */ {"STATE", N_("STATE"), 12, NULL, 0}, /* 4 */ {"DEFAULT", N_("DEFAULT"), 8, NULL, 0}, /* 5 */ {"DEFAULT6", N_("DEFAULT6"), 9, NULL, 0}, /* 6 */ {"SPEC-OBJECT", N_("SPEC-OBJECT"), 10, NULL, 0}, /* 7 */ {"VPN", N_("VPN"), 5, NULL, 0}, /* 8 */ {"DBUS-PATH", N_("DBUS-PATH"), 51, NULL, 0}, /* 9 */ {"CON-PATH", N_("CON-PATH"), 44, NULL, 0}, /* 10 */ {"ZONE", N_("ZONE"), 15, NULL, 0}, /* 11 */ {"MASTER-PATH", N_("MASTER-PATH"), 44, NULL, 0}, /* 12 */ {NULL, NULL, 0, NULL, 0} }; #define NMC_FIELDS_CON_STATUS_ALL "NAME,UUID,DEVICES,STATE,DEFAULT,DEFAULT6,VPN,ZONE,DBUS-PATH,CON-PATH,SPEC-OBJECT,MASTER-PATH" #define NMC_FIELDS_CON_STATUS_COMMON "NAME,UUID,DEVICES,DEFAULT,VPN,MASTER-PATH" /* Available fields for 'con status id/uuid/path ' */ static NmcOutputField nmc_fields_status_details_groups[] = { {"GENERAL", N_("GENERAL"), 9, NULL, 0}, /* 0 */ {"IP", N_("IP"), 5, NULL, 0}, /* 1 */ {"VPN", N_("VPN"), 5, NULL, 0}, /* 2 */ {NULL, NULL, 0, NULL, 0} }; #define NMC_FIELDS_CON_STATUS_DETAILS_ALL "GENERAL,IP,VPN" /* GENERAL group is the same as nmc_fields_con_status */ #define NMC_FIELDS_CON_STATUS_DETAILS_GENERAL_ALL "GROUP,"NMC_FIELDS_CON_STATUS_ALL /* IP group is handled by common.c */ /* Available fields for VPN group */ static NmcOutputField nmc_fields_status_details_vpn[] = { {"GROUP", N_("GROUP"), 9, NULL, 0}, /* 0 */ {"TYPE", N_("TYPE"), 15, NULL, 0}, /* 1 */ {"USERNAME", N_("USERNAME"), 15, NULL, 0}, /* 2 */ {"GATEWAY", N_("GATEWAY"), 25, NULL, 0}, /* 3 */ {"BANNER", N_("BANNER"), 120, NULL, 0}, /* 4 */ {"VPN-STATE", N_("VPN-STATE"), 40, NULL, 0}, /* 5 */ {"CFG", N_("CFG"), 120, NULL, 0}, /* 6 */ {NULL, NULL, 0, NULL, 0} }; #define NMC_FIELDS_CON_STATUS_DETAILS_VPN_ALL "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG" typedef struct { NmCli *nmc; int argc; char **argv; } ArgsInfo; /* glib main loop variable - defined in nmcli.c */ extern GMainLoop *loop; static ArgsInfo args_info; static guint progress_id = 0; /* ID of event source for displaying progress */ static void usage (void) { fprintf (stderr, _("Usage: nmcli connection { COMMAND | help }\n" " COMMAND := { list | status | up | down | delete }\n\n" " list [id | uuid ]\n" " status [id | uuid | path ]\n" #if WITH_WIMAX " up id | uuid [iface ] [ap ] [nsp ] [--nowait] [--timeout ]\n" #else " up id | uuid [iface ] [ap ] [--nowait] [--timeout ]\n" #endif " down id | uuid \n" " delete id | uuid \n" "\n" )); } /* The real commands that do something - i.e. not 'help', etc. */ static const char *real_con_commands[] = { "list", "status", "up", "down", "delete", NULL }; /* quit main loop */ static void quit (void) { if (progress_id) { g_source_remove (progress_id); nmc_terminal_erase_line (); } g_main_loop_quit (loop); /* quit main loop */ } static gboolean nmc_connection_detail (NMConnection *connection, NmCli *nmc) { GError *error = NULL; GArray *print_settings_array; int i; char *fields_str; char *fields_all = NMC_FIELDS_SETTINGS_NAMES_ALL; char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL; guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; gboolean was_output = FALSE; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) fields_str = fields_all; else fields_str = nmc->required_fields; print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, &error); if (error) { if (error->code == 0) g_string_printf (nmc->return_text, _("Error: 'con list': %s"), error->message); else g_string_printf (nmc->return_text, _("Error: 'con list': %s; allowed fields: %s"), error->message, NMC_FIELDS_SETTINGS_NAMES_ALL); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return FALSE; } nmc->allowed_fields = nmc_fields_settings_names; nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ONLY; nmc->print_fields.header_name = _("Connection details"); nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL, nmc->allowed_fields, NULL); print_fields (nmc->print_fields, nmc->allowed_fields); /* Loop through the required settings and print them. */ for (i = 0; i < print_settings_array->len; i++) { NMSetting *setting; int section_idx = g_array_index (print_settings_array, int, i); if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output) printf ("\n"); /* Empty line */ was_output = FALSE; setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name); if (setting) { setting_details (setting, nmc); was_output = TRUE; continue; } } if (print_settings_array) g_array_free (print_settings_array, FALSE); return NMC_RESULT_SUCCESS; } static void show_connection (NMConnection *data, gpointer user_data) { NMConnection *connection = (NMConnection *) data; NmCli *nmc = (NmCli *) user_data; NMSettingConnection *s_con; guint64 timestamp; time_t timestamp_real; char *timestamp_str; char timestamp_real_str[64]; s_con = nm_connection_get_setting_connection (connection); if (s_con) { /* Obtain field values */ timestamp = nm_setting_connection_get_timestamp (s_con); timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp); timestamp_real = timestamp; strftime (timestamp_real_str, sizeof (timestamp_real_str), "%c", localtime (×tamp_real)); nmc->allowed_fields[0].value = (char *) nm_setting_connection_get_id (s_con); nmc->allowed_fields[1].value = (char *) nm_setting_connection_get_uuid (s_con); nmc->allowed_fields[2].value = (char *) nm_setting_connection_get_connection_type (s_con); nmc->allowed_fields[3].value = timestamp_str; nmc->allowed_fields[4].value = timestamp ? timestamp_real_str : _("never"); nmc->allowed_fields[5].value = nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no"); nmc->allowed_fields[6].value = nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no"); nmc->allowed_fields[7].value = (char *) nm_connection_get_path (connection); nmc->print_fields.flags &= ~NMC_PF_FLAG_MAIN_HEADER_ADD & ~NMC_PF_FLAG_MAIN_HEADER_ONLY & ~NMC_PF_FLAG_FIELD_NAMES; /* Clear header flags */ print_fields (nmc->print_fields, nmc->allowed_fields); g_free (timestamp_str); } } static NMConnection * find_connection (GSList *list, const char *filter_type, const char *filter_val) { NMSettingConnection *s_con; NMConnection *connection; GSList *iterator; const char *id; const char *uuid; iterator = list; while (iterator) { connection = NM_CONNECTION (iterator->data); s_con = nm_connection_get_setting_connection (connection); if (s_con) { id = nm_setting_connection_get_id (s_con); uuid = nm_setting_connection_get_uuid (s_con); if (filter_type) { if ((strcmp (filter_type, "id") == 0 && strcmp (filter_val, id) == 0) || (strcmp (filter_type, "uuid") == 0 && strcmp (filter_val, uuid) == 0)) { return connection; } } } iterator = g_slist_next (iterator); } return NULL; } static NMCResultCode do_connections_list (NmCli *nmc, int argc, char **argv) { GError *error1 = NULL; GError *error2 = NULL; char *fields_str; char *fields_all = NMC_FIELDS_CON_LIST_ALL; char *fields_common = NMC_FIELDS_CON_LIST_COMMON; guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; gboolean valid_param_specified = FALSE; nmc->should_wait = FALSE; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) fields_str = fields_all; else fields_str = nmc->required_fields; nmc->allowed_fields = nmc_fields_con_list; nmc->print_fields.indices = parse_output_fields (fields_str, nmc->allowed_fields, &error1); /* error1 is checked later - it's not valid for connection details */ if (argc == 0) { if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error2)) goto error; if (error1) goto error; valid_param_specified = TRUE; /* Print headers */ nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; nmc->print_fields.header_name = _("Connection list"); print_fields (nmc->print_fields, nmc->allowed_fields); /* Print values */ g_slist_foreach (nmc->system_connections, (GFunc) show_connection, nmc); } else { while (argc > 0) { if (strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0) { const char *selector = *argv; NMConnection *con; if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } valid_param_specified = TRUE; if (!nmc->mode_specified) nmc->multiline_output = TRUE; /* multiline mode is default for 'con list id|uuid' */ con = find_connection (nmc->system_connections, selector, *argv); if (con) { nmc_connection_detail (con, nmc); } else { g_string_printf (nmc->return_text, _("Error: %s - no such connection."), *argv); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; } break; } else { fprintf (stderr, _("Unknown parameter: %s\n"), *argv); } argc--; argv++; } } if (!valid_param_specified) { g_string_printf (nmc->return_text, _("Error: no valid parameter specified.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; } return nmc->return_value; error: if (error1) { if (error1->code == 0) g_string_printf (nmc->return_text, _("Error: 'con list': %s"), error1->message); else g_string_printf (nmc->return_text, _("Error: 'con list': %s; allowed fields: %s"), error1->message, NMC_FIELDS_CON_LIST_ALL); g_error_free (error1); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; } if (error2) { g_string_printf (nmc->return_text, _("Error: %s."), error2->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; g_error_free (error2); } return nmc->return_value; } static const char * active_connection_state_to_string (NMActiveConnectionState state) { switch (state) { case NM_ACTIVE_CONNECTION_STATE_ACTIVATING: return _("activating"); case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: return _("activated"); case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: return _("deactivating"); case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED: return _("deactivated"); case NM_ACTIVE_CONNECTION_STATE_UNKNOWN: default: return _("unknown"); } } static const char * vpn_connection_state_to_string (NMVPNConnectionState state) { switch (state) { case NM_VPN_CONNECTION_STATE_PREPARE: return _("VPN connecting (prepare)"); case NM_VPN_CONNECTION_STATE_NEED_AUTH: return _("VPN connecting (need authentication)"); case NM_VPN_CONNECTION_STATE_CONNECT: return _("VPN connecting"); case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: return _("VPN connecting (getting IP configuration)"); case NM_VPN_CONNECTION_STATE_ACTIVATED: return _("VPN connected"); case NM_VPN_CONNECTION_STATE_FAILED: return _("VPN connection failed"); case NM_VPN_CONNECTION_STATE_DISCONNECTED: return _("VPN disconnected"); default: return _("unknown"); } } static NMConnection * get_connection_for_active (const GSList *con_list, NMActiveConnection *active) { const GSList *iter; const char *path; path = nm_active_connection_get_connection (active); g_return_val_if_fail (path != NULL, NULL); for (iter = con_list; iter; iter = g_slist_next (iter)) { NMConnection *candidate = NM_CONNECTION (iter->data); if (strcmp (nm_connection_get_path (candidate), path) == 0) return candidate; } return NULL; } static gboolean fill_in_fields_con_status (NMActiveConnection *active, GSList *con_list) { GSList *iter; const char *active_path; NMSettingConnection *s_con; const GPtrArray *devices; GString *dev_str; NMActiveConnectionState state; int i; gboolean success = FALSE; active_path = nm_active_connection_get_connection (active); state = nm_active_connection_get_state (active); /* Get devices of the active connection */ dev_str = g_string_new (NULL); devices = nm_active_connection_get_devices (active); for (i = 0; devices && (i < devices->len); i++) { NMDevice *device = g_ptr_array_index (devices, i); const char *dev_iface = nm_device_get_iface (device); if (dev_iface) { g_string_append (dev_str, dev_iface); g_string_append_c (dev_str, ','); } } if (dev_str->len > 0) g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */ /* Fill field values */ nmc_fields_con_status[0].value = (char *) nmc_fields_status_details_groups[0].name; nmc_fields_con_status[1].value = _("N/A"); nmc_fields_con_status[2].value = (char *) nm_active_connection_get_uuid (active); nmc_fields_con_status[3].value = dev_str->str; nmc_fields_con_status[4].value = (char *) active_connection_state_to_string (state); nmc_fields_con_status[5].value = nm_active_connection_get_default (active) ? _("yes") : _("no"); nmc_fields_con_status[6].value = nm_active_connection_get_default6 (active) ? _("yes") : _("no"); nmc_fields_con_status[7].value = (char *) nm_active_connection_get_specific_object (active); nmc_fields_con_status[8].value = NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no"); nmc_fields_con_status[9].value = (char *) nm_object_get_path (NM_OBJECT (active)); nmc_fields_con_status[10].value = (char *) nm_active_connection_get_connection (active); nmc_fields_con_status[11].value = _("N/A"); nmc_fields_con_status[12].value = (char *) nm_active_connection_get_master (active); for (iter = con_list; iter; iter = g_slist_next (iter)) { NMConnection *connection = (NMConnection *) iter->data; const char *con_path = nm_connection_get_path (connection); if (!strcmp (active_path, con_path)) { /* This connection is active */ s_con = nm_connection_get_setting_connection (connection); g_assert (s_con != NULL); /* Fill field values that depend on NMConnection */ nmc_fields_con_status[1].value = (char *) nm_setting_connection_get_id (s_con); nmc_fields_con_status[11].value = (char *) nm_setting_connection_get_zone (s_con); success = TRUE; break; } } /* Just free GString here, the char array has to be freed after printing * (by free_fields_con_status()) */ g_string_free (dev_str, FALSE); return success; } static void free_fields_con_status (void) { /* Just DEVICES string was dynamically allocated */ g_free ((char *) nmc_fields_con_status[3].value); nmc_fields_con_status[3].value = NULL; } static void show_active_connection (gpointer data, gpointer user_data) { NMActiveConnection *active = NM_ACTIVE_CONNECTION (data); NmCli *nmc = (NmCli *) user_data; fill_in_fields_con_status (active, nmc->system_connections); nmc->print_fields.flags &= ~NMC_PF_FLAG_MAIN_HEADER_ADD & ~NMC_PF_FLAG_MAIN_HEADER_ONLY & ~NMC_PF_FLAG_FIELD_NAMES; /* Clear header flags */ print_fields (nmc->print_fields, nmc->allowed_fields); free_fields_con_status (); } static NMActiveConnection * find_active_connection (const GPtrArray *active_cons, const GSList *cons, const char *filter_type, const char *filter_val) { int i; const char *s_path, *a_path; const char *id; const char *uuid; NMConnection *con; NMSettingConnection *s_con; for (i = 0; active_cons && (i < active_cons->len); i++) { NMActiveConnection *candidate = g_ptr_array_index (active_cons, i); s_path = nm_active_connection_get_connection (candidate); a_path = nm_object_get_path (NM_OBJECT (candidate)); uuid = nm_active_connection_get_uuid (candidate); con = get_connection_for_active (cons, candidate); s_con = nm_connection_get_setting_connection (con); g_assert (s_con != NULL); id = nm_setting_connection_get_id (s_con); if (filter_type) { if ( (strcmp (filter_type, "id") == 0 && strcmp (filter_val, id) == 0) || (strcmp (filter_type, "uuid") == 0 && strcmp (filter_val, uuid) == 0) || (strcmp (filter_type, "path") == 0 && strcmp (filter_val, s_path) == 0) || (strcmp (filter_type, "path") == 0 && strcmp (filter_val, a_path) == 0)) { return candidate; } } } return NULL; } typedef struct { char **array; guint32 idx; } FillVPNDataInfo; static void fill_vpn_data_item (const char *key, const char *value, gpointer user_data) { FillVPNDataInfo *info = (FillVPNDataInfo *) user_data; info->array[info->idx++] = g_strdup_printf ("%s = %s", key, value); } // FIXME: The same or similar code for VPN info appears also in nm-applet (applet-dialogs.c), // and in gnome-control-center as well. It could probably be shared somehow. static char * get_vpn_connection_type (NMConnection *connection) { const char *type, *p; /* The service type is in form of "org.freedesktop.NetworkManager.vpnc". * Extract end part after last dot, e.g. "vpnc" */ type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection)); p = strrchr (type, '.'); return g_strdup (p ? p + 1 : type); } /* VPN parameters can be found at: * http://git.gnome.org/browse/network-manager-openvpn/tree/src/nm-openvpn-service.h * http://git.gnome.org/browse/network-manager-vpnc/tree/src/nm-vpnc-service.h * http://git.gnome.org/browse/network-manager-pptp/tree/src/nm-pptp-service.h * http://git.gnome.org/browse/network-manager-openconnect/tree/src/nm-openconnect-service.h * http://git.gnome.org/browse/network-manager-openswan/tree/src/nm-openswan-service.h * See also 'properties' directory in these plugins. */ static const gchar * find_vpn_gateway_key (const char *vpn_type) { if (g_strcmp0 (vpn_type, "openvpn") == 0) return "remote"; if (g_strcmp0 (vpn_type, "vpnc") == 0) return "IPSec gateway"; if (g_strcmp0 (vpn_type, "pptp") == 0) return "gateway"; if (g_strcmp0 (vpn_type, "openconnect") == 0) return "gateway"; if (g_strcmp0 (vpn_type, "openswan") == 0) return "right"; return ""; } static const gchar * find_vpn_username_key (const char *vpn_type) { if (g_strcmp0 (vpn_type, "openvpn") == 0) return "username"; if (g_strcmp0 (vpn_type, "vpnc") == 0) return "Xauth username"; if (g_strcmp0 (vpn_type, "pptp") == 0) return "user"; if (g_strcmp0 (vpn_type, "openconnect") == 0) return "username"; if (g_strcmp0 (vpn_type, "openswan") == 0) return "leftxauthusername"; return ""; } enum VpnDataItem { VPN_DATA_ITEM_GATEWAY, VPN_DATA_ITEM_USERNAME }; static const gchar * get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item) { const char *key; char *type = get_vpn_connection_type (connection); switch (vpn_data_item) { case VPN_DATA_ITEM_GATEWAY: key = find_vpn_gateway_key (type); break; case VPN_DATA_ITEM_USERNAME: key = find_vpn_username_key (type); break; default: key = ""; break; } g_free (type); return nm_setting_vpn_get_data_item (nm_connection_get_setting_vpn (connection), key); } /* FIXME end */ static gboolean nmc_active_connection_detail (NMActiveConnection *acon, NmCli *nmc) { GError *error = NULL; GArray *print_groups; int i; char *fields_str; char *fields_all = NMC_FIELDS_CON_STATUS_DETAILS_ALL; char *fields_common = NMC_FIELDS_CON_STATUS_DETAILS_ALL; guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; gboolean was_output = FALSE; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) fields_str = fields_all; else fields_str = nmc->required_fields; print_groups = parse_output_fields (fields_str, nmc_fields_status_details_groups, &error); if (error) { if (error->code == 0) g_string_printf (nmc->return_text, _("Error: 'con status': %s"), error->message); else g_string_printf (nmc->return_text, _("Error: 'con status': %s; allowed fields: %s"), error->message, NMC_FIELDS_CON_STATUS_DETAILS_ALL); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return FALSE; } nmc->allowed_fields = nmc_fields_status_details_groups; nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ONLY; nmc->print_fields.header_name = _("Active connection details"); nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_STATUS_DETAILS_ALL, nmc->allowed_fields, NULL); print_fields (nmc->print_fields, nmc->allowed_fields); /* Loop through the groups and print them. */ for (i = 0; i < print_groups->len; i++) { int group_idx = g_array_index (print_groups, int, i); if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output) printf ("\n"); /* Empty line */ was_output = FALSE; /* GENERAL */ if (strcasecmp (nmc_fields_status_details_groups[group_idx].name, nmc_fields_status_details_groups[0].name) == 0) { nmc->allowed_fields = nmc_fields_con_status; nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_STATUS_DETAILS_GENERAL_ALL, nmc->allowed_fields, NULL); /* Print field names */ nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; print_fields (nmc->print_fields, nmc->allowed_fields); /* Fill in values */ fill_in_fields_con_status (acon, nmc->system_connections); /* and print them */ nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; print_fields (nmc->print_fields, nmc->allowed_fields); free_fields_con_status (); was_output = TRUE; } /* IP */ if (strcasecmp (nmc_fields_status_details_groups[group_idx].name, nmc_fields_status_details_groups[1].name) == 0) { const GPtrArray *devices; int j; devices = nm_active_connection_get_devices (acon); for (j = 0; devices && (j < devices->len); j++) { gboolean b1 = FALSE, b2 = FALSE, b3 = FALSE, b4 = FALSE; NMDevice *device = g_ptr_array_index (devices, j); NMIP4Config *cfg4 = nm_device_get_ip4_config (device); NMIP6Config *cfg6 = nm_device_get_ip6_config (device); NMDHCP4Config *dhcp4 = nm_device_get_dhcp4_config (device); NMDHCP6Config *dhcp6 = nm_device_get_dhcp6_config (device); b1 = print_ip4_config (cfg4, nmc, "IP4"); b2 = print_dhcp4_config (dhcp4, nmc, "DHCP4"); b3 = print_ip6_config (cfg6, nmc, "IP6"); b4 = print_dhcp6_config (dhcp6, nmc, "DHCP6"); was_output = was_output || b1 || b2 || b3 || b4; } } /* VPN */ if (NM_IS_VPN_CONNECTION (acon) && strcasecmp (nmc_fields_status_details_groups[group_idx].name, nmc_fields_status_details_groups[2].name) == 0) { NMConnection *con; NMSettingConnection *s_con; NMSettingVPN *s_vpn; NMVPNConnectionState vpn_state; char *type_str, *banner_str, *vpn_state_str; const char *username = NULL; char **vpn_data_array = NULL; guint32 items_num; con = get_connection_for_active (nmc->system_connections, acon); s_con = nm_connection_get_setting_connection (con); g_assert (s_con != NULL); nmc->allowed_fields = nmc_fields_status_details_vpn; nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_STATUS_DETAILS_VPN_ALL, nmc->allowed_fields, NULL); /* Print field names */ nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_FIELD_NAMES; print_fields (nmc->print_fields, nmc->allowed_fields); s_vpn = nm_connection_get_setting_vpn (con); if (s_vpn) { items_num = nm_setting_vpn_get_num_data_items (s_vpn); if (items_num > 0) { FillVPNDataInfo info; vpn_data_array = g_new (char *, items_num + 1); info.array = vpn_data_array; info.idx = 0; nm_setting_vpn_foreach_data_item (s_vpn, &fill_vpn_data_item, &info); vpn_data_array[items_num] = NULL; } username = nm_setting_vpn_get_user_name (s_vpn); } type_str = get_vpn_connection_type (con); banner_str = g_strescape (nm_vpn_connection_get_banner (NM_VPN_CONNECTION (acon)), ""); vpn_state = nm_vpn_connection_get_vpn_state (NM_VPN_CONNECTION (acon)); vpn_state_str = g_strdup_printf ("%d - %s", vpn_state, vpn_connection_state_to_string (vpn_state)); /* Print values */ set_val_str (nmc->allowed_fields, 0, (char *) nmc_fields_status_details_groups[2].name); set_val_str (nmc->allowed_fields, 1, type_str); set_val_str (nmc->allowed_fields, 2, (char *) (username ? username : get_vpn_data_item (con, VPN_DATA_ITEM_USERNAME))); set_val_str (nmc->allowed_fields, 3, (char *) get_vpn_data_item (con, VPN_DATA_ITEM_GATEWAY)); set_val_str (nmc->allowed_fields, 4, banner_str); set_val_str (nmc->allowed_fields, 5, vpn_state_str); set_val_arr (nmc->allowed_fields, 6, vpn_data_array); nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_SECTION_PREFIX; print_fields (nmc->print_fields, nmc->allowed_fields); g_free (type_str); g_free (banner_str); g_free (vpn_state_str); g_strfreev (vpn_data_array); was_output = TRUE; } } if (print_groups) g_array_free (print_groups, FALSE); return TRUE; } static NMCResultCode do_connections_status (NmCli *nmc, int argc, char **argv) { const GPtrArray *active_cons; GError *err = NULL; GError *err1 = NULL; nmc->should_wait = FALSE; if (!nmc_is_nm_running (nmc, &err)) { if (err) { g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), err->message); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; g_error_free (err); } else { g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; } goto error; } /* Get active connections */ nmc->get_client (nmc); active_cons = nm_client_get_active_connections (nmc->client); if (argc == 0) { char *fields_str; char *fields_all = NMC_FIELDS_CON_STATUS_ALL; char *fields_common = NMC_FIELDS_CON_STATUS_COMMON; guint32 mode_flag = (nmc->print_output == NMC_PRINT_PRETTY) ? NMC_PF_FLAG_PRETTY : (nmc->print_output == NMC_PRINT_TERSE) ? NMC_PF_FLAG_TERSE : 0; guint32 multiline_flag = nmc->multiline_output ? NMC_PF_FLAG_MULTILINE : 0; guint32 escape_flag = nmc->escape_values ? NMC_PF_FLAG_ESCAPE : 0; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) fields_str = fields_all; else fields_str = nmc->required_fields; nmc->allowed_fields = nmc_fields_con_status + 1 ; nmc->print_fields.indices = parse_output_fields (fields_str, nmc->allowed_fields, &err1); if (err1) goto error; /* Print headers */ nmc->print_fields.flags = multiline_flag | mode_flag | escape_flag | NMC_PF_FLAG_MAIN_HEADER_ADD | NMC_PF_FLAG_FIELD_NAMES; nmc->print_fields.header_name = _("Active connections"); print_fields (nmc->print_fields, nmc->allowed_fields); if (active_cons && active_cons->len) g_ptr_array_foreach ((GPtrArray *) active_cons, show_active_connection, (gpointer) nmc); } else { while (argc > 0) { if ( strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0 || strcmp (*argv, "path") == 0) { const char *selector = *argv; NMActiveConnection *acon; if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } if (!nmc->mode_specified) nmc->multiline_output = TRUE; /* multiline mode is default for 'con status id|uuid|path' */ acon = find_active_connection (active_cons, nmc->system_connections, selector, *argv); if (acon) { nmc_active_connection_detail (acon, nmc); } else { g_string_printf (nmc->return_text, _("Error: '%s' is not an active connection."), *argv); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; } break; } else { g_string_printf (nmc->return_text, _("Error: unknown parameter: %s"), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; } argc--; argv++; } } error: if (err1) { if (err1->code == 0) g_string_printf (nmc->return_text, _("Error: 'con status': %s"), err1->message); else g_string_printf (nmc->return_text, _("Error: 'con status': %s; allowed fields: %s"), err1->message, NMC_FIELDS_CON_STATUS_ALL); g_error_free (err1); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; } return nmc->return_value; } static NMActiveConnection * get_default_active_connection (NmCli *nmc, NMDevice **device) { NMActiveConnection *default_ac = NULL; NMDevice *non_default_device = NULL; NMActiveConnection *non_default_ac = NULL; const GPtrArray *connections; int i; g_return_val_if_fail (nmc != NULL, NULL); g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (*device == NULL, NULL); connections = nm_client_get_active_connections (nmc->client); for (i = 0; connections && (i < connections->len); i++) { NMActiveConnection *candidate = g_ptr_array_index (connections, i); const GPtrArray *devices; devices = nm_active_connection_get_devices (candidate); if (!devices || !devices->len) continue; if (nm_active_connection_get_default (candidate)) { if (!default_ac) { *device = g_ptr_array_index (devices, 0); default_ac = candidate; } } else { if (!non_default_ac) { non_default_device = g_ptr_array_index (devices, 0); non_default_ac = candidate; } } } /* Prefer the default connection if one exists, otherwise return the first * non-default connection. */ if (!default_ac && non_default_ac) { default_ac = non_default_ac; *device = non_default_device; } return default_ac; } /* Find a device to activate the connection on. * IN: connection: connection to activate * iface: device interface name to use (optional) * ap: access point to use (optional; valid just for 802-11-wireless) * nsp: Network Service Provider to use (option; valid only for wimax) * OUT: device: found device * spec_object: specific_object path of NMAccessPoint * RETURNS: TRUE when a device is found, FALSE otherwise. */ static gboolean find_device_for_connection (NmCli *nmc, NMConnection *connection, const char *iface, const char *ap, const char *nsp, NMDevice **device, const char **spec_object, GError **error) { NMSettingConnection *s_con; const char *con_type; int i, j; g_return_val_if_fail (nmc != NULL, FALSE); g_return_val_if_fail (device != NULL && *device == NULL, FALSE); g_return_val_if_fail (spec_object != NULL && *spec_object == NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); s_con = nm_connection_get_setting_connection (connection); g_assert (s_con); con_type = nm_setting_connection_get_connection_type (s_con); if (strcmp (con_type, NM_SETTING_VPN_SETTING_NAME) == 0) { /* VPN connections */ NMActiveConnection *active = NULL; if (iface) { *device = nm_client_get_device_by_iface (nmc->client, iface); if (*device) active = nm_device_get_active_connection (*device); if (!active) { g_set_error (error, NMCLI_ERROR, 0, _("no active connection on device '%s'"), iface); return FALSE; } *spec_object = nm_object_get_path (NM_OBJECT (active)); return TRUE; } else { active = get_default_active_connection (nmc, device); if (!active) { g_set_error_literal (error, NMCLI_ERROR, 0, _("no active connection or device")); return FALSE; } *spec_object = nm_object_get_path (NM_OBJECT (active)); return TRUE; } } else { /* Other connections */ NMDevice *found_device = NULL; const GPtrArray *devices = nm_client_get_devices (nmc->client); for (i = 0; devices && (i < devices->len) && !found_device; i++) { NMDevice *dev = g_ptr_array_index (devices, i); if (iface) { const char *dev_iface = nm_device_get_iface (dev); if ( !g_strcmp0 (dev_iface, iface) && nm_device_connection_compatible (dev, connection, NULL)) { found_device = dev; } } else { if (nm_device_connection_compatible (dev, connection, NULL)) { found_device = dev; } } if (found_device && ap && !strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME) && NM_IS_DEVICE_WIFI (dev)) { char *bssid_up = g_ascii_strup (ap, -1); const GPtrArray *aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev)); found_device = NULL; /* Mark as not found; set to the device again later, only if AP matches */ for (j = 0; aps && (j < aps->len); j++) { NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j); const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); if (!strcmp (bssid_up, candidate_bssid)) { found_device = dev; *spec_object = nm_object_get_path (NM_OBJECT (candidate_ap)); break; } } g_free (bssid_up); } #if WITH_WIMAX if ( found_device && nsp && !strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME) && NM_IS_DEVICE_WIMAX (dev)) { const GPtrArray *nsps = nm_device_wimax_get_nsps (NM_DEVICE_WIMAX (dev)); found_device = NULL; /* Mark as not found; set to the device again later, only if NSP matches */ for (j = 0; nsps && (j < nsps->len); j++) { NMWimaxNsp *candidate_nsp = g_ptr_array_index (nsps, j); const char *candidate_name = nm_wimax_nsp_get_name (candidate_nsp); if (!strcmp (nsp, candidate_name)) { found_device = dev; *spec_object = nm_object_get_path (NM_OBJECT (candidate_nsp)); break; } } } #endif } if (found_device) { *device = found_device; return TRUE; } else { if (iface) g_set_error (error, NMCLI_ERROR, 0, _("device '%s' not compatible with connection '%s'"), iface, nm_setting_connection_get_id (s_con)); else g_set_error (error, NMCLI_ERROR, 0, _("no device found for connection '%s'"), nm_setting_connection_get_id (s_con)); return FALSE; } } } static const char * vpn_connection_state_reason_to_string (NMVPNConnectionStateReason reason) { switch (reason) { case NM_VPN_CONNECTION_STATE_REASON_UNKNOWN: return _("unknown reason"); case NM_VPN_CONNECTION_STATE_REASON_NONE: return _("none"); case NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED: return _("the user was disconnected"); case NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED: return _("the base network connection was interrupted"); case NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED: return _("the VPN service stopped unexpectedly"); case NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID: return _("the VPN service returned invalid configuration"); case NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT: return _("the connection attempt timed out"); case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT: return _("the VPN service did not start in time"); case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED: return _("the VPN service failed to start"); case NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS: return _("no valid VPN secrets"); case NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED: return _("invalid VPN secrets"); case NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED: return _("the connection was removed"); default: return _("unknown"); } } static void active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpointer user_data) { NmCli *nmc = (NmCli *) user_data; NMActiveConnectionState state; state = nm_active_connection_get_state (active); if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { if (nmc->print_output == NMC_PRINT_PRETTY) { nmc_terminal_erase_line (); printf (_("Connection successfully activated (D-Bus active path: %s)\n"), nm_object_get_path (NM_OBJECT (active))); } quit (); } else if (state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) { g_string_printf (nmc->return_text, _("Error: Connection activation failed.")); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; quit (); } } static void vpn_connection_state_cb (NMVPNConnection *vpn, NMVPNConnectionState state, NMVPNConnectionStateReason reason, gpointer user_data) { NmCli *nmc = (NmCli *) user_data; switch (state) { case NM_VPN_CONNECTION_STATE_PREPARE: case NM_VPN_CONNECTION_STATE_NEED_AUTH: case NM_VPN_CONNECTION_STATE_CONNECT: case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: /* no operation */ break; case NM_VPN_CONNECTION_STATE_ACTIVATED: if (nmc->print_output == NMC_PRINT_PRETTY) { nmc_terminal_erase_line (); printf (_("VPN connection successfully activated (D-Bus active path: %s)\n"), nm_object_get_path (NM_OBJECT (vpn))); } quit (); break; case NM_VPN_CONNECTION_STATE_FAILED: case NM_VPN_CONNECTION_STATE_DISCONNECTED: g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."), vpn_connection_state_reason_to_string (reason)); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; quit (); break; default: break; } } /* --- VPN state workaround BEGIN --- */ static NMVPNConnectionState dbus_get_vpn_state (const char *path) { DBusGConnection *connection; DBusGProxy *proxy; GError *error = NULL; GValue value = G_VALUE_INIT; NMVPNConnectionState state = NM_VPN_CONNECTION_STATE_UNKNOWN; connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL); proxy = dbus_g_proxy_new_for_name (connection, NM_DBUS_SERVICE, path, "org.freedesktop.DBus.Properties"); if (!proxy) { g_warning ("%s: couldn't create D-Bus object proxy.", __func__); dbus_g_connection_unref (connection); return NM_VPN_CONNECTION_STATE_UNKNOWN; } /* Get VpnState property for object 'path' through D-Bus Get() call. */ if (dbus_g_proxy_call (proxy, "Get", &error, G_TYPE_STRING, NM_DBUS_INTERFACE_VPN_CONNECTION, G_TYPE_STRING, "VpnState", G_TYPE_INVALID, G_TYPE_VALUE, &value, G_TYPE_INVALID)) { state = g_value_get_uint (&value); } else { g_warning ("Error in getting active connection 'Vpn' property: (%d) %s", error->code, error->message); g_error_free (error); } g_object_unref (proxy); dbus_g_connection_unref (connection); return state; } typedef struct { NmCli *nmc; NMVPNConnection *vpn; } VpnGetStateInfo; static gboolean get_vpn_state_cb (gpointer user_data) { VpnGetStateInfo *info = (VpnGetStateInfo *) user_data; NmCli *nmc = info->nmc; NMVPNConnection *vpn = info->vpn; NMVPNConnectionState state; if (!NM_IS_OBJECT (vpn)) { /* Active connection failed and dissapeared, quit. */ g_string_printf (nmc->return_text, _("Error: Connection activation failed.")); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; quit (); g_free (info); return FALSE; } state = dbus_get_vpn_state (nm_object_get_path (NM_OBJECT (vpn))); vpn_connection_state_cb (vpn, state, NM_VPN_CONNECTION_STATE_REASON_UNKNOWN, nmc); if ( state == NM_VPN_CONNECTION_STATE_ACTIVATED || state == NM_VPN_CONNECTION_STATE_FAILED || state == NM_VPN_CONNECTION_STATE_DISCONNECTED) { g_free (info); return FALSE; } else return TRUE; } /* --- VPN state workaround END --- */ static gboolean timeout_cb (gpointer user_data) { /* Time expired -> exit nmcli */ NmCli *nmc = (NmCli *) user_data; g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout); nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED; quit (); return FALSE; } static gboolean progress_cb (gpointer user_data) { const char *str = (const char *) user_data; nmc_terminal_show_progress (str); return TRUE; } static gboolean progress_device_cb (gpointer user_data) { NMDevice *device = (NMDevice *) user_data; nmc_terminal_show_progress (device ? nmc_device_state_to_string (nm_device_get_state (device)) : ""); return TRUE; } static gboolean progress_vpn_cb (gpointer user_data) { NMVPNConnection *vpn = (NMVPNConnection *) user_data; const char *str; /* VPN state workaround */ #if 0 str = NM_IS_VPN_CONNECTION (vpn) ? vpn_connection_state_to_string (nm_vpn_connection_get_vpn_state (vpn)) : ""; #endif str = NM_IS_OBJECT (vpn) ? vpn_connection_state_to_string (dbus_get_vpn_state (nm_object_get_path (NM_OBJECT (vpn)))) : ""; nmc_terminal_show_progress (str); return TRUE; } typedef struct { NmCli *nmc; NMDevice *device; } ActivateConnectionInfo; static void activate_connection_cb (NMClient *client, NMActiveConnection *active, GError *error, gpointer user_data) { ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data; NmCli *nmc = info->nmc; NMDevice *device = info->device; NMActiveConnectionState state; if (error) { g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"), error->message); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; quit (); } else { state = nm_active_connection_get_state (active); if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { /* User doesn't want to wait or already activated */ if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED && nmc->print_output == NMC_PRINT_PRETTY) { printf (_("Connection successfully activated (D-Bus active path: %s)\n"), nm_object_get_path (NM_OBJECT (active))); } quit (); } else { if (NM_IS_VPN_CONNECTION (active)) { VpnGetStateInfo *vpn_info; /* Monitor VPN state */ g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc); /* Start progress indication showing VPN states */ if (nmc->print_output == NMC_PRINT_PRETTY) { if (progress_id) g_source_remove (progress_id); progress_id = g_timeout_add (120, progress_vpn_cb, NM_VPN_CONNECTION (active)); } /* --- workaround BEGIN --- */ /* There is a bug in libnm-glib or dbus-glib - 'vpn-state-changed' signal * is never issued. After inspection it appears that PropertiesChanged and * VpnStateChanged D-Bus signals are not processed, even if they are seen * on D-Bus (checked via dbus-monitor). Until the bug is identified and * fixed, we check the VPN state every second. */ vpn_info = g_malloc0 (sizeof (VpnGetStateInfo)); vpn_info->nmc = nmc; vpn_info->vpn = NM_VPN_CONNECTION (active); g_timeout_add_seconds (1, get_vpn_state_cb, vpn_info); /* --- workaround END --- */ } else { g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), nmc); /* Start progress indication showing device states */ if (nmc->print_output == NMC_PRINT_PRETTY) { if (progress_id) g_source_remove (progress_id); progress_id = g_timeout_add (120, progress_device_cb, device); } } /* Start timer not to loop forever when signals are not emitted */ g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); } } g_free (info); } static NMCResultCode do_connection_up (NmCli *nmc, int argc, char **argv) { ActivateConnectionInfo *info; NMDevice *device = NULL; const char *spec_object = NULL; gboolean device_found; NMConnection *connection = NULL; NMSettingConnection *s_con; const char *con_type; const char *iface = NULL; const char *ap = NULL; const char *nsp = NULL; gboolean id_specified = FALSE; gboolean wait = TRUE; GError *error = NULL; gboolean is_virtual = FALSE; /* Set default timeout for connection activation. It can take quite a long time. * Using 90 seconds. */ nmc->timeout = 90; while (argc > 0) { if (strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0) { const char *selector = *argv; id_specified = TRUE; if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } connection = find_connection (nmc->system_connections, selector, *argv); if (!connection) { g_string_printf (nmc->return_text, _("Error: Unknown connection: %s."), *argv); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; goto error; } } else if (strcmp (*argv, "iface") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } iface = *argv; } else if (strcmp (*argv, "ap") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } ap = *argv; } #if WITH_WIMAX else if (strcmp (*argv, "nsp") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } nsp = *argv; } #endif else if (strcmp (*argv, "--nowait") == 0) { wait = FALSE; } else if (strcmp (*argv, "--timeout") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } errno = 0; nmc->timeout = strtol (*argv, NULL, 10); if (errno || nmc->timeout < 0) { g_string_printf (nmc->return_text, _("Error: timeout value '%s' is not valid."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } } else { fprintf (stderr, _("Unknown parameter: %s\n"), *argv); } argc--; argv++; } if (!id_specified) { g_string_printf (nmc->return_text, _("Error: id or uuid has to be specified.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } if (!nmc_is_nm_running (nmc, &error)) { if (error) { g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; g_error_free (error); } else { g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; } goto error; } /* create NMClient */ nmc->get_client (nmc); s_con = nm_connection_get_setting_connection (connection); g_assert (s_con); con_type = nm_setting_connection_get_connection_type (s_con); if ( nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME) || nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME) || nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) is_virtual = TRUE; device_found = find_device_for_connection (nmc, connection, iface, ap, nsp, &device, &spec_object, &error); /* Virtual connection may not have their interfaces created yet */ if (!device_found && !is_virtual) { if (error) g_string_printf (nmc->return_text, _("Error: No suitable device found: %s."), error->message); else g_string_printf (nmc->return_text, _("Error: No suitable device found.")); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; g_clear_error (&error); goto error; } /* Use nowait_flag instead of should_wait because exiting has to be postponed till * active_connection_state_cb() is called. That gives NM time to check our permissions * and we can follow activation progress. */ nmc->nowait_flag = !wait; nmc->should_wait = TRUE; info = g_malloc0 (sizeof (ActivateConnectionInfo)); info->nmc = nmc; info->device = device; nm_client_activate_connection (nmc->client, connection, device, spec_object, activate_connection_cb, info); /* Start progress indication */ if (nmc->print_output == NMC_PRINT_PRETTY) progress_id = g_timeout_add (120, progress_cb, "preparing"); return nmc->return_value; error: nmc->should_wait = FALSE; return nmc->return_value; } static NMCResultCode do_connection_down (NmCli *nmc, int argc, char **argv) { NMConnection *connection = NULL; NMActiveConnection *active = NULL; GError *error = NULL; const GPtrArray *active_cons; const char *con_path; const char *active_path; gboolean id_specified = FALSE; gboolean wait = TRUE; int i; while (argc > 0) { if (strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0) { const char *selector = *argv; id_specified = TRUE; if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } connection = find_connection (nmc->system_connections, selector, *argv); if (!connection) { g_string_printf (nmc->return_text, _("Error: Unknown connection: %s."), *argv); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; goto error; } } else if (strcmp (*argv, "--nowait") == 0) { wait = FALSE; } else { fprintf (stderr, _("Unknown parameter: %s\n"), *argv); } argc--; argv++; } if (!id_specified) { g_string_printf (nmc->return_text, _("Error: id or uuid has to be specified.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } if (!nmc_is_nm_running (nmc, &error)) { if (error) { g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; g_error_free (error); } else { g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; } goto error; } /* create NMClient */ nmc->get_client (nmc); con_path = nm_connection_get_path (connection); active_cons = nm_client_get_active_connections (nmc->client); for (i = 0; active_cons && (i < active_cons->len); i++) { NMActiveConnection *candidate = g_ptr_array_index (active_cons, i); active_path = nm_active_connection_get_connection (candidate); if (!strcmp (active_path, con_path)) { active = candidate; break; } } if (active) nm_client_deactivate_connection (nmc->client, active); else fprintf (stderr, _("Warning: Connection not active\n")); sleep (1); /* Don't quit immediatelly and give NM time to check our permissions */ error: nmc->should_wait = FALSE; return nmc->return_value; } static void delete_cb (NMRemoteConnection *con, GError *err, gpointer user_data) { NmCli *nmc = (NmCli *) user_data; if (err) { g_string_printf (nmc->return_text, _("Error: Connection deletion failed: %s"), err->message); nmc->return_value = NMC_RESULT_ERROR_CON_DEL; } quit (); } static void connection_removed_cb (NMRemoteConnection *con, gpointer user_data) { NmCli *nmc = (NmCli *) user_data; nmc->return_value = NMC_RESULT_SUCCESS; quit (); } static NMCResultCode do_connection_delete (NmCli *nmc, int argc, char **argv) { NMConnection *connection = NULL; const char *selector = NULL; const char *id = NULL; GError *error = NULL; while (argc > 0) { if (strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0) { selector = *argv; if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } id = *argv; } else fprintf (stderr, _("Unknown parameter: %s\n"), *argv); argc--; argv++; } if (!id) { g_string_printf (nmc->return_text, _("Error: id or uuid has to be specified.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } if (!nmc_is_nm_running (nmc, &error)) { if (error) { g_string_printf (nmc->return_text, _("Error: Can't find out if NetworkManager is running: %s."), error->message); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; g_error_free (error); } else { g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; } goto error; } connection = find_connection (nmc->system_connections, selector, id); if (!connection) { g_string_printf (nmc->return_text, _("Error: Unknown connection: %s."), id); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; goto error; } /* We need to wait a bit so that nmcli's permissions can be queried. * We will exit either on "Removed" signal or when D-Bus return (error) * message is received. */ nmc->should_wait = TRUE; /* Connect to "Removed" signal to be able to exit when connection was removed */ g_signal_connect (connection, NM_REMOTE_CONNECTION_REMOVED, G_CALLBACK (connection_removed_cb), nmc); /* Delete the connection */ nm_remote_connection_delete (NM_REMOTE_CONNECTION (connection), delete_cb, nmc); return nmc->return_value; error: nmc->should_wait = FALSE; return nmc->return_value; } static NMCResultCode parse_cmd (NmCli *nmc, int argc, char **argv) { GError *error = NULL; if (argc == 0) { if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) goto opt_error; nmc->return_value = do_connections_list (nmc, argc, argv); } else { if (matches (*argv, "list") == 0) { nmc->return_value = do_connections_list (nmc, argc-1, argv+1); } else if (matches(*argv, "status") == 0) { if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) goto opt_error; nmc->return_value = do_connections_status (nmc, argc-1, argv+1); } else if (matches(*argv, "up") == 0) { nmc->return_value = do_connection_up (nmc, argc-1, argv+1); } else if (matches(*argv, "down") == 0) { nmc->return_value = do_connection_down (nmc, argc-1, argv+1); } else if (matches(*argv, "delete") == 0) { nmc->return_value = do_connection_delete (nmc, argc-1, argv+1); } else if ( matches (*argv, "help") == 0 || (g_str_has_prefix (*argv, "-") && matches ((*argv)+1, "help") == 0) || (g_str_has_prefix (*argv, "--") && matches ((*argv)+2, "help") == 0)) { usage (); nmc->should_wait = FALSE; } else { usage (); g_string_printf (nmc->return_text, _("Error: 'con' command '%s' is not valid."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; nmc->should_wait = FALSE; } } return nmc->return_value; opt_error: g_string_printf (nmc->return_text, _("Error: %s."), error->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; nmc->should_wait = FALSE; g_error_free (error); return nmc->return_value; } /* callback called when connections are obtained from the settings service */ static void get_connections_cb (NMRemoteSettings *settings, gpointer user_data) { ArgsInfo *args = (ArgsInfo *) user_data; /* Get the connection list */ args->nmc->system_connections = nm_remote_settings_list_connections (settings); parse_cmd (args->nmc, args->argc, args->argv); if (!args->nmc->should_wait) quit (); } /* Entry point function for connections-related commands: 'nmcli connection' */ NMCResultCode do_connections (NmCli *nmc, int argc, char **argv) { int i = 0; gboolean real_cmd = FALSE; if (argc == 0) real_cmd = TRUE; else { while (real_con_commands[i] && matches (*argv, real_con_commands[i]) != 0) i++; if (real_con_commands[i] != NULL) real_cmd = TRUE; } if (!real_cmd) { /* no real execution command - no need to get connections */ return parse_cmd (nmc, argc, argv); } else { if (!nmc_versions_match (nmc)) return nmc->return_value; /* Get NMClient object early */ nmc->get_client (nmc); nmc->should_wait = TRUE; args_info.nmc = nmc; args_info.argc = argc; args_info.argv = argv; /* get system settings */ if (!(nmc->system_settings = nm_remote_settings_new (NULL))) { g_string_printf (nmc->return_text, _("Error: Could not get system settings.")); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; nmc->should_wait = FALSE; return nmc->return_value; } /* find out whether settings service is running */ g_object_get (nmc->system_settings, NM_REMOTE_SETTINGS_SERVICE_RUNNING, &nmc->system_settings_running, NULL); if (!nmc->system_settings_running) { g_string_printf (nmc->return_text, _("Error: Can't obtain connections: settings service is not running.")); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; nmc->should_wait = FALSE; return nmc->return_value; } /* connect to signal "connections-read" - emitted when connections are fetched and ready */ g_signal_connect (nmc->system_settings, NM_REMOTE_SETTINGS_CONNECTIONS_READ, G_CALLBACK (get_connections_cb), &args_info); /* The rest will be done in get_connection_cb() callback. * We need to wait for signals that connections are read. */ return NMC_RESULT_SUCCESS; } }