
/*
 * This one (c) 1996 by Heikki Hannikainen, OH7LZB <hessu@pspt.fi>
 */

#define FIRST_KEY (3694	- 1)		/* Where to start looking for a key */
#define LAST_KEY (FIRST_KEY + 200) 	/* How far to search */
#define M_LEN 1024			/* Largest message transferred */

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

#include "config.h"
#include "node.h"
#include "io.h"

struct nmsgbuf {
	long mtype;		/* message type, must be > 0 */
	char mtext[M_LEN];	/* message data */
};

static int ipc_id = -1;

static void usr2_handler(int sig)
{
	struct nmsgbuf buf;
	
	if (msgrcv(ipc_id, (struct msgbuf *)&buf, M_LEN, 0, IPC_NOWAIT|MSG_NOERROR) != -1) {
		node_msg(buf.mtext);
	} else
		log(LOGLVL_ERROR, "usr2_handler: Catched SIGUSR2, but couldn't receive a message");

	signal(SIGUSR2, usr2_handler); /* Restore handler */
}

int ipc_send(key_t key, long mtype, char *mtext)
{
	struct nmsgbuf buf;
	int id;
	
	if ((id = msgget(key, 7)) == -1) {
		node_perror("ipc_send: Could not get transmit channel", errno);
		return -1;
	}
	
	buf.mtype = mtype;
	strncpy(buf.mtext, mtext, M_LEN);
	
	if (msgsnd(id, (struct msgbuf *)&buf, M_LEN, 0) == -1) {
		node_perror("ipc_send: Could not send message", errno);
		return -1;
	}
	
	return 0;
}

int ipc_open(void)
{
	key_t key = FIRST_KEY;
	
	do {
		key++;
		ipc_id = msgget(key, 7 | IPC_CREAT | IPC_EXCL);
	} while ((ipc_id == -1) && (key != LAST_KEY));

	if (ipc_id == -1)
		node_perror("ipc_open: Could not get an IPC channel", errno);

#if 0
	node_msg("debug: ipc_id=%d key=%d", ipc_id, key);
#endif

	User.ipc_key = key;

	if (key != -1)
		signal(SIGUSR2, usr2_handler);
	else
		signal(SIGUSR2, SIG_IGN);

	return 0;
}

int ipc_close(void)
{
	struct msqid_ds buf;
	
	if (ipc_id != -1)	/* Remove the IPC channel */
		if (msgctl(ipc_id, IPC_RMID, &buf) == -1) {
			log(LOGLVL_ERROR, "ipc_close: Could not remove IPC channel: %s", strerror(errno));
			return -1;
		}
	return 0;
}

int do_talk(int argc, char **argv)
{
	FILE *f;
	struct user u;
	char call[10];
	char mtext[M_LEN];
	int i, hits = 0, sent = 0;

	if (argc < 3) {
		node_msg("Usage: talk <user> <message>");
		return 0;
	}

	if ((f = fopen(DATA_NODE_LOGIN_FILE, "r")) == NULL) {
		node_perror(DATA_NODE_LOGIN_FILE, errno);
		return 0;
	}
	
	sprintf(mtext, "Message from %s:\n", User.call);
	for (i = 2; i < argc; i++) {
		strncat(mtext, argv[i], M_LEN - strlen(mtext));
		strncat(mtext, " ", M_LEN - strlen(mtext));
	}
	strncat(mtext, "\n--", M_LEN - strlen(mtext));
	
	strncpy(call, argv[1], 9);
	call[9] = 0;

	while (fread(&u, sizeof(u), 1, f) == 1) {
		if (u.pid == -1 || (kill(u.pid, 0) == -1 && errno == ESRCH))
			continue;
		if (!strcasecmp(u.call, call)) {
			hits++;
			if (u.ipc_key != -1 && u.state == STATE_IDLE) {
				ipc_send(u.ipc_key, 1, mtext);
				kill(u.pid, SIGUSR2);
				sent++;
			}
		}
	}
	fclose(f);
	
	if (hits == 0)
		node_msg("No such user %s", call);
	else if (sent == 0)
		node_msg("%s is busy, cannot talk to him right now.", call);

	return 0;
}
