Import dhcpcd-gtk
authorRoy Marples <roy@marples.name>
Thu, 22 Jan 2009 15:37:00 +0000 (15:37 +0000)
committerRoy Marples <roy@marples.name>
Thu, 22 Jan 2009 15:37:00 +0000 (15:37 +0000)
17 files changed:
Makefile [new file with mode: 0644]
config.h [new file with mode: 0644]
de-.mk [new file with mode: 0644]
de-GNOME.mk [new file with mode: 0644]
de-XFCE.mk [new file with mode: 0644]
dhcpcd-gtk.desktop [new file with mode: 0644]
main.c [new file with mode: 0644]
menu.c [new file with mode: 0644]
menu.h [new file with mode: 0644]
mk/cc.mk [new file with mode: 0644]
mk/depend-.mk [new file with mode: 0644]
mk/depend-gmake.mk [new file with mode: 0644]
mk/depend.mk [new file with mode: 0644]
mk/dist.mk [new file with mode: 0644]
mk/files.mk [new file with mode: 0644]
mk/prog.mk [new file with mode: 0644]
mk/sys.mk [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..dfbbe71
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,30 @@
+# Makefile based on BSD make.
+# Our mk stubs also work with GNU make.
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+PROG=          dhcpcd-gtk
+SRCS=          main.c menu.c
+
+SYSCONFDIR?=   ${PREFIX}/etc/xdg/autostart
+FILESDIR?=     ${SYSCONFDIR}
+FILES=         dhcpcd-gtk.desktop
+
+# Crappy include for Desktop Environment
+# We have mk for GNOME and XFCE
+include                de-${DE}.mk 
+
+_PKGCFLAGS_SH= pkg-config --cflags dbus-glib-1 gtk+-2.0 libnotify ${DEPKGS}
+_PKGCFLAGS!=   ${_PKGCFLAGS_SH}
+PKGCFLAGS?=    ${_PKGCFLAGS}$(shell ${_PKGCFLAGS_SH})
+CFLAGS+=       ${PKGCFLAGS}
+
+_PKGLIBS_SH=   pkg-config --libs dbus-glib-1 gtk+-2.0 libnotify ${DEPKGS}
+_PKGLIBS!=     ${_PKGLIBS_SH}
+PKGLIBS?=      ${_PKGLIBS}$(shell ${_PKGLIBS_SH})
+LDADD+=                ${PKGLIBS}
+
+CPPFLAGS+=     ${DECPPFLAGS}
+
+MK=            mk
+include ${MK}/sys.mk
+include ${MK}/prog.mk
diff --git a/config.h b/config.h
new file mode 100644 (file)
index 0000000..15a648d
--- /dev/null
+++ b/config.h
@@ -0,0 +1,42 @@
+/*
+ * dhcpcd-gtk
+ * Copyright 2009 Roy Marples <roy@marples.name>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define PACKAGE                        "dhcpcd-gtk"
+#define VERSION                        "0.1.1"
+
+#define DHCPCD_SERVICE         "name.marples.roy.dhcpcd"
+#define DHCPCD_PATH            "/name/marples/roy/dhcpcd"
+
+#if __GNUC__ > 2 || defined(__INTEL_COMPILER)
+# define _unused   __attribute__((__unused__))
+#else
+# define _unused
+#endif
+
+#endif
diff --git a/de-.mk b/de-.mk
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/de-GNOME.mk b/de-GNOME.mk
new file mode 100644 (file)
index 0000000..f757fd2
--- /dev/null
@@ -0,0 +1,2 @@
+DEPKGS=                libgnomeui-2.0
+DECPPFLAGS=    -DHAVE_GNOME
diff --git a/de-XFCE.mk b/de-XFCE.mk
new file mode 100644 (file)
index 0000000..2c8be82
--- /dev/null
@@ -0,0 +1,2 @@
+DEPKGS=                exo-0.3
+DECPPFLAGS=    -DHAVE_XFCE
diff --git a/dhcpcd-gtk.desktop b/dhcpcd-gtk.desktop
new file mode 100644 (file)
index 0000000..29eee49
--- /dev/null
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Name=dhcpcd Monitor
+Comment=Monitor your network connections
+Exec=dhcpcd-gtk
+Terminal=false
+Type=Application
+X-GNOME-Autostart-enabled=true
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..95d13be
--- /dev/null
+++ b/main.c
@@ -0,0 +1,514 @@
+/*
+ * dhcpcd-gtk
+ * Copyright 2009 Roy Marples <roy@marples.name>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <dbus/dbus-glib.h>
+#include <gtk/gtk.h>
+#include <libnotify/notify.h>
+
+#include "config.h"
+#include "menu.h"
+
+/* Work out if we have a private address or not
+ * 10/8
+ * 172.16/12
+ * 192.168/16
+ */
+#ifndef IN_PRIVATE
+# define IN_PRIVATE(addr) (((addr & IN_CLASSA_NET) == 0x0a000000) || \
+                          ((addr & 0xfff00000)    == 0xac100000) || \
+                          ((addr & IN_CLASSB_NET) == 0xc0a80000))
+#endif
+#ifndef IN_LINKLOCAL
+# define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == 0xa9fe0000)
+#endif
+
+struct if_msg {
+       char *name;
+       char *reason;
+       struct in_addr ip;
+       unsigned char cidr;
+};
+
+static DBusGProxy *bus_proxy;
+static GtkStatusIcon *status_icon;
+static GList *interfaces;
+static gboolean online;
+static NotifyNotification *nn;
+
+static char **interface_order;
+
+const char *const up_reasons[] = {
+       "BOUND",
+       "RENEW",
+       "REBIND",
+       "REBOOT",
+       "IPV4LL",
+       "INFORM",
+       "TIMEOUT",
+       NULL
+};
+
+const char *const down_reasons[] = {
+       "EXPIRE",
+       "FAIL",
+       "NAK",
+       "NOCARRIER",
+       "STOP",
+       NULL
+};
+
+/* Should be in a header */
+void notify_close(void);
+
+static gboolean
+ignore_if_msg(const struct if_msg *ifm)
+{
+       if (g_strcmp0(ifm->reason, "STOP") == 0 ||
+           g_strcmp0(ifm->reason, "RELEASE") == 0)
+               return TRUE;
+       return FALSE;
+}
+
+static void
+free_if_msg(struct if_msg *ifm)
+{
+       g_free(ifm->name);
+       g_free(ifm->reason);
+       g_free(ifm);
+}
+
+static void
+error_exit(const char *msg, GError *error)
+{
+       GtkWidget *dialog;
+
+       if (error) {
+               g_critical("%s: %s", msg, error->message);
+               dialog = gtk_message_dialog_new(NULL,
+                                               0,
+                                               GTK_MESSAGE_ERROR,
+                                               GTK_BUTTONS_CLOSE,
+                                               "%s: %s",
+                                               msg,
+                                               error->message);
+       } else {
+               g_critical("%s", msg);
+               dialog = gtk_message_dialog_new(NULL,
+                                               0,
+                                               GTK_MESSAGE_ERROR,
+                                               GTK_BUTTONS_CLOSE,
+                                               "%s",
+                                               msg);
+       }
+       gtk_dialog_run(GTK_DIALOG(dialog));
+       gtk_widget_destroy(dialog);
+       if (gtk_main_level())
+               gtk_main_quit();
+       else
+               exit(EXIT_FAILURE);
+}
+
+static struct if_msg *
+make_if_msg(GHashTable *config)
+{
+       GValue *val;
+       struct if_msg *ifm;
+
+       val = g_hash_table_lookup(config, "Interface");
+       if (val == NULL)
+               return NULL;
+       ifm = g_malloc0(sizeof(*ifm));
+       ifm->name = g_strdup(g_value_get_string(val));
+       val = g_hash_table_lookup(config, "Reason");
+       if (val)
+               ifm->reason = g_strdup(g_value_get_string(val));
+       val = g_hash_table_lookup(config, "IPAddress");
+       if (val)
+               ifm->ip.s_addr = g_value_get_uint(val);
+       val = g_hash_table_lookup(config, "SubnetCIDR");
+       if (val)
+               ifm->cidr = g_value_get_uchar(val);
+       val = g_hash_table_lookup(config, "InterfaceOrder");
+       if (val) {
+               g_strfreev(interface_order);
+               interface_order = g_strsplit(g_value_get_string(val), " ", 0);
+       }
+       return ifm;
+}
+
+static char *
+print_if_msg(const struct if_msg *ifm)
+{
+       char *msg, *p;
+       size_t len;
+       gboolean showip;
+
+       len = strlen(ifm->name) + 3;
+       len += strlen(ifm->reason) + 1;
+       if (ifm->ip.s_addr != 0) {
+               len += 16; /* 000. * 4 */
+               if (ifm->cidr != 0)
+                       len += 3; /* /32 */
+       }
+       msg = p = g_malloc(len);
+       p += g_snprintf(msg, len, "%s: %s", ifm->name, ifm->reason);
+       showip = TRUE;
+       if (g_strcmp0(ifm->reason, "NOCARRIER") == 0)
+               showip = FALSE;
+       if (ifm->ip.s_addr != 0 && showip) {
+               p += g_snprintf(p, len - (p - msg), " %s", inet_ntoa(ifm->ip));
+               if (ifm->cidr != 0)
+                       g_snprintf(p, len - (p - msg), "/%d", ifm->cidr);
+       }
+       return msg;
+}
+
+static gint
+if_msg_comparer(gconstpointer a, gconstpointer b)
+{
+       const struct if_msg *ifa, *ifb;
+       const char *const *order;
+
+       ifa = (const struct if_msg *)a;
+       ifb = (const struct if_msg *)b;
+       for (order = (const char *const *)interface_order; *order; order++) {
+               if (g_strcmp0(*order, ifa->name) == 0)
+                       return -1;
+               if (g_strcmp0(*order, ifb->name) == 0)
+                       return 1;
+       }
+       return 0;
+}
+
+static void
+update_online(char **buffer)
+{
+       gboolean ison;
+       char *msg, *msgs, *tmp;
+       const char *icon;
+       const char *const *r;
+       const GList *gl;
+       const struct if_msg *ifm;
+
+       ison = FALSE;
+       msgs = NULL;
+       for (gl = interfaces; gl; gl = gl->next) {
+               ifm = (const struct if_msg *)gl->data;          
+               for (r = up_reasons; *r; r++) {
+                       if (g_strcmp0(*r, ifm->reason) == 0) {
+                               ison = TRUE;
+                               break;
+                       }
+               }
+               msg = print_if_msg(ifm);
+               if (msgs) {
+                       tmp = g_strconcat(msgs, "\n", msg, NULL);
+                       g_free(msgs);
+                       g_free(msg);
+                       msgs = tmp;
+               } else
+                       msgs = msg;
+       }
+
+       if (online != ison) {
+               online = ison;
+               icon = online ? GTK_STOCK_CONNECT : GTK_STOCK_DISCONNECT;
+               gtk_status_icon_set_from_stock(status_icon, icon);
+       }
+       gtk_status_icon_set_tooltip(status_icon, msgs);
+       if (buffer)
+               *buffer = msgs;
+       else
+               g_free(msgs);
+}
+
+void
+notify_close(void)
+{
+       notify_notification_close(nn, NULL);
+}
+
+static void
+notify_closed(void)
+{
+       nn = NULL;
+}
+
+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 (nn != NULL)
+               notify_notification_close(nn, NULL);
+       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);
+       notify_notification_set_timeout(nn, 5000);
+       g_signal_connect(nn, "closed", G_CALLBACK(notify_closed), NULL);
+       notify_notification_show(nn, NULL);
+}
+
+static void
+dhcpcd_event(_unused DBusGProxy *proxy, GHashTable *config, _unused void *data)
+{
+       struct if_msg *ifm, *ifp;
+       gboolean rem;
+       GList *gl;
+       char *msg, *title;
+       const char *act, *net;
+       const char *const *r;
+       in_addr_t ipn;
+
+       ifm = make_if_msg(config);
+       if (ifm == NULL)
+               return;
+
+       rem = ignore_if_msg(ifm);
+       ifp = NULL;
+       for (gl = interfaces; gl; gl = gl->next) {
+               ifp = (struct if_msg *)gl->data;
+               if (g_strcmp0(ifp->name, ifm->name) == 0) {
+                       free_if_msg(ifp);
+                       if (rem)
+                               interfaces = g_list_delete_link(interfaces, gl);
+                       else
+                               gl->data = ifm;
+                       break;
+               }
+       }
+       if (ifp == NULL && !rem)
+               interfaces = g_list_prepend(interfaces, ifm);
+       interfaces = g_list_sort(interfaces, if_msg_comparer);
+
+       msg = print_if_msg(ifm);
+       act = NULL;
+       title = NULL;
+       for (r = up_reasons; *r; r++) {
+               if (g_strcmp0(*r, ifm->reason) == 0) {
+                       act = "Connected to ";
+                       break;
+               }
+       }
+       for (r = down_reasons; *r; r++) {
+               if (g_strcmp0(*r, ifm->reason) == 0) {
+                       act = "Disconnected from ";
+                       break;
+               }
+       }
+       if (act && ifm->ip.s_addr) {
+               ipn = htonl(ifm->ip.s_addr);
+               if (IN_LINKLOCAL(ipn))
+                       net = "private network";
+               else if (IN_PRIVATE(ipn))
+                       net = "LAN";
+               else
+                       net = "internet";
+               title = g_strconcat(act, net, NULL);
+       }
+
+       update_online(NULL);
+       if (title) {
+               notify(title, msg, GTK_STOCK_NETWORK);
+               g_free(title);
+       } else
+               notify("Interface event", msg, GTK_STOCK_NETWORK);
+       g_free(msg);
+}
+
+static void
+foreach_make_ifm(_unused gpointer key, gpointer value, _unused gpointer data)
+{
+       struct if_msg *ifm;
+
+       ifm = make_if_msg((GHashTable *)value);
+       if (ignore_if_msg(ifm))
+               g_free(ifm);
+       else if (ifm)
+               interfaces = g_list_prepend(interfaces, ifm);
+}
+
+static void
+dhcpcd_get_interfaces()
+{
+       GHashTable *ifs;
+       GError *error = NULL;
+       GType otype;
+       char *msg;
+
+       otype = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
+       otype = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, otype);
+       if (!dbus_g_proxy_call(bus_proxy, "GetInterfaces", &error,
+                              G_TYPE_INVALID,
+                              otype, &ifs, G_TYPE_INVALID))
+               error_exit("GetInterfaces", error);
+       g_hash_table_foreach(ifs, foreach_make_ifm, NULL);
+       g_hash_table_unref(ifs);
+
+       /* Each interface config only remembers the last order when
+        * that interface was configured, so get the real order now. */
+       g_strfreev(interface_order);
+       interface_order = NULL;
+       if (!dbus_g_proxy_call(bus_proxy, "ListInterfaces", &error,
+                              G_TYPE_INVALID,
+                              G_TYPE_STRV, &interface_order, G_TYPE_INVALID))
+               error_exit("ListInterfaces", error);
+       interfaces = g_list_sort(interfaces, if_msg_comparer);
+       msg = NULL;
+       update_online(&msg);
+       // GTK+ 2.16 msg = gtk_status_icon_get_tooltip_text(status_icon);
+       if (msg != NULL) {
+               notify("Interface status", msg, GTK_STOCK_NETWORK);
+               g_free(msg);
+       }
+}
+
+static void
+check_status(const char *status)
+{
+       static char *last = NULL;
+       GList *gl;
+       char *version;
+       const char *msg;
+       gboolean refresh;
+       GError *error = NULL;
+
+       g_message("status changed to %s", status);
+       if (g_strcmp0(status, "down") == 0) {
+               for (gl = interfaces; gl; gl = gl->next)
+                       free_if_msg((struct if_msg *)gl->data);
+               g_list_free(interfaces);
+               interfaces = NULL;
+               update_online(NULL);
+               msg = last? "Connection to dhcpcd lost" : "dhcpcd not running";
+               gtk_status_icon_set_tooltip(status_icon, msg);
+               notify("No network", msg, GTK_STOCK_NETWORK);
+       }
+
+       refresh = FALSE;
+       if (last == NULL) {
+               if (g_strcmp0(status, "down") != 0)
+                       refresh = TRUE;
+       } else {
+               if (g_strcmp0(status, last) == 0)
+                       return;
+               if (g_strcmp0(last, "down") == 0)
+                       refresh = TRUE;
+               g_free(last);
+       }
+       last = g_strdup(status);
+
+       if (!refresh)
+               return;
+       if (!dbus_g_proxy_call(bus_proxy, "GetDhcpcdVersion", &error,
+                              G_TYPE_INVALID,
+                              G_TYPE_STRING, &version, G_TYPE_INVALID))
+               error_exit("GetDhcpcdVersion", error);
+       g_message("Connected to dhcpcd-%s", version);
+       g_free(version);
+       dhcpcd_get_interfaces();
+}
+
+static void
+dhcpcd_status(_unused DBusGProxy *proxy, const char *status, _unused void *data)
+{
+       check_status(status);
+}
+
+int
+main(int argc, char *argv[])
+{
+       DBusGConnection *bus;
+       GError *error = NULL;
+       char *version = NULL;
+       GType otype;
+       
+#if defined(HAVE_GNOME) || defined(HAVE_XFCE)
+       if (!g_thread_supported())
+               g_thread_init(NULL);
+#endif
+
+       gtk_init(&argc, &argv);
+       g_set_application_name("dhcpcd Monitor");
+       status_icon = gtk_status_icon_new_from_stock(GTK_STOCK_DISCONNECT);
+       gtk_status_icon_set_tooltip(status_icon, "Connecting to dhcpcd ...");
+       gtk_status_icon_set_visible(status_icon, TRUE);
+
+       notify_init(PACKAGE);
+
+       bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
+       if (bus == NULL || error != NULL)
+               error_exit("could not connect to system bus", error);
+       bus_proxy = dbus_g_proxy_new_for_name(bus,
+                                             DHCPCD_SERVICE,
+                                             DHCPCD_PATH,
+                                             DHCPCD_SERVICE);
+       if (!dbus_g_proxy_call(bus_proxy, "GetVersion", &error,
+                              G_TYPE_INVALID,
+                              G_TYPE_STRING, &version, G_TYPE_INVALID))
+               error_exit("GetVersion", error);
+       g_message("Connected to dhcpcd-dbus-%s", version);
+       g_free(version);
+
+       gtk_status_icon_set_tooltip(status_icon, "Triggering dhcpcd ...");
+       online = FALSE;
+       menu_init(status_icon);
+
+       if (!dbus_g_proxy_call(bus_proxy, "GetStatus", &error,
+                              G_TYPE_INVALID,
+                              G_TYPE_STRING, &version, G_TYPE_INVALID))
+               error_exit("GetStatus", error);
+       check_status(version);
+       g_free(version);
+
+       otype = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
+       dbus_g_proxy_add_signal(bus_proxy, "Event",
+                               otype, G_TYPE_INVALID);
+       dbus_g_proxy_connect_signal(bus_proxy, "Event",
+                                   G_CALLBACK(dhcpcd_event),
+                                   NULL, NULL);
+       dbus_g_proxy_add_signal(bus_proxy, "StatusChanged",
+                               G_TYPE_STRING, G_TYPE_INVALID);
+       dbus_g_proxy_connect_signal(bus_proxy, "StatusChanged",
+                                   G_CALLBACK(dhcpcd_status),
+                                   NULL, NULL);
+
+       gtk_main();
+       return 0;
+}
diff --git a/menu.c b/menu.c
new file mode 100644 (file)
index 0000000..deb592d
--- /dev/null
+++ b/menu.c
@@ -0,0 +1,187 @@
+/*
+ * dhcpcd-gtk
+ * Copyright 2009 Roy Marples <roy@marples.name>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <gtk/gtk.h>
+
+#ifdef HAVE_GNOME
+# include <gnome.h>
+#endif
+
+#include "config.h"
+#include "menu.h"
+
+static const char *copyright = "Copyright (c) 2009 Roy Marples";
+
+static const char *authors[] = {
+       "Roy Marples <roy@marples.name>",
+       NULL
+};
+static const char *license =
+       "Licensed under the 2 clause BSD license.\n"
+       "\n"
+       "Redistribution and use in source and binary forms, with or without\n"
+       "modification, are permitted provided that the following conditions\n"
+       "are met:\n"
+       "1. Redistributions of source code must retain the above copyright\n"
+       "   notice, this list of conditions and the following disclaimer.\n"
+       "2. Redistributions in binary form must reproduce the above copyright\n"
+       "   notice, this list of conditions and the following disclaimer in the\n"
+       "   documentation and/or other materials provided with the distribution.\n"
+       "\n"
+       "THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n"
+       "ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
+       "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
+       "ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n"
+       "FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
+       "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n"
+       "OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
+       "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n"
+       "LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
+       "OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
+       "SUCH DAMAGE.";
+
+/* Should be in a header */
+void notify_close(void);
+
+static void
+on_quit(_unused GtkMenuItem *item, _unused gpointer data)
+{
+       gtk_main_quit();
+}
+
+static void
+on_help(_unused GtkMenuItem *item, _unused gpointer data)
+{
+}
+
+#ifdef HAVE_GNOME
+static void
+url_show(GtkAboutDialog *dialog, const char *url)
+{
+       GdkScreen *screen;
+
+       screen = gtk_widget_get_screen(GTK_WIDGET(dialog));
+       gnome_url_show_on_screen(url, screen, NULL);
+}
+
+static void
+email_hook(GtkAboutDialog *dialog, const char *url, _unused gpointer p)
+{
+       char *address;
+
+       address = g_strdup_printf("mailto:%s", url);
+       url_show(dialog, address);
+       g_free(address);
+}
+
+static void
+url_hook(GtkAboutDialog *dialog, const char *url, _unused gpointer p)
+{
+       url_show(dialog, url);
+}
+#endif
+
+static void
+on_about(_unused GtkMenuItem *item, _unused gpointer data)
+{
+       gtk_window_set_default_icon_name(GTK_STOCK_NETWORK);
+#ifdef HAVE_GNOME
+       gtk_about_dialog_set_email_hook(email_hook, NULL, NULL);
+       gtk_about_dialog_set_url_hook(url_hook, NULL, NULL);
+#elif HAVE_XFCE
+       gtk_about_dialog_set_email_hook(exo_url_about_dialog_hook, NULL, NULL);
+       gtk_about_dialog_set_url_hook(exo_url_about_dialog_hook, NULL, NULL);
+#endif
+       gtk_show_about_dialog(NULL,
+                             "version", VERSION,
+                             "copyright", copyright,
+                             "license", license,
+                             "website-label", "dhcpcd GTK Website",
+                             "website", "http://roy.marples.name/projects/dhcpcd",
+                             "authors", authors,
+                             "wrap-license", TRUE,
+                             "logo-icon-name", GTK_STOCK_NETWORK,
+                             NULL);
+}
+
+static void
+on_activate(_unused GtkStatusIcon *icon, _unused guint button, _unused guint32 atime, _unused gpointer data)
+{
+       notify_close();
+}
+
+static void
+on_popup(GtkStatusIcon *icon, guint button, guint32 atime, gpointer data)
+{
+       GtkMenu *menu;
+       GtkWidget *item, *image;
+
+       notify_close();
+
+       menu = (GtkMenu *)gtk_menu_new();
+
+       item = gtk_image_menu_item_new_with_mnemonic("_Quit");
+       image = gtk_image_new_from_icon_name(GTK_STOCK_QUIT,
+                                            GTK_ICON_SIZE_MENU);
+       gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
+       g_signal_connect(G_OBJECT(item), "activate",
+                        G_CALLBACK(on_quit), icon);
+       gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+       item = gtk_separator_menu_item_new();
+       gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+       item = gtk_image_menu_item_new_with_mnemonic("_Help");
+       image = gtk_image_new_from_icon_name(GTK_STOCK_HELP,
+                                            GTK_ICON_SIZE_MENU);
+       gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
+       g_signal_connect(G_OBJECT(item), "activate",
+                        G_CALLBACK(on_help), icon);
+       gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+       item = gtk_image_menu_item_new_with_mnemonic("_About");
+       image = gtk_image_new_from_icon_name(GTK_STOCK_ABOUT,
+                                            GTK_ICON_SIZE_MENU);
+       gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
+       g_signal_connect(G_OBJECT(item), "activate",
+                        G_CALLBACK(on_about), icon);
+       gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+       gtk_widget_show_all(GTK_WIDGET(menu));
+       gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
+                       gtk_status_icon_position_menu, data, button, atime);
+       if (button == 0)
+               gtk_menu_shell_select_first(GTK_MENU_SHELL(menu), FALSE);
+}
+
+void
+menu_init(GtkStatusIcon *icon)
+{
+       g_signal_connect_object(G_OBJECT(icon), "activate",
+                               G_CALLBACK(on_activate), icon, 0);
+       g_signal_connect_object(G_OBJECT(icon), "popup_menu",
+                               G_CALLBACK(on_popup), icon, 0);
+}
diff --git a/menu.h b/menu.h
new file mode 100644 (file)
index 0000000..d179f0f
--- /dev/null
+++ b/menu.h
@@ -0,0 +1,32 @@
+/*
+ * dhcpcd-gtk
+ * Copyright 2009 Roy Marples <roy@marples.name>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef MENU_H
+#define MENU_H
+
+void menu_init(GtkStatusIcon *);
+
+#endif
diff --git a/mk/cc.mk b/mk/cc.mk
new file mode 100644 (file)
index 0000000..f7ec756
--- /dev/null
+++ b/mk/cc.mk
@@ -0,0 +1,22 @@
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+# Setup some good default CFLAGS
+CFLAGS?=       -O2
+
+# Try and use some good cc flags if we're building from svn
+# We don't use -pedantic as it will warn about our perfectly valid
+# use of %m in our logger.
+_CCFLAGS=      -Wall -Wextra -Wimplicit -Wshadow -Wformat=2 \
+               -Wmissing-prototypes -Wmissing-declarations \
+               -Wmissing-noreturn -Wmissing-format-attribute \
+               -Wredundant-decls  -Wnested-externs \
+               -Winline -Wwrite-strings -Wcast-align -Wcast-qual \
+               -Wpointer-arith \
+               -Wdeclaration-after-statement -Wsequence-point
+_CC_FLAGS_SH=  if ! test -d .svn; then echo ""; else for f in ${_CCFLAGS}; do \
+               if echo "int main(void) { return 0;} " | \
+               ${CC} $$f -S -xc -o /dev/null - ; \
+               then printf "%s" "$$f "; fi \
+               done; fi
+_CC_FLAGS!=    ${_CC_FLAGS_SH}
+CFLAGS+=       ${_CC_FLAGS}$(shell ${_CC_FLAGS_SH})
diff --git a/mk/depend-.mk b/mk/depend-.mk
new file mode 100644 (file)
index 0000000..9d13b52
--- /dev/null
@@ -0,0 +1,2 @@
+# This space left intentionally blank because gmake does not load .depend
+# by default
diff --git a/mk/depend-gmake.mk b/mk/depend-gmake.mk
new file mode 100644 (file)
index 0000000..947843e
--- /dev/null
@@ -0,0 +1,3 @@
+# Tell gmake to include the optional dependency file.
+# This sucks, but I don't know any other way of portably making this work.
+-include .depend
diff --git a/mk/depend.mk b/mk/depend.mk
new file mode 100644 (file)
index 0000000..22471a1
--- /dev/null
@@ -0,0 +1,17 @@
+# Generate .depend
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+CLEANFILES+=   .depend
+
+.depend: ${SRCS}
+       ${CC} ${CPPFLAGS} ${CFLAGS} -MM ${SRCS} > .depend
+
+depend: .depend
+
+
+# Nasty hack. depend-.mk is a blank file, depend-gmake.mk has a gmake specific
+# command to optionally include .depend.
+# Someone should patch gmake to optionally include .depend if it exists.
+_INC_DEP=      $(shell if ${MAKE} --version | grep -q "^GNU "; then \
+               echo "gmake"; else echo ""; fi)
+include ${MK}/depend-${_INC_DEP}.mk
diff --git a/mk/dist.mk b/mk/dist.mk
new file mode 100644 (file)
index 0000000..455397d
--- /dev/null
@@ -0,0 +1,33 @@
+# rules to make a distribution tarball from a svn repo
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+GITREF?=       HEAD
+DISTPREFIX?=   ${PROG}-${VERSION}
+DISTFILE?=     ${DISTPREFIX}.tar.bz2
+
+CLEANFILES+=   *.tar.bz2
+
+_VERSION_SH=   sed -n 's/\#define VERSION[[:space:]]*"\(.*\)".*/\1/p' config.h
+_VERSION!=     ${_VERSION_SH}
+VERSION=       ${_VERSION}$(shell ${_VERSION_SH})
+
+_SNAP_SH=      date -u +%Y%m%d%H%M
+_SNAP!=                ${_SNAP_SH}
+SNAP=          ${_SNAP}$(shell ${_SNAP_SH})
+SNAPDIR=       ${DISTPREFIX}-${SNAP}
+SNAPFILE=      ${SNAPDIR}.tar.bz2
+
+dist:
+       svn export . ${DISTPREFIX}
+       tar cjpf ${DISTFILE} ${DISTPREFIX}
+       rm -rf ${DISTPREFIX}
+
+snapshot:
+       mkdir /tmp/${SNAPDIR}
+       cp -RPp * /tmp/${SNAPDIR}
+       (cd /tmp/${SNAPDIR}; make clean)
+       tar -cvjpf ${SNAPFILE} -C /tmp ${SNAPDIR}
+       rm -rf /tmp/${SNAPDIR}
+       ls -l ${SNAPFILE}
+
+snap: snapshot
diff --git a/mk/files.mk b/mk/files.mk
new file mode 100644 (file)
index 0000000..0682b03
--- /dev/null
@@ -0,0 +1,9 @@
+# Quick and dirty files
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+FILESDIR?=     ${BINDIR}
+FILESMODE?=    ${NONBINMODE}
+
+_filesinstall:
+       ${INSTALL} -d ${DESTDIR}${FILESDIR}
+       ${INSTALL} -m ${FILESMODE} ${FILES} ${DESTDIR}${FILESDIR}
diff --git a/mk/prog.mk b/mk/prog.mk
new file mode 100644 (file)
index 0000000..48b9d4e
--- /dev/null
@@ -0,0 +1,31 @@
+# rules to build a program 
+# based on FreeBSD's bsd.prog.mk
+
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+include ${MK}/cc.mk
+
+SRCS?=         ${PROG}.c
+OBJS+=         ${SRCS:.c=.o}
+
+all: ${PROG} ${SCRIPTS}
+
+.c.o:
+       ${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@
+
+${PROG}: .depend ${OBJS}
+       ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}
+
+_proginstall: ${PROG}
+       ${INSTALL} -d ${DESTDIR}${BINDIR}
+       ${INSTALL} -m ${BINMODE} ${PROG} ${DESTDIR}${BINDIR}
+       ${INSTALL} -d ${DESTDIR}${DBDIR}
+
+include ${MK}/depend.mk
+include ${MK}/files.mk
+include ${MK}/dist.mk
+
+install: _proginstall _filesinstall
+
+clean:
+       rm -f ${OBJS} ${PROG} ${PROG}.core ${CLEANFILES}
diff --git a/mk/sys.mk b/mk/sys.mk
new file mode 100644 (file)
index 0000000..174d433
--- /dev/null
+++ b/mk/sys.mk
@@ -0,0 +1,14 @@
+# Simple defaults
+
+BINDIR?=       ${PREFIX}/usr/local/bin
+BINMODE?=      0755
+NONBINMODE?=   0644
+
+SYSCONFDIR?=   ${PREFIX}/etc
+
+INSTALL?=      install
+SED?=          sed
+
+_LIBNAME_SH=           case `readlink /lib` in "") echo "lib";; *) basename `readlink /lib`;; esac
+_LIBNAME!=             ${_LIBNAME_SH}
+LIBNAME?=              ${_LIBNAME}$(shell ${_LIBNAME_SH})