10112f8c4SRick Parrish /*-
20112f8c4SRick Parrish  * SPDX-License-Identifier: BSD-2-Clause
30112f8c4SRick Parrish  *
40112f8c4SRick Parrish  * Copyright (c) 2024 Rick Parrish <unitrunker@unitrunker.net>.
50112f8c4SRick Parrish  *
60112f8c4SRick Parrish  * Redistribution and use in source and binary forms, with or without
70112f8c4SRick Parrish  * modification, are permitted provided that the following conditions
80112f8c4SRick Parrish  * are met:
90112f8c4SRick Parrish  * 1. Redistributions of source code must retain the above copyright
100112f8c4SRick Parrish  *	notice, this list of conditions and the following disclaimer.
110112f8c4SRick Parrish  * 2. Redistributions in binary form must reproduce the above copyright
120112f8c4SRick Parrish  *	notice, this list of conditions and the following disclaimer in the
130112f8c4SRick Parrish  *	documentation and/or other materials provided with the distribution.
140112f8c4SRick Parrish  *
150112f8c4SRick Parrish  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
160112f8c4SRick Parrish  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
170112f8c4SRick Parrish  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
180112f8c4SRick Parrish  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
190112f8c4SRick Parrish  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
200112f8c4SRick Parrish  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
210112f8c4SRick Parrish  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
220112f8c4SRick Parrish  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
230112f8c4SRick Parrish  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
240112f8c4SRick Parrish  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
250112f8c4SRick Parrish  * SUCH DAMAGE.
260112f8c4SRick Parrish  */
270112f8c4SRick Parrish 
280112f8c4SRick Parrish #include <sys/queue.h>
290112f8c4SRick Parrish #include <sys/stat.h>
300112f8c4SRick Parrish #include <err.h>
310112f8c4SRick Parrish #include <errno.h>
320112f8c4SRick Parrish #include <fcntl.h>
330112f8c4SRick Parrish #include <grp.h>
340112f8c4SRick Parrish #include <limits.h>
350112f8c4SRick Parrish #include <mqueue.h>
360112f8c4SRick Parrish #include <pwd.h>
370112f8c4SRick Parrish #include <stdbool.h>
380112f8c4SRick Parrish #include <stdio.h>
390112f8c4SRick Parrish #include <stdlib.h>
400112f8c4SRick Parrish #include <string.h>
410112f8c4SRick Parrish #include <sysexits.h>
420112f8c4SRick Parrish #include <unistd.h>
430112f8c4SRick Parrish 
440112f8c4SRick Parrish struct Creation {
450112f8c4SRick Parrish 	/* true if the queue exists. */
460112f8c4SRick Parrish 	bool exists;
470112f8c4SRick Parrish 	/* true if a mode value was specified. */
480112f8c4SRick Parrish 	bool set_mode;
490112f8c4SRick Parrish 	/* access mode with rwx permission bits. */
500112f8c4SRick Parrish 	mode_t mode;
510112f8c4SRick Parrish 	/* maximum queue depth. default to an invalid depth. */
520112f8c4SRick Parrish 	long depth;
530112f8c4SRick Parrish 	/* maximum message size. default to an invalid size. */
540112f8c4SRick Parrish 	long size;
550112f8c4SRick Parrish 	/* true for blocking I/O and false for non-blocking I/O. */
560112f8c4SRick Parrish 	bool block;
570112f8c4SRick Parrish 	/* true if a group ID was specified. */
580112f8c4SRick Parrish 	bool set_group;
590112f8c4SRick Parrish 	/* group ID. */
600112f8c4SRick Parrish 	gid_t group;
610112f8c4SRick Parrish 	/* true if a user ID was specified. */
620112f8c4SRick Parrish 	bool set_user;
630112f8c4SRick Parrish 	/* user ID. */
640112f8c4SRick Parrish 	uid_t user;
650112f8c4SRick Parrish };
660112f8c4SRick Parrish 
670112f8c4SRick Parrish struct element {
680112f8c4SRick Parrish 	STAILQ_ENTRY(element) links;
690112f8c4SRick Parrish 	const char *text;
700112f8c4SRick Parrish };
710112f8c4SRick Parrish 
720112f8c4SRick Parrish static struct element *
malloc_element(const char * context)730112f8c4SRick Parrish malloc_element(const char *context)
740112f8c4SRick Parrish {
750112f8c4SRick Parrish 	struct element *item = malloc(sizeof(struct element));
760112f8c4SRick Parrish 
770112f8c4SRick Parrish 	if (item == NULL)
780112f8c4SRick Parrish 		/* the only non-EX_* prefixed exit code. */
790112f8c4SRick Parrish 		err(1, "malloc(%s)", context);
800112f8c4SRick Parrish 	return (item);
810112f8c4SRick Parrish }
820112f8c4SRick Parrish 
830112f8c4SRick Parrish static STAILQ_HEAD(tqh, element)
840112f8c4SRick Parrish 	queues = STAILQ_HEAD_INITIALIZER(queues),
850112f8c4SRick Parrish 	contents = STAILQ_HEAD_INITIALIZER(contents);
860112f8c4SRick Parrish /* send defaults to medium priority. */
870112f8c4SRick Parrish static long priority = MQ_PRIO_MAX / 2;
880112f8c4SRick Parrish static struct Creation creation = {
890112f8c4SRick Parrish 	.exists = false,
900112f8c4SRick Parrish 	.set_mode = false,
910112f8c4SRick Parrish 	.mode = 0755,
920112f8c4SRick Parrish 	.depth = -1,
930112f8c4SRick Parrish 	.size = -1,
940112f8c4SRick Parrish 	.block = true,
950112f8c4SRick Parrish 	.set_group = false,
960112f8c4SRick Parrish 	.group = 0,
970112f8c4SRick Parrish 	.set_user = false,
980112f8c4SRick Parrish 	.user = 0
990112f8c4SRick Parrish };
1000112f8c4SRick Parrish static const mqd_t fail = (mqd_t)-1;
1010112f8c4SRick Parrish static const mode_t accepted_mode_bits =
1020112f8c4SRick Parrish     S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISTXT;
1030112f8c4SRick Parrish 
1040112f8c4SRick Parrish /* OPTIONS parsing utilitarian */
1050112f8c4SRick Parrish 
1060112f8c4SRick Parrish static void
parse_long(const char * text,long * capture,const char * knob,const char * name)1070112f8c4SRick Parrish parse_long(const char *text, long *capture, const char *knob, const char *name)
1080112f8c4SRick Parrish {
1090112f8c4SRick Parrish 	char *cursor = NULL;
1100112f8c4SRick Parrish 	long value = strtol(text, &cursor, 10);
1110112f8c4SRick Parrish 
1120112f8c4SRick Parrish 	if (cursor > text && *cursor == 0) {
1130112f8c4SRick Parrish 		*capture = value;
1140112f8c4SRick Parrish 	} else {
1150112f8c4SRick Parrish 		warnx("%s %s invalid format [%s].", knob, name, text);
1160112f8c4SRick Parrish 	}
1170112f8c4SRick Parrish }
1180112f8c4SRick Parrish 
1190112f8c4SRick Parrish static void
parse_unsigned(const char * text,bool * set,unsigned * capture,const char * knob,const char * name)1200112f8c4SRick Parrish parse_unsigned(const char *text, bool *set,
1210112f8c4SRick Parrish    unsigned *capture, const char *knob, const char *name)
1220112f8c4SRick Parrish {
1230112f8c4SRick Parrish 	char *cursor = NULL;
1240112f8c4SRick Parrish 	unsigned value = strtoul(text, &cursor, 8);
1250112f8c4SRick Parrish 
1260112f8c4SRick Parrish 	if (cursor > text && *cursor == 0) {
1270112f8c4SRick Parrish 		*set = true;
1280112f8c4SRick Parrish 		*capture = value;
1290112f8c4SRick Parrish 	} else {
1300112f8c4SRick Parrish 		warnx("%s %s format [%s] ignored.", knob, name, text);
1310112f8c4SRick Parrish 	}
1320112f8c4SRick Parrish }
1330112f8c4SRick Parrish 
1340112f8c4SRick Parrish static bool
sane_queue(const char * queue)1350112f8c4SRick Parrish sane_queue(const char *queue)
1360112f8c4SRick Parrish {
1370112f8c4SRick Parrish 	int size = 0;
1380112f8c4SRick Parrish 
1390112f8c4SRick Parrish 	if (queue[size] != '/') {
1400112f8c4SRick Parrish 		warnx("queue name [%-.*s] must start with '/'.", NAME_MAX, queue);
1410112f8c4SRick Parrish 		return (false);
1420112f8c4SRick Parrish 	}
1430112f8c4SRick Parrish 
1440112f8c4SRick Parrish 	for (size++; queue[size] != 0 && size < NAME_MAX; size++) {
1450112f8c4SRick Parrish 		if (queue[size] == '/') {
1460112f8c4SRick Parrish 			warnx("queue name [%-.*s] - only one '/' permitted.",
1470112f8c4SRick Parrish 			    NAME_MAX, queue);
1480112f8c4SRick Parrish 			return (false);
1490112f8c4SRick Parrish 		}
1500112f8c4SRick Parrish 	}
1510112f8c4SRick Parrish 
1520112f8c4SRick Parrish 	if (size == NAME_MAX && queue[size] != 0) {
1530112f8c4SRick Parrish 		warnx("queue name [%-.*s...] may not be longer than %d.",
1540112f8c4SRick Parrish 		    NAME_MAX, queue, NAME_MAX);
1550112f8c4SRick Parrish 		return (false);
1560112f8c4SRick Parrish 	}
1570112f8c4SRick Parrish 	return (true);
1580112f8c4SRick Parrish }
1590112f8c4SRick Parrish 
1600112f8c4SRick Parrish /* OPTIONS parsers */
1610112f8c4SRick Parrish 
1620112f8c4SRick Parrish static void
parse_block(const char * text)1630112f8c4SRick Parrish parse_block(const char *text)
1640112f8c4SRick Parrish {
1650112f8c4SRick Parrish 	if (strcmp(text, "true") == 0 || strcmp(text, "yes") == 0) {
1660112f8c4SRick Parrish 		creation.block = true;
1670112f8c4SRick Parrish 	} else if (strcmp(text, "false") == 0 || strcmp(text, "no") == 0) {
1680112f8c4SRick Parrish 		creation.block = false;
1690112f8c4SRick Parrish 	} else {
1700112f8c4SRick Parrish 		char *cursor = NULL;
1710112f8c4SRick Parrish 		long value = strtol(text, &cursor, 10);
1720112f8c4SRick Parrish 		if (cursor > text) {
1730112f8c4SRick Parrish 			creation.block = value != 0;
1740112f8c4SRick Parrish 		} else {
1750112f8c4SRick Parrish 			warnx("bad -b block format [%s] ignored.", text);
1760112f8c4SRick Parrish 		}
1770112f8c4SRick Parrish 	}
1780112f8c4SRick Parrish }
1790112f8c4SRick Parrish 
1800112f8c4SRick Parrish static void
parse_content(const char * content)1810112f8c4SRick Parrish parse_content(const char *content)
1820112f8c4SRick Parrish {
1830112f8c4SRick Parrish 	struct element *n1 = malloc_element("content");
1840112f8c4SRick Parrish 
1850112f8c4SRick Parrish 	n1->text = content;
1860112f8c4SRick Parrish 	STAILQ_INSERT_TAIL(&contents, n1, links);
1870112f8c4SRick Parrish }
1880112f8c4SRick Parrish 
1890112f8c4SRick Parrish static void
parse_depth(const char * text)1900112f8c4SRick Parrish parse_depth(const char *text)
1910112f8c4SRick Parrish {
1920112f8c4SRick Parrish 	parse_long(text, &creation.depth, "-d", "depth");
1930112f8c4SRick Parrish }
1940112f8c4SRick Parrish 
1950112f8c4SRick Parrish static void
parse_group(const char * text)1960112f8c4SRick Parrish parse_group(const char *text)
1970112f8c4SRick Parrish {
1980112f8c4SRick Parrish 	struct group *entry = getgrnam(text);
1990112f8c4SRick Parrish 
2000112f8c4SRick Parrish 	if (entry == NULL) {
2010112f8c4SRick Parrish 		parse_unsigned(text, &creation.set_group,
2020112f8c4SRick Parrish 		    &creation.group, "-g", "group");
2030112f8c4SRick Parrish 	} else {
2040112f8c4SRick Parrish 		creation.set_group = true;
2050112f8c4SRick Parrish 		creation.group = entry->gr_gid;
2060112f8c4SRick Parrish 	}
2070112f8c4SRick Parrish }
2080112f8c4SRick Parrish 
2090112f8c4SRick Parrish static void
parse_mode(const char * text)2100112f8c4SRick Parrish parse_mode(const char *text)
2110112f8c4SRick Parrish {
2120112f8c4SRick Parrish 	char *cursor = NULL;
2130112f8c4SRick Parrish 	long value = strtol(text, &cursor, 8);
2140112f8c4SRick Parrish 
2150112f8c4SRick Parrish 	// verify only accepted mode bits are set.
2160112f8c4SRick Parrish 	if (cursor > text && *cursor == 0 && (value & accepted_mode_bits) == value) {
2170112f8c4SRick Parrish 		creation.set_mode = true;
2180112f8c4SRick Parrish 		creation.mode = (mode_t)value;
2190112f8c4SRick Parrish 	} else {
2200112f8c4SRick Parrish 		warnx("impossible -m mode value [%s] ignored.", text);
2210112f8c4SRick Parrish 	}
2220112f8c4SRick Parrish }
2230112f8c4SRick Parrish 
2240112f8c4SRick Parrish static void
parse_priority(const char * text)2250112f8c4SRick Parrish parse_priority(const char *text)
2260112f8c4SRick Parrish {
2270112f8c4SRick Parrish 	char *cursor = NULL;
2280112f8c4SRick Parrish 	long value = strtol(text, &cursor, 10);
2290112f8c4SRick Parrish 
2300112f8c4SRick Parrish 	if (cursor > text && *cursor == 0) {
2310112f8c4SRick Parrish 		if (value >= 0 && value < MQ_PRIO_MAX) {
2320112f8c4SRick Parrish 			priority = value;
2330112f8c4SRick Parrish 		} else {
2340112f8c4SRick Parrish 			warnx("bad -p priority range [%s] ignored.", text);
2350112f8c4SRick Parrish 		}
2360112f8c4SRick Parrish 	} else {
2370112f8c4SRick Parrish 		warnx("bad -p priority format [%s] ignored.", text);
2380112f8c4SRick Parrish 	}
2390112f8c4SRick Parrish }
2400112f8c4SRick Parrish 
2410112f8c4SRick Parrish static void
parse_queue(const char * queue)2420112f8c4SRick Parrish parse_queue(const char *queue)
2430112f8c4SRick Parrish {
2440112f8c4SRick Parrish 	if (sane_queue(queue)) {
2450112f8c4SRick Parrish 		struct element *n1 = malloc_element("queue name");
2460112f8c4SRick Parrish 
2470112f8c4SRick Parrish 		n1->text = queue;
2480112f8c4SRick Parrish 		STAILQ_INSERT_TAIL(&queues, n1, links);
2490112f8c4SRick Parrish 	}
2500112f8c4SRick Parrish }
2510112f8c4SRick Parrish 
2520112f8c4SRick Parrish static void
parse_single_queue(const char * queue)2530112f8c4SRick Parrish parse_single_queue(const char *queue)
2540112f8c4SRick Parrish {
2550112f8c4SRick Parrish 	if (sane_queue(queue)) {
2560112f8c4SRick Parrish 		if (STAILQ_EMPTY(&queues)) {
2570112f8c4SRick Parrish 			struct element *n1 = malloc_element("queue name");
2580112f8c4SRick Parrish 
2590112f8c4SRick Parrish 			n1->text = queue;
2600112f8c4SRick Parrish 			STAILQ_INSERT_TAIL(&queues, n1, links);
2610112f8c4SRick Parrish 		} else
2620112f8c4SRick Parrish 			warnx("ignoring extra -q queue [%s].", queue);
2630112f8c4SRick Parrish 	}
2640112f8c4SRick Parrish }
2650112f8c4SRick Parrish 
2660112f8c4SRick Parrish static void
parse_size(const char * text)2670112f8c4SRick Parrish parse_size(const char *text)
2680112f8c4SRick Parrish {
2690112f8c4SRick Parrish 	parse_long(text, &creation.size, "-s", "size");
2700112f8c4SRick Parrish }
2710112f8c4SRick Parrish 
2720112f8c4SRick Parrish static void
parse_user(const char * text)2730112f8c4SRick Parrish parse_user(const char *text)
2740112f8c4SRick Parrish {
2750112f8c4SRick Parrish 	struct passwd *entry = getpwnam(text);
2760112f8c4SRick Parrish 	if (entry == NULL) {
2770112f8c4SRick Parrish 		parse_unsigned(text, &creation.set_user,
2780112f8c4SRick Parrish 		    &creation.user, "-u", "user");
2790112f8c4SRick Parrish 	} else {
2800112f8c4SRick Parrish 		creation.set_user = true;
2810112f8c4SRick Parrish 		creation.user = entry->pw_uid;
2820112f8c4SRick Parrish 	}
2830112f8c4SRick Parrish }
2840112f8c4SRick Parrish 
2850112f8c4SRick Parrish /* OPTIONS validators */
2860112f8c4SRick Parrish 
2870112f8c4SRick Parrish static bool
validate_always_true(void)2880112f8c4SRick Parrish validate_always_true(void)
2890112f8c4SRick Parrish {
2900112f8c4SRick Parrish 	return (true);
2910112f8c4SRick Parrish }
2920112f8c4SRick Parrish 
2930112f8c4SRick Parrish static bool
validate_content(void)2940112f8c4SRick Parrish validate_content(void)
2950112f8c4SRick Parrish {
2960112f8c4SRick Parrish 	bool valid = !STAILQ_EMPTY(&contents);
2970112f8c4SRick Parrish 
2980112f8c4SRick Parrish 	if (!valid)
2990112f8c4SRick Parrish 		warnx("no content to send.");
3000112f8c4SRick Parrish 	return (valid);
3010112f8c4SRick Parrish }
3020112f8c4SRick Parrish 
3030112f8c4SRick Parrish static bool
validate_depth(void)3040112f8c4SRick Parrish validate_depth(void)
3050112f8c4SRick Parrish {
3060112f8c4SRick Parrish 	bool valid = creation.exists || creation.depth > 0;
3070112f8c4SRick Parrish 
3080112f8c4SRick Parrish 	if (!valid)
3090112f8c4SRick Parrish 		warnx("-d maximum queue depth not provided.");
3100112f8c4SRick Parrish 	return (valid);
3110112f8c4SRick Parrish }
3120112f8c4SRick Parrish 
3130112f8c4SRick Parrish static bool
validate_queue(void)3140112f8c4SRick Parrish validate_queue(void)
3150112f8c4SRick Parrish {
3160112f8c4SRick Parrish 	bool valid = !STAILQ_EMPTY(&queues);
3170112f8c4SRick Parrish 
3180112f8c4SRick Parrish 	if (!valid)
3190112f8c4SRick Parrish 		warnx("missing -q, or no sane queue name given.");
3200112f8c4SRick Parrish 	return (valid);
3210112f8c4SRick Parrish }
3220112f8c4SRick Parrish 
3230112f8c4SRick Parrish static bool
validate_single_queue(void)3240112f8c4SRick Parrish validate_single_queue(void)
3250112f8c4SRick Parrish {
3260112f8c4SRick Parrish 	bool valid = !STAILQ_EMPTY(&queues) &&
3270112f8c4SRick Parrish 	    STAILQ_NEXT(STAILQ_FIRST(&queues), links) == NULL;
3280112f8c4SRick Parrish 
3290112f8c4SRick Parrish 	if (!valid)
3300112f8c4SRick Parrish 		warnx("expected one queue.");
3310112f8c4SRick Parrish 	return (valid);
3320112f8c4SRick Parrish }
3330112f8c4SRick Parrish 
3340112f8c4SRick Parrish static bool
validate_size(void)3350112f8c4SRick Parrish validate_size(void)
3360112f8c4SRick Parrish {
3370112f8c4SRick Parrish 	bool valid = creation.exists || creation.size > 0;
3380112f8c4SRick Parrish 
3390112f8c4SRick Parrish 	if (!valid)
3400112f8c4SRick Parrish 		warnx("-s maximum message size not provided.");
3410112f8c4SRick Parrish 	return (valid);
3420112f8c4SRick Parrish }
3430112f8c4SRick Parrish 
3440112f8c4SRick Parrish /* OPTIONS table handling. */
3450112f8c4SRick Parrish 
3460112f8c4SRick Parrish struct Option {
3470112f8c4SRick Parrish 	/* points to array of string pointers terminated by a null pointer. */
3480112f8c4SRick Parrish 	const char **pattern;
3490112f8c4SRick Parrish 	/* parse argument. */
3500112f8c4SRick Parrish 	void (*parse)(const char *);
3510112f8c4SRick Parrish 	/*
3520112f8c4SRick Parrish 	 * displays an error and returns false if this parameter is not valid.
3530112f8c4SRick Parrish 	 * returns true otherwise.
3540112f8c4SRick Parrish 	 */
3550112f8c4SRick Parrish 	bool (*validate)(void);
3560112f8c4SRick Parrish };
3570112f8c4SRick Parrish 
3580112f8c4SRick Parrish /*
3590112f8c4SRick Parrish  * parse options by table.
3600112f8c4SRick Parrish  * index - current index into argv list.
3610112f8c4SRick Parrish  * argc, argv - command line parameters.
3620112f8c4SRick Parrish  * options - null terminated list of pointers to options.
3630112f8c4SRick Parrish  */
3640112f8c4SRick Parrish static void
parse_options(int index,int argc,const char * argv[],const struct Option ** options)3650112f8c4SRick Parrish parse_options(int index, int argc,
3660112f8c4SRick Parrish     const char *argv[], const struct Option **options)
3670112f8c4SRick Parrish {
3680112f8c4SRick Parrish 	while ((index + 1) < argc) {
3690112f8c4SRick Parrish 		const struct Option **cursor = options;
3700112f8c4SRick Parrish 		bool match = false;
3710112f8c4SRick Parrish 		while (*cursor != NULL && !match) {
3720112f8c4SRick Parrish 			const struct Option *option = cursor[0];
3730112f8c4SRick Parrish 			const char **pattern = option->pattern;
3740112f8c4SRick Parrish 
3750112f8c4SRick Parrish 			while (*pattern != NULL && !match) {
3760112f8c4SRick Parrish 				const char *knob = *pattern;
3770112f8c4SRick Parrish 
3780112f8c4SRick Parrish 				match = strcmp(knob, argv[index]) == 0;
3790112f8c4SRick Parrish 				if (!match)
3800112f8c4SRick Parrish 					pattern++;
3810112f8c4SRick Parrish 			}
3820112f8c4SRick Parrish 
3830112f8c4SRick Parrish 			if (match) {
3840112f8c4SRick Parrish 				option->parse(argv[index + 1]);
3850112f8c4SRick Parrish 				index += 2;
3860112f8c4SRick Parrish 				break;
3870112f8c4SRick Parrish 			}
3880112f8c4SRick Parrish 			cursor++;
3890112f8c4SRick Parrish 		}
3900112f8c4SRick Parrish 
3910112f8c4SRick Parrish 		if (!match && index < argc) {
3920112f8c4SRick Parrish 			warnx("skipping [%s].", argv[index]);
3930112f8c4SRick Parrish 			index++;
3940112f8c4SRick Parrish 		}
3950112f8c4SRick Parrish 	}
3960112f8c4SRick Parrish 
3970112f8c4SRick Parrish 	if (index < argc) {
3980112f8c4SRick Parrish 		warnx("skipping [%s].", argv[index]);
3990112f8c4SRick Parrish 	}
4000112f8c4SRick Parrish }
4010112f8c4SRick Parrish 
4020112f8c4SRick Parrish /* options - null terminated list of pointers to options. */
4030112f8c4SRick Parrish static bool
validate_options(const struct Option ** options)4040112f8c4SRick Parrish validate_options(const struct Option **options)
4050112f8c4SRick Parrish {
4060112f8c4SRick Parrish 	bool valid = true;
4070112f8c4SRick Parrish 
4080112f8c4SRick Parrish 	while (*options != NULL) {
4090112f8c4SRick Parrish 		const struct Option *option = options[0];
4100112f8c4SRick Parrish 
4110112f8c4SRick Parrish 		if (!option->validate())
4120112f8c4SRick Parrish 			valid = false;
4130112f8c4SRick Parrish 		options++;
4140112f8c4SRick Parrish 	}
4150112f8c4SRick Parrish 	return (valid);
4160112f8c4SRick Parrish }
4170112f8c4SRick Parrish 
4180112f8c4SRick Parrish /* SUBCOMMANDS */
4190112f8c4SRick Parrish 
4200112f8c4SRick Parrish /*
4210112f8c4SRick Parrish  * queue: name of queue to be created.
4220112f8c4SRick Parrish  * q_creation: creation parameters (copied by value).
4230112f8c4SRick Parrish  */
4240112f8c4SRick Parrish static int
create(const char * queue,struct Creation q_creation)4250112f8c4SRick Parrish create(const char *queue, struct Creation q_creation)
4260112f8c4SRick Parrish {
4270112f8c4SRick Parrish 	int flags = O_RDWR;
4280112f8c4SRick Parrish 	struct mq_attr stuff = {
4290112f8c4SRick Parrish 		.mq_curmsgs = 0,
4300112f8c4SRick Parrish 		.mq_maxmsg = q_creation.depth,
4310112f8c4SRick Parrish 		.mq_msgsize = q_creation.size,
4320112f8c4SRick Parrish 		.mq_flags = 0
4330112f8c4SRick Parrish 	};
4340112f8c4SRick Parrish 
4350112f8c4SRick Parrish 	if (!q_creation.block) {
4360112f8c4SRick Parrish 		flags |= O_NONBLOCK;
4370112f8c4SRick Parrish 		stuff.mq_flags |= O_NONBLOCK;
4380112f8c4SRick Parrish 	}
4390112f8c4SRick Parrish 
4400112f8c4SRick Parrish 	mqd_t handle = mq_open(queue, flags);
4410112f8c4SRick Parrish 	q_creation.exists = handle != fail;
4420112f8c4SRick Parrish 	if (!q_creation.exists) {
4430112f8c4SRick Parrish 		/*
4440112f8c4SRick Parrish 		 * apply size and depth checks here.
4450112f8c4SRick Parrish 		 * if queue exists, we can default to existing depth and size.
4460112f8c4SRick Parrish 		 * but for a new queue, we require that input.
4470112f8c4SRick Parrish 		 */
4480112f8c4SRick Parrish 		if (validate_size() && validate_depth()) {
4490112f8c4SRick Parrish 			/* no need to re-apply mode. */
4500112f8c4SRick Parrish 			q_creation.set_mode = false;
4510112f8c4SRick Parrish 			flags |= O_CREAT;
4520112f8c4SRick Parrish 			handle = mq_open(queue, flags, q_creation.mode, &stuff);
4530112f8c4SRick Parrish 		}
4540112f8c4SRick Parrish 	}
4550112f8c4SRick Parrish 
4560112f8c4SRick Parrish 	if (handle == fail) {
4570112f8c4SRick Parrish 		errno_t what = errno;
4580112f8c4SRick Parrish 
4590112f8c4SRick Parrish 		warnc(what, "mq_open(create)");
4600112f8c4SRick Parrish 		return (what);
4610112f8c4SRick Parrish 	}
4620112f8c4SRick Parrish 
4630112f8c4SRick Parrish #ifdef __FreeBSD__
4640112f8c4SRick Parrish 	/*
4650112f8c4SRick Parrish 	 * undocumented.
4660112f8c4SRick Parrish 	 * See https://bugs.freebsd.org/bugzilla//show_bug.cgi?id=273230
4670112f8c4SRick Parrish 	 */
4680112f8c4SRick Parrish 	int fd = mq_getfd_np(handle);
4690112f8c4SRick Parrish 
4700112f8c4SRick Parrish 	if (fd < 0) {
4710112f8c4SRick Parrish 		errno_t what = errno;
4720112f8c4SRick Parrish 
4730112f8c4SRick Parrish 		warnc(what, "mq_getfd_np(create)");
4740112f8c4SRick Parrish 		mq_close(handle);
4750112f8c4SRick Parrish 		return (what);
4760112f8c4SRick Parrish 	}
4770112f8c4SRick Parrish 	struct stat status = {0};
4780112f8c4SRick Parrish 	int result = fstat(fd, &status);
4790112f8c4SRick Parrish 	if (result != 0) {
4800112f8c4SRick Parrish 		errno_t what = errno;
4810112f8c4SRick Parrish 
4820112f8c4SRick Parrish 		warnc(what, "fstat(create)");
4830112f8c4SRick Parrish 		mq_close(handle);
4840112f8c4SRick Parrish 		return (what);
4850112f8c4SRick Parrish 	}
4860112f8c4SRick Parrish 
4870112f8c4SRick Parrish 	/* do this only if group and / or user given. */
4880112f8c4SRick Parrish 	if (q_creation.set_group || q_creation.set_user) {
4890112f8c4SRick Parrish 		q_creation.user =
4900112f8c4SRick Parrish 		    q_creation.set_user ? q_creation.user : status.st_uid;
4910112f8c4SRick Parrish 		q_creation.group =
4920112f8c4SRick Parrish 		    q_creation.set_group ? q_creation.group : status.st_gid;
4930112f8c4SRick Parrish 		result = fchown(fd, q_creation.user, q_creation.group);
4940112f8c4SRick Parrish 		if (result != 0) {
4950112f8c4SRick Parrish 			errno_t what = errno;
4960112f8c4SRick Parrish 
4970112f8c4SRick Parrish 			warnc(what, "fchown(create)");
4980112f8c4SRick Parrish 			mq_close(handle);
4990112f8c4SRick Parrish 			return (what);
5000112f8c4SRick Parrish 		}
5010112f8c4SRick Parrish 	}
5020112f8c4SRick Parrish 
5030112f8c4SRick Parrish 	/* do this only if altering mode of an existing queue. */
5040112f8c4SRick Parrish 	if (q_creation.exists && q_creation.set_mode &&
5050112f8c4SRick Parrish 	    q_creation.mode != (status.st_mode & accepted_mode_bits)) {
5060112f8c4SRick Parrish 		result = fchmod(fd, q_creation.mode);
5070112f8c4SRick Parrish 		if (result != 0) {
5080112f8c4SRick Parrish 			errno_t what = errno;
5090112f8c4SRick Parrish 
5100112f8c4SRick Parrish 			warnc(what, "fchmod(create)");
5110112f8c4SRick Parrish 			mq_close(handle);
5120112f8c4SRick Parrish 			return (what);
5130112f8c4SRick Parrish 		}
5140112f8c4SRick Parrish 	}
5150112f8c4SRick Parrish #endif /* __FreeBSD__ */
5160112f8c4SRick Parrish 
5170112f8c4SRick Parrish 	return (mq_close(handle));
5180112f8c4SRick Parrish }
5190112f8c4SRick Parrish 
5200112f8c4SRick Parrish /* queue: name of queue to be removed. */
5210112f8c4SRick Parrish static int
rm(const char * queue)5220112f8c4SRick Parrish rm(const char *queue)
5230112f8c4SRick Parrish {
5240112f8c4SRick Parrish 	int result = mq_unlink(queue);
5250112f8c4SRick Parrish 
5260112f8c4SRick Parrish 	if (result != 0) {
5270112f8c4SRick Parrish 		errno_t what = errno;
5280112f8c4SRick Parrish 
5290112f8c4SRick Parrish 		warnc(what, "mq_unlink");
5300112f8c4SRick Parrish 		return (what);
5310112f8c4SRick Parrish 	}
5320112f8c4SRick Parrish 
5330112f8c4SRick Parrish 	return (result);
5340112f8c4SRick Parrish }
5350112f8c4SRick Parrish 
5360112f8c4SRick Parrish /* Return the display character for non-zero mode. */
5370112f8c4SRick Parrish static char
dual(mode_t mode,char display)5380112f8c4SRick Parrish dual(mode_t mode, char display)
5390112f8c4SRick Parrish {
5400112f8c4SRick Parrish 	return (mode != 0 ? display : '-');
5410112f8c4SRick Parrish }
5420112f8c4SRick Parrish 
5430112f8c4SRick Parrish /* Select one of four display characters based on mode and modifier. */
5440112f8c4SRick Parrish static char
quad(mode_t mode,mode_t modifier)5450112f8c4SRick Parrish quad(mode_t mode, mode_t modifier)
5460112f8c4SRick Parrish {
5470112f8c4SRick Parrish 	static const char display[] = "-xSs";
5480112f8c4SRick Parrish 	unsigned index = 0;
5490112f8c4SRick Parrish 	if (mode != 0)
5500112f8c4SRick Parrish 		index += 1;
5510112f8c4SRick Parrish 	if (modifier)
5520112f8c4SRick Parrish 		index += 2;
5530112f8c4SRick Parrish 	return (display[index]);
5540112f8c4SRick Parrish }
5550112f8c4SRick Parrish 
5560112f8c4SRick Parrish /* queue: name of queue to be inspected. */
5570112f8c4SRick Parrish static int
info(const char * queue)5580112f8c4SRick Parrish info(const char *queue)
5590112f8c4SRick Parrish {
5600112f8c4SRick Parrish 	mqd_t handle = mq_open(queue, O_RDONLY);
5610112f8c4SRick Parrish 
5620112f8c4SRick Parrish 	if (handle == fail) {
5630112f8c4SRick Parrish 		errno_t what = errno;
5640112f8c4SRick Parrish 
5650112f8c4SRick Parrish 		warnc(what, "mq_open(info)");
5660112f8c4SRick Parrish 		return (what);
5670112f8c4SRick Parrish 	}
5680112f8c4SRick Parrish 
5690112f8c4SRick Parrish 	struct mq_attr actual;
5700112f8c4SRick Parrish 
5710112f8c4SRick Parrish 	int result = mq_getattr(handle, &actual);
5720112f8c4SRick Parrish 	if (result != 0) {
5730112f8c4SRick Parrish 		errno_t what = errno;
5740112f8c4SRick Parrish 
5750112f8c4SRick Parrish 		warnc(what, "mq_getattr(info)");
5760112f8c4SRick Parrish 		return (what);
5770112f8c4SRick Parrish 	}
5780112f8c4SRick Parrish 
5790112f8c4SRick Parrish 	fprintf(stdout,
5800112f8c4SRick Parrish 	    "queue: '%s'\nQSIZE: %lu\nMSGSIZE: %ld\nMAXMSG: %ld\n"
5810112f8c4SRick Parrish 	    "CURMSG: %ld\nflags: %03ld\n",
5820112f8c4SRick Parrish 	    queue, actual.mq_msgsize * actual.mq_curmsgs, actual.mq_msgsize,
5830112f8c4SRick Parrish 	    actual.mq_maxmsg, actual.mq_curmsgs, actual.mq_flags);
5840112f8c4SRick Parrish #ifdef __FreeBSD__
5850112f8c4SRick Parrish 
5860112f8c4SRick Parrish 	int fd = mq_getfd_np(handle);
5870112f8c4SRick Parrish 	struct stat status;
5880112f8c4SRick Parrish 
5890112f8c4SRick Parrish 	result = fstat(fd, &status);
5900112f8c4SRick Parrish 	if (result != 0) {
5910112f8c4SRick Parrish 		warn("fstat(info)");
5920112f8c4SRick Parrish 	} else {
5930112f8c4SRick Parrish 		mode_t mode = status.st_mode;
5940112f8c4SRick Parrish 
5950112f8c4SRick Parrish 		fprintf(stdout, "UID: %u\nGID: %u\n", status.st_uid, status.st_gid);
5960112f8c4SRick Parrish 		fprintf(stdout, "MODE: %c%c%c%c%c%c%c%c%c%c\n",
5970112f8c4SRick Parrish 		    dual(mode & S_ISVTX, 's'),
5980112f8c4SRick Parrish 		    dual(mode & S_IRUSR, 'r'),
5990112f8c4SRick Parrish 		    dual(mode & S_IWUSR, 'w'),
6000112f8c4SRick Parrish 		    quad(mode & S_IXUSR, mode & S_ISUID),
6010112f8c4SRick Parrish 		    dual(mode & S_IRGRP, 'r'),
6020112f8c4SRick Parrish 		    dual(mode & S_IWGRP, 'w'),
6030112f8c4SRick Parrish 		    quad(mode & S_IXGRP, mode & S_ISGID),
6040112f8c4SRick Parrish 		    dual(mode & S_IROTH, 'r'),
6050112f8c4SRick Parrish 		    dual(mode & S_IWOTH, 'w'),
6060112f8c4SRick Parrish 		    dual(mode & S_IXOTH, 'x'));
6070112f8c4SRick Parrish 	}
6080112f8c4SRick Parrish #endif /* __FreeBSD__ */
6090112f8c4SRick Parrish 
6100112f8c4SRick Parrish 	return (mq_close(handle));
6110112f8c4SRick Parrish }
6120112f8c4SRick Parrish 
6130112f8c4SRick Parrish /* queue: name of queue to drain one message. */
6140112f8c4SRick Parrish static int
recv(const char * queue)6150112f8c4SRick Parrish recv(const char *queue)
6160112f8c4SRick Parrish {
6170112f8c4SRick Parrish 	mqd_t handle = mq_open(queue, O_RDONLY);
6180112f8c4SRick Parrish 
6190112f8c4SRick Parrish 	if (handle == fail) {
6200112f8c4SRick Parrish 		errno_t what = errno;
6210112f8c4SRick Parrish 
6220112f8c4SRick Parrish 		warnc(what, "mq_open(recv)");
6230112f8c4SRick Parrish 		return (what);
6240112f8c4SRick Parrish 	}
6250112f8c4SRick Parrish 
6260112f8c4SRick Parrish 	struct mq_attr actual;
6270112f8c4SRick Parrish 
6280112f8c4SRick Parrish 	int result = mq_getattr(handle, &actual);
6290112f8c4SRick Parrish 
6300112f8c4SRick Parrish 	if (result != 0) {
6310112f8c4SRick Parrish 		errno_t what = errno;
6320112f8c4SRick Parrish 
6330112f8c4SRick Parrish 		warnc(what, "mq_attr(recv)");
6340112f8c4SRick Parrish 		mq_close(handle);
6350112f8c4SRick Parrish 		return (what);
6360112f8c4SRick Parrish 	}
6370112f8c4SRick Parrish 
6380112f8c4SRick Parrish 	char *text = malloc(actual.mq_msgsize + 1);
6390112f8c4SRick Parrish 	unsigned q_priority = 0;
6400112f8c4SRick Parrish 
6410112f8c4SRick Parrish 	memset(text, 0, actual.mq_msgsize + 1);
6420112f8c4SRick Parrish 	result = mq_receive(handle, text, actual.mq_msgsize, &q_priority);
6430112f8c4SRick Parrish 	if (result < 0) {
6440112f8c4SRick Parrish 		errno_t what = errno;
6450112f8c4SRick Parrish 
6460112f8c4SRick Parrish 		warnc(what, "mq_receive");
6470112f8c4SRick Parrish 		mq_close(handle);
6480112f8c4SRick Parrish 		return (what);
6490112f8c4SRick Parrish 	}
6500112f8c4SRick Parrish 
6510112f8c4SRick Parrish 	fprintf(stdout, "[%u]: %-*.*s\n", q_priority, result, result, text);
6520112f8c4SRick Parrish 	return (mq_close(handle));
6530112f8c4SRick Parrish }
6540112f8c4SRick Parrish 
6550112f8c4SRick Parrish /*
6560112f8c4SRick Parrish  * queue: name of queue to send one message.
6570112f8c4SRick Parrish  * text: message text.
6580112f8c4SRick Parrish  * q_priority: message priority in range of 0 to 63.
6590112f8c4SRick Parrish  */
6600112f8c4SRick Parrish static int
send(const char * queue,const char * text,unsigned q_priority)6610112f8c4SRick Parrish send(const char *queue, const char *text, unsigned q_priority)
6620112f8c4SRick Parrish {
6630112f8c4SRick Parrish 	mqd_t handle = mq_open(queue, O_WRONLY);
6640112f8c4SRick Parrish 
6650112f8c4SRick Parrish 	if (handle == fail) {
6660112f8c4SRick Parrish 		errno_t what = errno;
6670112f8c4SRick Parrish 
6680112f8c4SRick Parrish 		warnc(what, "mq_open(send)");
6690112f8c4SRick Parrish 		return (what);
6700112f8c4SRick Parrish 	}
6710112f8c4SRick Parrish 
6720112f8c4SRick Parrish 	struct mq_attr actual;
6730112f8c4SRick Parrish 
6740112f8c4SRick Parrish 	int result = mq_getattr(handle, &actual);
6750112f8c4SRick Parrish 
6760112f8c4SRick Parrish 	if (result != 0) {
6770112f8c4SRick Parrish 		errno_t what = errno;
6780112f8c4SRick Parrish 
6790112f8c4SRick Parrish 		warnc(what, "mq_attr(send)");
6800112f8c4SRick Parrish 		mq_close(handle);
6810112f8c4SRick Parrish 		return (what);
6820112f8c4SRick Parrish 	}
6830112f8c4SRick Parrish 
6840112f8c4SRick Parrish 	int size = strlen(text);
6850112f8c4SRick Parrish 
6860112f8c4SRick Parrish 	if (size > actual.mq_msgsize) {
6870112f8c4SRick Parrish 		warnx("truncating message to %ld characters.\n", actual.mq_msgsize);
6880112f8c4SRick Parrish 		size = actual.mq_msgsize;
6890112f8c4SRick Parrish 	}
6900112f8c4SRick Parrish 
6910112f8c4SRick Parrish 	result = mq_send(handle, text, size, q_priority);
6920112f8c4SRick Parrish 
6930112f8c4SRick Parrish 	if (result != 0) {
6940112f8c4SRick Parrish 		errno_t what = errno;
6950112f8c4SRick Parrish 
6960112f8c4SRick Parrish 		warnc(what, "mq_send");
6970112f8c4SRick Parrish 		mq_close(handle);
6980112f8c4SRick Parrish 		return (what);
6990112f8c4SRick Parrish 	}
7000112f8c4SRick Parrish 
7010112f8c4SRick Parrish 	return (mq_close(handle));
7020112f8c4SRick Parrish }
7030112f8c4SRick Parrish 
7040112f8c4SRick Parrish static void
usage(FILE * file)7050112f8c4SRick Parrish usage(FILE *file)
7060112f8c4SRick Parrish {
7070112f8c4SRick Parrish 	fprintf(file,
7080112f8c4SRick Parrish 	    "usage:\n\tposixmqcontrol [rm|info|recv] -q <queue>\n"
7090112f8c4SRick Parrish 	    "\tposixmqcontrol create -q <queue> -s <maxsize> -d <maxdepth> "
7100112f8c4SRick Parrish 	    "[ -m <mode> ] [ -b <block> ] [-u <uid> ] [ -g <gid> ]\n"
7110112f8c4SRick Parrish 	    "\tposixmqcontrol send -q <queue> -c <content> "
7120112f8c4SRick Parrish 	    "[-p <priority> ]\n");
7130112f8c4SRick Parrish }
7140112f8c4SRick Parrish 
7150112f8c4SRick Parrish /* end of SUBCOMMANDS */
7160112f8c4SRick Parrish 
7170112f8c4SRick Parrish #define _countof(arg) ((sizeof(arg)) / (sizeof((arg)[0])))
7180112f8c4SRick Parrish 
7190112f8c4SRick Parrish /* convert an errno style error code to a sysexits code. */
7200112f8c4SRick Parrish static int
grace(int err_number)7210112f8c4SRick Parrish grace(int err_number)
7220112f8c4SRick Parrish {
7230112f8c4SRick Parrish 	static const int xlat[][2] = {
7240112f8c4SRick Parrish 		/* generally means the mqueuefs driver is not loaded. */
7250112f8c4SRick Parrish 		{ENOSYS, EX_UNAVAILABLE},
7260112f8c4SRick Parrish 		/* no such queue name. */
7270112f8c4SRick Parrish 		{ENOENT, EX_OSFILE},
7280112f8c4SRick Parrish 		{EIO, EX_IOERR},
7290112f8c4SRick Parrish 		{ENODEV, EX_IOERR},
7300112f8c4SRick Parrish 		{ENOTSUP, EX_TEMPFAIL},
7310112f8c4SRick Parrish 		{EAGAIN, EX_IOERR},
7320112f8c4SRick Parrish 		{EPERM, EX_NOPERM},
7330112f8c4SRick Parrish 		{EACCES, EX_NOPERM},
7340112f8c4SRick Parrish 		{0, EX_OK}
7350112f8c4SRick Parrish 	};
7360112f8c4SRick Parrish 
7370112f8c4SRick Parrish 	for (unsigned i = 0; i < _countof(xlat); i++) {
7380112f8c4SRick Parrish 		if (xlat[i][0] == err_number)
7390112f8c4SRick Parrish 			return (xlat[i][1]);
7400112f8c4SRick Parrish 	}
7410112f8c4SRick Parrish 
7420112f8c4SRick Parrish 	return (EX_OSERR);
7430112f8c4SRick Parrish }
7440112f8c4SRick Parrish 
7450112f8c4SRick Parrish /* OPTIONS tables */
7460112f8c4SRick Parrish 
7470112f8c4SRick Parrish /* careful: these 'names' arrays must be terminated by a null pointer. */
7480112f8c4SRick Parrish static const char *names_queue[] = {"-q", "--queue", "-t", "--topic", NULL};
7490112f8c4SRick Parrish static const struct Option option_queue = {
7500112f8c4SRick Parrish 	.pattern = names_queue,
7510112f8c4SRick Parrish 	.parse = parse_queue,
7520112f8c4SRick Parrish 	.validate = validate_queue};
7530112f8c4SRick Parrish static const struct Option option_single_queue = {
7540112f8c4SRick Parrish 	.pattern = names_queue,
7550112f8c4SRick Parrish 	.parse = parse_single_queue,
7560112f8c4SRick Parrish 	.validate = validate_single_queue};
7570112f8c4SRick Parrish static const char *names_depth[] = {"-d", "--depth", "--maxmsg", NULL};
7580112f8c4SRick Parrish static const struct Option option_depth = {
7590112f8c4SRick Parrish 	.pattern = names_depth,
7600112f8c4SRick Parrish 	.parse = parse_depth,
7610112f8c4SRick Parrish 	.validate = validate_always_true};
7620112f8c4SRick Parrish static const char *names_size[] = {"-s", "--size", "--msgsize", NULL};
7630112f8c4SRick Parrish static const struct Option option_size = {
7640112f8c4SRick Parrish 	.pattern = names_size,
7650112f8c4SRick Parrish 	.parse = parse_size,
7660112f8c4SRick Parrish 	.validate = validate_always_true};
7670112f8c4SRick Parrish static const char *names_block[] = {"-b", "--block", NULL};
7680112f8c4SRick Parrish static const struct Option option_block = {
7690112f8c4SRick Parrish 	.pattern = names_block,
7700112f8c4SRick Parrish 	.parse = parse_block,
7710112f8c4SRick Parrish 	.validate = validate_always_true};
7720112f8c4SRick Parrish static const char *names_content[] = {
7730112f8c4SRick Parrish 	"-c", "--content", "--data", "--message", NULL};
7740112f8c4SRick Parrish static const struct Option option_content = {
7750112f8c4SRick Parrish 	.pattern = names_content,
7760112f8c4SRick Parrish 	.parse = parse_content,
7770112f8c4SRick Parrish 	.validate = validate_content};
7780112f8c4SRick Parrish static const char *names_priority[] = {"-p", "--priority", NULL};
7790112f8c4SRick Parrish static const struct Option option_priority = {
7800112f8c4SRick Parrish 	.pattern = names_priority,
7810112f8c4SRick Parrish 	.parse = parse_priority,
7820112f8c4SRick Parrish 	.validate = validate_always_true};
7830112f8c4SRick Parrish static const char *names_mode[] = {"-m", "--mode", NULL};
7840112f8c4SRick Parrish static const struct Option option_mode = {
7850112f8c4SRick Parrish 	.pattern = names_mode,
7860112f8c4SRick Parrish 	.parse = parse_mode,
7870112f8c4SRick Parrish 	.validate = validate_always_true};
7880112f8c4SRick Parrish static const char *names_group[] = {"-g", "--gid", NULL};
7890112f8c4SRick Parrish static const struct Option option_group = {
7900112f8c4SRick Parrish 	.pattern = names_group,
7910112f8c4SRick Parrish 	.parse = parse_group,
7920112f8c4SRick Parrish 	.validate = validate_always_true};
7930112f8c4SRick Parrish static const char *names_user[] = {"-u", "--uid", NULL};
7940112f8c4SRick Parrish static const struct Option option_user = {
7950112f8c4SRick Parrish 	.pattern = names_user,
7960112f8c4SRick Parrish 	.parse = parse_user,
7970112f8c4SRick Parrish 	.validate = validate_always_true};
7980112f8c4SRick Parrish 
7990112f8c4SRick Parrish /* careful: these arrays must be terminated by a null pointer. */
8000112f8c4SRick Parrish #ifdef __FreeBSD__
8010112f8c4SRick Parrish static const struct Option *create_options[] = {
8020112f8c4SRick Parrish 	&option_queue, &option_depth, &option_size, &option_block,
8030112f8c4SRick Parrish 	&option_mode, &option_group, &option_user, NULL};
8040112f8c4SRick Parrish #else  /* !__FreeBSD__ */
8050112f8c4SRick Parrish static const struct Option *create_options[] = {
8060112f8c4SRick Parrish 	&option_queue, &option_depth, &option_size, &option_block,
8070112f8c4SRick Parrish 	&option_mode, NULL};
8080112f8c4SRick Parrish #endif /* __FreeBSD__ */
8090112f8c4SRick Parrish static const struct Option *info_options[] = {&option_queue, NULL};
8100112f8c4SRick Parrish static const struct Option *unlink_options[] = {&option_queue, NULL};
8110112f8c4SRick Parrish static const struct Option *recv_options[] = {&option_single_queue, NULL};
8120112f8c4SRick Parrish static const struct Option *send_options[] = {
8130112f8c4SRick Parrish 	&option_queue, &option_content, &option_priority, NULL};
8140112f8c4SRick Parrish 
8150112f8c4SRick Parrish int
main(int argc,const char * argv[])8160112f8c4SRick Parrish main(int argc, const char *argv[])
8170112f8c4SRick Parrish {
8180112f8c4SRick Parrish 	STAILQ_INIT(&queues);
8190112f8c4SRick Parrish 	STAILQ_INIT(&contents);
8200112f8c4SRick Parrish 
8210112f8c4SRick Parrish 	if (argc > 1) {
8220112f8c4SRick Parrish 		const char *verb = argv[1];
8230112f8c4SRick Parrish 		int index = 2;
8240112f8c4SRick Parrish 
8250112f8c4SRick Parrish 		if (strcmp("create", verb) == 0 || strcmp("attr", verb) == 0) {
8260112f8c4SRick Parrish 			parse_options(index, argc, argv, create_options);
8270112f8c4SRick Parrish 			if (validate_options(create_options)) {
8280112f8c4SRick Parrish 				int worst = 0;
8290112f8c4SRick Parrish 				struct element *itq;
8300112f8c4SRick Parrish 
8310112f8c4SRick Parrish 				STAILQ_FOREACH(itq, &queues, links) {
8320112f8c4SRick Parrish 					const char *queue = itq->text;
8330112f8c4SRick Parrish 
8340112f8c4SRick Parrish 					int result = create(queue, creation);
8350112f8c4SRick Parrish 					if (result != 0)
8360112f8c4SRick Parrish 						worst = result;
8370112f8c4SRick Parrish 				}
8380112f8c4SRick Parrish 
8390112f8c4SRick Parrish 				return (grace(worst));
8400112f8c4SRick Parrish 			}
8410112f8c4SRick Parrish 
8420112f8c4SRick Parrish 			return (EX_USAGE);
8430112f8c4SRick Parrish 		} else if (strcmp("info", verb) == 0 || strcmp("cat", verb) == 0) {
8440112f8c4SRick Parrish 			parse_options(index, argc, argv, info_options);
8450112f8c4SRick Parrish 			if (validate_options(info_options)) {
8460112f8c4SRick Parrish 				int worst = 0;
8470112f8c4SRick Parrish 				struct element *itq;
8480112f8c4SRick Parrish 
8490112f8c4SRick Parrish 				STAILQ_FOREACH(itq, &queues, links) {
8500112f8c4SRick Parrish 					const char *queue = itq->text;
8510112f8c4SRick Parrish 					int result = info(queue);
8520112f8c4SRick Parrish 
8530112f8c4SRick Parrish 					if (result != 0)
8540112f8c4SRick Parrish 						worst = result;
8550112f8c4SRick Parrish 				}
8560112f8c4SRick Parrish 
8570112f8c4SRick Parrish 				return (grace(worst));
8580112f8c4SRick Parrish 			}
8590112f8c4SRick Parrish 
8600112f8c4SRick Parrish 			return (EX_USAGE);
8610112f8c4SRick Parrish 		} else if (strcmp("send", verb) == 0) {
8620112f8c4SRick Parrish 			parse_options(index, argc, argv, send_options);
8630112f8c4SRick Parrish 			if (validate_options(send_options)) {
8640112f8c4SRick Parrish 				int worst = 0;
8650112f8c4SRick Parrish 				struct element *itq;
8660112f8c4SRick Parrish 
8670112f8c4SRick Parrish 				STAILQ_FOREACH(itq, &queues, links) {
8680112f8c4SRick Parrish 					const char *queue = itq->text;
8690112f8c4SRick Parrish 					struct element *itc;
8700112f8c4SRick Parrish 
8710112f8c4SRick Parrish 					STAILQ_FOREACH(itc, &contents, links) {
8720112f8c4SRick Parrish 						const char *content = itc->text;
8730112f8c4SRick Parrish 						int result = send(queue, content, priority);
8740112f8c4SRick Parrish 
8750112f8c4SRick Parrish 						if (result != 0)
8760112f8c4SRick Parrish 							worst = result;
8770112f8c4SRick Parrish 					}
8780112f8c4SRick Parrish 				}
8790112f8c4SRick Parrish 
8800112f8c4SRick Parrish 				return (grace(worst));
8810112f8c4SRick Parrish 			}
8820112f8c4SRick Parrish 			return (EX_USAGE);
8830112f8c4SRick Parrish 		} else if (strcmp("recv", verb) == 0 ||
8840112f8c4SRick Parrish 		    strcmp("receive", verb) == 0) {
8850112f8c4SRick Parrish 			parse_options(index, argc, argv, recv_options);
8860112f8c4SRick Parrish 			if (validate_options(recv_options)) {
8870112f8c4SRick Parrish 				const char *queue = STAILQ_FIRST(&queues)->text;
8880112f8c4SRick Parrish 				int worst = recv(queue);
8890112f8c4SRick Parrish 
8900112f8c4SRick Parrish 				return (grace(worst));
8910112f8c4SRick Parrish 			}
8920112f8c4SRick Parrish 
8930112f8c4SRick Parrish 			return (EX_USAGE);
8940112f8c4SRick Parrish 		} else if (strcmp("unlink", verb) == 0 ||
8950112f8c4SRick Parrish 		    strcmp("rm", verb) == 0) {
8960112f8c4SRick Parrish 			parse_options(index, argc, argv, unlink_options);
8970112f8c4SRick Parrish 			if (validate_options(unlink_options)) {
8980112f8c4SRick Parrish 				int worst = 0;
8990112f8c4SRick Parrish 				struct element *itq;
9000112f8c4SRick Parrish 
9010112f8c4SRick Parrish 				STAILQ_FOREACH(itq, &queues, links) {
9020112f8c4SRick Parrish 					const char *queue = itq->text;
9030112f8c4SRick Parrish 					int result = rm(queue);
9040112f8c4SRick Parrish 
9050112f8c4SRick Parrish 					if (result != 0)
9060112f8c4SRick Parrish 						worst = result;
9070112f8c4SRick Parrish 				}
9080112f8c4SRick Parrish 
9090112f8c4SRick Parrish 				return (grace(worst));
9100112f8c4SRick Parrish 			}
9110112f8c4SRick Parrish 
9120112f8c4SRick Parrish 			return (EX_USAGE);
9130112f8c4SRick Parrish 		} else if (strcmp("help", verb) == 0) {
9140112f8c4SRick Parrish 			usage(stdout);
9150112f8c4SRick Parrish 			return (EX_OK);
9160112f8c4SRick Parrish 		} else {
9170112f8c4SRick Parrish 			warnx("Unknown verb [%s]", verb);
9180112f8c4SRick Parrish 			return (EX_USAGE);
9190112f8c4SRick Parrish 		}
9200112f8c4SRick Parrish 	}
9210112f8c4SRick Parrish 
9220112f8c4SRick Parrish 	usage(stdout);
9230112f8c4SRick Parrish 	return (EX_OK);
9240112f8c4SRick Parrish }
925