/*	Program:			usrsnd
 *
 *	Filename:			listing1.c
 *
 *	Description:		Demonstration program that uses a message
 *					queue to enable users to send one line messages
 *					to each other.
 *
 *	Contents:			main(), send_mssg(), recv_mssg(),
 *					get_msg_queue()
 *
 *	Author/Programmer:	W. J. Freda
 *					Automated Concepts Inc.
 */
#include <stdio.h>
#include <signal.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define KEY     0x5642412
static struct mssg  {
	long mtype;			/* Message type */
	char mtext[BUFSIZ];		/* Message text */
} Msgbuf;
static int Msqid;			/* Message queue identifier */
static int Firstime=1;

/*	MAIN
 *
 *	DESCRIPTION:	Provides a simple menu that ties the
 *				send_msg() and recv_msg() functions
 *				together.
 *
 *	INPUT:		NONE
 *	OUTPUT:		Program termination
*/
main()
{
	char *mssg, *recv_mssg();
	char selection[80];

	while(1)  {
		printf("\n\n\t1) Send a message to a specific user\n");
		printf("\t2) Receive messages\n");
		printf("\t3) Exit\n");
		printf("\n\t\tEnter selection: ");

		gets(selection);

		switch(*selection)  {
			case '1':
				send_mssg();
				break;
			case '2':
				if ((mssg=recv_mssg()) == NULL)  
					printf("\n\nNo messages on queue\n\n");
				else
					printf("\n\n%s\n\n",mssg);
				break;
			case '3':
				exit(0);
			default:
				printf("\tInvalid selection.\n\n");
				break;
		}
	}
}


/*	SEND_MSSG FUNCTION
 *
 *	DESCRIPTION:	Prompts the user for a username and message
 *				and sends the message to that user via a message
 *				queue.  The user can read the message using
 *				rcv_mssg().
 *
 *	INPUT:		NONE
 *	OUTPUT:		0	If successful
 *				-1	Otherwise
*/
send_mssg()
{
	extern int Msqid;			/* Message queue identifier  */
	extern struct mssg Msgbuf;	/* Message structure         */
	extern int errno;
	int msglen;				/* Length of message        */
	char message[BUFSIZ];		/* Message text             */
	char username[20];
	struct passwd *pass, *getpwnam();

	if (Firstime)  {
		if ((Msqid=get_msg_queue()) == -1) 
			return(-1);

		Firstime=0;
	}

	/* Prompt for user name */
	printf("\n\nSend message to which user? ");
	gets(username);

	/* Validate username */
	setpwent();
	pass=getpwnam(username);
	endpwent();

	if (pass == NULL)  {
		printf("Invalid username.\n\n");
		return(0);
	}

	/* Prompt user for a message to send */
	printf("Enter message: ");
	gets(Msgbuf.mtext);

	if (*(Msgbuf.mtext) == NULL)  
		/* Nothing to send */
		return(0);
        
	/* Set the message length */
	msglen=strlen(Msgbuf.mtext);

	/* Set the message type equal to the userid
	 * we want to send the message to.
	*/
	Msgbuf.mtype = pass->pw_uid;

	/* Send the message.  The IPC_NOWAIT flag informs msgsnd()
	 * not to send the message if the message queue is full.
	 * Without the IPC_NOWAIT flag this process would be put
	 * to sleep by the kernel until the message can be sent.
	*/
	if (msgsnd(Msqid, &Msgbuf, msglen, IPC_NOWAIT) == -1)  {
		/* Error occurred */
		return(-1);
	}

	return(0);
}


/*	RECV_MSSG FUNCTION		char *recv_mssg()
 *
 *	DESCRIPTION:	Attempts to retrieve a message from the 
 *				message queue.
 *
 *	INPUT:		NONE
 *	OUTPUT:		text	Returns a pointer to the first 
 *					character of the message text.
 *				NULL	If there are no messages or an 
 *					error occurred.
*/
char *recv_mssg()
{
	extern int Msqid;			/* Message queue identifier  */
	extern struct mssg Msgbuf;	/* Message structure         */
	extern int Firstime;
	int uid;					/* User id                   */
	int len;

	if (Firstime)  {
		if ((Msqid=get_msg_queue()) == -1)  {
			return(NULL);
		}
		Firstime=0;
	}

	/* Get the user id */
	uid=getuid();

	/* Receive a message.  The IPC_NOWAIT flag informs msgrcv()
 	 * to return immediately if no message is on queue.  Without
 	 * the IPC_NOWAIT flag this process would be put to sleep by 
 	 * the kernel until a message of type uid is received.
	*/
	if ((len=msgrcv(Msqid, &Msgbuf, BUFSIZ, uid, IPC_NOWAIT)) == -1)
		return(NULL);

	/* Null terminate the message */
	Msgbuf.mtext[len]=NULL;
	return(Msgbuf.mtext);
}
        
/*	GET_MSG_QUEUE FUNCTION
 *
 *	DESCRIPTION:	Gets and possibly creates a message
 *				queue identifier via the msgget(2) 
 *				system call.
 *      
 *	INPUT:		NONE
 *	OUTPUT:		qid	Message queue ID
 *				-1	Otherwise
*/
get_msg_queue()
{
	int qid;

	/* Acquire a message queue.  
 	 * The IPC_CREAT flag informs msgget() to create 
 	 * the message queue with the permission 0666.
 	 * The IPC_CREAT flags inform msgget() to create
 	 * the message queue if it doesn't already exist.
	*/
	if ((qid=msgget((key_t)KEY, 0666 | IPC_CREAT)) == -1)  {
		return(-1);
	}

	return(qid);
}
                

