BBB File /usr/local/sbin/ipwatchd

From Wiki
Jump to: navigation, search

WARNING: Not fully tested. This page is not complete.

The monitoring process consists of a small C daemon that uses netlink and rtnetlink interface to watch for address changes on any of the network interfaces.

When a change is detected, 'ipwatchd' runs the /usr/local/sbin/ifdisplay script. That script outputs the configuration of eth0 and wlan0 to 'lpr', which then spools it to a serially attached printer.

'ipwatchd' uses the Debian services framework for start/stop/restart control, PID file management, and any of the other stuff that an application shouldn't be responsible for. See /etc/init.d/ipwatchd for the source to the init.d file, and how to install it.

As root, compile with 'gcc -W -Wall ipwatchd.c -o /usr/local/sbin/ipwatchd'

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <syslog.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>

#define DAEMON_NAME "simpledaemon"

//
//
//
static void signal_handler (int sig)
{
  switch (sig)
  {
    case SIGHUP:
      syslog (LOG_WARNING, "Received SIGHUP signal.");
      break;

    case SIGINT:
    case SIGTERM:
      syslog (LOG_INFO, "Daemon exiting");
      exit (EXIT_SUCCESS);
      break;

    default:
      syslog (LOG_WARNING, "Unhandled signal %s", strsignal (sig));
      break;
  }
}

//
//
//
static void daemonize (char *rundir)
{
  int pid, sid, i;
  struct sigaction newSigAction;
  sigset_t newSigSet;

  if (getppid () == 1)
    return;

  //
  //  Set signal mask - signals we want to block
  //
  sigemptyset (&newSigSet);
  sigaddset (&newSigSet, SIGCHLD);            // ignore child - i.e. we don't need to wait for it
  sigaddset (&newSigSet, SIGTSTP);            // ignore Tty stop signals
  sigaddset (&newSigSet, SIGTTOU);            // ignore Tty background writes
  sigaddset (&newSigSet, SIGTTIN);            // ignore Tty background reads
  sigprocmask (SIG_BLOCK, &newSigSet, NULL);  // Block the above specified signals

  //
  //  Set up a signal handler
  //
  newSigAction.sa_handler = signal_handler;
  sigemptyset (&newSigAction.sa_mask);
  newSigAction.sa_flags = 0;

  //
  //  Signals to handle
  //
  sigaction (SIGHUP,  &newSigAction, NULL); // catch hangup signal
  sigaction (SIGTERM, &newSigAction, NULL); // catch term signal
  sigaction (SIGINT,  &newSigAction, NULL); // catch interrupt signal

  if ((pid = fork ()) < 0)
    exit (EXIT_FAILURE);

  if (pid > 0)
    exit (EXIT_SUCCESS);

  umask (027);

  if ((sid = setsid ()) < 0)
    exit (EXIT_FAILURE);

  //
  //  Close all descriptors
  //
  for (i = getdtablesize (); i >= 0; --i)
    close (i);

  //
  //  Set stdin, stdout, stderr to /dev/null
  //
  i = open ("/dev/null", O_RDWR);
  dup (i);
  dup (i);

  //
  //  Change directory we run from
  //
  chdir (rundir);
}

static void monitorNetlink (void)
{
  struct sockaddr_nl addr;
  int nls;
  unsigned int len;
  char buffer [4096];
  struct nlmsghdr *nlh;

  if ((nls = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
  {
    syslog (LOG_ERR, "socket() failed: %d/%s", errno, strerror (errno));
    exit (EXIT_FAILURE);
  }

  memset (&addr, 0, sizeof(addr));
  addr.nl_family = AF_NETLINK;
  addr.nl_groups = RTMGRP_IPV4_IFADDR;

  if (bind (nls, (struct sockaddr *) &addr, sizeof (addr)) == -1)
  {
    syslog (LOG_ERR, "bind() failed: %d/%s", errno, strerror (errno));
    exit (EXIT_FAILURE);
  }

  nlh = (struct nlmsghdr *) buffer;

  while ((len = recv (nls, nlh, 4096, 0)) > 0)
    for (; (NLMSG_OK (nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE); nlh = NLMSG_NEXT (nlh, len))
      if (nlh->nlmsg_type == RTM_NEWADDR)
        system ("/root/bin/ifdisplay");
}

int main (int argc __attribute__ ((unused)), char **argv __attribute__ ((unused)))
{
  setlogmask (LOG_UPTO (LOG_INFO));
  openlog (DAEMON_NAME, LOG_CONS | LOG_PERROR, LOG_USER);

  syslog (LOG_INFO, "ipwatch starting up");

  daemonize ("/tmp/");

  syslog (LOG_INFO, "ipwatch running");

  while (1)
    monitorNetlink ();
}