/*
* dhcpcd-gtk
- * Copyright 2009 Roy Marples <roy@marples.name>
+ * Copyright 2009-2014 Roy Marples <roy@marples.name>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* SUCH DAMAGE.
*/
+#include <errno.h>
#include <locale.h>
-#include <poll.h>
#include <stdlib.h>
#include <string.h>
#ifdef NOTIFY
# include <libnotify/notify.h>
+#ifndef NOTIFY_CHECK_VERSION
+# define NOTIFY_CHECK_VERSION(a,b,c) 0
+#endif
static NotifyNotification *nn;
#endif
+#include "config.h"
+#include "dhcpcd.h"
#include "dhcpcd-gtk.h"
static GtkStatusIcon *status_icon;
-static int ani_timer;
+static guint ani_timer;
static int ani_counter;
static bool online;
static bool carrier;
struct watch {
- struct pollfd pollfd;
- int eventid;
+ gpointer ref;
+ int fd;
+ guint eventid;
GIOChannel *gio;
struct watch *next;
};
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)
{
animate_carrier(_unused gpointer data)
{
const char *icon;
-
+
if (ani_timer == 0)
return false;
animate_online(_unused gpointer data)
{
const char *icon;
-
+
if (ani_timer == 0)
return false;
msgs = NULL;
ifs = dhcpcd_interfaces(con);
for (i = ifs; i; i = i->next) {
- if (showif)
+ 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);
+ if (msgs) {
+ tmp = g_strconcat(msgs, "\n", msg, NULL);
+ g_free(msgs);
+ g_free(msg);
+ msgs = tmp;
+ } else
+ msgs = msg;
+ } else if (showif)
g_message("%s: %s", i->ifname, i->reason);
- if (strcmp(i->reason, "RELEASE") == 0 ||
- strcmp(i->reason, "STOP") == 0)
- continue;
- if (dhcpcd_if_up(i))
- ison = iscarrier = true;
- if (!iscarrier && g_strcmp0(i->reason, "CARRIER") == 0)
- iscarrier = true;
- msg = dhcpcd_if_message(i);
- if (msgs) {
- tmp = g_strconcat(msgs, "\n", msg, NULL);
- g_free(msgs);
- g_free(msg);
- msgs = tmp;
- } else
- msgs = msg;
}
if (online != ison || carrier != iscarrier) {
online = ison;
+ carrier = iscarrier;
if (ani_timer != 0) {
g_source_remove(ani_timer);
ani_timer = 0;
"network-offline");
}
}
- gtk_status_icon_set_tooltip(status_icon, msgs);
+ gtk_status_icon_set_tooltip_text(status_icon, msgs);
g_free(msgs);
}
#endif
}
+#ifdef NOTIFY
+static char *notify_last_msg;
+
static void
notify_closed(void)
{
-#ifdef NOTIFY
nn = NULL;
-#endif
}
-#ifdef NOTIFY
static void
notify(const char *title, const char *msg, const char *icon)
{
- char **msgs, **m;
- msgs = g_strsplit(msg, "\n", 0);
- for (m = msgs; *m; m++)
- g_message("%s", *m);
- g_strfreev(msgs);
+ if (msg == NULL)
+ return;
+ /* Don't spam the same message */
+ if (notify_last_msg) {
+ if (notify_last_msg && strcmp(msg, notify_last_msg) == 0)
+ return;
+ g_free(notify_last_msg);
+ }
+ notify_last_msg = g_strdup(msg);
+
if (nn != NULL)
notify_notification_close(nn, NULL);
+
+#if NOTIFY_CHECK_VERSION(0,7,0)
+ nn = notify_notification_new(title, msg, icon);
+ notify_notification_set_hint(nn, "transient",
+ g_variant_new_boolean(TRUE));
+#else
if (gtk_status_icon_get_visible(status_icon))
nn = notify_notification_new_with_status_icon(title,
msg, icon, status_icon);
else
nn = notify_notification_new(title, msg, icon, NULL);
+#endif
+
notify_notification_set_timeout(nn, 5000);
g_signal_connect(nn, "closed", G_CALLBACK(notify_closed), NULL);
notify_notification_show(nn, NULL);
# 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("%s: %s", i->ifname, i->reason);
- update_online(con, false);
-
- /* 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;
+ 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);
+ }
+}
- msg = dhcpcd_if_message(i);
- if (dhcpcd_if_up(i))
- icon = "network-transmit-receive";
- else
- icon = "network-transmit";
- if (dhcpcd_if_down(i))
- icon = "network-offline";
- notify(_("Network event"), msg, icon);
- g_free(msg);
+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;
+ }
+
+ 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;
if (g_strcmp0(status, "down") == 0) {
msg = N_(last ?
"Connection to dhcpcd lost" : "dhcpcd not running");
- gtk_status_icon_set_tooltip(status_icon, msg);
- notify(_("No network"), msg, "network-offline");
+ if (ani_timer != 0) {
+ g_source_remove(ani_timer);
+ 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);
dhcpcd_prefs_abort();
while (wi_scans) {
w = wi_scans->next;
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;
+ /* 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);
- scans = dhcpcd_wi_scans(con, i);
+ 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;
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;
- int flags, eventid;
-
- /* Remove any existing watch */
- delete_watch_cb(con, fd, data);
-
- gio = g_io_channel_unix_new(fd->fd);
- if (gio == NULL) {
- g_error(_("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_error(_("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, "");
bindtextdomain(PACKAGE, NULL);
bind_textdomain_codeset(PACKAGE, "UTF-8");
- textdomain(PACKAGE);
+ textdomain(PACKAGE);
gtk_init(&argc, &argv);
g_set_application_name("Network Configurator");
gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(),
ICONDIR);
status_icon = gtk_status_icon_new_from_icon_name("network-offline");
-
- gtk_status_icon_set_tooltip(status_icon,
+
+ 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(status_icon, _("Triggering dhcpcd ..."));
- online = false;
-
- if (!dhcpcd_command(con, "GetVersion", NULL, &version)) {
- g_critical("libdhcpcd: GetVersion: %s", dhcpcd_error(con));
+ g_critical("libdhcpcd: %s", strerror(errno));
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_error("libdhcpcd: %s", dhcpcd_error(con));
+ 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;
}