/*
 *    Common support routines for sockets
 *
 *       James L. Peterson
 *
 * Copyright (C) 1987 MCC
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of MCC not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  MCC makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * MCC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL MCC BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 */
/*
 * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

#include "scope.h"
#include <fcntl.h>
#include <unistd.h>

/* ********************************************** */
/*						  */
/*       Debugging support routines               */
/*						  */
/* ********************************************** */

void
enterprocedure(const char *s)
{
    debug(2, (stderr, "-> %s\n", s));
}

void
warn(const char *s)
{
    fprintf(stderr, "####### %s\n", s);
}

void
panic(const char *s)
{
    fprintf(stderr, "%s\n", s);
    exit(1);
}

/* ************************************************************ */
/*								*/
/*    Signal Handling support					*/
/*								*/
/* ************************************************************ */

#define __USE_BSD_SIGNAL
#include <signal.h>

static void
SignalURG(int sig)
{
    debug(1, (stderr, "==> SIGURG received\n"));
}

static void
SignalPIPE(int sig)
{
    signal(SIGPIPE, SignalPIPE);
    debug(1, (stderr, "==> SIGPIPE received\n"));
}

static void
SignalINT(int sig)
{
    signal(SIGINT, SignalINT);
    debug(1, (stderr, "==> SIGINT received\n"));
    Interrupt = 1;
}

static void _X_NORETURN
SignalQUIT(int sig)
{
    debug(1, (stderr, "==> SIGQUIT received\n"));
    exit(1);
}

static void _X_NORETURN
SignalTERM(int sig)
{
    debug(1, (stderr, "==> SIGTERM received\n"));
    exit(1);
}

static void
SignalTSTP(int sig)
{
    debug(1, (stderr, "==> SIGTSTP received\n"));
}

static void
SignalCONT(int sig)
{
    debug(1, (stderr, "==> SIGCONT received\n"));
}

static void
SignalUSR1(int sig)
{
    debug(1, (stderr, "==> SIGUSR1 received\n"));
    ScopeEnabled = !ScopeEnabled;
}

void
SetSignalHandling(void)
{
    enterprocedure("SetSignalHandling");
    (void) signal(SIGURG, SignalURG);
    (void) signal(SIGPIPE, SignalPIPE);
    (void) signal(SIGINT, SignalINT);
    (void) signal(SIGQUIT, SignalQUIT);
    (void) signal(SIGTERM, SignalTERM);
    (void) signal(SIGTSTP, SignalTSTP);
    (void) signal(SIGCONT, SignalCONT);
    if (HandleSIGUSR1)
        (void) signal(SIGUSR1, SignalUSR1);
}



/* ************************************************************ */
/*								*/
/*   Create a socket for a service to listen for clients        */
/*								*/
/* ************************************************************ */

#ifdef USE_XTRANS

#define TRANS_CLIENT
#define TRANS_SERVER
#define X11_t
#include <X11/Xtrans/Xtrans.h>
static XtransConnInfo *ListenTransConns = NULL;
static int *ListenTransFds = NULL;
static int ListenTransCount;

#else

#include <sys/types.h>          /* needed by sys/socket.h and netinet/in.h */
#include <sys/uio.h>            /* for struct iovec, used by socket.h */
#include <sys/socket.h>         /* for AF_INET, SOCK_STREAM, ... */
#include <sys/ioctl.h>          /* for FIONCLEX, FIONBIO, ... */
#include <sys/fcntl.h>          /* for FIONCLEX, FIONBIO, ... */
#if !defined(FIOCLEX) && defined(HAVE_SYS_FILIO_H)
#include <sys/filio.h>
#endif

#include <netinet/in.h>         /* struct sockaddr_in */
#include <netdb.h>              /* struct servent * and struct hostent *  */

#define	BACKLOG	5
#endif

