Don't rebind all if found a SSID to rebind.
[dhcpcd-ui] / src / dhcpcd-gtk / main.c
index 54e1dd74558e89582c0f9dc0c38cbe7019132423..5b9d8abf8d1bff1d84b9fd9981273afe5117c4a3 100644 (file)
@@ -24,8 +24,8 @@
  * SUCH DAMAGE.
  */
 
+#include <errno.h>
 #include <locale.h>
-#include <poll.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -38,6 +38,7 @@ static NotifyNotification *nn;
 #endif
 
 #include "config.h"
+#include "dhcpcd.h"
 #include "dhcpcd-gtk.h"
 
 static GtkStatusIcon *status_icon;
@@ -47,7 +48,8 @@ static bool online;
 static bool carrier;
 
 struct watch {
-       struct pollfd pollfd;
+       gpointer ref;
+       int fd;
        guint eventid;
        GIOChannel *gio;
        struct watch *next;
@@ -56,6 +58,9 @@ static struct watch *watches;
 
 WI_SCAN *wi_scans;
 
+static gboolean dhcpcd_try_open(gpointer data);
+static gboolean dhcpcd_wpa_try_open(gpointer data);
+
 WI_SCAN *
 wi_scan_find(DHCPCD_WI_SCAN *scan)
 {
@@ -129,11 +134,14 @@ update_online(DHCPCD_CONNECTION *con, bool showif)
        msgs = NULL;
        ifs = dhcpcd_interfaces(con);
        for (i = ifs; i; i = i->next) {
-               if (i->up)
-                       ison = iscarrier = true;
-               if (!iscarrier && g_strcmp0(i->reason, "CARRIER") == 0)
-                       iscarrier = true;
-               msg = dhcpcd_if_message(i);
+               if (g_strcmp0(i->type, "link") == 0) {
+                       if (i->up)
+                               iscarrier = true;
+               } else {
+                       if (i->up)
+                               ison = true;
+               }
+               msg = dhcpcd_if_message(i, NULL);
                if (msg) {
                        if (showif)
                                g_message("%s", msg);
@@ -150,6 +158,7 @@ update_online(DHCPCD_CONNECTION *con, bool showif)
 
        if (online != ison || carrier != iscarrier) {
                online = ison;
+               carrier = iscarrier;
                if (ani_timer != 0) {
                        g_source_remove(ani_timer);
                        ani_timer = 0;
@@ -191,24 +200,17 @@ notify_closed(void)
 static void
 notify(const char *title, const char *msg, const char *icon)
 {
-       char **msgs, **m;
 
        if (msg == NULL)
                return;
        /* Don't spam the same message */
        if (notify_last_msg) {
-                       return;
                if (notify_last_msg && strcmp(msg, notify_last_msg) == 0)
                        return;
                g_free(notify_last_msg);
        }
        notify_last_msg = g_strdup(msg);
 
-       msgs = g_strsplit(msg, "\n", 0);
-       for (m = msgs; *m; m++)
-               g_message("%s", *m);
-       g_strfreev(msgs);
-
        if (nn != NULL)
                notify_notification_close(nn, NULL);
 
@@ -232,39 +234,96 @@ notify(const char *title, const char *msg, const char *icon)
 #  define notify(a, b, c)
 #endif
 
+static struct watch *
+dhcpcd_findwatch(int fd, gpointer data, struct watch **last)
+{
+       struct watch *w;
+
+       if (last)
+               *last = NULL;
+       for (w = watches; w; w = w->next) {
+               if (w->fd == fd || w->ref == data)
+                       return w;
+               if (last)
+                       *last = w;
+       }
+       return NULL;
+}
+
 static void
-event_cb(DHCPCD_CONNECTION *con, DHCPCD_IF *i, _unused void *data)
+dhcpcd_unwatch(int fd, gpointer data)
 {
-       char *msg;
-       const char *icon;
+       struct watch *w, *l;
 
-       g_message("interface event: %s: %s", i->ifname, i->reason);
-       update_online(con, false);
+       if ((w = dhcpcd_findwatch(fd, data, &l))) {
+               if (l)
+                       l->next = w->next;
+               else
+                       watches = w->next;
+               g_source_remove(w->eventid);
+               g_io_channel_unref(w->gio);
+               g_free(w);
+       }
+}
 
-       /* We should ignore renew and stop so we don't annoy the user */
-       if (g_strcmp0(i->reason, "RENEW") == 0 ||
-           g_strcmp0(i->reason, "STOP") == 0)
-               return;
+static gboolean
+dhcpcd_watch(int fd,
+    gboolean (*cb)(GIOChannel *, GIOCondition, gpointer),
+    gpointer data)
+{
+       struct watch *w, *l;
+       GIOChannel *gio;
+       GIOCondition flags;
+       guint eventid;
+
+       /* Sanity */
+       if ((w = dhcpcd_findwatch(fd, data, &l))) {
+               if (w->fd == fd)
+                       return TRUE;
+               if (l)
+                       l->next = w->next;
+               else
+                       watches = w->next;
+               g_source_remove(w->eventid);
+               g_io_channel_unref(w->gio);
+               g_free(w);
+       }
+
+       gio = g_io_channel_unix_new(fd);
+       if (gio == NULL) {
+               g_warning(_("Error creating new GIO Channel\n"));
+               return FALSE;
+       }
+       flags = G_IO_IN | G_IO_ERR | G_IO_HUP;
+       if ((eventid = g_io_add_watch(gio, flags, cb, data)) == 0) {
+               g_warning(_("Error creating watch\n"));
+               g_io_channel_unref(gio);
+               return FALSE;
+       }
 
-       msg = dhcpcd_if_message(i);
-       if (msg) {
-               g_message("%s", msg);
-               if (i->up)
-                       icon = "network-transmit-receive";
-               //else
-               //      icon = "network-transmit";
-               if (!i->up)
-                       icon = "network-offline";
-               notify(_("Network event"), msg, icon);
-               g_free(msg);
+       w = g_try_malloc(sizeof(*w));
+       if (w == NULL) {
+               g_warning(_("g_try_malloc\n"));
+               g_source_remove(eventid);
+               g_io_channel_unref(gio);
+               return FALSE;
        }
+
+       w->ref = data;
+       w->fd = fd;
+       w->eventid = eventid;
+       w->gio = gio;
+       w->next = watches;
+       watches = w;
+
+       return TRUE;
 }
 
 static void
-status_cb(DHCPCD_CONNECTION *con, const char *status, _unused void *data)
+dhcpcd_status_cb(DHCPCD_CONNECTION *con, const char *status,
+    _unused void *data)
 {
        static char *last = NULL;
-       char *version;
        const char *msg;
        bool refresh;
        WI_SCAN *w;
@@ -278,10 +337,10 @@ status_cb(DHCPCD_CONNECTION *con, const char *status, _unused void *data)
                        ani_timer = 0;
                        ani_counter = 0;
                }
+               online = carrier = false;
                gtk_status_icon_set_from_icon_name(status_icon,
                    "network-offline");
                gtk_status_icon_set_tooltip_text(status_icon, msg);
-               notify(_("No network"), msg, "network-offline");
                dhcpcd_prefs_abort();
                while (wi_scans) {
                        w = wi_scans->next;
@@ -289,40 +348,192 @@ status_cb(DHCPCD_CONNECTION *con, const char *status, _unused void *data)
                        g_free(wi_scans);
                        wi_scans = w;
                }
+               dhcpcd_unwatch(-1, con);
+               g_timeout_add(DHCPCD_RETRYOPEN, dhcpcd_try_open, con);
        } else {
-               if ((last == NULL || g_strcmp0(last, "down") == 0) &&
-                   dhcpcd_command(con, "GetDhcpcdVersion", NULL, &version))
-               {
-                       g_message(_("Connected to %s-%s"), "dhcpcd", version);
-                       g_free(version);
+               if ((last == NULL || g_strcmp0(last, "down") == 0)) {
+                       g_message(_("Connected to %s-%s"), "dhcpcd",
+                           dhcpcd_version(con));
                        refresh = true;
                } else
-                       refresh = false;
+                       refresh = g_strcmp0(last, "opened") ? false : true;
                update_online(con, refresh);
        }
+
+       g_free(last);
        last = g_strdup(status);
 }
 
+static gboolean
+dhcpcd_cb(_unused GIOChannel *gio, _unused GIOCondition c, gpointer data)
+{
+       DHCPCD_CONNECTION *con;
+
+       con = (DHCPCD_CONNECTION *)data;
+       if (dhcpcd_get_fd(con) == -1) {
+               g_warning(_("dhcpcd connection lost"));
+               dhcpcd_unwatch(-1, con);
+               g_timeout_add(DHCPCD_RETRYOPEN, dhcpcd_try_open, con);
+               return FALSE;
+       }
+
+       dhcpcd_dispatch(con);
+       return TRUE;
+}
+
+static gboolean
+dhcpcd_try_open(gpointer data)
+{
+       DHCPCD_CONNECTION *con;
+       int fd;
+       static int last_error;
+
+       con = (DHCPCD_CONNECTION *)data;
+       fd = dhcpcd_open(con, true);
+       if (fd == -1) {
+               if (errno == EACCES || errno == EPERM) {
+                       if ((fd = dhcpcd_open(con, false)) != -1)
+                               goto unprived;
+               }
+               if (errno != last_error) {
+                       g_critical("dhcpcd_open: %s", strerror(errno));
+                       last_error = errno;
+               }
+               return TRUE;
+       }
+
+unprived:
+       if (!dhcpcd_watch(fd, dhcpcd_cb, con)) {
+               dhcpcd_close(con);
+               return TRUE;
+       }
+
+       /* Start listening to WPA events */
+       dhcpcd_wpa_start(con);
+
+       return FALSE;
+}
+
 static void
-scan_cb(DHCPCD_CONNECTION *con, DHCPCD_IF *i, _unused void *data)
+dhcpcd_if_cb(DHCPCD_IF *i, _unused void *data)
 {
+       DHCPCD_CONNECTION *con;
+       char *msg;
+       const char *icon;
+       bool new_msg;
+
+       /* We should ignore renew and stop so we don't annoy the user */
+       if (g_strcmp0(i->reason, "RENEW") &&
+           g_strcmp0(i->reason, "STOP") &&
+           g_strcmp0(i->reason, "STOPPED"))
+       {
+               msg = dhcpcd_if_message(i, &new_msg);
+               if (msg) {
+                       g_message("%s", msg);
+                       if (new_msg) {
+                               if (i->up)
+                                       icon = "network-transmit-receive";
+                               //else
+                               //      icon = "network-transmit";
+                               if (!i->up)
+                                       icon = "network-offline";
+                               notify(_("Network event"), msg, icon);
+                       }
+                       g_free(msg);
+               }
+       }
+
+       /* Update the tooltip with connection information */
+       con = dhcpcd_if_connection(i);
+       update_online(con, false);
+}
+
+static gboolean
+dhcpcd_wpa_cb(_unused GIOChannel *gio, _unused GIOCondition c,
+    gpointer data)
+{
+       DHCPCD_WPA *wpa;
+       DHCPCD_IF *i;
+
+       wpa = (DHCPCD_WPA *)data;
+       if (dhcpcd_wpa_get_fd(wpa) == -1) {
+               dhcpcd_unwatch(-1, wpa);
+
+               /* If the interface hasn't left, try re-opening */
+               i = dhcpcd_wpa_if(wpa);
+               if (i == NULL ||
+                   g_strcmp0(i->reason, "DEPARTED") == 0 ||
+                   g_strcmp0(i->reason, "STOPPED") == 0)
+                       return TRUE;
+               g_warning(_("dhcpcd WPA connection lost: %s"), i->ifname);
+               g_timeout_add(DHCPCD_RETRYOPEN, dhcpcd_wpa_try_open, wpa);
+               return FALSE;
+       }
+
+       dhcpcd_wpa_dispatch(wpa);
+       return TRUE;
+}
+
+static gboolean
+dhcpcd_wpa_try_open(gpointer data)
+{
+       DHCPCD_WPA *wpa;
+       int fd;
+       static int last_error;
+
+       wpa = (DHCPCD_WPA *)data;
+       fd = dhcpcd_wpa_open(wpa);
+       if (fd == -1) {
+               if (errno != last_error)
+                       g_critical("dhcpcd_wpa_open: %s", strerror(errno));
+               last_error = errno;
+               return TRUE;
+       }
+
+       if (!dhcpcd_watch(fd, dhcpcd_wpa_cb, wpa)) {
+               dhcpcd_wpa_close(wpa);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+dhcpcd_wpa_scan_cb(DHCPCD_WPA *wpa, _unused void *data)
+{
+       DHCPCD_IF *i;
        WI_SCAN *w;
        DHCPCD_WI_SCAN *scans, *s1, *s2;
        char *txt, *t;
+       int lerrno, fd;
        const char *msg;
 
-       g_message(_("%s: Received scan results"), i->ifname);
-       scans = dhcpcd_wi_scans(con, i);
-       if (scans == NULL && dhcpcd_error(con) != NULL) {
-               g_warning("%s: %s", i->ifname, dhcpcd_error(con));
-               dhcpcd_error_clear(con);
+       /* This could be a new WPA so watch it */
+       fd = dhcpcd_wpa_get_fd(wpa);
+       if (fd == -1) {
+               g_critical("No fd for WPA %p", wpa);
+               dhcpcd_unwatch(-1, wpa);
+               return;
+       }
+       dhcpcd_watch(fd, dhcpcd_wpa_cb, wpa);
+
+       i = dhcpcd_wpa_if(wpa);
+       if (i == NULL) {
+               g_critical("No interface for WPA %p", wpa);
+               return;
        }
+       g_message(_("%s: Received scan results"), i->ifname);
+       lerrno = errno;
+       errno = 0;
+       scans = dhcpcd_wi_scans(i);
+       if (scans == NULL && errno)
+               g_warning("%s: %s", i->ifname, strerror(errno));
+       errno = lerrno;
        for (w = wi_scans; w; w = w->next)
-               if (w->connection == con && w->interface == i)
+               if (w->interface == i)
                        break;
        if (w == NULL) {
                w = g_malloc(sizeof(*w));
-               w->connection = con;
                w->interface = i;
                w->next = wi_scans;
                wi_scans = w;
@@ -349,86 +560,15 @@ scan_cb(DHCPCD_CONNECTION *con, DHCPCD_IF *i, _unused void *data)
                        notify(msg, txt, "network-wireless");
                        g_free(txt);
                }
+               menu_update_scans(w->interface, scans);
                dhcpcd_wi_scans_free(w->scans);
        }
        w->scans = scans;
 }
 
-static gboolean
-gio_callback(GIOChannel *gio, _unused GIOCondition c, _unused gpointer d)
-{
-       int fd;
-
-       fd = g_io_channel_unix_get_fd(gio);
-       dhcpcd_dispatch(fd);
-       return true;
-}
-
-static void
-delete_watch_cb(_unused DHCPCD_CONNECTION *con, const struct pollfd *fd,
-    _unused void *data)
-{
-       struct watch *w, *l;
-
-       l = NULL;
-       for (w = watches; w; w = w->next) {
-               if (w->pollfd.fd == fd->fd) {
-                       if (l == NULL)
-                               watches = w->next;
-                       else
-                               l->next = w->next;
-                       g_source_remove(w->eventid);
-                       g_io_channel_unref(w->gio);
-                       g_free(w);
-                       break;
-               }
-       }
-}
-
-static void
-add_watch_cb(DHCPCD_CONNECTION *con, const struct pollfd *fd,
-    _unused void *data)
-{
-       struct watch *w;
-       GIOChannel *gio;
-       GIOCondition flags;
-       guint eventid;
-
-       /* Remove any existing watch */
-       delete_watch_cb(con, fd, data);
-
-       gio = g_io_channel_unix_new(fd->fd);
-       if (gio == NULL) {
-               g_warning(_("Error creating new GIO Channel\n"));
-               return;
-       }
-       flags = 0;
-       if (fd->events & POLLIN)
-               flags |= G_IO_IN;
-       if (fd->events & POLLOUT)
-               flags |= G_IO_OUT;
-       if (fd->events & POLLERR)
-               flags |= G_IO_ERR;
-       if (fd->events & POLLHUP)
-               flags |= G_IO_HUP;
-       if ((eventid = g_io_add_watch(gio, flags, gio_callback, con)) == 0) {
-               g_io_channel_unref(gio);
-               g_warning(_("Error creating watch\n"));
-               return;
-       }
-       w = g_malloc(sizeof(*w));
-       memcpy(&w->pollfd, fd, sizeof(w->pollfd));
-       w->eventid = eventid;
-       w->gio = gio;
-       w->next = watches;
-       watches = w;
-}
-
 int
 main(int argc, char *argv[])
 {
-       char *error = NULL;
-       char *version = NULL;
        DHCPCD_CONNECTION *con;
 
        setlocale(LC_ALL, "");
@@ -445,39 +585,29 @@ main(int argc, char *argv[])
        gtk_status_icon_set_tooltip_text(status_icon,
            _("Connecting to dhcpcd ..."));
        gtk_status_icon_set_visible(status_icon, true);
-
+       online = false;
 #ifdef NOTIFY
        notify_init(PACKAGE);
 #endif
 
        g_message(_("Connecting ..."));
-       con = dhcpcd_open(&error);
+       con = dhcpcd_new();
        if (con ==  NULL) {
-               g_critical("libdhcpcd: %s", error);
-               exit(EXIT_FAILURE);
-       }
-
-       gtk_status_icon_set_tooltip_text(status_icon,
-           _("Triggering dhcpcd ..."));
-       online = false;
-
-       if (!dhcpcd_command(con, "GetVersion", NULL, &version)) {
-               g_critical("libdhcpcd: GetVersion: %s", dhcpcd_error(con));
-               exit(EXIT_FAILURE);
-       }
-       g_message(_("Connected to %s-%s"), "dhcpcd-dbus", version);
-       g_free(version);
-
-       dhcpcd_set_watch_functions(con, add_watch_cb, delete_watch_cb, NULL);
-       dhcpcd_set_signal_functions(con, event_cb, status_cb, scan_cb, NULL);
-       if (dhcpcd_error(con)) {
-               g_critical("libdhcpcd: %s", dhcpcd_error(con));
+               g_critical("libdhcpcd: %s", strerror(errno));
                exit(EXIT_FAILURE);
        }
+       dhcpcd_set_progname(con, "dhcpcd-gtk");
+       dhcpcd_set_status_callback(con, dhcpcd_status_cb, NULL);
+       dhcpcd_set_if_callback(con, dhcpcd_if_cb, NULL);
+       dhcpcd_wpa_set_scan_callback(con, dhcpcd_wpa_scan_cb, NULL);
+       //dhcpcd_wpa_set_status_callback(con, dhcpcd_wpa_status_cb, NULL);
+       if (dhcpcd_try_open(con))
+               g_timeout_add(DHCPCD_RETRYOPEN, dhcpcd_try_open, con);
 
        menu_init(status_icon, con);
 
        gtk_main();
        dhcpcd_close(con);
+       dhcpcd_free(con);
        return 0;
 }