summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2003-07-31 13:00:31 +0000
committerLennart Poettering <lennart@poettering.net>2003-07-31 13:00:31 +0000
commit86add5ad710adde499af6d08fe5479098dadaf29 (patch)
treec0358f501a174b66363c183f0b87540f5509b715 /src
parentd2fd8fc1f819883d092b715b31d393295a9bc4e2 (diff)
moved to trunk
git-svn-id: file:///home/lennart/svn/public/ifplugd/trunk@26 2bf48fe7-cfc1-0310-909f-d9042e1e0fef
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am37
-rw-r--r--src/ethtool-kernel.h352
-rw-r--r--src/ethtool-local.h32
-rw-r--r--src/ifplugd.c792
-rw-r--r--src/ifstatus.c234
-rw-r--r--src/interface.c219
-rw-r--r--src/interface.h37
7 files changed, 1703 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..cd2d344
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,37 @@
+# $Id: Makefile.am 22 2003-06-15 16:36:33Z lennart $
+
+# This file is part of ifplugd.
+#
+# ifplugd 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.
+#
+# ifplugd 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 ifplugd; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+AM_CFLAGS = -DSYSCONFDIR="\"$(sysconfdir)\""
+
+sbin_PROGRAMS = ifplugd ifstatus
+ifplugd_SOURCES = ifplugd.c interface.c interface.h ethtool-kernel.h ethtool-local.h svn-revision.h
+ifstatus_SOURCES = ifstatus.c interface.c interface.h ethtool-kernel.h ethtool-local.h svn-revision.h
+
+MAINTAINERCLEANFILES = svn-revision.h
+
+ifplugd.o: svn-revision.h
+ifstatus.o: svn-revision.h
+
+svn-revision.h: Makefile
+ if test -d "$(top_srcdir)/.svn" ; then \
+ if REV=`svn info "$(top_srcdir)" | grep ^Revision | cut -f2 -d" "` 2> /dev/null ; then \
+ echo -e "#ifndef foosvnrevisionhfoo\n#define foosvnrevisionhfoo\n#define SVN_REVISION \"$$REV\"\n#endif" > $@ ; \
+ fi \
+ else \
+ test -f $@ || touch $@ ; \
+ fi
diff --git a/src/ethtool-kernel.h b/src/ethtool-kernel.h
new file mode 100644
index 0000000..a4af66c
--- /dev/null
+++ b/src/ethtool-kernel.h
@@ -0,0 +1,352 @@
+/*
+ * ethtool.h: Defines for Linux ethtool.
+ *
+ * Copyright (C) 1998 David S. Miller (davem@redhat.com)
+ * Copyright 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+ * Portions Copyright 2001 Sun Microsystems (thockin@sun.com)
+ * Portions Copyright 2002 Intel (eli.kupermann@intel.com,
+ * christopher.leech@intel.com,
+ * scott.feldman@intel.com)
+ */
+
+#ifndef _LINUX_ETHTOOL_H
+#define _LINUX_ETHTOOL_H
+
+
+/* This should work for both 32 and 64 bit userland. */
+struct ethtool_cmd {
+ u32 cmd;
+ u32 supported; /* Features this interface supports */
+ u32 advertising; /* Features this interface advertises */
+ u16 speed; /* The forced speed, 10Mb, 100Mb, gigabit */
+ u8 duplex; /* Duplex, half or full */
+ u8 port; /* Which connector port */
+ u8 phy_address;
+ u8 transceiver; /* Which tranceiver to use */
+ u8 autoneg; /* Enable or disable autonegotiation */
+ u32 maxtxpkt; /* Tx pkts before generating tx int */
+ u32 maxrxpkt; /* Rx pkts before generating rx int */
+ u32 reserved[4];
+};
+
+#define ETHTOOL_BUSINFO_LEN 32
+/* these strings are set to whatever the driver author decides... */
+struct ethtool_drvinfo {
+ u32 cmd;
+ char driver[32]; /* driver short name, "tulip", "eepro100" */
+ char version[32]; /* driver version string */
+ char fw_version[32]; /* firmware version string, if applicable */
+ char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */
+ /* For PCI devices, use pci_dev->slot_name. */
+ char reserved1[32];
+ char reserved2[20];
+ u32 testinfo_len;
+ u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */
+ u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */
+};
+
+#define SOPASS_MAX 6
+/* wake-on-lan settings */
+struct ethtool_wolinfo {
+ u32 cmd;
+ u32 supported;
+ u32 wolopts;
+ u8 sopass[SOPASS_MAX]; /* SecureOn(tm) password */
+};
+
+/* for passing single values */
+struct ethtool_value {
+ u32 cmd;
+ u32 data;
+};
+
+/* for passing big chunks of data */
+struct ethtool_regs {
+ u32 cmd;
+ u32 version; /* driver-specific, indicates different chips/revs */
+ u32 len; /* bytes */
+ u8 data[0];
+};
+
+/* for passing EEPROM chunks */
+struct ethtool_eeprom {
+ u32 cmd;
+ u32 magic;
+ u32 offset; /* in bytes */
+ u32 len; /* in bytes */
+ u8 data[0];
+};
+
+/* for configuring coalescing parameters of chip */
+struct ethtool_coalesce {
+ u32 cmd; /* ETHTOOL_{G,S}COALESCE */
+
+ /* How many usecs to delay an RX interrupt after
+ * a packet arrives. If 0, only rx_max_coalesced_frames
+ * is used.
+ */
+ u32 rx_coalesce_usecs;
+
+ /* How many packets to delay an RX interrupt after
+ * a packet arrives. If 0, only rx_coalesce_usecs is
+ * used. It is illegal to set both usecs and max frames
+ * to zero as this would cause RX interrupts to never be
+ * generated.
+ */
+ u32 rx_max_coalesced_frames;
+
+ /* Same as above two parameters, except that these values
+ * apply while an IRQ is being services by the host. Not
+ * all cards support this feature and the values are ignored
+ * in that case.
+ */
+ u32 rx_coalesce_usecs_irq;
+ u32 rx_max_coalesced_frames_irq;
+
+ /* How many usecs to delay a TX interrupt after
+ * a packet is sent. If 0, only tx_max_coalesced_frames
+ * is used.
+ */
+ u32 tx_coalesce_usecs;
+
+ /* How many packets to delay a TX interrupt after
+ * a packet is sent. If 0, only tx_coalesce_usecs is
+ * used. It is illegal to set both usecs and max frames
+ * to zero as this would cause TX interrupts to never be
+ * generated.
+ */
+ u32 tx_max_coalesced_frames;
+
+ /* Same as above two parameters, except that these values
+ * apply while an IRQ is being services by the host. Not
+ * all cards support this feature and the values are ignored
+ * in that case.
+ */
+ u32 tx_coalesce_usecs_irq;
+ u32 tx_max_coalesced_frames_irq;
+
+ /* How many usecs to delay in-memory statistics
+ * block updates. Some drivers do not have an in-memory
+ * statistic block, and in such cases this value is ignored.
+ * This value must not be zero.
+ */
+ u32 stats_block_coalesce_usecs;
+
+ /* Adaptive RX/TX coalescing is an algorithm implemented by
+ * some drivers to improve latency under low packet rates and
+ * improve throughput under high packet rates. Some drivers
+ * only implement one of RX or TX adaptive coalescing. Anything
+ * not implemented by the driver causes these values to be
+ * silently ignored.
+ */
+ u32 use_adaptive_rx_coalesce;
+ u32 use_adaptive_tx_coalesce;
+
+ /* When the packet rate (measured in packets per second)
+ * is below pkt_rate_low, the {rx,tx}_*_low parameters are
+ * used.
+ */
+ u32 pkt_rate_low;
+ u32 rx_coalesce_usecs_low;
+ u32 rx_max_coalesced_frames_low;
+ u32 tx_coalesce_usecs_low;
+ u32 tx_max_coalesced_frames_low;
+
+ /* When the packet rate is below pkt_rate_high but above
+ * pkt_rate_low (both measured in packets per second) the
+ * normal {rx,tx}_* coalescing parameters are used.
+ */
+
+ /* When the packet rate is (measured in packets per second)
+ * is above pkt_rate_high, the {rx,tx}_*_high parameters are
+ * used.
+ */
+ u32 pkt_rate_high;
+ u32 rx_coalesce_usecs_high;
+ u32 rx_max_coalesced_frames_high;
+ u32 tx_coalesce_usecs_high;
+ u32 tx_max_coalesced_frames_high;
+
+ /* How often to do adaptive coalescing packet rate sampling,
+ * measured in seconds. Must not be zero.
+ */
+ u32 rate_sample_interval;
+};
+
+/* for configuring RX/TX ring parameters */
+struct ethtool_ringparam {
+ u32 cmd; /* ETHTOOL_{G,S}RINGPARAM */
+
+ /* Read only attributes. These indicate the maximum number
+ * of pending RX/TX ring entries the driver will allow the
+ * user to set.
+ */
+ u32 rx_max_pending;
+ u32 rx_mini_max_pending;
+ u32 rx_jumbo_max_pending;
+ u32 tx_max_pending;
+
+ /* Values changeable by the user. The valid values are
+ * in the range 1 to the "*_max_pending" counterpart above.
+ */
+ u32 rx_pending;
+ u32 rx_mini_pending;
+ u32 rx_jumbo_pending;
+ u32 tx_pending;
+};
+
+/* for configuring link flow control parameters */
+struct ethtool_pauseparam {
+ u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */
+
+ /* If the link is being auto-negotiated (via ethtool_cmd.autoneg
+ * being true) the user may set 'autonet' here non-zero to have the
+ * pause parameters be auto-negotiated too. In such a case, the
+ * {rx,tx}_pause values below determine what capabilities are
+ * advertised.
+ *
+ * If 'autoneg' is zero or the link is not being auto-negotiated,
+ * then {rx,tx}_pause force the driver to use/not-use pause
+ * flow control.
+ */
+ u32 autoneg;
+ u32 rx_pause;
+ u32 tx_pause;
+};
+
+#define ETH_GSTRING_LEN 32
+enum ethtool_stringset {
+ ETH_SS_TEST = 0,
+ ETH_SS_STATS,
+};
+
+/* for passing string sets for data tagging */
+struct ethtool_gstrings {
+ u32 cmd; /* ETHTOOL_GSTRINGS */
+ u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/
+ u32 len; /* number of strings in the string set */
+ u8 data[0];
+};
+
+enum ethtool_test_flags {
+ ETH_TEST_FL_OFFLINE = (1 << 0), /* online / offline */
+ ETH_TEST_FL_FAILED = (1 << 1), /* test passed / failed */
+};
+
+/* for requesting NIC test and getting results*/
+struct ethtool_test {
+ u32 cmd; /* ETHTOOL_TEST */
+ u32 flags; /* ETH_TEST_FL_xxx */
+ u32 reserved;
+ u32 len; /* result length, in number of u64 elements */
+ u64 data[0];
+};
+
+/* CMDs currently supported */
+#define ETHTOOL_GSET 0x00000001 /* Get settings. */
+#define ETHTOOL_SSET 0x00000002 /* Set settings, privileged. */
+#define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */
+#define ETHTOOL_GREGS 0x00000004 /* Get NIC registers, privileged. */
+#define ETHTOOL_GWOL 0x00000005 /* Get wake-on-lan options. */
+#define ETHTOOL_SWOL 0x00000006 /* Set wake-on-lan options, priv. */
+#define ETHTOOL_GMSGLVL 0x00000007 /* Get driver message level */
+#define ETHTOOL_SMSGLVL 0x00000008 /* Set driver msg level, priv. */
+#define ETHTOOL_NWAY_RST 0x00000009 /* Restart autonegotiation, priv. */
+#define ETHTOOL_GLINK 0x0000000a /* Get link status (ethtool_value) */
+#define ETHTOOL_GEEPROM 0x0000000b /* Get EEPROM data */
+#define ETHTOOL_SEEPROM 0x0000000c /* Set EEPROM data, priv. */
+#define ETHTOOL_GCOALESCE 0x0000000e /* Get coalesce config */
+#define ETHTOOL_SCOALESCE 0x0000000f /* Set coalesce config, priv. */
+#define ETHTOOL_GRINGPARAM 0x00000010 /* Get ring parameters */
+#define ETHTOOL_SRINGPARAM 0x00000011 /* Set ring parameters, priv. */
+#define ETHTOOL_GPAUSEPARAM 0x00000012 /* Get pause parameters */
+#define ETHTOOL_SPAUSEPARAM 0x00000013 /* Set pause parameters, priv. */
+#define ETHTOOL_GRXCSUM 0x00000014 /* Get RX hw csum enable (ethtool_value) */
+#define ETHTOOL_SRXCSUM 0x00000015 /* Set RX hw csum enable (ethtool_value) */
+#define ETHTOOL_GTXCSUM 0x00000016 /* Get TX hw csum enable (ethtool_value) */
+#define ETHTOOL_STXCSUM 0x00000017 /* Set TX hw csum enable (ethtool_value) */
+#define ETHTOOL_GSG 0x00000018 /* Get scatter-gather enable
+ * (ethtool_value) */
+#define ETHTOOL_SSG 0x00000019 /* Set scatter-gather enable
+ * (ethtool_value), priv. */
+#define ETHTOOL_TEST 0x0000001a /* execute NIC self-test, priv. */
+#define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */
+#define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */
+
+/* compatibility with older code */
+#define SPARC_ETH_GSET ETHTOOL_GSET
+#define SPARC_ETH_SSET ETHTOOL_SSET
+
+/* Indicates what features are supported by the interface. */
+#define SUPPORTED_10baseT_Half (1 << 0)
+#define SUPPORTED_10baseT_Full (1 << 1)
+#define SUPPORTED_100baseT_Half (1 << 2)
+#define SUPPORTED_100baseT_Full (1 << 3)
+#define SUPPORTED_1000baseT_Half (1 << 4)
+#define SUPPORTED_1000baseT_Full (1 << 5)
+#define SUPPORTED_Autoneg (1 << 6)
+#define SUPPORTED_TP (1 << 7)
+#define SUPPORTED_AUI (1 << 8)
+#define SUPPORTED_MII (1 << 9)
+#define SUPPORTED_FIBRE (1 << 10)
+#define SUPPORTED_BNC (1 << 11)
+
+/* Indicates what features are advertised by the interface. */
+#define ADVERTISED_10baseT_Half (1 << 0)
+#define ADVERTISED_10baseT_Full (1 << 1)
+#define ADVERTISED_100baseT_Half (1 << 2)
+#define ADVERTISED_100baseT_Full (1 << 3)
+#define ADVERTISED_1000baseT_Half (1 << 4)
+#define ADVERTISED_1000baseT_Full (1 << 5)
+#define ADVERTISED_Autoneg (1 << 6)
+#define ADVERTISED_TP (1 << 7)
+#define ADVERTISED_AUI (1 << 8)
+#define ADVERTISED_MII (1 << 9)
+#define ADVERTISED_FIBRE (1 << 10)
+#define ADVERTISED_BNC (1 << 11)
+
+/* The following are all involved in forcing a particular link
+ * mode for the device for setting things. When getting the
+ * devices settings, these indicate the current mode and whether
+ * it was foced up into this mode or autonegotiated.
+ */
+
+/* The forced speed, 10Mb, 100Mb, gigabit. */
+#define SPEED_10 10
+#define SPEED_100 100
+#define SPEED_1000 1000
+
+/* Duplex, half or full. */
+#define DUPLEX_HALF 0x00
+#define DUPLEX_FULL 0x01
+
+/* Which connector port. */
+#define PORT_TP 0x00
+#define PORT_AUI 0x01
+#define PORT_MII 0x02
+#define PORT_FIBRE 0x03
+#define PORT_BNC 0x04
+
+/* Which tranceiver to use. */
+#define XCVR_INTERNAL 0x00
+#define XCVR_EXTERNAL 0x01
+#define XCVR_DUMMY1 0x02
+#define XCVR_DUMMY2 0x03
+#define XCVR_DUMMY3 0x04
+
+/* Enable or disable autonegotiation. If this is set to enable,
+ * the forced link modes above are completely ignored.
+ */
+#define AUTONEG_DISABLE 0x00
+#define AUTONEG_ENABLE 0x01
+
+/* Wake-On-Lan options. */
+#define WAKE_PHY (1 << 0)
+#define WAKE_UCAST (1 << 1)
+#define WAKE_MCAST (1 << 2)
+#define WAKE_BCAST (1 << 3)
+#define WAKE_ARP (1 << 4)
+#define WAKE_MAGIC (1 << 5)
+#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */
+
+#endif /* _LINUX_ETHTOOL_H */
diff --git a/src/ethtool-local.h b/src/ethtool-local.h
new file mode 100644
index 0000000..19a1cf9
--- /dev/null
+++ b/src/ethtool-local.h
@@ -0,0 +1,32 @@
+#ifndef fooethtoollocalhfoo
+#define fooethtoollocalhfoo
+
+/* $Id: ethtool-local.h 1.2 Wed, 23 Oct 2002 20:49:08 +0200 lennart $ */
+
+/*
+ * This file is part of ifplugd.
+ *
+ * ifplugd 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.
+ *
+ * ifplugd 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 ifplugd; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+typedef unsigned long long u64;
+typedef __uint32_t u32;
+typedef __uint16_t u16;
+typedef __uint8_t u8;
+
+#include "ethtool-kernel.h"
+
+#endif
+
diff --git a/src/ifplugd.c b/src/ifplugd.c
new file mode 100644
index 0000000..9445383
--- /dev/null
+++ b/src/ifplugd.c
@@ -0,0 +1,792 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* $Id: ifplugd.c 1.12 Sat, 01 Feb 2003 03:00:07 +0100 lennart $ */
+
+/*
+ * This file is part of ifplugd.
+ *
+ * ifplugd 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.
+ *
+ * ifplugd 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 ifplugd; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <linux/kd.h>
+#include <sys/ioctl.h>
+#include <limits.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <libdaemon/dlog.h>
+#include <libdaemon/dpid.h>
+#include <libdaemon/dsignal.h>
+#include <libdaemon/dfork.h>
+
+#include "ethtool-local.h"
+#include "interface.h"
+#include "svn-revision.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define VARRUN "/var/run"
+#define IFPLUGD_ENV_PREVIOUS "IFPLUGD_PREVIOUS"
+#define IFPLUGD_ENV_CURRENT "IFPLUGD_CURRENT"
+
+int interface_auto_up = 1, interface_do_message = 1;
+
+char *interface = "eth0";
+char *run = SYSCONFDIR"/ifplugd/ifplugd.action";
+char *extra_arg = NULL;
+int polltime = 1, delay_up = 0, delay_down = 5;
+int daemonize = 1, use_beep = 1, no_shutdown_script = 0, wait_on_fork = 0, use_syslog = 1, ignore_retval = 0, initial_down = 0;
+interface_status_t failure_status = IFSTATUS_ERR;
+
+enum { API_AUTO, API_ETHTOOL, API_MII, API_PRIVATE, API_WLAN } api_mode = API_AUTO;
+interface_status_t (*detect_beat_func)(int, char*);
+
+// 0: high, 1: low, 2: very low
+void beep(int b) {
+ int fd = -1, argp;
+
+ if (!use_beep)
+ return;
+
+ if ((fd = open("/dev/tty1", O_WRONLY)) < 0) {
+ use_beep = 0;
+ daemon_log(LOG_WARNING, "Could not open /dev/tty, cannot beep.");
+ goto finish;
+ }
+
+ switch (b) {
+ case 0: argp = (100<<16) + 0x637; break;
+ case 1: argp = (100<<16) + 0x937; break;
+ default: argp = (100<<16) + 0x1237; break;
+ }
+
+ if (ioctl(fd, KDMKTONE, argp) != 0) {
+ use_beep = 0;
+ daemon_log(LOG_WARNING, "Beep failure, disabled.");
+ goto finish;
+ }
+
+ usleep((argp >> 16)*1000);
+
+finish:
+
+ if (fd >= 0)
+ close(fd);
+
+ return;
+}
+
+const char *pid_file_proc() {
+ static char fn[PATH_MAX];
+ snprintf(fn, sizeof(fn), "%s/ifplugd.%s.pid", VARRUN, interface);
+ return fn;
+}
+
+int action(interface_status_t status) {
+ pid_t pid;
+ int _pipe[2];
+ unsigned n = 0;
+ static char buf[256];
+ char *arg = (status == IFSTATUS_UP ? "up" : "down");
+ int sigfd, r;
+ fd_set rfds;
+
+ daemon_log(LOG_INFO, "Executing '%s %s %s'.", run, interface, arg);
+
+ if (pipe(_pipe) < 0) {
+ daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno));
+ return -1;
+ }
+
+ if ((pid = fork()) < 0) {
+ daemon_log(LOG_ERR, "fork() failed: %s", strerror(errno));
+ return -1;
+
+ } else if (pid == 0) {
+ dup2(_pipe[1], 1);
+ dup2(_pipe[1], 2);
+
+ if (_pipe[0] > 2)
+ close(_pipe[0]);
+
+ if (_pipe[1] > 2)
+ close(_pipe[1]);
+
+ umask(0022); // Set up a sane umask
+
+ execl(run, run, interface, arg, extra_arg, 0);
+
+ _exit(EXIT_FAILURE);
+ }
+
+ close(_pipe[1]);
+
+ FD_ZERO(&rfds);
+ FD_SET(_pipe[0], &rfds);
+ FD_SET(sigfd = daemon_signal_fd(), &rfds);
+
+ n = 0;
+
+ for (;;) {
+ fd_set wrfds = rfds;
+
+ if (select(FD_SETSIZE, &wrfds, NULL, NULL, NULL) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ break;
+ }
+
+
+ if (FD_ISSET(_pipe[0], &wrfds)) {
+ char c;
+
+ if (read(_pipe[0], &c, 1) != 1)
+ break;
+
+ buf[n] = c;
+
+ if (c == '\n' || n >= sizeof(buf) - 2) {
+ if (c != '\n') n++;
+ buf[n] = 0;
+
+ if (buf[0])
+ daemon_log(LOG_WARNING, "client: %s", buf);
+
+ n = 0;
+ } else
+ n++;
+ }
+
+ if (FD_ISSET(sigfd, &wrfds)) {
+ int sig;
+
+ if ((sig = daemon_signal_next()) < 0) {
+ daemon_log(LOG_ERR, "daemon_signal_next(): %s", strerror(errno));
+ break;
+ }
+
+ if (sig != SIGCHLD) {
+ daemon_log(LOG_WARNING, "Killing child.");
+ kill(pid, SIGTERM);
+ }
+
+ break;
+ }
+
+ }
+
+ if (n > 0) {
+ buf[n] = 0;
+ daemon_log(LOG_WARNING, "client: %s", buf);
+ }
+
+ waitpid(pid, &r, 0);
+
+ close(_pipe[0]);
+
+ if (!WIFEXITED(r) || WEXITSTATUS(r) != 0) {
+ if (status == IFSTATUS_UP)
+ beep(2);
+ daemon_log(LOG_ERR, "Program execution failed, return value is %i.", WEXITSTATUS(r));
+
+ return ignore_retval ? 0 : -1;
+ } else {
+ if (status == IFSTATUS_UP)
+ beep(0);
+
+ daemon_log(LOG_INFO, "Program executed successfully.");
+ return 0;
+ }
+}
+
+interface_status_t detect_beat_auto(int fd, char *iface) {
+ interface_status_t status;
+ static interface_status_t (*cached_func)(int, char*) = NULL;
+
+ if (cached_func && (status = cached_func(fd, iface)) != IFSTATUS_ERR)
+ return status;
+
+ if ((status = interface_detect_beat_mii(fd, iface)) != IFSTATUS_ERR) {
+ cached_func = interface_detect_beat_mii;
+ daemon_log(LOG_INFO, "Using detection mode: SIOCGMIIPHY");
+ return status;
+ }
+
+ if ((status = interface_detect_beat_ethtool(fd, iface)) != IFSTATUS_ERR) {
+ cached_func = interface_detect_beat_ethtool;
+ daemon_log(LOG_INFO, "Using detection mode: SIOCETHTOOL");
+ return status;
+ }
+
+ if ((status = interface_detect_beat_wlan(fd, iface)) != IFSTATUS_ERR) {
+ cached_func = interface_detect_beat_wlan;
+ daemon_log(LOG_INFO, "Using detection mode: wireless extension");
+ return status;
+ }
+
+ if ((status = interface_detect_beat_priv(fd, iface)) != IFSTATUS_ERR) {
+ cached_func = interface_detect_beat_priv;
+ daemon_log(LOG_INFO, "Using detection mode: SIOCDEVPRIVATE");
+ return status;
+ }
+
+ return IFSTATUS_ERR;
+}
+
+char *strstatus(interface_status_t s) {
+ switch(s) {
+ case IFSTATUS_UP: return "up";
+ case IFSTATUS_DOWN: return "down";
+ case IFSTATUS_ERR: return "error";
+ default: return "disabled";
+ }
+}
+
+interface_status_t detect_beat(int fd, char*iface) {
+ interface_status_t status;
+ static interface_status_t last_status = (interface_status_t) -1;
+
+ if ((status = detect_beat_func(fd, iface)) == IFSTATUS_ERR)
+ status = failure_status;
+
+ if (status == IFSTATUS_ERR && detect_beat_func == detect_beat_auto)
+ daemon_log(LOG_INFO, "Failed to find working plug detection mode for %s", interface);
+
+ if (status != last_status) {
+ setenv(IFPLUGD_ENV_PREVIOUS, strstatus(last_status), 1);
+ setenv(IFPLUGD_ENV_CURRENT, strstatus(status), 1);
+ last_status = status;
+ }
+
+ return status;
+}
+
+int open_iface(char *iface) {
+ int fd;
+ struct ifreq ifr;
+ struct ethtool_drvinfo drvinfo;
+ char txt[256];
+
+ if ((fd = interface_open(iface)) < 0) {
+ daemon_log(LOG_ERR, "Could not create socket: %s", strerror(errno));
+ return fd;
+ }
+
+ if (interface_auto_up)
+ interface_up(fd, iface);
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
+
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1)
+ snprintf(txt, sizeof(txt)-1, "Using interface %s", iface);
+ else
+ snprintf(txt, sizeof(txt)-1, "Using interface %s/%02X:%02X:%02X:%02X:%02X:%02X", iface, ifr.ifr_hwaddr.sa_data[0] & 0xFF, ifr.ifr_hwaddr.sa_data[1] & 0xFF, ifr.ifr_hwaddr.sa_data[2] & 0xFF, ifr.ifr_hwaddr.sa_data[3] & 0xFF, ifr.ifr_hwaddr.sa_data[4] & 0xFF, ifr.ifr_hwaddr.sa_data[5] & 0xFF);
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
+
+ drvinfo.cmd = ETHTOOL_GDRVINFO;
+ ifr.ifr_data = (caddr_t) &drvinfo;
+
+ if (ioctl(fd, SIOCETHTOOL, &ifr) != -1)
+ daemon_log(LOG_INFO, "%s with driver <%s> (version: %s)", txt, drvinfo.driver, drvinfo.version);
+ else
+ daemon_log(LOG_INFO, "%s", txt);
+
+ return fd;
+}
+
+void work() {
+ interface_status_t status;
+ int fd = -1;
+ fd_set rfds;
+ int sigfd;
+ time_t t = 0;
+ int send_retval = 1;
+ int paused = 0;
+ static char log_ident[256];
+
+ snprintf(log_ident, sizeof(log_ident), "ifplugd(%s)", interface);
+
+ daemon_log_ident = log_ident;
+
+
+ if (daemon_pid_file_create() < 0) {
+ daemon_log(LOG_ERR, "Could not create PID file %s.", daemon_pid_file_proc());
+ goto finish;
+ }
+
+ if (daemon_signal_init(SIGINT, SIGTERM, SIGQUIT, SIGHUP, SIGCHLD, SIGUSR1, SIGUSR2, -1) < 0) {
+ daemon_log(LOG_ERR, "Could not register signal handler: %s", strerror(errno));
+ goto finish;
+ }
+
+ switch (api_mode) {
+ case API_ETHTOOL: detect_beat_func = interface_detect_beat_ethtool; break;
+ case API_MII: detect_beat_func = interface_detect_beat_mii; break;
+ case API_PRIVATE: detect_beat_func = interface_detect_beat_priv; break;
+ case API_WLAN: detect_beat_func = interface_detect_beat_wlan;break;
+
+ default:
+ detect_beat_func = detect_beat_auto;
+ interface_do_message = 0;
+ break;
+ }
+
+ if ((fd = open_iface(interface)) < 0)
+ goto finish;
+
+ if ((status = detect_beat(fd, interface)) == IFSTATUS_ERR)
+ goto finish;
+
+ daemon_log(LOG_INFO, "ifplugd "VERSION" successfully initialized, link beat %sdetected.", status == IFSTATUS_UP ? "" : "not ");
+ beep(status == IFSTATUS_UP ? 0 : 1);
+
+ if (status == IFSTATUS_UP || initial_down)
+ if (action(status) < 0)
+ goto finish;
+
+ if (daemonize && wait_on_fork) {
+ char c = status == IFSTATUS_UP ? 2 : (status == IFSTATUS_DOWN ? 3 : 1);
+ daemon_retval_send(c);
+ send_retval = 0;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(sigfd = daemon_signal_fd(), &rfds);
+
+ for (;;) {
+ interface_status_t r;
+ fd_set wrfds = rfds;
+ struct timeval tv = { polltime, 0 };
+
+ if (select(FD_SETSIZE, &wrfds, NULL, NULL, &tv) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ daemon_log(LOG_ERR, "select(): %s", strerror(errno));
+ goto finish;
+ }
+
+
+ if (FD_ISSET(sigfd, &wrfds)) {
+ int sig;
+
+ if ((sig = daemon_signal_next()) < 0) {
+ daemon_log(LOG_ERR, "daemon_signal_next(): %s", strerror(errno));
+ goto finish;
+ }
+
+
+ switch (sig) {
+
+ case SIGINT:
+ case SIGTERM:
+ goto cleanup;
+
+ case SIGQUIT:
+ goto finish;
+
+ case SIGCHLD:
+ break;
+
+ case SIGHUP:
+ daemon_log(LOG_INFO, "SIGHUP: %s, link detected on %s: %s", paused ? "Suspended" : "Running", interface, status == IFSTATUS_DOWN ? "no" : "yes");
+ break;
+
+ case SIGUSR1:
+ daemon_log(LOG_INFO, "SIGUSR1: Daemon suspended (#%i)", ++paused);
+ break;
+
+ case SIGUSR2:
+ if (paused > 0) {
+ daemon_log(LOG_INFO, "SIGUSR2: Daemon resumed (#%i)", paused);
+ paused --;
+ }
+
+ break;
+
+ default:
+ daemon_log(LOG_INFO, "Ignoring unknown signal %s", strsignal(sig));
+ break;
+ }
+ }
+
+
+ if (!paused) {
+ if ((r = detect_beat(fd, interface)) == IFSTATUS_ERR)
+ break;
+
+ if (status != r) {
+ status = r;
+
+ daemon_log(LOG_INFO, "Link beat %s.", status == IFSTATUS_DOWN ? "lost" : "detected");
+ beep(status == IFSTATUS_UP ? 0 : 1);
+
+
+ if (t)
+ t = 0;
+ else {
+ t = time(NULL);
+
+ if (status == IFSTATUS_UP)
+ t += delay_up;
+
+ if (status == IFSTATUS_DOWN)
+ t += delay_down;
+ }
+ }
+ }
+
+
+ if (t && t < time(NULL)) {
+ t = 0;
+
+ if (action(status) < 0)
+ goto finish;
+ }
+ }
+
+cleanup:
+ if (!no_shutdown_script && status == IFSTATUS_UP) {
+ setenv(IFPLUGD_ENV_PREVIOUS, strstatus(status), 1);
+ setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
+ action(IFSTATUS_DOWN);
+ beep(1);
+ }
+
+finish:
+
+ if (fd >= 0)
+ close(fd);
+
+ if (send_retval && daemonize && wait_on_fork)
+ daemon_retval_send(1);
+
+ daemon_pid_file_remove();
+ daemon_signal_done();
+
+ daemon_log(LOG_INFO, "Exiting.");
+}
+
+void usage(char *p) {
+ char *m;
+
+ switch (api_mode) {
+ case API_ETHTOOL: m = "ethtool"; break;
+ case API_MII: m = "mii"; break;
+ case API_PRIVATE: m = "priv"; break;
+ case API_WLAN: m = "wlan"; break;
+ default: m = "auto";
+ }
+
+ if (strrchr(p, '/'))
+ p = strchr(p, '/')+1;
+
+ printf("%s [options]\n"
+ " -a --no-auto Do not enable interface automatically (%s)\n"
+ " -n --no-daemon Do not daemonize (for debugging) (%s)\n"
+ " -s --no-syslog Do not use syslog, use stderr instead (for debugging) (%s)\n"
+ " -b --no-beep Do not beep (%s)\n"
+ " -f --ignore-fail Ignore detection failure, retry instead (failure is treated as DOWN) (%s)\n"
+ " -F --ignore-fail-positive Ignore detection failure, retry instead (failure is treated as UP) (%s)\n"
+ " -i --iface=IFACE Specify ethernet interface (%s)\n"
+ " -r --run=EXEC Specify program to execute (%s)\n"
+ " -I --ignore-retval Don't exit on nonzero return value of program executed (%s)\n"
+ " -t --poll-time=SECS Specify poll time in seconds (%i)\n"
+ " -u --delay-up=SECS Specify delay for configuring interface (%i)\n"
+ " -d --delay-down=SECS Specify delay for deconfiguring interface (%i)\n"
+ " -m --api-mode=MODE Force API mode (mii, priv, ethtool, wlan, auto) (%s)\n"
+ " -q --no-shutdown Don't run script on daemon quit (%s)\n"
+ " -l --initial-down Run \"down\" script on startup if now cable is detected (%s)\n"
+ " -w --wait-on-fork Wait until daemon fork finished (%s)\n"
+ " -x --extra-arg Specify an extra argument for action script\n"
+ " -h --help Show this help\n"
+ " -k --kill Kill a running daemon\n"
+ " -c --check-running Check if a daemon is currently running\n"
+ " -v --version Show version\n"
+ " -S --suspend Suspend running daemon\n"
+ " -R --resume Resume running daemon\n"
+ " -z --info Write status of running daemon to syslog\n",
+ p,
+ !interface_auto_up ? "on" : "off",
+ !daemonize ? "on" : "off",
+ !use_syslog ? "on" : "off",
+ !use_beep ? "on" : "off",
+ failure_status == IFSTATUS_DOWN ? "on" : "off",
+ failure_status == IFSTATUS_UP ? "on" : "off",
+ interface,
+ run,
+ ignore_retval ? "on" : "off",
+ polltime,
+ delay_up,
+ delay_down,
+ m,
+ no_shutdown_script ? "on" : "off",
+ initial_down ? "on" : "off",
+ wait_on_fork ? "on" : "off");
+}
+
+void parse_args(int argc, char *argv[]) {
+ static struct option long_options[] = {
+ {"no-auto", no_argument, 0, 'a'},
+ {"no-daemon", no_argument, 0, 'n'},
+ {"no-syslog", no_argument, 0, 's'},
+ {"no-beep", no_argument, 0, 'b'},
+ {"ignore-fail", no_argument, 0, 'f'},
+ {"ignore-fail-positive", no_argument, 0, 'F'},
+ {"ignore-retval", no_argument, 0, 'I'},
+ {"iface", required_argument, 0, 'i'},
+ {"run", required_argument, 0, 'r'},
+ {"poll-time", required_argument, 0, 't'},
+ {"delay-up", required_argument, 0, 'u'},
+ {"delay-down", required_argument, 0, 'd'},
+ {"api-mode", required_argument, 0, 'm'},
+ {"wait-on-fork", no_argument, 0, 'w'},
+ {"no-shutdown", no_argument, 0, 'q'},
+ {"help", no_argument, 0, 'h'},
+ {"kill", no_argument, 0, 'k'},
+ {"check-running", no_argument, 0, 'c'},
+ {"version", no_argument, 0, 'v'},
+ {"extra-arg", required_argument, 0, 'x'},
+ {"suspend", no_argument, 0, 'S'},
+ {"resume", no_argument, 0, 'R'},
+ {"info", no_argument, 0, 'z'},
+ {"inital-down", no_argument, 0, 'l'},
+ {0, 0, 0, 0}
+ };
+ int option_index = 0;
+ int help = 0, _kill = 0, _check = 0, _version = 0, _suspend = 0, _resume = 0, _info = 0;
+
+ for (;;) {
+ int c;
+
+ if ((c = getopt_long(argc, argv, "asni:r:t:u:d:hkbfFvm:qwx:cISRzl", long_options, &option_index)) < 0)
+ break;
+
+ switch (c) {
+ case 'a' :
+ interface_auto_up = !interface_auto_up;
+ break;
+ case 's' :
+ use_syslog = !use_syslog;
+ break;
+ case 'n' :
+ daemonize = !daemonize;
+ break;
+ case 'i' :
+ interface = strdup(optarg);
+ break;
+ case 'r':
+ run = strdup(optarg);
+ break;
+ case 'I':
+ ignore_retval = !ignore_retval;
+ break;
+ case 't':
+ polltime = atoi(optarg);
+ if (polltime < 0) polltime = 0;
+ break;
+ case 'u':
+ delay_up = atoi(optarg);
+ break;
+ case 'd':
+ delay_down = atoi(optarg);
+ break;
+ case 'h':
+ help = 1;
+ break;
+ case 'k':
+ _kill = 1;
+ break;
+ case 'c':
+ _check = 1;
+ break;
+ case 'v':
+ _version = 1;
+ break;
+ case 'b':
+ use_beep = !use_beep;
+ break;
+ case 'f':
+ failure_status = IFSTATUS_DOWN;
+ break;
+ case 'F':
+ failure_status = IFSTATUS_UP;
+ break;
+ case 'm':
+ switch (tolower(optarg[0])) {
+ case 'e': api_mode = API_ETHTOOL; break;
+ case 'm': api_mode = API_MII; break;
+ case 'p': api_mode = API_PRIVATE; break;
+ case 'w': api_mode = API_WLAN; break;
+ case 'a': api_mode = API_AUTO; break;
+ default:
+ fprintf(stderr, "Unknown API mode: %s\n", optarg);
+ exit(2);
+ }
+ break;
+ case 'q':
+ no_shutdown_script = !no_shutdown_script;
+ break;
+ case 'l':
+ initial_down = !initial_down;
+ break;
+ case 'w':
+ wait_on_fork = !wait_on_fork;
+ break;
+ case 'x':
+ extra_arg = strdup(optarg);
+ break;
+ case 'S':
+ _suspend = 1;
+ break;
+ case 'R':
+ _resume = 1;
+ break;
+ case 'z':
+ _info = 1;
+ break;
+ default:
+ fprintf(stderr, "Unknown parameter.\n");
+ exit(1);
+ }
+ }
+
+
+ if (!use_syslog)
+ daemon_log_use = DAEMON_LOG_STDERR;
+
+
+ if (help) {
+ usage(argv[0]);
+ exit(0);
+ }
+
+ if (_kill || _resume || _suspend || _info) {
+ if (daemon_pid_file_kill(_kill ? SIGINT : (_resume ? SIGUSR2 : (_info ? SIGHUP : SIGUSR1))) < 0) {
+ daemon_log(LOG_ERR, "Failed to kill daemon. (%s)", strerror(errno));
+ exit(6);
+ }
+
+ exit(0);
+ }
+
+ if (_version) {
+
+#ifdef SVN_REVISION
+ printf("ifplugd "VERSION" (SVN: "SVN_REVISION")\n");
+#else
+ printf("ifplugd "VERSION"\n");
+#endif
+
+ exit(0);
+ }
+
+ if (_check) {
+ pid_t pid = daemon_pid_file_is_running();
+
+ if (pid == (pid_t) -1)
+ printf("ifplugd not running.\n");
+ else
+ printf("ifplugd process for device %s running as pid %u.\n", interface, pid);
+
+ exit(pid == 0 ? 255 : 0);
+ }
+
+}
+
+static volatile int alarmed = 0;
+
+void sigalrm() {
+ alarmed = 1;
+}
+
+int main(int argc, char* argv[]) {
+
+ daemon_pid_file_proc = pid_file_proc;
+
+ if ((daemon_log_ident = strrchr(argv[0], '/')))
+ daemon_log_ident++;
+ else
+ daemon_log_ident = argv[0];
+
+ parse_args(argc, argv);
+
+ if (geteuid() != 0) {
+ daemon_log(LOG_ERR, "Sorry, you need to be root to run this binary.");
+ return 2;
+ }
+
+
+ if (daemon_pid_file_is_running() >= 0) {
+ daemon_log(LOG_ERR, "Sorry, there is already an instance of ifplugd for %s running.", interface);
+ return 4;
+ }
+
+ if (daemonize) {
+ pid_t pid;
+
+ if (wait_on_fork)
+ if (daemon_retval_init() < 0) {
+ daemon_log(LOG_ERR, "Sorry, could not create pipe: %s", strerror(errno));
+ return 4;
+ }
+
+ if ((pid = daemon_fork()) < 0)
+ return 3;
+
+ if (pid) {
+ int c = 0;
+
+ // Parent process
+
+ if (wait_on_fork)
+ if ((c = daemon_retval_wait(60)) < 0) {
+ daemon_log(LOG_WARNING, "Killing background process.");
+ kill(pid, SIGTERM);
+ }
+
+
+ return c;
+ }
+ }
+
+ // Let's go
+ work();
+ return 0;
+}
diff --git a/src/ifstatus.c b/src/ifstatus.c
new file mode 100644
index 0000000..35a0b48
--- /dev/null
+++ b/src/ifstatus.c
@@ -0,0 +1,234 @@
+/* $Id: ifstatus.c 1.3 Wed, 13 Nov 2002 22:37:54 +0100 lennart $ */
+
+/*
+ * This file is part of ifplugd.
+ *
+ * ifplugd 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.
+ *
+ * ifplugd 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 ifplugd; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+#include <libdaemon/dlog.h>
+
+#include "interface.h"
+#include "svn-revision.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+int interface_auto_up = 0, interface_do_message = 0;
+
+int verbose = 0;
+char *interface = NULL;
+
+int handle(char *iface) {
+ int fd, r = 0;
+ interface_status_t s;
+
+ if ((fd = interface_open(iface)) < 0)
+ return -1;
+
+ if (verbose > 0) {
+ printf("%s:\n", iface);
+
+ if ((s = interface_detect_beat_ethtool(fd, iface)) == IFSTATUS_ERR)
+ printf(" SIOCETHTOOL failed (%s)\n", strerror(errno));
+ else
+ printf(" SIOCETHTOOL: %s\n", s == IFSTATUS_UP ? "link beat detected" : "unplugged");
+
+ if ((s = interface_detect_beat_mii(fd, iface)) == IFSTATUS_ERR)
+ printf(" SIOCGMIIPHY failed (%s)\n", strerror(errno));
+ else
+ printf(" SIOCGMIIPHY: %s\n", s == IFSTATUS_UP ? "link beat detected" : "unplugged");
+
+ if ((s = interface_detect_beat_priv(fd, iface)) == IFSTATUS_ERR)
+ printf(" SIOCDEVPRIVATE failed (%s)\n", strerror(errno));
+ else
+ printf(" SIOCDEVPRIVATE: %s\n", s == IFSTATUS_UP ? "link beat detected" : "unplugged");
+
+ if ((s = interface_detect_beat_wlan(fd, iface)) == IFSTATUS_ERR)
+ printf(" Wireless failed.\n");
+ else
+ printf(" Wireless: %s\n", s == IFSTATUS_UP ? "link beat detected" : "unplugged");
+
+ } else {
+
+ if ((s = interface_detect_beat_mii(fd, iface)) == IFSTATUS_ERR)
+ if ((s = interface_detect_beat_ethtool(fd, iface)) == IFSTATUS_ERR)
+ if ((s = interface_detect_beat_wlan(fd, iface)) == IFSTATUS_ERR)
+ s = interface_detect_beat_priv(fd, iface);
+
+ switch(s) {
+ case IFSTATUS_UP:
+ if (!verbose)
+ printf("%s: link beat detected\n", iface);
+ r = 1;
+ break;
+
+ case IFSTATUS_DOWN:
+ if (!verbose)
+ printf("%s: unplugged\n", iface);
+ r = 2;
+ break;
+
+ default:
+ if (!verbose)
+ printf("%s: not supported\n", iface);
+
+ r = -1;
+ break;
+ }
+ }
+
+
+ close(fd);
+ return r;
+}
+
+void usage(char *p) {
+ if (strrchr(p, '/'))
+ p = strchr(p, '/')+1;
+
+ printf("%s [options] [INTERFACE]\n"
+ " -a --auto Enable interface automatically (%s)\n"
+ " -q --quiet Quiet behaviour (%s)\n"
+ " -v --verbose Enable verbosity (%s)\n"
+ " -h --help Show this help\n"
+ " -V --version Show version number\n",
+ p, interface_auto_up ? "on" : "off", verbose < 0 ? "on" : "off", verbose > 0 ? "on" : "off");
+}
+
+
+void parse(int argc, char *argv[]) {
+ static struct option long_options[] = {
+ {"auto", no_argument, 0, 'a'},
+ {"quiet", no_argument, 0, 'q'},
+ {"verbose", no_argument, 0, 'v'},
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0}
+ };
+ int option_index = 0;
+ int help = 0;
+
+ for (;;) {
+ int c;
+
+ if ((c = getopt_long(argc, argv, "avhqV", long_options, &option_index)) < 0)
+ break;
+
+ switch (c) {
+ case 'a' :
+ interface_auto_up = !interface_auto_up;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'q':
+ verbose--;
+ break;
+ case 'h':
+ help = 1;
+ break;
+ case 'V':
+#ifdef SVN_REVISION
+ printf("ifstatus "VERSION" (SVN: "SVN_REVISION")\n");
+#else
+ printf("ifstatus "VERSION"\n");
+#endif
+ exit(0);
+ default:
+ fprintf(stderr, "Unknown parameter.\n");
+ exit(1);
+
+ }
+ }
+
+ if (help) {
+ usage(argv[0]);
+ exit(0);
+ }
+
+ if (optind < argc)
+ interface = argv[optind];
+}
+
+
+int main(int argc, char *argv[]) {
+ parse(argc, argv);
+
+ if (interface) {
+ int r;
+
+ if ((r = handle(interface)) < 0) {
+ if (verbose == 0)
+ fprintf(stderr, "Failure (%s)\n", strerror(errno));
+ return 1;
+ }
+
+ return r+1;
+
+ } else {
+ struct ifconf ifconf;
+ struct ifreq *ifr;
+ int m, n, s, fd;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ fprintf(stderr, "socket(): %s\n", strerror(errno));
+ return 1;
+ }
+
+ s = sizeof(struct ifreq)*5;
+ for (;;) {
+ ifr = malloc(s);
+
+ ifconf.ifc_len = s;
+ ifconf.ifc_req = ifr;
+
+ if (ioctl(fd, SIOCGIFCONF, &ifconf) < 0) {
+ fprintf(stderr, "SIOCGIFCONF: %s\n", strerror(errno));
+ free(ifr);
+ close(fd);
+ return 1;
+ }
+
+
+ if (ifconf.ifc_len != s)
+ break;
+
+ free(ifr);
+ s *= 2;
+ }
+
+ close(fd);
+
+ m = ifconf.ifc_len/sizeof(struct ifreq);
+ for (n = 0; n < m; n++)
+ if (strcmp(ifconf.ifc_req[n].ifr_name, "lo"))
+ handle(ifconf.ifc_req[n].ifr_name);
+
+ free(ifr);
+ }
+
+ return 0;
+}
diff --git a/src/interface.c b/src/interface.c
new file mode 100644
index 0000000..894922c
--- /dev/null
+++ b/src/interface.c
@@ -0,0 +1,219 @@
+/* $Id: interface.c 1.4 Thu, 07 Nov 2002 22:27:37 +0100 lennart $ */
+
+/*
+ * This file is part of ifplugd.
+ *
+ * ifplugd 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.
+ *
+ * ifplugd 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 ifplugd; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <linux/sockios.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "ethtool-local.h"
+#include "interface.h"
+
+#include <libdaemon/dlog.h>
+
+int interface_open(char *iface) {
+ return socket(AF_INET, SOCK_DGRAM, 0);
+}
+
+void interface_up(int fd, 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) < 0) {
+ if (interface_do_message)
+ daemon_log(LOG_WARNING, "Warning: Could not get interface flags.");
+
+ return;
+ }
+
+ if ((ifr.ifr_flags & IFF_UP) == IFF_UP)
+ return;
+
+ if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
+ if (interface_do_message)
+ daemon_log(LOG_WARNING, "Warning: Could not get interface address.");
+ } else if (ifr.ifr_addr.sa_family != AF_INET) {
+ if (interface_do_message)
+ daemon_log(LOG_WARNING, "Warning: The interface is not IP-based.");
+ } else {
+ ((struct sockaddr_in *)(&ifr.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
+ if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) {
+ if (interface_do_message)
+ daemon_log(LOG_WARNING, "Warning: Could not set interface address.");
+ }
+ }
+
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
+ if (interface_do_message)
+ daemon_log(LOG_WARNING, "Warning: Could not get interface flags.");
+
+ return;
+ }
+
+ ifr.ifr_flags |= IFF_UP;
+
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
+ if (interface_do_message)
+ daemon_log(LOG_WARNING, "Warning: Could not set interface flags.");
+}
+
+interface_status_t interface_detect_beat_mii(int fd, char *iface) {
+ struct ifreq ifr;
+
+ if (interface_auto_up)
+ interface_up(fd, iface);
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
+
+ if (ioctl(fd, SIOCGMIIPHY, &ifr) == -1) {
+ if (interface_do_message)
+ daemon_log(LOG_ERR, "SIOCGMIIPHY failed: %s", strerror(errno));
+
+ return IFSTATUS_ERR;
+ }
+
+ ((unsigned short*) &ifr.ifr_data)[1] = 1;
+
+ if (ioctl(fd, SIOCGMIIREG, &ifr) == -1) {
+ if (interface_do_message)
+ daemon_log(LOG_ERR, "SIOCGMIIREG failed: %s", strerror(errno));
+
+ return IFSTATUS_ERR;
+ }
+
+ return (((unsigned short*) &ifr.ifr_data)[3] & 0x0016) == 0x0004 ? IFSTATUS_UP : IFSTATUS_DOWN;
+}
+
+interface_status_t interface_detect_beat_priv(int fd, char *iface) {
+ struct ifreq ifr;
+
+ if (interface_auto_up)
+ interface_up(fd, iface);
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
+
+ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) == -1) {
+ if (interface_do_message)
+ daemon_log(LOG_ERR, "SIOCDEVPRIVATE failed: %s", strerror(errno));
+
+ return IFSTATUS_ERR;
+ }
+
+ ((unsigned short*) &ifr.ifr_data)[1] = 1;
+
+ if (ioctl(fd, SIOCDEVPRIVATE+1, &ifr) == -1) {
+ if (interface_do_message)
+ daemon_log(LOG_ERR, "SIOCDEVPRIVATE+1 failed: %s", strerror(errno));
+
+ return IFSTATUS_ERR;
+ }
+
+ return (((unsigned short*) &ifr.ifr_data)[3] & 0x0016) == 0x0004 ? IFSTATUS_UP : IFSTATUS_DOWN;
+}
+
+interface_status_t interface_detect_beat_ethtool(int fd, char *iface) {
+
+ struct ifreq ifr;
+ struct ethtool_value edata;
+
+ if (interface_auto_up)
+ interface_up(fd, iface);
+
+ 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) {
+ if (interface_do_message)
+ daemon_log(LOG_ERR, "ETHTOOL_GLINK failed: %s", strerror(errno));
+
+ return IFSTATUS_ERR;
+ }
+
+ return edata.data ? IFSTATUS_UP : IFSTATUS_DOWN;
+}
+
+interface_status_t interface_detect_beat_wlan(int fd, char *iface) {
+ FILE *f;
+ char buf[256];
+ char *bp;
+ int l;
+
+ if (interface_auto_up)
+ interface_up(fd, iface);
+
+ l = strlen(iface);
+
+ if (!(f = fopen("/proc/net/wireless", "r"))) {
+ if (interface_do_message)
+ daemon_log(LOG_WARNING, "Failed to open /proc/net/wireless: %s",strerror(errno));
+
+ return IFSTATUS_ERR;
+ }
+
+ while (fgets(buf, sizeof(buf)-1, f)) {
+ bp = buf;
+
+ while (*bp && isspace(*bp))
+ bp++;
+
+ if(!strncmp(bp, iface, l) && bp[l]==':') {
+
+ /*skip device name */
+ if (!(bp = strchr(bp,' ')))
+ break;
+
+ bp++;
+
+ /*skip status*/
+ if (!(bp = strchr(bp,' ')))
+ break;
+
+ fclose(f);
+
+ if (atoi(bp) > 0)
+ return IFSTATUS_UP;
+ else
+ return IFSTATUS_DOWN;
+ };
+ }
+
+ if (interface_do_message)
+ daemon_log(LOG_ERR, "Failed to find wireless interface %s\n", iface);
+
+ fclose(f);
+
+ return IFSTATUS_ERR;
+}
+
diff --git a/src/interface.h b/src/interface.h
new file mode 100644
index 0000000..2f9490e
--- /dev/null
+++ b/src/interface.h
@@ -0,0 +1,37 @@
+#ifndef foointerfacehfoo
+#define foointerfacehfoo
+
+/* $Id: interface.h 1.2 Wed, 23 Oct 2002 20:49:08 +0200 lennart $ */
+
+/*
+ * This file is part of ifplugd.
+ *
+ * ifplugd 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.
+ *
+ * ifplugd 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 ifplugd; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+int interface_auto_up;
+int interface_do_message;
+
+typedef enum { IFSTATUS_UP, IFSTATUS_DOWN, IFSTATUS_ERR } interface_status_t;
+
+int interface_open(char *iface);
+void interface_up(int fd, char *iface);
+
+interface_status_t interface_detect_beat_mii(int fd, char *iface);
+interface_status_t interface_detect_beat_priv(int fd, char *iface);
+interface_status_t interface_detect_beat_ethtool(int fd, char *iface);
+interface_status_t interface_detect_beat_wlan(int fd, char *iface);
+
+#endif