void
SetUpConnectionSocket(int iport, void (*connectionFunc) (int))
{
#ifdef USE_XTRANS
    char port[20];
    int partial;
    int i;
#else
    FD ConnectionSocket;
    struct sockaddr_in sin;
    short port;
    int one = 1;
    int ON = 1;                 /* used in ioctl */
#ifndef	SO_DONTLINGER
    struct linger linger;
#endif                          /* SO_DONTLINGER */
#endif
    char MyHostName[256];

    enterprocedure("SetUpConnectionSocket");

    (void) gethostname(MyHostName, sizeof(MyHostName));
    ScopeHost = strdup(MyHostName);
    if (ScopeHost == NULL)
        panic("Can't allocate memory for hostname");

#ifdef USE_XTRANS
    ScopePort = iport - ServerBasePort;
    snprintf(port, sizeof(port), "%d", ScopePort);
    if ((_X11TransMakeAllCOTSServerListeners(port, &partial, &ListenTransCount,
                                             &ListenTransConns) >= 0) &&
        (ListenTransCount >= 1)) {
        if (partial) {
            debug(4, (stderr,
                      "Warning: Failed to establish listening connections on some transports\n"));
        }
        ListenTransFds = malloc(ListenTransCount * sizeof(int));
        if (ListenTransFds == NULL)
            panic("Can't allocate memory for ListenTransFds");

        for (i = 0; i < ListenTransCount; i++) {
            int fd = _X11TransGetConnectionNumber(ListenTransConns[i]);

            ListenTransFds[i] = fd;
            debug(4, (stderr, "Listening on FD %d\n", fd));
            UsingFD(fd, NewConnection, NULL, ListenTransConns[i]);
        }
    }
    else {
        panic("Could not open any listening connections");
    }
#else

    /* create the connection socket and set its parameters of use */
    ConnectionSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (ConnectionSocket < 0) {
        perror("socket");
        exit(-1);
    }
    (void) setsockopt(ConnectionSocket, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
                      sizeof(int));
#ifdef SO_USELOOPBACK
    (void) setsockopt(ConnectionSocket, SOL_SOCKET, SO_USELOOPBACK,
                      (char *) NULL, 0);
#endif
#ifdef	SO_DONTLINGER
    (void) setsockopt(ConnectionSocket, SOL_SOCKET, SO_DONTLINGER,
                      (char *) NULL, 0);
#else                           /* SO_DONTLINGER */
    linger.l_onoff = 0;
    linger.l_linger = 0;
    (void) setsockopt(ConnectionSocket, SOL_SOCKET, SO_LINGER, (char *) &linger,
                      sizeof linger);
#endif                          /* SO_DONTLINGER */

    /* define the name and port to be used with the connection socket */
    bzero((char *) &sin, sizeof(sin));
    sin.sin_family = AF_INET;

    /* the address of the socket is composed of two parts: the host machine and
       the port number.  We need the host machine address for the current host
     */
    {
        /* define the host part of the address */
        struct hostent *hp = gethostbyname(MyHostName);

        if (hp == NULL)
            panic("No address for our host");
        memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
    }
    /* new code -- INADDR_ANY should be better than using the name of the
       host machine.  The host machine may have several different network
       addresses.  INADDR_ANY should work with all of them at once. */
    sin.sin_addr.s_addr = INADDR_ANY;

    port = iport;
    sin.sin_port = htons(port);
    ScopePort = port;

    /* bind the name and port number to the connection socket */
    if (bind(ConnectionSocket, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
        perror("bind");
        exit(-1);
    }

    debug(4, (stderr, "Socket is FD %d for %s,%d\n",
              ConnectionSocket, ScopeHost, ScopePort));

    /* now activate the named connection socket to get messages */
    if (listen(ConnectionSocket, BACKLOG) < 0) {
        perror("listen");
        exit(-1);
    };

    /* a few more parameter settings */
#ifdef FD_CLOEXEC
    (void) fcntl(ConnectionSocket, F_SETFD, FD_CLOEXEC);
#else
    (void) ioctl(ConnectionSocket, FIOCLEX, 0);
#endif
    /* ultrix reads hang on Unix sockets, hpux reads fail */
#if defined(O_NONBLOCK) && (!defined(ultrix) && !defined(hpux))
    (void) fcntl(ConnectionSocket, F_SETFL, O_NONBLOCK);
#else
#ifdef FIOSNBIO
    (void) ioctl(ConnectionSocket, FIOSNBIO, &ON);
#else
    (void) fcntl(ConnectionSocket, F_SETFL, FNDELAY);
#endif
#endif

    debug(4, (stderr, "Listening on FD %d\n", ConnectionSocket));
    UsingFD(ConnectionSocket, connectionFunc, NULL, NULL);
#endif
}
