From 49dfe63373172b0c2d8c4eedc648b161ce588c77 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 7 Sep 2006 19:17:20 +0000 Subject: [PATCH] 2006-09-07 Dan Williams * test/Makefile.am test/libnm-util/Makefile.am test/nm-supplicant-test.c - Add test program emulating the way NM drives wpa_supplicant to help debug supplicant issues git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@2022 4912f4e0-d625-0410-9fb7-b9a5a253dbdc --- ChangeLog | 8 + test/Makefile.am | 13 +- test/libnm-util/Makefile.am | 8 +- test/nm-supplicant-test.c | 822 ++++++++++++++++++++++++++++++++++++ 4 files changed, 845 insertions(+), 6 deletions(-) create mode 100644 test/nm-supplicant-test.c diff --git a/ChangeLog b/ChangeLog index f0c473ffef..a478860489 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2006-09-07 Dan Williams + + * test/Makefile.am + test/libnm-util/Makefile.am + test/nm-supplicant-test.c + - Add test program emulating the way NM drives wpa_supplicant + to help debug supplicant issues + 2006-08-24 Dan Williams * configure.in diff --git a/test/Makefile.am b/test/Makefile.am index 91a4ca4acc..b4fca1ac4b 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -11,9 +11,16 @@ AM_CPPFLAGS = \ $(HAL_CFLAGS) \ -DDBUS_API_SUBJECT_TO_CHANGE \ -DBINDIR=\"$(bindir)\" \ + -DWPA_SUPPLICANT_BIN=\"$(WPA_SUPPLICANT_BINARY_PATH)\" \ + -DNM_RUN_DIR=\"$(rundir)\" \ -DDATADIR=\"$(datadir)\" -noinst_PROGRAMS = nm-tool nm-online nminfotest nmtestdevices libnm_glib_test +noinst_PROGRAMS = nm-tool \ + nm-online \ + nminfotest \ + nmtestdevices \ + libnm_glib_test \ + nm-supplicant-test nm_tool_SOURCES = nm-tool.c nm_tool_LDADD = $(DBUS_LIBS) $(GTHREAD_LIBS) $(HAL_LIBS) \ @@ -34,5 +41,7 @@ nmtestdevices_LDADD = $(DBUS_LIBS) $(GTHREAD_LIBS) \ libnm_glib_test_SOURCES = libnm_glib_test.c libnm_glib_test_LDADD = $(DBUS_LIBS) $(GTHREAD_LIBS) \ $(top_builddir)/utils/libnmutils.la \ - ../gnome/libnm_glib/libnm_glib.la + $(top_builddir)/gnome/libnm_glib/libnm_glib.la +nm_supplicant_test_SOURCES = nm-supplicant-test.c +nm_supplicant_test_LDADD = $(GLIB_LIBS) $(GTHREAD_LIBS) diff --git a/test/libnm-util/Makefile.am b/test/libnm-util/Makefile.am index 971802ca6a..075d66b3b2 100644 --- a/test/libnm-util/Makefile.am +++ b/test/libnm-util/Makefile.am @@ -2,8 +2,8 @@ INCLUDES = -I${top_srcdir} \ -I${top_srcdir}/utils \ -I${top_srcdir}/include \ -I${top_srcdir}/libnm-util \ - -I${top_srcdir}/test \ - -I${top_srcdir}/test/test-common + -I${top_srcdir}/test \ + -I${top_srcdir}/test/test-common noinst_PROGRAMS = test-ciphers test-dbus-helpers test-dbus-dict-helpers @@ -19,8 +19,8 @@ test_ciphers_CPPFLAGS = \ test_ciphers_LDADD = \ $(DBUS_LIBS) \ $(GLIB_LIBS) \ - $(top_builddir)/libnm-util/libnm-util.la \ - $(top_builddir)/test/test-common/libtest-common.la + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/test/test-common/libtest-common.la test_dbus_helpers_SOURCES = test-dbus-helpers.c test-inputs.h diff --git a/test/nm-supplicant-test.c b/test/nm-supplicant-test.c new file mode 100644 index 0000000000..1ef2091ca6 --- /dev/null +++ b/test/nm-supplicant-test.c @@ -0,0 +1,822 @@ +/* supplicant test utility + * + * Dan Williams + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * (C) Copyright 2006 Red Hat, Inc. + */ + + +#include +#include +#include +#include +#include +#include "../src/wpa_ctrl.h" +#include "../src/wpa_ctrl.c" + +#define nm_info(fmt, args...) fprintf(stdout, fmt "\n", ##args) +#define nm_warning(fmt, args...) fprintf(stdout, fmt "\n", ##args) +#define nm_warning_str(fmt_str, args...) fprintf(stdout, "%s\n", fmt_str, ##args) + + +struct opt { + char *key; + char *value; +}; + +#define STATE_DISCONNECTED 0 +#define STATE_DEVICE_CONFIG 1 +#define STATE_CONNECTED 2 + +struct supplicant { + GMainLoop * loop; + GPid pid; + GSource * watch; + GSource * status; + struct wpa_ctrl * ctrl; + GSource * timeout; + GSource * stdout; + GSource * link_timeout; + char * iface; + guint32 ap_scan; + GSList * options; + guint32 state; + const char * ssid; +}; + + +#define SUPPLICANT_DEBUG +#define RESPONSE_SIZE 2048 + + +static char * +kill_newline (char *s, size_t *l) +{ + g_return_val_if_fail (l != NULL, s); + + while ((--(*l) > 0) && (s[*l] != '\n')) + ; + if (s[*l] == '\n') + s[*l] = '\0'; + return s; +} + +static char * +nm_utils_supplicant_request (struct wpa_ctrl *ctrl, + const char *format, + ...) +{ + va_list args; + size_t len; + char * response = NULL; + char * command; + + g_return_val_if_fail (ctrl != NULL, NULL); + g_return_val_if_fail (format != NULL, NULL); + + va_start (args, format); + if (!(command = g_strdup_vprintf (format, args))) + return NULL; + va_end (args); + + response = g_malloc (RESPONSE_SIZE); + len = RESPONSE_SIZE; + nm_info ("SUP: sending command '%s'", command); + wpa_ctrl_request (ctrl, command, strlen (command), response, &len, NULL); + g_free (command); + response[len] = '\0'; + { + response = kill_newline (response, &len); + nm_info ("SUP: response was '%s'", response); + } + return response; +} + +static gboolean +nm_utils_supplicant_request_with_check (struct wpa_ctrl *ctrl, + const char *expected, + const char *func, + const char *err_msg_cmd, + const char *format, + ...) +{ + va_list args; + gboolean success = FALSE; + size_t len; + char * response = NULL; + char * command; + char * temp; + + g_return_val_if_fail (ctrl != NULL, FALSE); + g_return_val_if_fail (expected != NULL, FALSE); + g_return_val_if_fail (format != NULL, FALSE); + + va_start (args, format); + if (!(command = g_strdup_vprintf (format, args))) + goto out; + + response = g_malloc (RESPONSE_SIZE); + len = RESPONSE_SIZE; + nm_info ("SUP: sending command '%s'", err_msg_cmd ? err_msg_cmd : command); + wpa_ctrl_request (ctrl, command, strlen (command), response, &len, NULL); + response[len] = '\0'; + { + response = kill_newline (response, &len); + nm_info ("SUP: response was '%s'", response); + } + + if (response) + { + if (strncmp (response, expected, strlen (expected)) == 0) + success = TRUE; + else + { + response = kill_newline (response, &len); + temp = g_strdup_printf ("%s: supplicant error for '%s'. Response: '%s'", + func, err_msg_cmd ? err_msg_cmd : command, response); + nm_warning_str (temp); + g_free (temp); + } + g_free (response); + } + else + { + temp = g_strdup_printf ("%s: supplicant error for '%s'. No response.", + func, err_msg_cmd ? err_msg_cmd : command); + nm_warning_str (temp); + g_free (temp); + } + g_free (command); + +out: + va_end (args); + return success; +} + + +/****************************************************************************/ +/* WPA Supplicant control stuff + * + * Originally from: + * + * wpa_supplicant wrapper + * + * Copyright (C) 2005 Kay Sievers + * + * 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 version 2 of the License. + */ + +#define WPA_SUPPLICANT_GLOBAL_SOCKET "/var/run/wpa_supplicant-global" +#define WPA_SUPPLICANT_CONTROL_SOCKET "/var/run/wpa_supplicant" +#define WPA_SUPPLICANT_NUM_RETRIES 20 +#define WPA_SUPPLICANT_RETRY_TIME_US 100*1000 + +static void +remove_link_timeout (struct supplicant *sup) +{ + if (sup->link_timeout != NULL) + { + g_source_destroy (sup->link_timeout); + sup->link_timeout = NULL; + } +} + +static void +supplicant_remove_timeout (struct supplicant *sup) +{ + /* Remove any pending timeouts on the request */ + if (sup->timeout != NULL) + { + g_source_destroy (sup->timeout); + sup->timeout = NULL; + } +} + +static char * +supplicant_get_device_socket_path (struct supplicant *sup) +{ + return g_strdup_printf (WPA_SUPPLICANT_CONTROL_SOCKET "/%s", sup->iface); +} + +static void +supplicant_cleanup (struct supplicant *sup) +{ + char * sock_path; + + if (sup->pid > 0) + { + kill (sup->pid, SIGTERM); + sup->pid = -1; + } + if (sup->watch) + { + g_source_destroy (sup->watch); + sup->watch = NULL; + } + if (sup->status) + { + g_source_destroy (sup->status); + sup->status = NULL; + } + if (sup->ctrl) + { + wpa_ctrl_close (sup->ctrl); + sup->ctrl = NULL; + } + if (sup->stdout) + { + g_source_destroy (sup->stdout); + sup->stdout = NULL; + } + + supplicant_remove_timeout (sup); + remove_link_timeout (sup); + + /* HACK: should be fixed in wpa_supplicant. Will likely + * require accomodations for selinux. + */ + unlink (WPA_SUPPLICANT_GLOBAL_SOCKET); + sock_path = supplicant_get_device_socket_path (sup); + unlink (sock_path); + g_free (sock_path); +} + +static void +supplicant_watch_cb (GPid pid, + gint status, + gpointer user_data) +{ + struct supplicant *sup = (struct supplicant *) user_data; + + if (WIFEXITED (status)) + nm_warning ("wpa_supplicant exited with error code %d", WEXITSTATUS (status)); + else if (WIFSTOPPED (status)) + nm_warning ("wpa_supplicant stopped unexpectedly with signal %d", WSTOPSIG (status)); + else if (WIFSIGNALED (status)) + nm_warning ("wpa_supplicant died with signal %d", WTERMSIG (status)); + else + nm_warning ("wpa_supplicant died from an unknown cause"); + + supplicant_cleanup (sup); +} + + +/* + * link_timeout_cb + * + * Called when the link to the access point has been down for a specified + * period of time. + */ +static gboolean +link_timeout_cb (gpointer user_data) +{ + struct supplicant * sup = (struct supplicant *) user_data; + +#if 0 + /* Disconnect event during initial authentication and credentials + * ARE checked - we are likely to have wrong key. Ask the user for + * another one. + */ + if ( (nm_act_request_get_stage (req) == NM_ACT_STAGE_DEVICE_CONFIG) + && (ap_is_auth_required (ap, &has_key) && has_key)) + { + /* Association/authentication failed, we must have bad encryption key */ + nm_info ("Activation (%s/wireless): disconnected during association," + " asking for new key.", nm_device_get_iface (dev)); + supplicant_remove_timeout(self); + nm_dbus_get_user_key_for_network (data->dbus_connection, req, TRUE); + } + else +#endif + { + nm_info ("%s: link timed out.", sup->iface); + sup->state = STATE_DISCONNECTED; + } + + return FALSE; +} + + +#define MESSAGE_LEN 2048 + +static gboolean +supplicant_status_cb (GIOChannel *source, + GIOCondition condition, + gpointer user_data) +{ + struct supplicant * sup = (struct supplicant *) user_data; + char * message; + size_t len; + + message = g_malloc (MESSAGE_LEN); + len = MESSAGE_LEN; + wpa_ctrl_recv (sup->ctrl, message, &len); + message[len] = '\0'; + + if (strstr (message, WPA_EVENT_CONNECTED) != NULL) + { + remove_link_timeout (sup); + + /* If this is the initial association during device activation, + * schedule the next activation stage. + */ + if (sup->state == STATE_DEVICE_CONFIG) + { + nm_info ("Activation (%s/wireless) Stage 2 of 5 (Device Configure) " + "successful. Connected to access point '%s'.", + sup->iface, sup->ssid); + supplicant_remove_timeout (sup); + sup->state = STATE_CONNECTED; + } + } + else if (strstr (message, WPA_EVENT_DISCONNECTED) != NULL) + { + if (sup->state == STATE_CONNECTED || sup->state == STATE_DEVICE_CONFIG) + { + /* Start the link timeout so we allow some time for reauthentication */ + if (sup->link_timeout == NULL) + { + sup->link_timeout = g_timeout_source_new (8000); + g_source_set_callback (sup->link_timeout, link_timeout_cb, sup, NULL); + g_source_attach (sup->link_timeout, NULL); + } + } + } + + g_free (message); + + return TRUE; +} + + +#define NM_SUPPLICANT_TIMEOUT 20 /* how long we wait for wpa_supplicant to associate (in seconds) */ + +static unsigned int +get_supplicant_timeout (struct supplicant *sup) +{ +#if 0 + if (self->priv->num_freqs > 14) + return NM_SUPPLICANT_TIMEOUT * 2; +#endif + return NM_SUPPLICANT_TIMEOUT; +} + + +/* + * supplicant_timeout_cb + * + * Called when the supplicant has been unable to connect to an access point + * within a specified period of time. + */ +static gboolean +supplicant_timeout_cb (gpointer user_data) +{ + struct supplicant * sup = (struct supplicant *) user_data; + +#if 0 + /* Timed out waiting for authentication success; if the security method + * in use does not require access point side authentication (Open System + * WEP, for example) then we are likely using the wrong authentication + * algorithm or key. Request new one from the user. + */ + if (!ap_is_auth_required (ap, &has_key) && has_key) + { + /* Activation failed, we must have bad encryption key */ + nm_info ("Activation (%s/wireless): association took too long (>%us), asking for new key.", + nm_device_get_iface (dev), get_supplicant_timeout (self)); + nm_dbus_get_user_key_for_network (data->dbus_connection, req, TRUE); + } + else +#endif + { + nm_info ("Activation (%s/wireless): association took too long (>%us), failing activation.", + sup->iface, get_supplicant_timeout (sup)); + if (sup->state == STATE_DEVICE_CONFIG) + sup->state = STATE_DISCONNECTED; + } + + return FALSE; +} + +/* + * supplicant_log_stdout + * + * Read text from a GIOChannel that's hooked up to the stdout of + * wpa_supplicant, then write that text to NM's syslog service. + * Adapted from Gnome's bug-buddy. + * + */ +static gboolean +supplicant_log_stdout (GIOChannel *ioc, GIOCondition condition, gpointer data) +{ + struct supplicant *sup = (struct supplicant *) data; + gboolean retval = FALSE; + char *buf; + gsize len; + GIOStatus io_status; + GTimeVal start_time, cur_time; + + #define LINE_SIZE 1024 + buf = g_malloc0 (LINE_SIZE); + g_get_current_time (&start_time); + try_read: + io_status = g_io_channel_read_chars (ioc, buf, LINE_SIZE-1, &len, NULL); + switch (io_status) + { + case G_IO_STATUS_AGAIN: + g_usleep (G_USEC_PER_SEC / 60); + /* Only wait for data for 1/2 a second */ + g_get_current_time (&cur_time); + /* Subtract 1/2 second from current time so we don't have + * to modify start_time. + */ + g_time_val_add (&cur_time, -1 * (G_USEC_PER_SEC / 2)); + /* Compare times. If cur_time is less, keep trying to read */ + if ((cur_time.tv_sec < start_time.tv_sec) + || ((cur_time.tv_sec == start_time.tv_sec) + && (cur_time.tv_usec < start_time.tv_usec))) + goto try_read; + nm_warning ("Waited too long for wpa_supplicant output, some may be lost."); + break; + case G_IO_STATUS_ERROR: + nm_warning ("Error reading wpa_supplicant output."); + break; + case G_IO_STATUS_NORMAL: + retval = TRUE; + break; + default: + break; + } + + if (len > 0) + { + char *end; + char *start; + + /* Log each line separately; sometimes we get a couple lines at a time */ + buf[LINE_SIZE-1] = '\0'; + start = end = &buf[0]; + while (*end != '\0') + { + if (*end == '\n') + { + *end = '\0'; + nm_info ("wpa_supplicant(%d): %s", sup->pid, start); + start = end + 1; + } + end++; + } + } + g_free (buf); + + return retval; +} + + +static gboolean +supplicant_exec (struct supplicant *sup) +{ + gboolean success = FALSE; + char * argv[5]; + GError * error = NULL; + GPid pid = -1; + int sup_stdout; + + argv[0] = WPA_SUPPLICANT_BIN; + argv[1] = "-g"; + argv[2] = WPA_SUPPLICANT_GLOBAL_SOCKET; + argv[3] = "-dd"; + argv[4] = NULL; + + success = g_spawn_async_with_pipes ("/", argv, NULL, 0, NULL, NULL, + &pid, NULL, &sup_stdout, NULL, &error); + if (!success) + { + nm_warning ("Couldn't start wpa_supplicant. Error: (%d) %s", error->code, error->message); + g_error_free (error); + } + else + { + GIOChannel * channel; + const char * charset = NULL; + + /* Monitor output from supplicant and redirect to syslog */ + channel = g_io_channel_unix_new (sup_stdout); + g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL); + g_get_charset (&charset); + g_io_channel_set_encoding (channel, charset, NULL); + sup->stdout = g_io_create_watch (channel, G_IO_IN | G_IO_ERR); + g_source_set_priority (sup->stdout, G_PRIORITY_LOW); + g_source_set_callback (sup->stdout, (GSourceFunc) supplicant_log_stdout, sup, NULL); + g_source_attach (sup->stdout, NULL); + g_io_channel_unref (channel); + + /* Monitor the child process so we know when it stops */ + sup->pid = pid; + if (sup->watch) + g_source_destroy (sup->watch); + sup->watch = g_child_watch_source_new (pid); + g_source_set_callback (sup->watch, (GSourceFunc) supplicant_watch_cb, sup, NULL); + g_source_attach (sup->watch, NULL); + } + + return success; +} + +static gboolean +supplicant_interface_init (struct supplicant *sup) +{ + struct wpa_ctrl * ctrl = NULL; + char * socket_path; + gboolean success = FALSE; + int tries = 0; + + /* Try to open wpa_supplicant's global control socket */ + for (tries = 0; tries < WPA_SUPPLICANT_NUM_RETRIES && !ctrl; tries++) + { + ctrl = wpa_ctrl_open (WPA_SUPPLICANT_GLOBAL_SOCKET, NM_RUN_DIR); + g_usleep (WPA_SUPPLICANT_RETRY_TIME_US); + } + + if (!ctrl) + { + nm_info ("Error opening supplicant global control interface."); + goto exit; + } + + /* wpa_cli -g/var/run/wpa_supplicant-global interface_add eth1 "" wext /var/run/wpa_supplicant */ + if (!nm_utils_supplicant_request_with_check (ctrl, "OK", __func__, NULL, + "INTERFACE_ADD %s\t\twext\t" WPA_SUPPLICANT_CONTROL_SOCKET "\t", sup->iface)) + goto exit; + wpa_ctrl_close (ctrl); + + /* Get a control socket to wpa_supplicant for this interface. + * Try a couple times to work around naive socket naming + * in wpa_ctrl that sometimes collides with stale ones. + */ + socket_path = supplicant_get_device_socket_path (sup); + while (!sup->ctrl && (tries++ < 10)) + sup->ctrl = wpa_ctrl_open (socket_path, NM_RUN_DIR); + g_free (socket_path); + if (!sup->ctrl) + { + nm_info ("Error opening control interface to supplicant."); + goto exit; + } + success = TRUE; + +exit: + return success; +} + +static gboolean +supplicant_send_network_config (struct supplicant *sup) +{ + gboolean success = FALSE; + char * response = NULL; + int nwid; + GSList * elt; + + g_assert (sup->ctrl); + + /* Tell wpa_supplicant that we'll do the scanning */ + if (!nm_utils_supplicant_request_with_check (sup->ctrl, "OK", __func__, NULL, "AP_SCAN %d", + sup->ap_scan)) + goto out; + + /* Standard network setup info */ + if (!(response = nm_utils_supplicant_request (sup->ctrl, "ADD_NETWORK"))) { + nm_warning ("Supplicant error for ADD_NETWORK.\n"); + goto out; + } + if (sscanf (response, "%i\n", &nwid) != 1) { + nm_warning ("Supplicant error for ADD_NETWORK. Response: '%s'\n", response); + g_free (response); + goto out; + } + g_free (response); + + for (elt = sup->options; elt; elt = g_slist_next (elt)) { + struct opt * item = (struct opt *)(elt->data); + + if (!nm_utils_supplicant_request_with_check (sup->ctrl, "OK", __func__, NULL, + "SET_NETWORK %i %s %s", nwid, item->key, item->value)) + goto out; + } + + if (!nm_utils_supplicant_request_with_check (sup->ctrl, "OK", __func__, NULL, + "ENABLE_NETWORK %i", nwid)) + goto out; + + success = TRUE; +out: + return success; +} + + +static gboolean +supplicant_monitor_start (struct supplicant *sup) +{ + gboolean success = FALSE; + int fd = -1; + GIOChannel * channel; + + /* register network event monitor */ + if (wpa_ctrl_attach (sup->ctrl) != 0) + goto out; + + if ((fd = wpa_ctrl_get_fd (sup->ctrl)) < 0) + goto out; + + channel = g_io_channel_unix_new (fd); + sup->status = g_io_create_watch (channel, G_IO_IN); + g_source_set_callback (sup->status, (GSourceFunc) supplicant_status_cb, sup, NULL); + g_source_attach (sup->status, NULL); + + /* Set up a timeout on the association to kill it after get_supplicant_time() seconds */ + sup->timeout = g_timeout_source_new (get_supplicant_timeout (sup) * 1000); + g_source_set_callback (sup->timeout, supplicant_timeout_cb, sup, NULL); + g_source_attach (sup->timeout, NULL); + + success = TRUE; + +out: + return success; +} + + +static gboolean +handle_connect (gpointer user_data) +{ + struct supplicant *sup = (struct supplicant *) user_data; + + sup->state = STATE_DEVICE_CONFIG; + if (!supplicant_exec (sup)) + { + nm_warning ("Activation (%s/wireless): couldn't start the supplicant.", + sup->iface); + g_main_loop_quit (sup->loop); + goto out; + } + if (!supplicant_interface_init (sup)) + { + nm_warning ("Activation (%s/wireless): couldn't connect to the supplicant.", + sup->iface); + g_main_loop_quit (sup->loop); + goto out; + } + if (!supplicant_send_network_config (sup)) + { + nm_warning ("Activation (%s/wireless): couldn't send wireless configuration" + " to the supplicant.", sup->iface); + g_main_loop_quit (sup->loop); + goto out; + } + if (!supplicant_monitor_start (sup)) + { + nm_warning ("Activation (%s/wireless): couldn't monitor the supplicant.", + sup->iface); + g_main_loop_quit (sup->loop); + goto out; + } + +out: + return FALSE; +} + + +static void +parse_config(struct supplicant *sup, const char *file) +{ + gboolean success; + GError *err = NULL; + gsize len; + gchar *contents; + gchar **config_lines; + gchar **line; + gboolean in_network = FALSE; + + success = g_file_get_contents (file, &contents, &len, &err); + if (!success) { + nm_warning ("Error opening config %s: %s\n", file, err->message); + g_error_free (err); + exit (1); + } + + config_lines = g_strsplit (contents, "\n", -1); + g_free (contents); + if (!config_lines) { + nm_warning ("Error reading file contents.\n"); + exit (1); + } + + for (line = config_lines; *line; line++) { + struct opt * item; + char **vals; + + if (!strlen (*line)) + continue; + + g_strstrip (*line); + + /* Ignore comments */ + if (*line[0] == '#') + continue; + + /* End of first network block; we're done */ + if (*line[0] == '}') + break; + + vals = g_strsplit (*line, "=", 2); + /* Only accept two values */ + if (!vals || !vals[0] || !vals[1] || vals[2]) { + nm_warning ("Bad config line %s.\n", *line); + exit (1); + } + vals[0] = g_strstrip (vals[0]); + vals[1] = g_strstrip (vals[1]); + if ((strcmp (vals[0], "network") == 0) && (strcmp (vals[1], "{") == 0)) { + in_network = TRUE; + } else if (!in_network) { + if (strcmp (vals[0], "ap_scan") == 0) { + sup->ap_scan = atoi (vals[1]); + if (sup->ap_scan < 0 || sup->ap_scan > 2) { + nm_warning ("Bad ap_scan value (not between 0 and 2 inclusive)\n"); + exit (1); + } + } + } else { + item = g_malloc0 (sizeof (struct opt)); + item->key = g_strdup (vals[0]); + item->value = g_strdup (vals[1]); + if (strcmp (item->key, "ssid") == 0) + sup->ssid = item->value; + sup->options = g_slist_append (sup->options, item); + } + g_strfreev (vals); + } + + g_strfreev (config_lines); + + { + GSList *elt; + fprintf (stderr, "ap_scan: %d\n", sup->ap_scan); + for (elt = sup->options; elt; elt = g_slist_next (elt)) { + struct opt *item = (struct opt *)(elt->data); + fprintf (stdout, "Key: %s, Value: %s\n", item->key, item->value); + } + } +} + + +static void +print_usage(const char *prog) +{ + gchar *base = g_filename_display_basename (prog); + fprintf (stdout, "Usage: %s \n\n", base); + g_free (base); +} + +int +main (int argc, char **argv) +{ + struct supplicant *sup; + + if (argc != 3) { + print_usage(argv[0]); + exit(1); + } + + sup = g_malloc0 (sizeof (struct supplicant)); + sup->ap_scan = 2; + sup->loop = g_main_loop_new (NULL, FALSE); + sup->iface = g_strdup (argv[1]); + sup->state = STATE_DISCONNECTED; + + parse_config (sup, argv[2]); + + g_idle_add (handle_connect, sup); + g_main_loop_run (sup->loop); + + + supplicant_cleanup (sup); + g_free (sup->iface); + g_free (sup); + return 0; +}