/*
 * Test sending and receiving messages.
 *
 * Probe a few allocation boundary conditions.
 *
 * Here's the output on my system (there is a five second pause just
 * before the last message):
 *
 *------------------start of output --------------------
 * msgrcv failed: Resource temporarily unavailable
 * msgrcv failed: Resource temporarily unavailable
 * perm = (100, 20, 100, 20, 0777, 435, 132451)
 * qnum=0, qbytes=2048, lspid=0, lrpid=0,
 * stime=0, rtime=0, ctime=753220293
 * trial = 0
 * trial = 1
 * trial = 2
 * trial = 3
 * trial = 4
 * trial = 5
 * trial = 6
 * trial = 7
 * asked for -3, got message type 1, body "hello there world #0"
 * asked for -3, got message type 2, body "hello there world #1"
 * asked for -3, got message type 3, body "hello there world #2"
 * asked for 6, got message type 6, body "hello there world #5"
 * asked for 7, got message type 7, body "hello there world #6"
 * asked for 0, got message type 4, body "hello there world #3"
 * asked for 0, got message type 5, body "hello there world #4"
 * asked for 0, got message type 8, body "hello there world #7"
 * msgrcv interrupted by alarm clock - exiting (normally)
 *-------------------- end of output -----------------
 *
 */

#include <stdio.h>

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

#define NUM_MSGS	8

struct msgnode { long typ; char msg[100]; };

void
catch_alarm(int sig)
{
    /* Nothing to do here except return */
    return;
}

main()
{
    int msgkey = 0x132451;
    int trial;
    int i, rval, msqid;
    long typ;
    struct msqid_ds ds;

    msqid = msgget(msgkey, IPC_CREAT | 0777);
    if ( msqid < 0 ) {
	perror("msgget #1 failed");
	exit(1);
    }
    if ( msgctl(msqid,IPC_RMID,NULL) < 0 ) {
	perror("msgctl(IPC_RMID) failed");
	exit(1);
    }
    msqid = msgget(msgkey, IPC_CREAT | 0777);
    if ( msqid < 0 ) {
	perror("msgget #2 failed");
	exit(1);
    }
    if ( msgctl(msqid, IPC_STAT, &ds) < 0 ) {
	perror("msgctl(IPC_STAT) failed");
	exit(1);
    }
    printf("perm = (%d, %d, %d, %d, 0%o, %d, %x)\n",
	ds.msg_perm.cuid,
	ds.msg_perm.cgid,
	ds.msg_perm.uid,
	ds.msg_perm.gid,
	ds.msg_perm.mode,
	ds.msg_perm.seq,
	ds.msg_perm.key);
    printf("qnum=%d, qbytes=%d, lspid=%d, lrpid=%d,\nstime=%d, rtime=%d, ctime=%d\n",
	ds.msg_qnum,
	ds.msg_qbytes,
	ds.msg_lspid,
	ds.msg_lrpid,
	ds.msg_stime,
	ds.msg_rtime,
	ds.msg_ctime);
    for ( trial = 0; trial < NUM_MSGS; trial += 1 ) {
	struct msgnode msg;
	printf("trial = %d\n",trial);
	msg.typ = trial + 1;
	sprintf(msg.msg,"hello there world #%d",trial);
	if ( msgsnd(msqid, &msg, strlen(msg.msg)+1,IPC_NOWAIT) < 0 ) {
	    perror("msgsnd failed");
	    if ( msgsnd(msqid, &msg, strlen(msg.msg)+1,0) < 0 ) {
		perror("msgsnd failed hard");
		exit(1);
	    }
	}
    }
    while (1) {
	int len;
	struct msgnode msg;
	len = msgrcv(msqid,&msg,100,-3,IPC_NOWAIT);
	if ( len < 0 ) {
	    perror("msgrcv failed");
	    break;
	}
	if ( strlen(msg.msg) != len - 1 ) {
	    printf("len = %d, strlen(msg.msg)=%d\n",len,strlen(msg.msg));
	}
	printf("asked for %d, got message type %d, body \"%s\"\n",-3,msg.typ,msg.msg);
    }
    typ = 6;
    while (1) {
	int len;
	struct msgnode msg;
	len = msgrcv(msqid,&msg,100,typ % NUM_MSGS,IPC_NOWAIT);
	if ( len < 0 ) {
	    perror("msgrcv failed");
	    break;
	}
	if ( strlen(msg.msg) != len - 1 ) {
	    printf("len = %d, strlen(msg.msg)=%d\n",len,strlen(msg.msg));
	}
	printf("asked for %d, got message type %d, body \"%s\"\n",typ % NUM_MSGS,msg.typ,msg.msg);
	typ += 1;
    }

    while (1) {
	int len;
	struct msgnode msg;
	alarm(5);
	signal(SIGALRM,catch_alarm);		/* Program actually exits inside catch_alarm */
	len = msgrcv(msqid,&msg,100,0,0);	/* Program should eventually hang here */
	if ( len < 0 ) {
	    if ( errno == EINTR ) {
		printf("msgrcv interrupted by alarm clock - exiting (normally)\n");
		exit(0);
	    } else {
		perror("msgrcv failed");
		exit(1);
	    }
	}
	alarm(0);
	if ( strlen(msg.msg) != len - 1 ) {
	    printf("len = %d, strlen(msg.msg)=%d\n",len,strlen(msg.msg));
	}
	printf("asked for %d, got message type %d, body \"%s\"\n",0,msg.typ,msg.msg);
    }
}
