now minihal can monitor netdevice to bring up/down the interface
author"Luc Saillard <luc@saillard.org>"
Sun Jun 15 08:55:11 2008 +0200 (2 months ago)
changeset 821b9d1ca06642
parent 81c5458b1fa288
child 83d50bc9040307
now minihal can monitor netdevice to bring up/down the interface
src/device.c
src/device.h
src/plugins/Makefile.am
src/plugins/net.c
src/sysfs.c
src/udev.c
--- a/src/device.c Mon May 26 00:04:35 2008 +0200
+++ b/src/device.c Sun Jun 15 08:55:11 2008 +0200
@@ -105,6 +105,11 @@ static const struct device_ops ops_for_d
.name = "keyboard",
.read = device_keyboard_read,
},
+ {
+ .type = DEVICE_NET_ETHERNET,
+ .name = "net",
+ // .read = device_keyboard_read,
+ },
};
@@ -195,6 +200,11 @@ device_new(int device_type, const char *
if (name)
g_hash_table_insert(dev->props, g_strdup("INPUT_NAME"), name);
}
+ else if (device_type == DEVICE_NET_ETHERNET)
+ {
+
+
+ }
return dev;
}
@@ -215,17 +225,17 @@ void
void
device_stop_all(void)
{
- g_hash_table_foreach_remove(devices_table, htable_check_for_removal, NULL);
+ g_hash_table_foreach_remove(devices_table, htable_check_for_removal, NULL);
}
void
device_stop_by_sysfs_path(const char *sysfs_path)
{
struct device_t *device;
- trace("stopping %s\n", sysfs_path);
/* Search in the list of master block devices */
device = g_hash_table_lookup(devices_table, sysfs_path);
+ trace("sysfs_path=%s %s\n", sysfs_path, device?"found":"not found");
if (device)
device_unref(device);
}
--- a/src/device.h Mon May 26 00:04:35 2008 +0200
+++ b/src/device.h Sun Jun 15 08:55:11 2008 +0200
@@ -60,6 +60,7 @@ enum
DEVICE_BLOCK_TYPE_DISK,
DEVICE_INPUT_KEYBOARD,
DEVICE_INPUT_MOUSE,
+ DEVICE_NET_ETHERNET,
} device_block_type;
enum
@@ -68,6 +69,7 @@ enum
DEVICE_BUS_USB,
DEVICE_BUS_SCSI,
DEVICE_BUS_IDE,
+ DEVICE_BUS_PCI,
} device_bus_type;
--- a/src/plugins/Makefile.am Mon May 26 00:04:35 2008 +0200
+++ b/src/plugins/Makefile.am Sun Jun 15 08:55:11 2008 +0200
@@ -2,7 +2,8 @@ pkglib_PROGRAMS = \
pkglib_PROGRAMS = \
cdrom \
disk \
- keyboard
+ keyboard \
+ net
cdrom_SOURCES = cdrom.c ../trace.c ../trace.h
cdrom_LDADD =
@@ -16,3 +17,7 @@ keyboard_LDADD = $(ALSA_LIBS) -lm
keyboard_LDADD = $(ALSA_LIBS) -lm
keyboard_CPPFLAGS = $(ALSA_CFLAGS) -I..
+net_SOURCES = net.c ../trace.c ../trace.h
+net_LDADD =
+net_CPPFLAGS = -I..
+
--- a/src/sysfs.c Mon May 26 00:04:35 2008 +0200
+++ b/src/sysfs.c Sun Jun 15 08:55:11 2008 +0200
@@ -585,7 +585,6 @@ sysfs_coldplug_scan_input_devices(void)
DIR *dir;
struct dirent *entry;
int input_type, bus_type;
- char *device_name;
struct device_t *dev;
dir = opendir("/sys/class/input");
@@ -609,8 +608,6 @@ sysfs_coldplug_scan_input_devices(void)
trace("sysfs_path = %s\n", sysfs_path);
if (sysfs_get_input_event_type(sysfs_path, &input_type) == FALSE)
continue;
-
- device_name = NULL;
/* We are interesting only by usb keyboard */
bus_type = sysfs_get_bus_type(sysfs_path);
@@ -626,6 +623,46 @@ sysfs_coldplug_scan_input_devices(void)
closedir(dir);
}
+static void
+sysfs_coldplug_scan_net_devices(void)
+{
+ char tempfilename[256];
+ char sysfs_path[PATH_MAX];
+ DIR *dir;
+ struct dirent *entry;
+ struct device_t *dev;
+
+ dir = opendir("/sys/class/net");
+ if (dir == NULL)
+ return;
+
+ for (entry = readdir(dir); entry; entry = readdir(dir))
+ {
+ /* We are not interested with lo interface */
+ if (strcmp(entry->d_name, "lo") == 0)
+ continue;
+ if (entry->d_name[0] == '.')
+ continue;
+
+ /* Make the path to sysfs_path, and resolv it without symlink */
+ snprintf(tempfilename, sizeof(tempfilename), "/sys/class/net/%s", entry->d_name);
+ if (realpath(tempfilename, sysfs_path) == NULL)
+ {
+ trace("realpath on %s failed: %s\n", tempfilename, strerror(errno));
+ continue;
+ }
+
+ trace("sysfs_path = %s\n", sysfs_path);
+
+ /* We are interesting only by usb keyboard */
+ dev = device_new(DEVICE_NET_ETHERNET, sysfs_path, entry->d_name);
+ device_watch(dev);
+ device_unref(dev);
+ }
+
+ closedir(dir);
+}
+
/*
* Parse /sys and monitor all devices that are already present
*/
@@ -633,6 +670,7 @@ int sysfs_coldplug(void)
{
sysfs_coldplug_scan_block_devices();
sysfs_coldplug_scan_input_devices();
+ sysfs_coldplug_scan_net_devices();
return 0;
}
--- a/src/udev.c Mon May 26 00:04:35 2008 +0200
+++ b/src/udev.c Sun Jun 15 08:55:11 2008 +0200
@@ -35,6 +35,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
+#include <net/if.h>
#include "trace.h"
@@ -77,6 +78,8 @@ typedef struct
char device_file[PATH_MAX]; /* Device node for the device */
unsigned long long seqnum; /* kernel uevent sequence number */
+ char ifname[IFNAMSIZ+1]; /* Interface name when this is 'net' event */
+
} HotplugEvent;
static gboolean event_process_event(HotplugEvent *event);
@@ -86,6 +89,8 @@ static void hotplug_remove_sysfs_input(H
static void hotplug_remove_sysfs_input(HotplugEvent *event);
static void hotplug_add_sysfs_device(HotplugEvent *event);
static void hotplug_remove_sysfs_device(HotplugEvent *event);
+static void hotplug_add_sysfs_net(HotplugEvent *event);
+static void hotplug_remove_sysfs_net(HotplugEvent *event);
#if 0
static void hotplug_mount(HotplugEvent *event);
static void hotplug_umount(HotplugEvent *event);
@@ -176,16 +181,20 @@ udev_event_handler(GIOChannel *source,
break;
bufpos += keylen + 1;
+ trace("> %s\n", key);
+
if (strncmp(key, "ACTION=", 7) == 0)
action = &key[7];
else if (strncmp(key, "DEVPATH=", 8) == 0)
snprintf(hotplug_event->sysfs_path, sizeof(hotplug_event->sysfs_path), "/sys%s", key+8);
else if (strncmp(key, "SUBSYSTEM=", 10) == 0)
- g_strlcpy (hotplug_event->subsystem, &key[10], sizeof (hotplug_event->subsystem));
+ g_strlcpy(hotplug_event->subsystem, &key[10], sizeof (hotplug_event->subsystem));
else if (strncmp(key, "DEVNAME=", 8) == 0)
- g_strlcpy (hotplug_event->device_file, &key[8], sizeof (hotplug_event->device_file));
+ g_strlcpy(hotplug_event->device_file, &key[8], sizeof (hotplug_event->device_file));
else if (strncmp(key, "SEQNUM=", 7) == 0)
hotplug_event->seqnum = strtoull(&key[7], NULL, 10);
+ else if (strncmp(key, "INTERFACE=", strlen("INTERFACE=")) == 0)
+ g_strlcpy(hotplug_event->ifname, &key[strlen("INTERFACE=")], sizeof(hotplug_event->ifname));
}
if (!action)
@@ -298,10 +307,19 @@ event_process_event(HotplugEvent *event)
{
if (strcmp(event->subsystem, "input") == 0)
{
+ /* When a driver register a new input device (like mouse, keyboard, ...) */
if (event->action == HOTPLUG_ACTION_ADD)
hotplug_add_sysfs_input(event);
else if (event->action == HOTPLUG_ACTION_REMOVE)
hotplug_remove_sysfs_input(event);
+ }
+ else if (strcmp(event->subsystem, "net") == 0)
+ {
+ /* When a driver register a new interface */
+ if (event->action == HOTPLUG_ACTION_ADD)
+ hotplug_add_sysfs_net(event);
+ else if (event->action == HOTPLUG_ACTION_REMOVE)
+ hotplug_remove_sysfs_net(event);
}
}
else if (event->type == HOTPLUG_EVENT_SYSFS_BUS)
@@ -359,6 +377,26 @@ static void
static void
hotplug_remove_sysfs_block(HotplugEvent *event)
{
+ device_stop_by_sysfs_path(event->sysfs_path);
+}
+
+static void
+hotplug_add_sysfs_net(HotplugEvent *event)
+{
+ struct device_t *device;
+
+ trace("[%s] sysfs_path=%s\n", event->ifname, event->sysfs_path);
+
+ device = device_new(DEVICE_NET_ETHERNET, event->sysfs_path, event->ifname);
+ device_watch(device);
+ device_unref(device);
+}
+
+static void
+hotplug_remove_sysfs_net(HotplugEvent *event)
+{
+ trace("sysfs_path=%s\n", event->sysfs_path);
+
device_stop_by_sysfs_path(event->sysfs_path);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/net.c Sun Jun 15 08:55:11 2008 +0200
@@ -0,0 +1,355 @@
+/*
+ * minihal: a small automounter
+ * Copyright (C) 2007 Luc Saillard <luc@saillard.org>
+ * 2007 Neuf Cegetel
+ *
+ * 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.
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+#include <time.h>
+
+#include "trace.h"
+
+
+#define DEFAULT_POLLTIME 4
+
+
+
+static int
+ifup(const char *iface)
+{
+ char cmdline[PATH_MAX];
+ snprintf(cmdline, sizeof(cmdline), "/sbin/ifup %s", iface);
+ trace("%s\n", cmdline);
+ system(cmdline);
+ return 0;
+}
+
+
+static int
+ifdown(const char *iface)
+{
+ char cmdline[PATH_MAX];
+ snprintf(cmdline, sizeof(cmdline), "/sbin/ifdown %s", iface);
+ trace("%s\n", cmdline);
+ system(cmdline);
+ return 0;
+}
+
+
+/*
+ * Use SIOCGIFFLAGS to get interface status
+ *
+ * @param fd The socket connected to the kernel
+ * @param iface
+ * @return -1 error
+ * 0 link down
+ * 1 link up
+ */
+static int
+net_interface_get_flags(int fd, const char *iface)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) == -1)
+ {
+ trace("SIOCGIFFLAGS failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return ifr.ifr_flags;
+}
+
+
+/*
+ * Use SIOCGIFADDR to get interface ip address
+ *
+ * @param fd The socket connected to the kernel
+ * @param iface
+ * @return -1 error
+ * 0 link down
+ * 1 link up
+ */
+static int
+net_interface_has_ip_address(int fd, const char *iface)
+{
+ struct ifreq ifr;
+ struct sockaddr *sa;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
+
+ if (ioctl(fd, SIOCGIFADDR, &ifr) == -1)
+ {
+ if (errno == EADDRNOTAVAIL)
+ return 0;
+ trace("SIOCGIFADDR failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ sa = (struct sockaddr *)&(ifr.ifr_addr);
+ if (sa->sa_family == AF_INET)
+ return 1;
+ else
+ return 0;
+}
+
+
+
+/*
+ * Use SIOCETHTOOL to get interface status
+ *
+ * @param fd The socket connected to the kernel
+ * @param iface
+ * @return -1 error
+ * 0 link down
+ * 1 link up
+ */
+static int
+net_interface_detect_using_ethtool(int fd, const char *iface)
+{
+ struct ifreq ifr;
+ struct ethtool_value edata;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
+
+ edata.cmd = ETHTOOL_GLINK;
+ ifr.ifr_data = (caddr_t) &edata;
+
+ if (ioctl(fd, SIOCETHTOOL, &ifr) == -1)
+ {
+ trace("SIOCETHTOOL(ETHTOOL_GLINK) failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (edata.data)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * Use SIOCGIFFLAGS to get interface beat status
+ *
+ * @param fd The socket connected to the kernel
+ * @param iface
+ * @return -1 error
+ * 0 link down
+ * 1 link up
+ */
+static int
+net_interface_detect_using_ioctl(int fd, const char *iface)
+{
+ int status = net_interface_get_flags(fd, iface);
+ if (status == -1)
+ return -1;
+ if (status & IFF_RUNNING)
+ return 1;
+ else
+ return 0;
+}
+
+
+static int
+net_interface_get_beat_status(int fd, const char *iface)
+{
+ static int (*working_net_status)(int , const char *) = NULL;
+ int status;
+
+ if (working_net_status)
+ return working_net_status(fd, iface);
+
+ status = net_interface_detect_using_ethtool(fd, iface);
+ if (status >= 0)
+ {
+ working_net_status = net_interface_detect_using_ethtool;
+ return status;
+ }
+
+ status = net_interface_detect_using_ioctl(fd, iface);
+ if (status >= 0)
+ {
+ working_net_status = net_interface_detect_using_ioctl;
+ return status;
+ }
+
+ return -1;
+}
+
+
+
+
+static int
+net_monitor(int fd, const char *iface)
+{
+ int old_status, status, flags;
+ int polltime = DEFAULT_POLLTIME;
+ time_t last_time_changed = 0;
+
+ flags = net_interface_get_flags(fd, iface);
+ if (flags & IFF_LOOPBACK)
+ {
+ trace("not monitoring loopback interface\n");
+ return -1;
+ }
+
+ old_status = net_interface_get_beat_status(fd, iface);
+
+ if ( (flags & (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING) )
+ {
+ /* The interface is up and running, do they have an address ? */
+ if (net_interface_has_ip_address(fd, iface) == 0)
+ {
+ /* no address ? => launch ifup */
+ ifup(iface);
+ }
+ }
+
+
+ while (1)
+ {
+ struct timeval tv;
+
+ tv.tv_sec = polltime;
+ tv.tv_usec = 0;
+
+ status = select(0, NULL, NULL, NULL, &tv);
+ if (status == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "select() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+
+ status = net_interface_get_beat_status(fd, iface);
+ if (status == -1)
+ {
+ /* ethernet device is down ... (disable the interface ?) */
+ return -1;
+ }
+
+ trace("%s: Link beat %s\n", iface, status == 0?"lost":"detected");
+
+ if (old_status != status)
+ {
+ trace("%s: Link beat %s\n", iface, status == 0?"lost":"detected");
+ /* To avoid to many up/down, we wait a little to have a sane value */
+ last_time_changed = time(NULL);
+ polltime = 1; /* Now check more often */
+ old_status = status;
+ }
+ else if (last_time_changed)
+ {
+ if (status == 0)
+ {
+ /* When a interface is down, wait a little longer ... */
+ if (last_time_changed+5 > time(NULL))
+ {
+ ifdown(iface);
+ last_time_changed = 0;
+ polltime = DEFAULT_POLLTIME;
+ }
+ }
+ else if (status == 1)
+ {
+ /* When a interface is up, execute ifup immediatly */
+ ifup(iface);
+ last_time_changed = 0;
+ polltime = DEFAULT_POLLTIME;
+ }
+ }
+ }
+}
+
+
+
+
+static void
+set_proctitle(int argc, char **argv, const char *device_path)
+{
+ int i, argv_size;
+ extern char **environ;
+ char *endptr;
+
+ /* This code is really really ugly. We make some memory layout
+ * assumptions and reuse the environment array as memory to store
+ * our process title in */
+ for (i = 0; environ[i] != NULL; i++)
+ ;
+
+ endptr = i ? environ[i-1] + strlen (environ[i-1]) : argv[argc-1] + strlen (argv[argc-1]);
+
+ argv_size = endptr - argv[0];
+
+ i = snprintf(argv[0], argv_size, "net polling %s", device_path);
+ if (i < argv_size)
+ memset(argv[0]+i, 0, argv_size-i);
+ argv[1] = NULL;
+}
+
+int main(int argc, char **argv)
+{
+ char *iface;
+ int socket_fd;
+ int status;
+
+ /* Get all parameters via environnement (but copy all because we are erasing
+ * the data after that) */
+ iface = getenv("DEVICE_FILE");
+ if (iface == NULL)
+ {
+ fprintf(stderr, "Please set DEVICE_FILE to the interface name to monitor\n");
+ return 1;
+ }
+ iface = strdup(iface);
+
+ set_proctitle(argc, argv, iface);
+ setlinebuf(stdout);
+
+ socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (socket_fd == -1)
+ {
+ fprintf(stderr, "socket() failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ trace("iface=%s socket_fd=%d\n", iface, socket_fd);
+
+ status = net_monitor(socket_fd, iface);
+ if (status == -1)
+ return 1;
+ else
+ return 0;
+}