/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:ctl_net.c 12.0$ */
/* $ACIS:ctl_net.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ibm/rvd/misc/RCS/ctl_net.c,v $ */

#ifndef lint
static char *rcsid = "$Header:ctl_net.c 12.0$";
#endif


#ifndef lint
static char rcsid_ctl_net_c[] = "$Header:ctl_net.c 12.0$";
#endif lint

/* Copyright 1984 by the Massachusetts Institute of Technology */
/* See permission and disclaimer notice in the file "notice.h" */
#include "notice.h"


/* This file includes the routines to send and receive packets on
 * the control connection.
 */

#include	<sys/types.h>
#include	<sys/socket.h>
#include	<sys/ioctl.h>
#include	<sys/param.h>
#include	<sys/ioctl.h>
#include	<stdio.h>
#include	<netdb.h>
#include	<errno.h>
#include	<netinet/in.h>

#include	"rvd_types.h"
#include	"logging.h"
#include	"control.h"
#include	"extern.h"

static	int	ctl_sock;		/* control socket descriptor */

struct	ctl_stats {			/* control statistics */
	int	cs_recvs;		/* received packets */
	int	cs_sends;		/* sent packets */
	int	cs_serr;		/* send errors */
} ctl_stats;


int
ctl_init()

/* Initialize the control connection.  Open a socket on the RVD-CONTROL/UDP
 * service, and set it to non-blocking mode.  If the socket can't be opened,
 * kill the process immediately.
 * Returns a bitmask which can be passed to select (possibly or'ed with
 * other bitmasks) to wait for input to become available on the control
 * connection.
 */
{
	register struct	servent	*ctl_serv; /* ptr to service structure */
	register struct	protoent *ctl_proto; /* ptr to protocol struct */
	struct	sockaddr_in	ctl_addr; /* local end's address */
	int	onoff = 1;		/* arg for ioctl FIONBIO */

	if ((ctl_serv = getservbyname("rvd-control", "udp")) == NULL) {
		fprintf(stderr, "ctl_init: rvd-control service unknown\n");
		exit(1);
	}

	if ((ctl_proto = getprotobyname(ctl_serv->s_proto)) == NULL) {
		fprintf(stderr, "ctl_init: udp protocol unknown\n");
		exit(1);
	}

	ctl_addr.sin_family = AF_INET;
	ctl_addr.sin_port = ctl_serv->s_port;
	ctl_addr.sin_addr.s_addr = INADDR_ANY;

	if ((ctl_sock = socket(AF_INET, SOCK_DGRAM, ctl_proto->p_proto)) < 0 ||
	    bind(ctl_sock, (struct sockaddr *)&ctl_addr,
	    sizeof(struct sockaddr_in) ) < 0 ||
	    ioctl(ctl_sock, (int)FIONBIO, (char *)&onoff) < 0) {
		perror("ctl_init: ");
		exit(1);
	}

	return(1 << ctl_sock);
}


int
ctl_recv(fhost, buf, buflen)

/* Receive the next available packet from the control socket.  The buf argument
 * is the address of a buffer of size buflen into which the packet is to be
 * received.  Fhost is a pointer to a place to put the source address of the
 * packet.  Returns the number of bytes received, or 0 if no packets
 * are available.
 */

struct	sockaddr_in	*fhost;			/* for foreign host's addr */
char	*buf;					/* buffer for packet */
int	buflen;					/* size of buffer in bytes */
{
	int	fhlen;				/* for length of fhost */
	int	len;				/* received packet length */

	fhlen = sizeof(struct sockaddr_in);

	while ((len = recvfrom(ctl_sock, buf, buflen, 0,
	    (struct sockaddr *)fhost, &fhlen)) < 0) {
		if (errno == EWOULDBLOCK)	/* none available */
			return(0);
		if (errno != EINTR)		/* fatal error */
			bughalt(errno < sys_nerr ? sys_errlist[errno] :
			    "ctl_recv: unknown error");
	}

	ctl_stats.cs_recvs++;

	if (loglevel(LOG_TRACE))
		ctl_log(fhost, buf, len, 1);

	return(len);
}


ctl_send(fhost, buf, buflen)

/* Send the specified buffer of the specified length to the specified
 * destination host.
 */

struct	sockaddr_in	*fhost;		/* foreign host */
char	*buf;				/* buffer containing packet */
int	buflen;				/* size of buffer in bytes */
{
	if (loglevel(LOG_TRACE))
		ctl_log(fhost, buf, buflen, 0);

	if (sendto(ctl_sock, buf, buflen, 0, (struct sockaddr *)fhost,
	    sizeof(struct sockaddr_in)) != buflen) {
		ctl_stats.cs_serr++;
		perror("ctl_send: ");
		return;
	}
	ctl_stats.cs_sends++;
}


ctl_log(fhost, buf, buflen, dir)

/* Log the specified control packet to the standard output.
 */

register struct	sockaddr_in	*fhost;	/* foreign host */
char	*buf;				/* packet */
int	buflen;				/* size of packet in bytes */
int	dir;				/* 0 ==> input, 1 ==> output */
{
	printf("%s control packet %s %X len %D\n", (dir ? "Output" : "Input"),
	    (dir ? "to" : "from"), fhost->sin_addr.s_addr, buflen);
	(void)fwrite(buf, 1, buflen, stdout);
	printf("\n\n");
}


ctl_show()

/* Show control connection statistics in log.
 */
{
	fprintf(stderr, "Control network connection statistics:\n");
	fprintf(stderr, "%D	received packets\n", ctl_stats.cs_recvs);
	fprintf(stderr, "%D	transmitted packets\n", ctl_stats.cs_sends);
	fprintf(stderr, "%D	send errors\n\n", ctl_stats.cs_serr);
}
