connection.c 6.8 KB
/*
 * connection.c: connect to the clients.
 *
 * Copyright 1995, Silicon Graphics, Inc.
 * All Rights Reserved.
 *
 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
 * the contents of this file may not be disclosed to third parties, copied or
 * duplicated in any form, in whole or in part, without the prior written
 * permission of Silicon Graphics, Inc.
 *
 * RESTRICTED RIGHTS LEGEND:
 * Use, duplication or disclosure by the Government is subject to restrictions
 * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
 * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
 * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
 * rights reserved under the Copyright Laws of the United States.
 *
 * $Revision: 1.1.1.1 $
 */

/*
 * standard includes needed for various reasons.
 */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <bstring.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/uio.h>
#include <sys/stat.h>

/*
 * includes needed for socket stuff
 */
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

/*
 * Local includes
 */
#include "driverd.h"
#include "utils.h"

extern char *daemon;		/* The daemon number */


static int ParentProcess;
static int unixDomainConnection = -1;

static void ErrorConnMax(int fd);
static int open_tcp_socket(void);

fd_set AllSockets;		/* select using these fd's */
fd_set SelectReturnMask;	/* select returns available fd's in this set */

/*
 * this is used to limit the number of open connections to the daemon.
 */
static int lastfdesc;		/* maximum file descriptor */

extern ClientPtr *clients;	/* Pointer to array of available clients */
extern int 	 nClients;	/* Number of existing clients */
int nextFreeClientID;		/* always MIN free client ID */

#define MAXSOCKS 128
long MaxClients = MAXSOCKS ;
int  currentMaxClients;   /* current size of clients array */

void
InitClientConnections(void)
{
    int i;

    lastfdesc = getdtablesize() - 1;

    if (lastfdesc > MAXSOCKS)
	lastfdesc = MAXSOCKS;

    FD_ZERO(&AllSockets);
}


/*
 * int NextAvailableClient
 *
 * Returns NULL if there are no free clients.
 */

ClientPtr
NextAvailableClient(int fd)
{
    int i;
    ClientPtr client;

    i = nextFreeClientID;
    if (i == MAXCLIENTS)
	return NULL;
    clients[i] = client = (ClientPtr)malloc(sizeof(ClientRec));
    if (!client)
	return NULL;


    client->fd = fd;
    client->id = NULL;
    client->min_event = NULL;
    client->max_event = NULL;
    client->index = i;

    if (i == currentMaxClients)
	currentMaxClients++;

    while ((nextFreeClientID < MAXCLIENTS) && clients[nextFreeClientID])
	nextFreeClientID++;
    
    return(client);
}

int
AddNewClient(int fd)
{
    ClientPtr client;

    if (fd >= lastfdesc)
	return -1;
    client = NextAvailableClient(fd);
    if (!client)
	return -1;

    client->fd = fd;			/* Client will tell us who they are, */
    client->id = NULL;			/* Client will tell us who they are, */
    FD_SET(fd, &AllSockets);
    nClients++;
    return 0;
}

/*
 * Close down the existing client.  The daemon finds out about clients which 
 * have gone away when select returns (indicating that there's data to be 
 * read), but a FIONREAD check indicates otherwise.
 */ 
void CloseDownClient(ClientPtr client)
{
    if (client->index < nextFreeClientID) {
	nextFreeClientID = client->index;
    }
    FD_CLR(client->fd, &AllSockets);
    clients[client->index] = NULL;
    free(client);
    nClients--;
}

#define MAX_LISTENING_SOCKETS	5

int numListeningSockets;
uint ListeningSockets[MAX_LISTENING_SOCKETS];

/*
 * AddListeningSocket: add a connection that we will listen for new clients on.
 *
 * This has a fatal error if there are more sockets than allowed.
 */
void
AddListeningSocket(int fd)
{
    if (numListeningSockets >= MAX_LISTENING_SOCKETS)
	FatalError("Too many sockets to listen to");
    FD_SET((uint)fd, &AllSockets);
    ListeningSockets[numListeningSockets++] = (uint)fd;
}

static int
open_tcp_socket(void)
{
    struct sockaddr_in insock;
    int request;
    int retry;
    static int linger[2] = { 0, 0 };

    /* are we a child of inetd? */
    {
#define NAME_SIZE 100

	char name[NAME_SIZE];
	int namelen = NAME_SIZE;

	/* recieving a -1 here (errno = [ENOTSOCK|EBADF]) means
	 * that fd = 0 isn't a socket. that's ok, we'll just
	 * create the socket ourselves and assume this is a
	 * debug session.
	 */
	if (getsockname(0, name, &namelen) >= 0)
	    return 0;
    }

    if ((request = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
    {
	ErrorF("Creating TCP socket");
	return -1;
    } 
    /* Necesary to restart the server without a reboot */
    {
	int one = 1;
	setsockopt(request, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
    }

    bzero((char *)&insock, sizeof(insock));
    insock.sin_family = AF_INET;
    insock.sin_port = htons((unsigned short)(DRIVERD_TCP_PORT + atoi(daemon)));
    insock.sin_addr.s_addr = htonl(INADDR_ANY);
    retry = 20;
    while (bind(request, (struct sockaddr *) &insock, sizeof(insock)))
    {
	if (--retry == 0) {
	    ErrorF("Binding TCP socket");
	    close(request);
	    return -1;
	}
	sleep(1);
    }

    if(setsockopt(request, SOL_SOCKET, SO_LINGER,
		   (char *)linger, sizeof(linger)))
	ErrorF("Setting TCP SO_LINGER");

    if (listen(request, 5)) {
	ErrorF("TCP Listening");
	close(request);
	return -1;
    }
    return request;
}

static struct sockaddr_un unsock;

/*
 * CreateWellKnownSockets
 *    At initialization, create the sockets to listen on for new clients.
 */
void
CreateWellKnownSockets(void)
{
    int	request;
    int sockCount = 0;

    InitClientConnections();

    if ((request = open_tcp_socket()) != -1) {
	sockCount += 1;
	AddListeningSocket(request);
    }
}

static const fd_set zero_set;

static int
ANYSET(fd_set * const src)
{
    return bcmp(src, &zero_set, sizeof(fd_set));
}

/*
 * WaitForSomething():
 *
 * call select on all available file descriptors and wait for one of the
 * following events to occur:
 *
 * 	1. Writes on well known socket from potential new clients.
 *	2. Writes on private sockets from established clients.
 *
 *	     NOTE:
 *
 *	     Writes on the private socket from emulator (corresponding to 
 *	     "events" from the game) are a subset of writes from established 
 *	     clients; the daemon distinguishes between the two by the values 
 *	     written to the socket.
 *
 * Return a list of clients with data ready to be read.
 */

int
WaitForSomething(void)
{
    int nready;

    bcopy(&AllSockets, &SelectReturnMask, sizeof(fd_set));

    /*
     * timeout arg is NULL, so this blocks until data is available.
     */
    if ( ANYSET(&SelectReturnMask) ) {

	nready = select(MAXSOCKS, &SelectReturnMask, NULL, NULL, NULL);

    } else {
	fprintf(stderr, 
	"WaitForSomething: no socket fd's specified for listening to!\n");
	exit(1);
    }
}