view rvinterf/lowlevel/localsock.c @ 200:2847b6cbd915

fc-tmsh rvinterf invokation mode: support passing -B as well
author Michael Spacefalcon <msokolov@ivan.Harhan.ORG>
date Thu, 19 Dec 2013 08:15:47 +0000
parents 549e6cd1e77d
children 2f285f20d617
line wrap: on
line source

/*
 * This rvinterf module handles the local UNIX domain socket interface
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include "../localsock.h"
#include "client.h"

int listener;

extern struct client *client_head;
extern int max_fd;
extern char *socket_pathname;
extern int socketpair_fd;

create_listener_socket()
{
	/* local socket binding voodoo copied from osmocon */
	struct sockaddr_un local;
	unsigned int namelen;
	int rc;

	listener = socket(AF_UNIX, SOCK_STREAM, 0);
	if (listener < 0) {
		perror("socket(AF_UNIX, SOCK_STREAM, 0)");
		exit(1);
	}

	local.sun_family = AF_UNIX;
	strncpy(local.sun_path, socket_pathname, sizeof(local.sun_path));
	local.sun_path[sizeof(local.sun_path) - 1] = '\0';
	unlink(local.sun_path);

	/* we use the same magic that X11 uses in Xtranssock.c for
	 * calculating the proper length of the sockaddr */
#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
	local.sun_len = strlen(local.sun_path);
#endif
#if defined(BSD44SOCKETS) || defined(SUN_LEN)
	namelen = SUN_LEN(&local);
#else
	namelen = strlen(local.sun_path) +
		  offsetof(struct sockaddr_un, sun_path) + 1;
#endif

	rc = bind(listener, (struct sockaddr *) &local, namelen);
	if (rc != 0) {
		perror("bind on local socket");
		exit(1);
	}
	rc = listen(listener, 3);
	if (rc != 0) {
		perror("listen");
		exit(1);
	}

	if (listener > max_fd)
		max_fd = listener;
	return(0);
}

static void
prep_for_length_rx(cli)
	struct client *cli;
{
	cli->rx_state = 0;
	cli->rx_ptr = cli->rx_buf;
	cli->rx_left = 2;
}

static void
prep_for_message_rx(cli)
	struct client *cli;
{
	cli->rx_state = 1;
	cli->rx_ptr = cli->rx_buf;
	cli->rx_left = cli->rx_msglen;
}

handle_listener_select()
{
	struct sockaddr_un un_addr;
	socklen_t len;
	int rc;
	struct client *newcli;

	len = sizeof(un_addr);
	rc = accept(listener, (struct sockaddr *) &un_addr, &len);
	if (rc < 0) {
		perror("rvinterf: accept");
		exit(1);
	}
	if (rc > max_fd)
		max_fd = rc;
	newcli = malloc(sizeof(struct client));
	if (!newcli) {
		perror("rvinterf: malloc for new client");
		exit(1);
	}
	bzero(newcli, sizeof(struct client));
	newcli->fd = rc;
	newcli->next = client_head;
	client_head = newcli;
	prep_for_length_rx(newcli);
	output_line("*** Client program connected");
	return(0);
}

create_socketpair_client()
{
	struct client *cli;

	if (socketpair_fd > max_fd)
		max_fd = socketpair_fd;
	cli = malloc(sizeof(struct client));
	if (!cli) {
		perror("rvinterf: malloc for socketpair client");
		exit(1);
	}
	bzero(cli, sizeof(struct client));
	cli->fd = socketpair_fd;
	client_head = cli;
	prep_for_length_rx(cli);
	return(0);
}

send_local_msg_to_client(cli, msg)
	struct client *cli;
	char *msg;
{
	int len, len1;
	u_char hdr[3];

	len = strlen(msg);
	len1 = len + 1;
	hdr[0] = len1 >> 8;
	hdr[1] = len1 & 0xFF;
	hdr[2] = RVI2CLI_LOCAL_CMD_RESP;
	write(cli->fd, hdr, 3);
	write(cli->fd, msg, len);
}

void
handle_client_select(cli)
	struct client *cli;
{
	int cc;

	cc = read(cli->fd, cli->rx_ptr, cli->rx_left);
	if (cc <= 0) {
		/* normal client exit condition */
		output_line("*** Client program disconnected");
close_socket:	cli->rx_state = 2;
		return;
	}
	cli->rx_ptr += cc;
	cli->rx_left -= cc;
	if (cli->rx_left)
		return;
	/* got the thing, process it */
	if (cli->rx_state) {
		prep_for_length_rx(cli);
		process_msg_from_client(cli);
	} else {
		cli->rx_msglen = cli->rx_buf[0] << 8 | cli->rx_buf[1];
		if (cli->rx_msglen < 1 || cli->rx_msglen > LOCALSOCK_MAX_MSG) {
			send_local_msg_to_client(cli,
					"-Invalid length, closing socket");
			goto close_socket;
		}
		prep_for_message_rx(cli);
	}
}