cli: show progress in --pretty mode for 'nmcli con up'

We need to use a workaround for VPN connection state. There is a bug somewhere
causing that vpn-state-changed signal is not issued on active VPN connection.
Debugging showed that D-Bus PropertiesChanged and VpnStateChanged signals are
not processed properly. That's why libnm-glib's VPN connection object is not
updated. The issue may lie in libnm-glib or even in dbus-glib.
I wasn't able to get VpnStateChanged signal via dbus-glib
(dbus_g_proxy_add_signal() and dbus_g_proxy_connect_signal()), however received
the signal successfully using GDBus.

Until the issue is analyzed and fixed, we need to ask for VPN state of active
VPN connection every second in order to show progress and exit correctly.
This commit is contained in:
Jiří Klimeš
2012-05-09 12:35:27 +02:00
parent d58c32d214
commit 191da673a8
4 changed files with 401 additions and 214 deletions

View File

@@ -178,6 +178,7 @@ typedef struct {
extern GMainLoop *loop;
static ArgsInfo args_info;
static guint progress_id = 0; /* ID of event source for displaying progress */
static void
usage (void)
@@ -210,6 +211,11 @@ static const char *real_con_commands[] = {
static void
quit (void)
{
if (progress_id) {
g_source_remove (progress_id);
nmc_terminal_erase_line ();
}
g_main_loop_quit (loop); /* quit main loop */
}
@@ -1336,10 +1342,12 @@ active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpoin
state = nm_active_connection_get_state (active);
printf (_("state: %s\n"), active_connection_state_to_string (state));
if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
printf (_("Connection activated\n"));
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."));
@@ -1361,17 +1369,22 @@ vpn_connection_state_cb (NMVPNConnection *vpn,
case NM_VPN_CONNECTION_STATE_NEED_AUTH:
case NM_VPN_CONNECTION_STATE_CONNECT:
case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
printf (_("state: %s (%d)\n"), vpn_connection_state_to_string (state), state);
/* no operation */
break;
case NM_VPN_CONNECTION_STATE_ACTIVATED:
printf (_("Connection activated\n"));
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));
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;
@@ -1381,6 +1394,85 @@ vpn_connection_state_cb (NMVPNConnection *vpn,
}
}
/* --- VPN state workaround BEGIN --- */
static NMVPNConnectionState
dbus_get_vpn_state (const char *path)
{
DBusGConnection *connection;
DBusGProxy *proxy;
GError *error = NULL;
GValue value = {0,};
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)
{
@@ -1394,10 +1486,58 @@ timeout_cb (gpointer user_data)
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)
{
NmCli *nmc = (NmCli *) user_data;
ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
NmCli *nmc = info->nmc;
NMDevice *device = info->device;
NMActiveConnectionState state;
if (error) {
@@ -1407,27 +1547,61 @@ activate_connection_cb (NMClient *client, NMActiveConnection *active, GError *er
} else {
state = nm_active_connection_get_state (active);
printf (_("Active connection state: %s\n"), active_connection_state_to_string (state));
printf (_("Active connection path: %s\n"), nm_object_get_path (NM_OBJECT (active)));
if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
/* don't want to wait or already 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))
g_signal_connect (NM_VPN_CONNECTION (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc);
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;
@@ -1566,12 +1740,21 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
*/
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,
nmc);
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: