IPC: POSIX Message Queues for bash

POSIX IPC is strangely enough painfully missing in the GNU commandline tools.

I really wanted it so I wrote a POSIX IPC implementation for bash (and ofcourse also usable for any other shell scripting language)

Documentation is included as comments in the source below.
More info about POSIX IPC: man mq_overview

(note: mq_open and mq_close are implicit.)

#include <fcntl.h>
#include <limits.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

/* compile with: gcc -Wall -Wextra -o posix_mq posix_mq.c -lrt
 *  or:          tcc -Wall -lrt -o posix_mq posix_mq.c
 *
 * Create commands:
 *  ln -s posix_mq mq_send
 *  ln -s posix_mq mq_receive
 *  ln -s posix_mq mq_unlink
 *
 * example:
 *  mq_send /mytest "hello world"
 *  mq_receive /mytest
 *  mq_unlink /mytest
 *
 * this code is largely based on code written by Peter Hofmann:
 *  https://www.uninformativ.de/blog/postings/2016-05-16/0/POSTING-en.html
 *
 * His work, and therefore also this derivative work,
 * is published under the following licence:
 * -----------------------------------------------------------------------
 * "THE PIZZA_WARE LICENCE" (Revision 42):
 * This application is largely based on code written by Peter Hofmann
 * As long as you retain this notice,
 * you can do whatever you want with this stuff. If you meet him some day,
 * and you think this stuff is worth it, you can buy him a pizza in return
 * -----------------------------------------------------------------------
 * 
 * grtz, tkn (2017)
 */

int main(int argc, char *argv[])
{
	mqd_t queue;
	struct mq_attr attrs;
	char *msg_ptr;
	ssize_t recvd;
	size_t i, msg_len;
	int n;

	/* after the last underscore ... */
	for( n=strlen(argv[0]) ; n>=0 && argv[0][n]!='_' ; --n );

	/* ... get me the first character  */
	switch(argv[0][++n])
	{
		case 's':
			if (argc < 3)
			{
				fprintf(stderr, "Usage: %s <queuename> <message>\n", argv[0]);
				return 1;
			}
			queue = mq_open(argv[1], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR, NULL);
			if (queue == (mqd_t)-1)
			{
				perror("mq_open");
				return 1;
			}
			if (mq_getattr(queue, &attrs) == -1)
			{
				perror("mq_getattr");
				mq_close(queue);
				return 1;
			}
			msg_len = strlen(argv[2]);
			if (msg_len > LONG_MAX || (long)msg_len > attrs.mq_msgsize)
			{
				fprintf(stderr, "Your message is too long for the queue.\n");
				mq_close(queue);
				return 1;
			}
			if (mq_send(queue, argv[2], strlen(argv[2]), 0) == -1)
			{
				perror("posix_mq");
				mq_close(queue);
				return 1;
			}
			break;

		case 'r':
			if (argc < 2)
			{
				fprintf(stderr, "Usage: %s <queuename>\n", argv[0]);
				return 1;
			}
			queue = mq_open(argv[1], O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR, NULL);
			if (queue == (mqd_t)-1)
			{
				perror("mq_open");
				return 1;
			}
			if (mq_getattr(queue, &attrs) == -1)
			{
				perror("mq_getattr");
				mq_close(queue);
				return 1;
			}
			msg_ptr = calloc(1, attrs.mq_msgsize);
			if (msg_ptr == NULL)
			{
				perror("calloc for msg_ptr");
				mq_close(queue);
				return 1;
			}
			recvd = mq_receive(queue, msg_ptr, attrs.mq_msgsize, NULL);
			if (recvd == -1)
			{
				perror("mq_receive");
				return 1;
			}
			for (i = 0; i < (size_t)recvd; ++i) putchar(msg_ptr[i]);
			printf("\n");
			break;

		case 'u':
			if (argc < 2)
			{
				fprintf(stderr, "Usage: %s <queuename>\n", argv[0]);
				return 1;
			}
			if ( mq_unlink(argv[1]) )
			{
				perror("mq_unlink");
				return 1;
			}
			break;

		default :
			{
				fprintf(stderr,
				"\nposix_mq implements posix ipc for bash/ash/dash\n\n"
				"mq_send    /queuename message    : send a message to a queue\n"
				"mq_receive /queuename            : receive a message from a queue\n"
				"mq_unlink  /queuename            : delete a queue\n\n"
				"extra info about posix messagequeues: man mq_overview\n\n"
				);
				return 1;
			}
			break;
	}
	return 0;
}
5 Likes

Probably you may find it interesting too:

4 Likes