/*      NetBSD: status.c,v 1.3 2008/05/04 13:30:54 martin Exp   */

/*
* status.c - pppd plugin to implement an `lcpstatus' option.
* This is intended as more of an example than perfected feature,
* but this code has been in use on my local network for a year and
* is quite useful as is (with a bit of external help, at any rate).
*
* Written January 2003 by John F. Woods, [email protected]
*/

/*
* Copyright (c) 2004 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by John F. Woods, [email protected].
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stddef.h>
#include <time.h>
#include <stdio.h>
#include "pppd.h"

/* this version string will be checked against pppd's version string when
* this plugin is loaded.
*/
char pppd_version[] = VERSION;

/*
* Usage: to use this status plug as-is, add the lines:
plugin status
lcpstatus /var/run/ppp0.status
* to your PPP options file (or add the appropriate options to your
* pppd command line), where /var/run/ppp0.status can vary to taste.
* This plugin will then proceed to create the lcp status file and
* write one of four strings to the file based on the most recent LCP
* event:  "up", "down", "?", or "!", representing LCP up, LCP down,
* LCP echo not received, and received-our-own-LCP-echo (probably
* indicating a loopback or a disconnected modem echoing back characters).
* On my system, I have a separate program which reads that file every
* second and sends a UDP broadcast packet on my ethernet with the contents
* of the file; each of the other computers on my ethernet (all Macs) have
* a tiny little program which listens for that UDP broadcast packet and
* updates a menubar status indicator; the end result is that when PPP
* shuts down, users on my LAN immediately know without having to go look
* at the modem.  (Or without demanding that *I* go look at the modem...)
*
* If you want to modify this plugin, other ways you could use and display
* the data generated by the transitions would include:
* + directly broadcasting the results from inside the pppd task (rather
*   than having a separate process do it)
* + store the ppp state in an SNMP database so it could be displayed with
*   a standard form of client rather than a goofy little Mac OS X menubar
*   widget.
*/

static char *statusfilename = 0;
static char *laststatus = 0;
static char UP[] = "up";
static char DOWN[] = "down";
static char MISS[] = "?";
static char MINE[] = "!";

static option_t status_options[] = {
       { "lcpstatus", o_string, &statusfilename,
         "Name of file to which LCP status string will be written" },
       { NULL }
};

/* status should be one of the canned constants above. */
static void writestatus(char *status)
{
       FILE *statusfile;
       if (status == laststatus) return;  /* we knew that already */
       statusfile = fopen(statusfilename, "w");
       if (!statusfile) {
               warn("can't write %s to log LCP status", statusfilename);
               free(statusfilename);
               statusfilename = 0;
               return;
       }
       fprintf(statusfile, "%s\n", status);
       fclose(statusfile);
       laststatus = status;
}

static void status_lcp_up(void)
{
       if (!statusfilename) return; /* not enabled */
       writestatus(UP);
}

static void status_lcp_down(void)
{
       if (!statusfilename) return; /* not enabled */
       writestatus(DOWN);
}

static void status_lcp_echo(int pending)
{
       if (!statusfilename) return; /* not enabled */
       if (pending == 0)
               writestatus(UP);
       else if (laststatus != MINE)
               writestatus(MISS);
}

static void status_lcp_echoreply(int mine)
{
       if (!statusfilename) return; /* not enabled */
       if (mine == 0)
               writestatus(UP);
       else
               writestatus(MINE);
}

void plugin_init(void)
{
       info("Initialize PPP status plugin.");
       add_options(status_options);
       lcp_up_hook = status_lcp_up;
       lcp_down_hook = status_lcp_down;
       lcp_echo_hook = status_lcp_echo;
       lcp_echoreply_hook = status_lcp_echoreply;
}