From 86add5ad710adde499af6d08fe5479098dadaf29 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 31 Jul 2003 13:00:31 +0000 Subject: moved to trunk git-svn-id: file:///home/lennart/svn/public/ifplugd/trunk@26 2bf48fe7-cfc1-0310-909f-d9042e1e0fef --- src/Makefile.am | 37 +++ src/ethtool-kernel.h | 352 +++++++++++++++++++++++ src/ethtool-local.h | 32 +++ src/ifplugd.c | 792 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/ifstatus.c | 234 +++++++++++++++ src/interface.c | 219 ++++++++++++++ src/interface.h | 37 +++ 7 files changed, 1703 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/ethtool-kernel.h create mode 100644 src/ethtool-local.h create mode 100644 src/ifplugd.c create mode 100644 src/ifstatus.c create mode 100644 src/interface.c create mode 100644 src/interface.h (limited to 'src') 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ethtool-local.h" +#include "interface.h" +#include "svn-revision.h" + +#ifdef HAVE_CONFIG_H +#include +#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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "interface.h" +#include "svn-revision.h" + +#ifdef HAVE_CONFIG_H +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ethtool-local.h" +#include "interface.h" + +#include + +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 -- cgit