Commit 276e0a4d authored by Dan Williams's avatar Dan Williams
Browse files

2005-05-04 Dan Williams <dcbw@redhat.com>

	* Remove NM_STATE_SCANNING from NetworkManager.h and applet code

	* Fix some holes in device activation and retaining the currently connected
		access point


git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@602 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
parent 993d3903
2005-05-04 Dan Williams <dcbw@redhat.com>
* Remove NM_STATE_SCANNING from NetworkManager.h and applet code
* Fix some holes in device activation and retaining the currently connected
access point
2005-05-03 Dan Williams <dcbw@redhat.com>
* Kill dhcpcd. We now use "dhcdbd", a dbus daemon that controls dhclient.
......
......@@ -83,7 +83,6 @@ typedef enum NMState
{
NM_STATE_UNKNOWN = 0,
NM_STATE_ASLEEP,
NM_STATE_SCANNING,
NM_STATE_CONNECTING,
NM_STATE_CONNECTED,
NM_STATE_DISCONNECTED
......
......@@ -739,11 +739,6 @@ static void nmwa_update_state (NMWirelessApplet *applet)
need_animation = TRUE;
break;
case NM_STATE_SCANNING:
need_animation = TRUE;
tip = g_strdup (_("Scanning for wireless networks..."));
break;
default:
break;
}
......@@ -1920,9 +1915,6 @@ static void nmwa_icons_free (NMWirelessApplet *applet)
for (i = 0; i < NUM_WIRELESS_CONNECTING_FRAMES; i++)
g_object_unref (applet->wireless_connecting_icons[i]);
for (i = 0; i < NUM_WIRELESS_SCANNING_FRAMES; i++)
g_object_unref (applet->wireless_scanning_icons[i]);
}
static void
......@@ -1969,15 +1961,6 @@ nmwa_icons_load_from_disk (NMWirelessApplet *applet, GtkIconTheme *icon_theme)
success = FALSE;
}
for (i = 0; i < NUM_WIRELESS_SCANNING_FRAMES; i++)
{
name = g_strdup_printf ("nm-detect%02d", i+1);
applet->wireless_scanning_icons[i] = gtk_icon_theme_load_icon (icon_theme, name, icon_size, 0, NULL);
g_free (name);
if (!applet->wireless_scanning_icons[i])
success = FALSE;
}
if (!success)
{
show_warning_dialog (_("The NetworkManager applet could not find some required resources. It cannot continue.\n"));
......
......@@ -110,8 +110,6 @@ typedef struct
GdkPixbuf * wireless_100_icon;
#define NUM_WIRELESS_CONNECTING_FRAMES 11
GdkPixbuf * wireless_connecting_icons[NUM_WIRELESS_CONNECTING_FRAMES];
#define NUM_WIRELESS_SCANNING_FRAMES 16
GdkPixbuf * wireless_scanning_icons[NUM_WIRELESS_SCANNING_FRAMES];
GdkPixbuf * vpn_lock_icon;
/* Animation stuff */
......
......@@ -21,22 +21,6 @@ smallicon_DATA= \
nm-connecting09.png \
nm-connecting10.png \
nm-connecting11.png \
nm-detect01.png \
nm-detect02.png \
nm-detect03.png \
nm-detect04.png \
nm-detect05.png \
nm-detect06.png \
nm-detect07.png \
nm-detect08.png \
nm-detect09.png \
nm-detect10.png \
nm-detect11.png \
nm-detect12.png \
nm-detect13.png \
nm-detect14.png \
nm-detect15.png \
nm-detect16.png \
nm-signal-00.png \
nm-signal-25.png \
nm-signal-50.png \
......
......@@ -165,7 +165,8 @@ static void libnm_glib_update_state (libnm_glib_ctx *ctx, NMState state)
ctx->nm_state = LIBNM_ACTIVE_NETWORK_CONNECTION;
break;
case NM_STATE_SCANNING:
case NM_STATE_ASLEEP:
case NM_STATE_CONNECTING:
case NM_STATE_DISCONNECTED:
ctx->nm_state = LIBNM_NO_NETWORK_CONNECTION;
break;
......
......@@ -51,6 +51,7 @@ static gboolean nm_device_wireless_scan (gpointer user_data);
static gboolean supports_mii_carrier_detect (NMDevice *dev);
static gboolean supports_ethtool_carrier_detect (NMDevice *dev);
static gboolean nm_device_bring_up_wait (NMDevice *dev, gboolean cancelable);
static gboolean link_to_specific_ap (NMDevice *dev, NMAccessPoint *ap);
static void nm_device_activate_schedule_stage1_device_prepare (NMActRequest *req);
static void nm_device_activate_schedule_stage2_device_config (NMActRequest *req);
......@@ -786,17 +787,10 @@ static gboolean nm_device_probe_wireless_link_state (NMDevice *dev)
if (!nm_try_acquire_mutex (dev->options.wireless.scan_mutex, __FUNCTION__))
return nm_device_has_active_link (dev);
if (nm_device_wireless_is_associated (dev))
if ((best_ap = nm_device_get_best_ap (dev)))
{
/* If we don't have a "best" ap, or that's not the AP we are currently
* using, then we can't logically have a valid link.
*/
if ((best_ap = nm_device_get_best_ap (dev)))
{
if (nm_device_get_essid (dev) && nm_ap_get_essid (best_ap))
link = (strcmp (nm_device_get_essid (dev), nm_ap_get_essid (best_ap)) == 0);
nm_ap_unref (best_ap);
}
link = link_to_specific_ap (dev, best_ap);
nm_ap_unref (best_ap);
}
nm_unlock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__);
......@@ -903,7 +897,7 @@ char * nm_device_get_essid (NMDevice *dev)
essid = nm_ap_get_essid (best_ap);
nm_ap_unref (best_ap);
}
return (essid);
return essid;
}
if ((sk = nm_dev_sock_open (dev, DEV_WIRELESS, __FUNCTION__, NULL)))
......@@ -1800,10 +1794,11 @@ gboolean nm_device_activation_start (NMActRequest *req)
g_return_val_if_fail (req != NULL, FALSE);
data = nm_act_request_get_data (req);
g_return_val_if_fail (data != NULL, FALSE);
g_assert (data);
dev = nm_act_request_get_dev (req);
g_return_val_if_fail (dev != NULL, FALSE);
g_assert (dev);
g_return_val_if_fail (!nm_device_is_activating (dev), TRUE); /* Return if activation has already begun */
if (nm_device_get_driver_support_level (dev) == NM_DRIVER_UNSUPPORTED)
......@@ -1875,11 +1870,11 @@ void nm_device_schedule_activation_handle_cancel (NMActRequest *req)
dev = nm_act_request_get_dev (req);
g_assert (dev);
nm_info ("Activation (%s) cancellation handler scheduled...", nm_device_get_iface (dev));
source = g_idle_source_new ();
g_source_set_callback (source, (GSourceFunc) nm_device_activation_handle_cancel, req, NULL);
g_source_attach (source, dev->context);
g_source_unref (source);
nm_info ("Activation (%s) cancellation handler scheduled...", nm_device_get_iface (dev));
}
......@@ -1953,7 +1948,7 @@ static gboolean nm_device_activate_stage1_device_prepare (NMActRequest *req)
if (nm_device_is_wireless (dev))
{
ap = nm_act_request_get_ap (req);
g_return_val_if_fail (ap != NULL, FALSE);
g_assert (ap);
if (nm_ap_get_artificial (ap))
{
......@@ -2176,12 +2171,18 @@ static void nm_device_wireless_configure_adhoc (NMActRequest *req)
nm_ap_list_iter_free (iter);
if ((sk = nm_dev_sock_open (dev, DEV_WIRELESS, __FUNCTION__, NULL)) == NULL)
{
nm_policy_schedule_activation_failed (req);
return;
}
err = iw_get_range_info (nm_dev_sock_get_fd (sk), nm_device_get_iface (dev), &range);
nm_dev_sock_close (sk);
if (err < 0)
{
nm_policy_schedule_activation_failed (req);
return;
}
/* Ok, find the first non-zero freq in our table and use it.
* For now we only try to use a channel in the 802.11b channel
......@@ -2209,23 +2210,24 @@ static void nm_device_wireless_configure_adhoc (NMActRequest *req)
freq_to_use = pfreq;
}
if (freq_to_use)
if (!freq_to_use)
{
nm_ap_set_freq (ap, freq_to_use);
nm_info ("Will create network '%s' with frequency %f.", nm_ap_get_essid (ap), nm_ap_get_freq (ap));
nm_device_set_wireless_config (dev, ap);
nm_policy_schedule_activation_failed (req);
return;
}
if (nm_device_activation_should_cancel (dev))
{
nm_device_schedule_activation_handle_cancel (req);
return;
}
nm_ap_set_freq (ap, freq_to_use);
nm_device_activate_schedule_stage3_ip_config_start (req);
nm_info ("Will create network '%s' with frequency %f.", nm_ap_get_essid (ap), nm_ap_get_freq (ap));
nm_device_set_wireless_config (dev, ap);
if (nm_device_activation_should_cancel (dev))
{
nm_device_schedule_activation_handle_cancel (req);
return;
}
else
nm_policy_schedule_activation_failed (req);
nm_device_activate_schedule_stage3_ip_config_start (req);
}
......@@ -2333,8 +2335,7 @@ static gboolean ap_need_key (NMDevice *dev, NMAccessPoint *ap)
if (!nm_ap_get_encrypted (ap))
{
nm_info ("Activation (%s/wireless): access point '%s' is "
"unencrypted, no key needed.",
nm_info ("Activation (%s/wireless): access point '%s' is unencrypted, no key needed.",
nm_device_get_iface (dev), essid ? essid : "(null)");
}
else
......@@ -2355,7 +2356,7 @@ static gboolean ap_need_key (NMDevice *dev, NMAccessPoint *ap)
}
}
return (need_key);
return need_key;
}
......@@ -2587,7 +2588,8 @@ static gboolean nm_device_activate_stage3_ip_config_start (NMActRequest *req)
if (!(ap && nm_ap_get_user_created (ap)) && nm_device_get_use_dhcp (dev))
{
/* Begin a DHCP transaction on the interface */
nm_dhcp_manager_begin_transaction (data->dhcp_manager, req);
if (!nm_dhcp_manager_begin_transaction (data->dhcp_manager, req))
nm_policy_schedule_activation_failed (req);
goto out;
}
......@@ -2677,13 +2679,16 @@ static gboolean nm_device_activate_stage4_ip_config_get (NMActRequest *req)
g_return_val_if_fail (req != NULL, FALSE);
data = nm_act_request_get_data (req);
g_return_val_if_fail (data != NULL, FALSE);
g_assert (data);
dev = nm_act_request_get_dev (req);
g_return_val_if_fail (dev != NULL, FALSE);
g_assert (data);
if (nm_device_is_wireless (dev))
{
ap = nm_act_request_get_ap (req);
g_assert (ap);
}
nm_info ("Activation (%s) Stage 4 (IP Configure Get) started...", nm_device_get_iface (dev));
......@@ -2909,6 +2914,7 @@ static gboolean nm_device_activate_stage5_ip_config_commit (NMActRequest *req)
nm_policy_schedule_activation_failed (req);
out:
nm_device_set_link_active (dev, nm_device_probe_link_state (dev));
nm_info ("Activation (%s) Stage 5 (IP Configure Commit) complete.", nm_device_get_iface (dev));
return FALSE;
}
......@@ -2949,6 +2955,7 @@ gboolean nm_device_is_activating (NMDevice *dev)
{
NMActRequest * req;
NMActStage stage;
gboolean activating = FALSE;
g_return_val_if_fail (dev != NULL, FALSE);
......@@ -2956,10 +2963,26 @@ gboolean nm_device_is_activating (NMDevice *dev)
return FALSE;
stage = nm_act_request_get_stage (req);
if ((stage != ACT_STAGE_UNKNOWN) && (stage != ACT_STAGE_ACTIVATED))
return TRUE;
switch (stage)
{
case ACT_STAGE_DEVICE_PREPARE:
case ACT_STAGE_DEVICE_CONFIG:
case ACT_STAGE_NEED_USER_KEY:
case ACT_STAGE_IP_CONFIG_START:
case ACT_STAGE_IP_CONFIG_GET:
case ACT_STAGE_IP_CONFIG_COMMIT:
activating = TRUE;
break;
return FALSE;
case ACT_STAGE_ACTIVATED:
case ACT_STAGE_FAILED:
case ACT_STAGE_CANCELLED:
case ACT_STAGE_UNKNOWN:
default:
break;
}
return activating;
}
......@@ -2985,7 +3008,6 @@ static gboolean nm_ac_test (int tries, nm_completion_args args)
if (nm_device_is_activating (dev))
{
/* FIXME: stop any ongoing DHCP transactions */
if (tries % 20 == 0)
nm_debug ("Activation (%s/wireless): waiting on dhcp to cease or device to finish activation", nm_device_get_iface(dev));
return FALSE;
......@@ -3010,7 +3032,8 @@ void nm_device_activation_cancel (NMDevice *dev)
if (nm_device_is_activating (dev))
{
NMActRequest *req = nm_device_get_act_request (dev);
NMActRequest * req = nm_device_get_act_request (dev);
gboolean clear_act_request = FALSE;
nm_debug ("Activation (%s/wireless): cancelling...", nm_device_get_iface (dev));
dev->quit_activation = TRUE;
......@@ -3019,12 +3042,16 @@ void nm_device_activation_cancel (NMDevice *dev)
if (nm_act_request_get_stage (req) == ACT_STAGE_NEED_USER_KEY)
{
nm_dbus_cancel_get_user_key_for_network (dev->app_data->dbus_connection, req);
dev->act_request = NULL;
nm_act_request_unref (req);
clear_act_request = TRUE;
}
else if (nm_act_request_get_stage (req) == ACT_STAGE_IP_CONFIG_START)
{
nm_dhcp_manager_cancel_transaction (dev->app_data->dhcp_manager, req);
clear_act_request = TRUE;
}
if (clear_act_request)
{
dev->act_request = NULL;
nm_act_request_unref (req);
}
......@@ -3134,7 +3161,16 @@ void nm_device_set_user_key_for_network (NMActRequest *req, const char *key, con
}
else
{
NMAccessPoint * allowed_ap = nm_ap_list_get_ap_by_essid (data->allowed_ap_list, nm_ap_get_essid (ap));
/* Start off at Open System auth mode with the new key */
nm_ap_set_auth_method (ap, NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM);
nm_ap_set_enc_key_source (ap, key, enc_type);
/* Be sure to update NMI with the new auth mode */
nm_ap_set_auth_method (allowed_ap, NM_DEVICE_AUTH_METHOD_OPEN_SYSTEM);
nm_dbus_update_network_auth_method (data->dbus_connection, nm_ap_get_essid (allowed_ap), nm_ap_get_auth_method (allowed_ap));
nm_device_activate_schedule_stage1_device_prepare (req);
}
}
......@@ -3275,6 +3311,23 @@ NMAccessPointList *nm_device_ap_list_get (NMDevice *dev)
return (dev->options.wireless.ap_list);
}
static gboolean link_to_specific_ap (NMDevice *dev, NMAccessPoint *ap)
{
gboolean link = FALSE;
if (nm_device_wireless_is_associated (dev))
{
char * dev_essid = nm_device_get_essid (dev);
char * ap_essid = nm_ap_get_essid (ap);
if (dev_essid && ap_essid)
link = (strcmp (dev_essid, ap_essid) == 0);
}
return link;
}
/*
* nm_device_update_best_ap
*
......@@ -3307,10 +3360,8 @@ NMAccessPoint * nm_device_get_best_ap (NMDevice *dev)
if (!(ap_list = nm_device_ap_list_get (dev)))
return NULL;
/* Iterate over the device's ap list to make sure the current
* "best" ap is still in the device's ap list (so that if its
* not, we can "unfreeze" the best ap if its been frozen already).
* If it is, we don't change the best ap here.
/* We prefer the currently selected access point if its user-chosen or if there
* is still a hardware link to it.
*/
if ((req = nm_device_get_act_request (dev)))
{
......@@ -3323,8 +3374,14 @@ NMAccessPoint * nm_device_get_best_ap (NMDevice *dev)
keep = TRUE;
else if (nm_act_request_get_user_requested (req))
keep = TRUE;
else if (nm_device_has_active_link (dev))
keep = TRUE;
else
{
/* Checking hardware's ESSID during a scan is doesn't work. */
nm_lock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__);
if (link_to_specific_ap (dev, cur_ap))
keep = TRUE;
nm_unlock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__);
}
/* Only keep if its not in the invalid list and its _is_ in our scaned list */
if ( keep
......@@ -3662,10 +3719,17 @@ static gboolean nm_device_wireless_process_scan_results (gpointer user_data)
g_get_current_time (&cur_time);
if (nm_device_ap_list_get (dev) && (iter = nm_ap_list_iter_new (nm_device_ap_list_get (dev))))
{
NMAccessPoint *outdated_ap;
GSList *outdated_list = NULL;
GSList *elt;
NMAccessPoint *best_ap = nm_device_get_best_ap (dev);
NMAccessPoint *outdated_ap;
GSList * outdated_list = NULL;
GSList * elt;
NMActRequest * req = nm_device_get_act_request (dev);
NMAccessPoint *cur_ap = NULL;
if (req)
{
cur_ap = nm_act_request_get_ap (req);
g_assert (cur_ap);
}
while ((outdated_ap = nm_ap_list_iter_next (iter)))
{
......@@ -3674,7 +3738,7 @@ static gboolean nm_device_wireless_process_scan_results (gpointer user_data)
/* Don't ever get prune the AP we're currently associated with */
if ( nm_ap_get_essid (outdated_ap)
&& (best_ap && (nm_null_safe_strcmp (nm_ap_get_essid (best_ap), nm_ap_get_essid (outdated_ap))) == 0))
&& (cur_ap && (nm_null_safe_strcmp (nm_ap_get_essid (cur_ap), nm_ap_get_essid (outdated_ap))) == 0))
keep_around = TRUE;
if (!keep_around && (ap_time->tv_sec + 120 < cur_time.tv_sec))
......@@ -3682,10 +3746,6 @@ static gboolean nm_device_wireless_process_scan_results (gpointer user_data)
}
nm_ap_list_iter_free (iter);
/* nm_device_get_best_ap() refs the ap */
if (best_ap)
nm_ap_unref (best_ap);
/* Ok, now remove outdated ones. We have to do it after the lock
* because nm_ap_list_remove_ap() locks the list too.
*/
......
......@@ -178,6 +178,8 @@ void nm_policy_schedule_activation_failed (NMActRequest *req)
dev = nm_act_request_get_dev (req);
g_assert (dev);
nm_act_request_set_stage (req, ACT_STAGE_FAILED);
source = g_idle_source_new ();
g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
g_source_set_callback (source, (GSourceFunc) nm_policy_activation_failed, req, NULL);
......@@ -223,7 +225,7 @@ static NMDevice * nm_policy_auto_get_best_device (NMData *data, NMAccessPoint **
dev_type = nm_device_get_type (dev);
link_active = nm_device_has_active_link (dev);
if (dev_type == DEVICE_TYPE_WIRED_ETHERNET)
if (nm_device_is_wired (dev))
{
/* We never automatically choose devices that don't support carrier detect */
if (!nm_device_get_supports_carrier_detect (dev))
......@@ -241,7 +243,7 @@ static NMDevice * nm_policy_auto_get_best_device (NMData *data, NMAccessPoint **
best_wired_prio = prio;
}
}
else if ((dev_type == DEVICE_TYPE_WIRELESS_ETHERNET) && data->wireless_enabled)
else if (nm_device_is_wireless (dev) && data->wireless_enabled)
{
if (link_active)
prio += 1;
......@@ -254,6 +256,10 @@ static NMDevice * nm_policy_auto_get_best_device (NMData *data, NMAccessPoint **
if (nm_device_get_act_request (dev) && link_active)
prio += 3;
/* Stick with an already active non-scanning device if the user chose one */
if (!nm_device_get_supports_wireless_scan (dev) && nm_device_get_act_request (dev))
prio += 2;
if (prio > best_wireless_prio)
{
best_wireless_dev = dev;
......@@ -612,7 +618,7 @@ void nm_policy_schedule_device_ap_lists_update_from_allowed (NMData *app_data)
/* We want this idle source to run before any other idle source */
g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
g_source_set_callback (source, nm_policy_allowed_ap_list_update, app_data, NULL);
g_source_set_callback (source, (GSourceFunc) nm_policy_device_list_update_from_allowed_list, app_data, NULL);
g_source_attach (source, app_data->main_context);
g_source_unref (source);
......
......@@ -170,7 +170,8 @@ guint8 nm_dhcp_manager_get_state_for_device (NMDHCPManager *manager, NMDevice *d
dbus_message_unref (message);
if (dbus_error_is_set (&error))
{
nm_info ("Error from dhcdbd on 'reason' request because: name '%s', message '%s'.", error.name, error.message);
if (strcmp (error.name, "org.freedesktop.DBus.Error.UnknownMethod") != 0)
nm_info ("Error from dhcdbd on 'reason' request because: name '%s', message '%s'.", error.name, error.message);
dbus_error_free (&error);
}
if (reply)
......@@ -207,9 +208,9 @@ gboolean nm_dhcp_manager_handle_timeout (NMActRequest *req)
if (nm_act_request_get_stage (req) == ACT_STAGE_IP_CONFIG_START)
{
nm_device_activate_schedule_stage4_ip_config_timeout (req);
nm_act_request_set_dhcp_timeout (req, 0);
nm_dhcp_manager_cancel_transaction (data->dhcp_manager, req);
nm_device_activate_schedule_stage4_ip_config_timeout (req);
}
return FALSE;
......@@ -260,15 +261,15 @@ gboolean nm_dhcp_manager_begin_transaction (NMDHCPManager *manager, NMActRequest
dbus_message_append_args (message, DBUS_TYPE_UINT32, &opt1, DBUS_TYPE_UINT32, &opt2, DBUS_TYPE_INVALID);
dbus_error_init (&error);
reply = dbus_connection_send_with_reply_and_block (manager->data->dbus_connection, message, -1, &error);
if ((reply = dbus_connection_send_with_reply_and_block (manager->data->dbus_connection, message, -1, &error)))
dbus_message_unref (reply);
dbus_message_unref (message);
if (dbus_error_is_set (&error))
{
nm_info ("Couldn't send DHCP 'up' message because: name '%s', message '%s'.", error.name, error.message);
dbus_error_free (&error);
return FALSE;
}
if (reply)
dbus_message_unref (reply);
dbus_message_unref (message);
/* Set up a timeout on the transaction to kill it after 25s */
source = g_timeout_source_new (25000);
......@@ -595,6 +596,7 @@ gboolean nm_dhcp_manager_process_signal (NMDHCPManager *manager, DBusMessage *me
case 9: /* FAIL */
case 13: /* ABEND */
// case 14: /* END */
if (nm_act_request_get_stage (req) == ACT_STAGE_IP_CONFIG_START)
{
nm_policy_schedule_activation_failed (req);
......
......@@ -40,7 +40,9 @@ typedef enum NMActStage
ACT_STAGE_IP_CONFIG_START,
ACT_STAGE_IP_CONFIG_GET,
ACT_STAGE_IP_CONFIG_COMMIT,
ACT_STAGE_ACTIVATED
ACT_STAGE_ACTIVATED,
ACT_STAGE_FAILED,
ACT_STAGE_CANCELLED
} NMActStage;
......
......@@ -352,10 +352,7 @@ void set_device_network (DBusConnection *connection, const char *path, const cha
DBusMessage *reply;
DBusError error;
message = dbus_message_new_method_call ("org.freedesktop.NetworkManager",
"/org/freedesktop/NetworkManager",
"org.freedesktop.NetworkManager",
"setActiveDevice");
message = dbus_message_new_method_call (NM_DBUS_SERVICE, NM_DBUS_PATH, NM_DBUS_INTERFACE, "setActiveDevice");
if (message == NULL)
{
fprintf (stderr, "Couldn't allocate the dbus message\n");
......@@ -403,7 +400,7 @@ int main( int argc, char *argv[] )
fprintf (stderr, "NetworkManager appears not to be running (could not get its state). Will exit.\n");
return (1);
}
fprintf (stderr, "NM State: '%u'\n", state);
fprintf (stderr, "NM State: %u\n", state);
path = get_active_device (connection);
fprintf (stderr, "Active device: '%s'\n", path ? path : "(none)");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment