/*
 * $Id$
 *
 * Layer Two Tunnelling Protocol Daemon
 * Copyright (C) 1998 Adtran, Inc.
 * Copyright (C) 2002 Jeff McAdams
 *
 * Mark Spencer
 *
 * This software is distributed under the terms
 * of the GPL, which you should have received
 * along with this source.
 *
 * Main Daemon source.
 *
 */

#include <stdlib.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#if (__GLIBC__ < 2)
# if defined(FREEBSD)
#  include <sys/signal.h>
# elif defined(LINUX)
#  include <bsd/signal.h>
# elif defined(SOLARIS)
#  include <signal.h>
# endif
#else
# include <signal.h>
#endif
#include <netdb.h>
#include <string.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef USE_KERNEL
#include <sys/ioctl.h>
#endif
#include "l2tp.h"

struct tunnel_list tunnels;
int max_tunnels = DEF_MAX_TUNNELS;
struct utsname uts;
int ppd = 1;                    /* Packet processing delay */
int control_fd;                 /* descriptor of control area */
char *args;

char *dial_no_tmp;              /* jz: Dialnumber for Outgoing Call */
int switch_io = 0;              /* jz: Switch for Incoming or Outgoing Call */

void init_tunnel_list (struct tunnel_list *t)
{
    t->head = NULL;
    t->count = 0;
    t->calls = 0;
}

/* Now sends to syslog instead - MvO */
void show_status (void)
{
    struct schedule_entry *se;
    struct tunnel *t;
    struct call *c;
    struct lns *tlns;
    struct lac *tlac;
    struct host *h;
    int s = 0;
    log (LOG_WARN, "====== l2tpd statistics ========\n");
    log (LOG_WARN, " Scheduler entries:\n");
    se = events;
    while (se)
    {
        s++;
        t = (struct tunnel *) se->data;
        tlac = (struct lac *) se->data;
        c = (struct call *) se->data;
        if (se->func == &hello)
        {
            log (LOG_WARN, "%d: HELLO to %d\n", s, t->tid);
        }
        else if (se->func == &magic_lac_dial)
        {
            log (LOG_WARN, "%d: Magic dial on %s\n", s, tlac->entname);
        }
        else if (se->func == &send_zlb)
        {
            log (LOG_WARN, "%d: Send payload ZLB on call %d:%d\n", s,
                     c->container->tid, c->cid);
        }
        else if (se->func == &dethrottle)
        {
            log (LOG_WARN, "%d: Dethrottle call %d:%d\n", s, c->container->tid,
                     c->cid);
        }
        else
            log (LOG_WARN, "%d: Unknown event\n", s);
        se = se->next;
    };
    log (LOG_WARN, "Total Events scheduled: %d\n", s);
    log (LOG_WARN, "Number of tunnels open: %d\n", tunnels.count);
    t = tunnels.head;
    while (t)
    {
        log (LOG_WARN, "Tunnel %s, ID = %d (local), %d (remote) to %s:%d\n"
                 "   control_seq_num = %d, control_rec_seq_num = %d,\n"
                 "   cLr = %d\n",
                 (t->lac ? t->lac->entname : (t->lns ? t->lns->entname : "")),
                 t->ourtid, t->tid, IPADDY (t->peer.sin_addr),
                 ntohs (t->peer.sin_port), t->control_seq_num,
                 t->control_rec_seq_num, t->cLr);
        c = t->call_head;
        while (c)
        {
            log (LOG_WARN,
                     "Call %s, ID = %d (local), %d (remote), serno = %u,\n"
                     "      data_seq_num = %d, data_rec_seq_num = %d,\n"
                     "      pLr = %d, tx = %u bytes (%u), rx= %u bytes (%u)\n",
                     (c->lac ? c->lac->
                      entname : (c->lns ? c->lns->entname : "")), c->ourcid,
                     c->cid, c->serno, c->data_seq_num, c->data_rec_seq_num,
                     c->pLr, c->tx_bytes, c->tx_pkts, c->rx_bytes, c->rx_pkts);
            c = c->next;
        }
        t = t->next;
    }
    log (LOG_WARN, "==========Config File===========\n");
    tlns = lnslist;
    while (tlns)
    {
        log (LOG_WARN, "LNS entry %s\n",
                 tlns->entname[0] ? tlns->entname : "(unnamed)");
        tlns = tlns->next;
    };
    tlac = laclist;
    while (tlac)
    {
        log (LOG_WARN, "LAC entry %s, LNS is/are:",
                 tlac->entname[0] ? tlac->entname : "(unnamed)");
        h = tlac->lns;
        if (h)
        {
            while (h)
            {
                log (LOG_WARN, " %s", h->hostname);
                h = h->next;
            }
        }
        else
            log (LOG_WARN, " [none]");
        log (LOG_WARN, "\n");
        tlac = tlac->next;
    };
    log (LOG_WARN, "================================\n");
}

void null_handler(int sig)
{
       /* FIXME
        * A sighup is received when a call is terminated, unknown origine ..
        * I catch it and ll looks good, but ..
	*/
}

void status_handler (int sig)
{
    show_status ();
}

void child_handler (int signal)
{
    /*
     * Oops, somebody we launched was killed.
     * It's time to reap them and close that call.
     * But first, we have to find out what PID died.
     * unfortunately, pppd will
     */
    struct tunnel *t;
    struct call *c;
    pid_t pid;
    int status;
    t = tunnels.head;
    /*
     we need to know when pppd exit for pc(tbs).
     at most time we get pppd exit.
    */
    printf("pppd exit.\n");
    /* Keep looping until all are cleared */
    for(;;)
    {
        pid = waitpid (-1, &status, WNOHANG);
        if (pid < 1)
        {
            /*
             * Oh well, nobody there.  Maybe we reaped it
             * somewhere else already
             */
            return;
        }
        while (t)
        {
            c = t->call_head;
            while (c)
            {
                if (c->pppd == pid)
                {
                    if ( WIFEXITED( status ) )
                    {
                        log (LOG_DEBUG, "%s : pppd exited for call %d with code %d\n", __FUNCTION__,
                         c->cid, WEXITSTATUS( status ) );
                    }
                    else if( WIFSIGNALED( status ) )
                    {
                        log (LOG_DEBUG, "%s : pppd terminated for call %d by signal %d\n", __FUNCTION__,
                         c->cid, WTERMSIG( status ) );
                    }
                    else
                    {
                        log (LOG_DEBUG, "%s : pppd exited for call %d for unknown reason\n", __FUNCTION__,
                         c->cid );
                    }
                    c->needclose = -1;
                    /*
                     * OK...pppd died, we can go ahead and close the pty for
                     * it
                     */
                    close (c->fd);
                    c->fd = -1;
                    return;
                }
                c = c->next;
            }
            t = t->next;
        }
    }
}

void death_handler (int signal)
{
    /*
       * If we get here, somebody terminated us with a kill or a control-c.
       * we call call_close on each tunnel twice to get a StopCCN out
       * for each one (we can't pause to make sure it's received.
       * Then we close the connections
     */
    struct tunnel *st, *st2;
    int sec;
    log (LOG_CRIT, "%s: Fatal signal %d received\n", __FUNCTION__, signal);
    st = tunnels.head;
    while (st)
    {
        st2 = st->next;
        strcpy (st->self->errormsg, "Server closing");
        sec = st->self->closing;
        if (st->lac)
            st->lac->redial = 0;
        call_close (st->self);
        if (!sec)
        {
            st->self->closing = -1;
            call_close (st->self);
        }
        st = st2;
    }

    /* erase pid file */
	unlink (gconfig.pidfile);

    exit (1);
}

int start_pppd (struct call *c, struct ppp_opts *opts)
{
    char a, b;
    char tty[80];
    char *stropt[80];
    struct ppp_opts *p;
#ifdef USE_KERNEL
    struct l2tp_call_opts co;
#endif
    int pos = 1;
    int fd2;
#ifdef DEBUG_PPPD
    int x;
#endif
    struct termios ptyconf;
    char *str;
    p = opts;
    stropt[0] = strdup (PPPD);
    while (p)
    {
        stropt[pos] = (char *) malloc (strlen (p->option) + 1);
        strncpy (stropt[pos], p->option, strlen (p->option) + 1);
        pos++;
        p = p->next;
    }
    stropt[pos] = NULL;
    if (c->pppd > 0)
    {
        log (LOG_WARN, "%s: PPP already started on call!\n", __FUNCTION__);
        return -EINVAL;
    }
    if (c->fd > -1)
    {
        log (LOG_WARN, "%s: file descriptor already assigned!\n",
             __FUNCTION__);
        return -EINVAL;
    }
#ifdef USE_KERNEL
    if (kernel_support)
    {
        co.ourtid = c->container->ourtid;
        co.ourcid = c->ourcid;
        ioctl (server_socket, L2TPIOCGETCALLOPTS, &co);
        stropt[pos++] = strdup ("channel");
        stropt[pos] = (char *) malloc (10);
        snprintf (stropt[pos], 10, "%d", co.id);
        pos++;
        stropt[pos] = NULL;
    }
    else
    {
#endif
        if ((c->fd = getPtyMaster (&a, &b)) < 0)
        {
            log (LOG_WARN, "%s: unable to allocate pty, abandoning!\n",
                 __FUNCTION__);
            return -EINVAL;
        }

        /* set fd opened above to not echo so we don't see read our own packets
           back of the file descriptor that we just wrote them to */
        tcgetattr (c->fd, &ptyconf);
        *(c->oldptyconf) = ptyconf;
        ptyconf.c_cflag &= ~(ICANON | ECHO);
        ptyconf.c_lflag &= ~ECHO;
        tcsetattr (c->fd, TCSANOW, &ptyconf);

        snprintf (tty, sizeof (tty), "/dev/tty%c%c", a, b);
        fd2 = open (tty, O_RDWR);
	stropt[pos++] = strdup(tty);
	stropt[pos] = NULL;

#ifdef USE_KERNEL
    }
#endif
    str = stropt[0];
#ifdef DEBUG_PPPD
    log (LOG_DEBUG, "%s: I'm running:  ", __FUNCTION__);
    for (x = 0; stropt[x]; x++)
    {
        log (LOG_DEBUG, "\"%s\" ", stropt[x]);
    };
    log (LOG_DEBUG, "\n");
#endif
    c->pppd = fork ();
    if (c->pppd < 0)
    {
        log (LOG_WARN, "%s: unable to fork(), abandoning!\n", __FUNCTION__);
        return -EINVAL;
    }
    else if (!c->pppd)
    {
        struct call *sc;
        struct tunnel *st;

		/*
		 * deleled by lichuang, if not doing so,
		 * pc process cannot monitor pppd's output
		 */
		/*
        close (0);
        close (1);
        close (2);
#ifdef USE_KERNEL
        if (!kernel_support && (fd2 < 0))
#else
        if (fd2 < 0)
#endif
        {
            log (LOG_WARN, "%s: Unable to open %s to launch pppd!\n",
                 __FUNCTION__, tty);
            exit (1);
        }
        dup2 (fd2, 0);
        dup2 (fd2, 1);
*/

        /* close all the calls pty fds */
        st = tunnels.head;
        while (st)
        {
            sc = st->call_head;
            while (sc)
            {
                close (sc->fd);
                sc = sc->next;
            }
            st = st->next;
        }

        /* close the UDP socket fd */
        close (server_socket);

        /* close the control pipe fd */
        close (control_fd);

        if( c->dialing[0] )
        {
            setenv( "CALLER_ID", c->dialing, 1 );
        }
        execv (PPPD, stropt);
        log (LOG_WARN, "%s: Exec of %s failed!\n", __FUNCTION__, PPPD);
        exit (1);
    };
    close (fd2);
    pos = 0;
    while (stropt[pos])
    {
        free (stropt[pos]);
        pos++;
    };
    return 0;
}

void destroy_tunnel (struct tunnel *t)
{
    /*
     * Immediately destroy a tunnel (and all its calls)
     * and free its resources.  This may be called
     * by the tunnel itself,so it needs to be
     * "suicide safe"
     */

    struct call *c, *me;
    struct tunnel *p;
    struct timeval tv;
    if (!t)
        return;

    /*
     * Save ourselves until the very
     * end, since we might be calling this ourselves.
     * We must divorce ourself from the tunnel
     * structure, however, to avoid recursion
     * because of the logic of the destroy_call
     */
    me = t->self;

    /*
     * Destroy all the member calls
     */
    c = t->call_head;
    while (c)
    {
        destroy_call (c);
        c = c->next;
    };
    /*
     * Remove ourselves from the list of tunnels
     */

    if (tunnels.head == t)
    {
        tunnels.head = t->next;
        tunnels.count--;
    }
    else
    {
        p = tunnels.head;
        if (p)
        {
            while (p->next && (p->next != t))
                p = p->next;
            if (p->next)
            {
                p->next = t->next;
                tunnels.count--;
            }
            else
            {
                log (LOG_WARN,
                     "%s: unable to locate tunnel in tunnel list\n",
                     __FUNCTION__);
            }
        }
        else
        {
            log (LOG_WARN, "%s: tunnel list is empty!\n", __FUNCTION__);
        }
    }
    if (t->lac)
    {
        t->lac->t = NULL;
        if (t->lac->redial && (t->lac->rtimeout > 0) && !t->lac->rsched &&
            t->lac->active)
        {
            log (LOG_LOG, "%s: Will redial in %d seconds\n", __FUNCTION__,
                 t->lac->rtimeout);
            tv.tv_sec = t->lac->rtimeout;
            tv.tv_usec = 0;
            t->lac->rsched = schedule (tv, magic_lac_dial, t->lac);
        }
    }
    /* XXX L2TP/IPSec: remove relevant SAs here?  NTB 20011010
     * XXX But what if another tunnel is using same SA?
     */
    if (t->lns)
        t->lns->t = NULL;
    free (t);
    free (me);
}

struct tunnel *l2tp_call (char *host, int port, struct lac *lac,
                          struct lns *lns)
{
    /*
     * Establish a tunnel from us to host
     * on port port
     */
    struct call *tmp = NULL;
    struct hostent *hp;
    unsigned int addr;
    port = htons (port);
    hp = gethostbyname (host);
    if (!hp)
    {
        log (LOG_WARN, "%s: gethostbyname() failed for %s.\n", __FUNCTION__,
             host);
        return NULL;
    }
    bcopy (hp->h_addr, &addr, hp->h_length);
    /* Force creation of a new tunnel
       and set it's tid to 0 to cause
       negotiation to occur */
    /* XXX L2TP/IPSec: Set up SA to addr:port here?  NTB 20011010
     */
    tmp = get_call (0, 0, addr, port);
    if (!tmp)
    {
        log (LOG_WARN, "%s: Unable to create tunnel to %s.\n", __FUNCTION__,
             host);
        return NULL;
    }
    tmp->container->tid = 0;
    tmp->container->lac = lac;
    tmp->container->lns = lns;
    tmp->lac = lac;
    tmp->lns = lns;
    if (lac)
        lac->t = tmp->container;
    if (lns)
        lns->t = tmp->container;
    /*
     * Since our state is 0, we will establish a tunnel now
     */
    log (LOG_LOG, "%s:Connecting to host %s, port %d\n", __FUNCTION__, host,
         ntohs (port));
    control_finish (tmp->container, tmp);
    return tmp->container;
}

void magic_lac_tunnel (void *data)
{
    struct lac *lac;
    lac = (struct lac *) data;
    if (!lac)
    {
        log (LOG_WARN, "%s: magic_lac_tunnel: called on NULL lac!\n",
             __FUNCTION__);
        return;
    }
    if (lac->lns)
    {
        /* FIXME: I should try different LNS's if I get failures */
        l2tp_call (lac->lns->hostname, lac->lns->port, lac, NULL);
        return;
    }
    else if (deflac && deflac->lns)
    {
        l2tp_call (deflac->lns->hostname, deflac->lns->port, lac, NULL);
        return;
    }
    else
    {
        log (LOG_WARN, "%s: Unable to find hostname to dial for '%s'\n",
             __FUNCTION__, lac->entname);
        return;
    }
}

struct call *lac_call (int tid, struct lac *lac, struct lns *lns)
{
    struct tunnel *t = tunnels.head;
    struct call *tmp;
    while (t)
    {
        if (t->ourtid == tid)
        {
            tmp = new_call (t);
            if (!tmp)
            {
                log (LOG_WARN, "%s: unable to create new call\n",
                     __FUNCTION__);
                return NULL;
            }
            tmp->next = t->call_head;
            t->call_head = tmp;
            t->count++;
            tmp->cid = 0;
            tmp->lac = lac;
            tmp->lns = lns;
            if (lac)
                lac->c = tmp;
            log (LOG_LOG, "%s: Calling on tunnel %d\n", __FUNCTION__, tid);
            strcpy (tmp->dial_no, dial_no_tmp); /*  jz: copy dialnumber to tmp->dial_no  */
            control_finish (t, tmp);
            return tmp;
        }
        t = t->next;
    };
    log (LOG_DEBUG, "%s: No such tunnel %d to generate call.\n", __FUNCTION__,
         tid);
    return NULL;
}

void magic_lac_dial (void *data)
{
    struct lac *lac;
    lac = (struct lac *) data;
    if (!lac->active)
    {
        log (LOG_DEBUG, "%s: LAC %s not active", __FUNCTION__, lac->entname);
        return;
    }
    lac->rsched = NULL;
    lac->rtries++;
    if (lac->rmax && (lac->rtries > lac->rmax))
    {
        log (LOG_LOG, "%s: maximum retries exceeded.\n", __FUNCTION__);
        return;
    }
    if (!lac)
    {
        log (LOG_WARN, "%s : called on NULL lac!\n", __FUNCTION__);
        return;
    }
    if (!lac->t)
    {
#ifdef DEGUG_MAGIC
        log (LOG_DEBUG, "%s : tunnel not up!  Connecting!\n", __FUNCTION__);
#endif
        magic_lac_tunnel (lac);
        return;
    }
    lac_call (lac->t->ourtid, lac, NULL);
}

void lac_hangup (int cid)
{
    struct tunnel *t = tunnels.head;
    struct call *tmp;
    while (t)
    {
        tmp = t->call_head;
        while (tmp)
        {
            if (tmp->ourcid == cid)
            {
                log (LOG_LOG,
                     "%s :Hanging up call %d, Local: %d, Remote: %d\n",
                     __FUNCTION__, tmp->serno, tmp->ourcid, tmp->cid);
                strcpy (tmp->errormsg, "Goodbye!");
/*				tmp->needclose = -1; */
                kill (tmp->pppd, SIGTERM);
                return;
            }
            tmp = tmp->next;
        }
        t = t->next;
    };
    log (LOG_DEBUG, "%s : No such call %d to hang up.\n", __FUNCTION__, cid);
    return;
}

void lac_disconnect (int tid)
{
    struct tunnel *t = tunnels.head;
    while (t)
    {
        if (t->ourtid == tid)
        {
            log (LOG_LOG,
                 "%s: Disconnecting from %s, Local: %d, Remote: %d\n",
                 __FUNCTION__, IPADDY (t->peer.sin_addr), t->ourtid, t->tid);
            t->self->needclose = -1;
            strcpy (t->self->errormsg, "Goodbye!");
            call_close (t->self);
            return;
        }
        t = t->next;
    };
    log (LOG_DEBUG, "%s: No such tunnel %d to hang up.\n", __FUNCTION__, tid);
    return;
}

struct tunnel *new_tunnel ()
{
    char entropy_buf[2] = {'\0'};
    struct tunnel *tmp = malloc (sizeof (struct tunnel));
    if (!tmp)
        return NULL;
    tmp->control_seq_num = 0;
    tmp->control_rec_seq_num = 0;
    tmp->cLr = 0;
    tmp->call_head = NULL;
    tmp->next = NULL;
    tmp->debug = -1;
    tmp->tid = -1;
    tmp->hello = NULL;
#ifndef TESTING
/*	while(get_call((tmp->ourtid = rand() & 0xFFFF),0,0,0)); */
#ifdef USE_KERNEL
    if (kernel_support)
        tmp->ourtid = ioctl (server_socket, L2TPIOCADDTUNNEL, 0);
    else
#endif
/*        tmp->ourtid = rand () & 0xFFFF; */
        /* get_entropy((char *)&tmp->ourtid, 2); */
        get_entropy(entropy_buf, 2);
        {
            short temp = 0;
            memcpy(&temp, entropy_buf, 2);
            tmp->ourtid = temp & 0xFFFF;
#ifdef DEBUG_ENTROPY
            log(LOG_DEBUG, "ourtid = %u, entropy_buf = %hx\n", tmp->ourtid, temp);
#endif
        }
#else
    tmp->ourtid = 0x6227;
#endif
    tmp->nego = 0;
    tmp->count = 0;
    tmp->state = 0;             /* Nothing */
    tmp->peer.sin_family = AF_INET;
    tmp->peer.sin_port = 0;
    bzero (&(tmp->peer.sin_addr), sizeof (tmp->peer.sin_addr));
    tmp->sanity = -1;
    tmp->qtid = -1;
    tmp->ourfc = ASYNC_FRAMING | SYNC_FRAMING;
    tmp->ourbc = 0;
    tmp->ourtb = (((_u64) rand ()) << 32) | ((_u64) rand ());
    tmp->fc = -1;               /* These really need to be specified by the peer */
    tmp->bc = -1;               /* And we want to know if they forgot */
    tmp->hostname[0] = 0;
    tmp->vendor[0] = 0;
    tmp->secret[0] = 0;
    if (!(tmp->self = new_call (tmp)))
    {
        free (tmp);
        return NULL;
    };
    tmp->ourrws = DEFAULT_RWS_SIZE;
    tmp->self->ourfbit = FBIT;
    tmp->lac = NULL;
    tmp->lns = NULL;
    tmp->chal_us.state = 0;
    tmp->chal_us.secret[0] = 0;
    memset (tmp->chal_us.reply, 0, MD_SIG_SIZE);
    tmp->chal_them.state = 0;
    tmp->chal_them.secret[0] = 0;
    memset (tmp->chal_them.reply, 0, MD_SIG_SIZE);
    tmp->chal_them.vector = (unsigned char *) malloc (VECTOR_SIZE);
    tmp->chal_us.vector = NULL;
    tmp->hbit = 0;
    return tmp;
}

void do_control ()
{
    char buf[1024];
    char *host;
    char *tunstr;
    char *callstr;

    char *sub_str;              /* jz: use by the strtok function */
    char *tmp_ptr;              /* jz: use by the strtok function */
    struct lac *lac;
    int call;
    int tunl;
    int cnt = -1;
    while (cnt)
    {
        cnt = read (control_fd, buf, sizeof (buf));
        if (cnt > 0)
        {
            if (buf[cnt - 1] == '\n')
                buf[--cnt] = 0;
#ifdef DEBUG_CONTROL
            log (LOG_DEBUG, "%s: Got message %s (%d bytes long)\n",
                 __FUNCTION__, buf, cnt);
#endif
            switch (buf[0])
            {
            case 't':
                host = strchr (buf, ' ') + 1;
#ifdef DEBUG_CONTROL
                log (LOG_DEBUG, "%s: Attempting to tunnel to %s\n",
                     __FUNCTION__, host);
#endif
                l2tp_call (host, UDP_LISTEN_PORT, NULL, NULL);
                break;
            case 'c':

                switch_io = 1;  /* jz: Switch for Incoming - Outgoing Calls */

                tunstr = strchr (buf, ' ') + 1;
                lac = laclist;
                while (lac)
                {
                    if (!strcasecmp (lac->entname, tunstr))
                    {
                        lac->active = -1;
                        lac->rtries = 0;
                        if (!lac->c)
                            magic_lac_dial (lac);
                        else
                            log (LOG_DEBUG,
                                 "%s: Session '%s' already active!\n",
                                 __FUNCTION__, lac->entname);
                        break;
                    }
                    lac = lac->next;
                }
                if (lac)
                    break;
                tunl = atoi (tunstr);
                if (!tunl)
                {
                    log (LOG_DEBUG, "%s: No such tunnel '%s'\n", __FUNCTION__,
                         tunstr);
                    break;
                }
#ifdef DEBUG_CONTROL
                log (LOG_DEBUG, "%s: Attempting to call on tunnel %d\n",
                     __FUNCTION__, tunl);
#endif
                lac_call (tunl, NULL, NULL);
                break;

            case 'o':          /* jz: option 'o' for doing a outgoing call */

                switch_io = 0;  /* jz: Switch for incoming - outgoing Calls */

                sub_str = strchr (buf, ' ') + 1;

                tunstr = strtok (sub_str, " "); /* jz: using strtok function to get */
                tmp_ptr = strtok (NULL, " ");   /*     params out of the pipe       */
                strcpy (dial_no_tmp, tmp_ptr);

                lac = laclist;
                while (lac)
                {
                    if (!strcasecmp (lac->entname, tunstr))
                    {
                        lac->active = -1;
                        lac->rtries = 0;
                        if (!lac->c)
                            magic_lac_dial (lac);
                        else
                            log (LOG_DEBUG,
                                 "%s: Session '%s' already active!\n",
                                 __FUNCTION__, lac->entname);
                        break;
                    }
                    lac = lac->next;
                }
                if (lac)
                    break;
                tunl = atoi (tunstr);
                if (!tunl)
                {
                    log (LOG_DEBUG, "%s: No such tunnel '%s'\n", __FUNCTION__,
                         tunstr);
                    break;
                }
#ifdef DEBUG_CONTROL
                log (LOG_DEBUG, "%s: Attempting to call on tunnel %d\n",
                     __FUNCTION__, tunl);
#endif
                lac_call (tunl, NULL, NULL);
                break;

            case 'h':
                callstr = strchr (buf, ' ') + 1;
                call = atoi (callstr);
#ifdef DEBUG_CONTROL
                log (LOG_DEBUG, "%s: Attempting to call %d\n", __FUNCTION__,
                     call);
#endif
                lac_hangup (call);
                break;
            case 'd':
                tunstr = strchr (buf, ' ') + 1;
                lac = laclist;
                while (lac)
                {
                    if (!strcasecmp (lac->entname, tunstr))
                    {
                        lac->active = 0;
                        lac->rtries = 0;
                        if (lac->t)
                            lac_disconnect (lac->t->ourtid);
                        else
                            log (LOG_DEBUG, "%s: Session '%s' not up\n",
                                 __FUNCTION__, lac->entname);
                        break;
                    }
                    lac = lac->next;
                }
                if (lac)
                    break;
                tunl = atoi (tunstr);
                if (!tunl)
                {
                    log (LOG_DEBUG, "%s: No such tunnel '%s'\n", __FUNCTION__,
                         tunstr);
                    break;
                }
#ifdef DEBUG_CONTROL
                log (LOG_DEBUG, "%s: Attempting to disconnect tunnel %d\n",
                     __FUNCTION__, tunl);
#endif
                lac_disconnect (tunl);
                break;
            case 's':
                show_status ();
                break;
            default:
                log (LOG_DEBUG, "%s: Unknown command %c\n", __FUNCTION__,
                     buf[0]);
            }
        }
    }
    /* Otherwise select goes nuts */
    close (control_fd);
    control_fd = open (CONTROL_PIPE, O_RDONLY | O_NONBLOCK, 0600);
}

void usage(void) {
    printf("Usage: l2tpd -D -c [config file] -s [secret file] -p [pid file]\n");
    printf("\n");
    exit(1);
}

void init_args(int argc, char *argv[]) {
    int i=0;
    gconfig.daemon=1;
    memset(gconfig.altauthfile,0,STRLEN);
    memset(gconfig.altconfigfile,0,STRLEN);
    memset(gconfig.authfile,0,STRLEN);
    memset(gconfig.configfile,0,STRLEN);
    memset(gconfig.pidfile,0,STRLEN);
    strncpy(gconfig.altauthfile,ALT_DEFAULT_AUTH_FILE,
            sizeof(gconfig.altauthfile) - 1);
    strncpy(gconfig.altconfigfile,ALT_DEFAULT_CONFIG_FILE,
            sizeof(gconfig.altconfigfile) - 1);
    strncpy(gconfig.authfile,DEFAULT_AUTH_FILE,
            sizeof(gconfig.authfile) - 1);
    strncpy(gconfig.configfile,DEFAULT_CONFIG_FILE,
            sizeof(gconfig.configfile) - 1);
    strncpy(gconfig.pidfile,DEFAULT_PID_FILE,
            sizeof(gconfig.pidfile) - 1);
    for (i = 1; i < argc; i++) {
        if(! strncmp(argv[i],"-c",2)) {
            if(++i == argc)
                usage();
            else
                strncpy(gconfig.configfile,argv[i],
                        sizeof(gconfig.configfile) - 1);
        }
        else if (! strncmp(argv[i],"-D",2)) {
            gconfig.daemon=0;
        }
        else if (! strncmp(argv[i],"-s",2)) {
            if(++i == argc)
                usage();
            else
                strncpy(gconfig.authfile,argv[i],
                        sizeof(gconfig.authfile) - 1);
        }
        else if (! strncmp(argv[i],"-p",2)) {
            if(++i == argc)
                usage();
            else
                strncpy(gconfig.pidfile,argv[i],
                        sizeof(gconfig.pidfile) - 1);
        }
        else {
            usage();
        }
    }
}


void daemonize() {
    int pid=0;
    int i,l;
    char buf[STRLEN];
    int pidfilewritten=0;

    if((pid = fork()) < 0) {
        log(LOG_LOG, "%s: Unable to fork ()\n",__FUNCTION__);
        close(server_socket);
        exit(1);
    }
    else if (pid)
        exit(0);

    close(0);
    close(1);
    close(2);

    /* Read previous pid file. */
    if ((i = open(gconfig.pidfile,O_RDONLY)) > 0) {
        l=read(i,buf,sizeof(buf)-1);
        if (i < 0) {
            log(LOG_LOG, "%s: Unable to read pid file [%s]\n",
                    __FUNCTION__, gconfig.pidfile);
        }
        buf[i] = '\0';
        pid = atoi(buf);

        /* If the previous server process is not still running,
           write a new pid file immediately. */
        if (pid && (pid == getpid () || kill (pid, 0) < 0)) {
            unlink (gconfig.pidfile);
            if ((i = open (gconfig.pidfile, O_WRONLY | O_CREAT, 0640)) >= 0)
            {
                snprintf (buf, sizeof(buf), "%d\n", (int)getpid());
                write (i, buf, strlen(buf));
                close (i);
                pidfilewritten = 1;
            }
        }
        else
        {
            log(LOG_LOG, "%s: There's already a l2tpd server running.\n",
                    __FUNCTION__);
            close(server_socket);
            exit(1);
        }
    }

    pid = setsid();

    if(! pidfilewritten) {
        unlink(gconfig.pidfile);
        if ((i = open (gconfig.pidfile, O_WRONLY | O_CREAT, 0640)) >= 0) {
            snprintf (buf, strlen(buf), "%d\n", (int)getpid());
            write (i, buf, strlen(buf));
            close (i);
            pidfilewritten = 1;
        }
    }
}



void init (int argc,char *argv[])
{
    struct lac *lac;
    struct in_addr listenaddr;

    init_args (argc,argv);
    //srand( time(NULL) );
    rand_source = RAND_SYS;
    init_addr ();
    if (init_config ())
    {
        log (LOG_CRIT, "%s: Unable to load config file\n", __FUNCTION__);
        exit (1);
    }
    if (uname (&uts))
    {
        log (LOG_CRIT, "%s : Unable to determine host system\n",
             __FUNCTION__);
        exit (1);
    }
    init_tunnel_list (&tunnels);
    if (init_network ())
        exit (1);
    if (gconfig.daemon)
	daemonize ();
    signal (SIGTERM, &death_handler);
    signal (SIGINT, &death_handler);
    signal (SIGCHLD, &child_handler);
    signal (SIGUSR1, &status_handler);
    signal (SIGHUP, &null_handler);
    init_scheduler ();
    mkfifo (CONTROL_PIPE, 0600);
    control_fd = open (CONTROL_PIPE, O_RDONLY | O_NONBLOCK, 0600);
    if (control_fd < 0)
    {
        log (LOG_CRIT, "%s: Unable to open " CONTROL_PIPE " for reading.",
             __FUNCTION__);
        exit (1);
    }
    log (LOG_LOG, "l2tpd version " SERVER_VERSION " started on %s PID:%d\n",
         hostname, getpid ());
    log (LOG_LOG,
         "Written by Mark Spencer, Copyright (C) 1998, Adtran, Inc.\n");
    log (LOG_LOG, "Forked by Scott Balmos and David Stipp, (C) 2001\n");
    log (LOG_LOG, "Inherited by Jeff McAdams, (C) 2002\n");
    listenaddr.s_addr = gconfig.listenaddr;
    log (LOG_LOG, "%s version %s on a %s, listening on IP address %s, port %d\n", uts.sysname,
       uts.release, uts.machine, inet_ntoa(listenaddr), gconfig.port);
    lac = laclist;
    while (lac)
    {
        if (lac->autodial)
        {
#ifdef DEBUG_MAGIC
            log (LOG_DEBUG, "%s: Autodialing '%s'\n", __FUNCTION__,
                 lac->entname[0] ? lac->entname : "(unnamed)");
#endif
            lac->active = -1;
            switch_io = 1;      /* If we're a LAC, autodials will be ICRQ's */
            magic_lac_dial (lac);
        }
        lac = lac->next;
    }
}

int main (int argc, char *argv[])
{
    init(argc,argv);
    printf("L2tpd started.\n");
    dial_no_tmp = calloc (128, sizeof (char));
    network_thread ();
    return 0;
}
