xref: /freebsd/usr.bin/posixmqcontrol/posixmqcontrol.c (revision 0112f8c4a88e75342bdb6b9815fa220c5f645aa0)
1*0112f8c4SRick Parrish /*-
2*0112f8c4SRick Parrish  * SPDX-License-Identifier: BSD-2-Clause
3*0112f8c4SRick Parrish  *
4*0112f8c4SRick Parrish  * Copyright (c) 2024 Rick Parrish <unitrunker@unitrunker.net>.
5*0112f8c4SRick Parrish  *
6*0112f8c4SRick Parrish  * Redistribution and use in source and binary forms, with or without
7*0112f8c4SRick Parrish  * modification, are permitted provided that the following conditions
8*0112f8c4SRick Parrish  * are met:
9*0112f8c4SRick Parrish  * 1. Redistributions of source code must retain the above copyright
10*0112f8c4SRick Parrish  *	notice, this list of conditions and the following disclaimer.
11*0112f8c4SRick Parrish  * 2. Redistributions in binary form must reproduce the above copyright
12*0112f8c4SRick Parrish  *	notice, this list of conditions and the following disclaimer in the
13*0112f8c4SRick Parrish  *	documentation and/or other materials provided with the distribution.
14*0112f8c4SRick Parrish  *
15*0112f8c4SRick Parrish  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*0112f8c4SRick Parrish  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*0112f8c4SRick Parrish  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*0112f8c4SRick Parrish  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*0112f8c4SRick Parrish  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*0112f8c4SRick Parrish  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*0112f8c4SRick Parrish  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*0112f8c4SRick Parrish  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*0112f8c4SRick Parrish  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*0112f8c4SRick Parrish  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*0112f8c4SRick Parrish  * SUCH DAMAGE.
26*0112f8c4SRick Parrish  */
27*0112f8c4SRick Parrish 
28*0112f8c4SRick Parrish #include <sys/queue.h>
29*0112f8c4SRick Parrish #include <sys/stat.h>
30*0112f8c4SRick Parrish #include <err.h>
31*0112f8c4SRick Parrish #include <errno.h>
32*0112f8c4SRick Parrish #include <fcntl.h>
33*0112f8c4SRick Parrish #include <grp.h>
34*0112f8c4SRick Parrish #include <limits.h>
35*0112f8c4SRick Parrish #include <mqueue.h>
36*0112f8c4SRick Parrish #include <pwd.h>
37*0112f8c4SRick Parrish #include <stdbool.h>
38*0112f8c4SRick Parrish #include <stdio.h>
39*0112f8c4SRick Parrish #include <stdlib.h>
40*0112f8c4SRick Parrish #include <string.h>
41*0112f8c4SRick Parrish #include <sysexits.h>
42*0112f8c4SRick Parrish #include <unistd.h>
43*0112f8c4SRick Parrish 
44*0112f8c4SRick Parrish struct Creation {
45*0112f8c4SRick Parrish 	/* true if the queue exists. */
46*0112f8c4SRick Parrish 	bool exists;
47*0112f8c4SRick Parrish 	/* true if a mode value was specified. */
48*0112f8c4SRick Parrish 	bool set_mode;
49*0112f8c4SRick Parrish 	/* access mode with rwx permission bits. */
50*0112f8c4SRick Parrish 	mode_t mode;
51*0112f8c4SRick Parrish 	/* maximum queue depth. default to an invalid depth. */
52*0112f8c4SRick Parrish 	long depth;
53*0112f8c4SRick Parrish 	/* maximum message size. default to an invalid size. */
54*0112f8c4SRick Parrish 	long size;
55*0112f8c4SRick Parrish 	/* true for blocking I/O and false for non-blocking I/O. */
56*0112f8c4SRick Parrish 	bool block;
57*0112f8c4SRick Parrish 	/* true if a group ID was specified. */
58*0112f8c4SRick Parrish 	bool set_group;
59*0112f8c4SRick Parrish 	/* group ID. */
60*0112f8c4SRick Parrish 	gid_t group;
61*0112f8c4SRick Parrish 	/* true if a user ID was specified. */
62*0112f8c4SRick Parrish 	bool set_user;
63*0112f8c4SRick Parrish 	/* user ID. */
64*0112f8c4SRick Parrish 	uid_t user;
65*0112f8c4SRick Parrish };
66*0112f8c4SRick Parrish 
67*0112f8c4SRick Parrish struct element {
68*0112f8c4SRick Parrish 	STAILQ_ENTRY(element) links;
69*0112f8c4SRick Parrish 	const char *text;
70*0112f8c4SRick Parrish };
71*0112f8c4SRick Parrish 
72*0112f8c4SRick Parrish static struct element *
malloc_element(const char * context)73*0112f8c4SRick Parrish malloc_element(const char *context)
74*0112f8c4SRick Parrish {
75*0112f8c4SRick Parrish 	struct element *item = malloc(sizeof(struct element));
76*0112f8c4SRick Parrish 
77*0112f8c4SRick Parrish 	if (item == NULL)
78*0112f8c4SRick Parrish 		/* the only non-EX_* prefixed exit code. */
79*0112f8c4SRick Parrish 		err(1, "malloc(%s)", context);
80*0112f8c4SRick Parrish 	return (item);
81*0112f8c4SRick Parrish }
82*0112f8c4SRick Parrish 
83*0112f8c4SRick Parrish static STAILQ_HEAD(tqh, element)
84*0112f8c4SRick Parrish 	queues = STAILQ_HEAD_INITIALIZER(queues),
85*0112f8c4SRick Parrish 	contents = STAILQ_HEAD_INITIALIZER(contents);
86*0112f8c4SRick Parrish /* send defaults to medium priority. */
87*0112f8c4SRick Parrish static long priority = MQ_PRIO_MAX / 2;
88*0112f8c4SRick Parrish static struct Creation creation = {
89*0112f8c4SRick Parrish 	.exists = false,
90*0112f8c4SRick Parrish 	.set_mode = false,
91*0112f8c4SRick Parrish 	.mode = 0755,
92*0112f8c4SRick Parrish 	.depth = -1,
93*0112f8c4SRick Parrish 	.size = -1,
94*0112f8c4SRick Parrish 	.block = true,
95*0112f8c4SRick Parrish 	.set_group = false,
96*0112f8c4SRick Parrish 	.group = 0,
97*0112f8c4SRick Parrish 	.set_user = false,
98*0112f8c4SRick Parrish 	.user = 0
99*0112f8c4SRick Parrish };
100*0112f8c4SRick Parrish static const mqd_t fail = (mqd_t)-1;
101*0112f8c4SRick Parrish static const mode_t accepted_mode_bits =
102*0112f8c4SRick Parrish     S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISTXT;
103*0112f8c4SRick Parrish 
104*0112f8c4SRick Parrish /* OPTIONS parsing utilitarian */
105*0112f8c4SRick Parrish 
106*0112f8c4SRick Parrish static void
parse_long(const char * text,long * capture,const char * knob,const char * name)107*0112f8c4SRick Parrish parse_long(const char *text, long *capture, const char *knob, const char *name)
108*0112f8c4SRick Parrish {
109*0112f8c4SRick Parrish 	char *cursor = NULL;
110*0112f8c4SRick Parrish 	long value = strtol(text, &cursor, 10);
111*0112f8c4SRick Parrish 
112*0112f8c4SRick Parrish 	if (cursor > text && *cursor == 0) {
113*0112f8c4SRick Parrish 		*capture = value;
114*0112f8c4SRick Parrish 	} else {
115*0112f8c4SRick Parrish 		warnx("%s %s invalid format [%s].", knob, name, text);
116*0112f8c4SRick Parrish 	}
117*0112f8c4SRick Parrish }
118*0112f8c4SRick Parrish 
119*0112f8c4SRick Parrish static void
parse_unsigned(const char * text,bool * set,unsigned * capture,const char * knob,const char * name)120*0112f8c4SRick Parrish parse_unsigned(const char *text, bool *set,
121*0112f8c4SRick Parrish    unsigned *capture, const char *knob, const char *name)
122*0112f8c4SRick Parrish {
123*0112f8c4SRick Parrish 	char *cursor = NULL;
124*0112f8c4SRick Parrish 	unsigned value = strtoul(text, &cursor, 8);
125*0112f8c4SRick Parrish 
126*0112f8c4SRick Parrish 	if (cursor > text && *cursor == 0) {
127*0112f8c4SRick Parrish 		*set = true;
128*0112f8c4SRick Parrish 		*capture = value;
129*0112f8c4SRick Parrish 	} else {
130*0112f8c4SRick Parrish 		warnx("%s %s format [%s] ignored.", knob, name, text);
131*0112f8c4SRick Parrish 	}
132*0112f8c4SRick Parrish }
133*0112f8c4SRick Parrish 
134*0112f8c4SRick Parrish static bool
sane_queue(const char * queue)135*0112f8c4SRick Parrish sane_queue(const char *queue)
136*0112f8c4SRick Parrish {
137*0112f8c4SRick Parrish 	int size = 0;
138*0112f8c4SRick Parrish 
139*0112f8c4SRick Parrish 	if (queue[size] != '/') {
140*0112f8c4SRick Parrish 		warnx("queue name [%-.*s] must start with '/'.", NAME_MAX, queue);
141*0112f8c4SRick Parrish 		return (false);
142*0112f8c4SRick Parrish 	}
143*0112f8c4SRick Parrish 
144*0112f8c4SRick Parrish 	for (size++; queue[size] != 0 && size < NAME_MAX; size++) {
145*0112f8c4SRick Parrish 		if (queue[size] == '/') {
146*0112f8c4SRick Parrish 			warnx("queue name [%-.*s] - only one '/' permitted.",
147*0112f8c4SRick Parrish 			    NAME_MAX, queue);
148*0112f8c4SRick Parrish 			return (false);
149*0112f8c4SRick Parrish 		}
150*0112f8c4SRick Parrish 	}
151*0112f8c4SRick Parrish 
152*0112f8c4SRick Parrish 	if (size == NAME_MAX && queue[size] != 0) {
153*0112f8c4SRick Parrish 		warnx("queue name [%-.*s...] may not be longer than %d.",
154*0112f8c4SRick Parrish 		    NAME_MAX, queue, NAME_MAX);
155*0112f8c4SRick Parrish 		return (false);
156*0112f8c4SRick Parrish 	}
157*0112f8c4SRick Parrish 	return (true);
158*0112f8c4SRick Parrish }
159*0112f8c4SRick Parrish 
160*0112f8c4SRick Parrish /* OPTIONS parsers */
161*0112f8c4SRick Parrish 
162*0112f8c4SRick Parrish static void
parse_block(const char * text)163*0112f8c4SRick Parrish parse_block(const char *text)
164*0112f8c4SRick Parrish {
165*0112f8c4SRick Parrish 	if (strcmp(text, "true") == 0 || strcmp(text, "yes") == 0) {
166*0112f8c4SRick Parrish 		creation.block = true;
167*0112f8c4SRick Parrish 	} else if (strcmp(text, "false") == 0 || strcmp(text, "no") == 0) {
168*0112f8c4SRick Parrish 		creation.block = false;
169*0112f8c4SRick Parrish 	} else {
170*0112f8c4SRick Parrish 		char *cursor = NULL;
171*0112f8c4SRick Parrish 		long value = strtol(text, &cursor, 10);
172*0112f8c4SRick Parrish 		if (cursor > text) {
173*0112f8c4SRick Parrish 			creation.block = value != 0;
174*0112f8c4SRick Parrish 		} else {
175*0112f8c4SRick Parrish 			warnx("bad -b block format [%s] ignored.", text);
176*0112f8c4SRick Parrish 		}
177*0112f8c4SRick Parrish 	}
178*0112f8c4SRick Parrish }
179*0112f8c4SRick Parrish 
180*0112f8c4SRick Parrish static void
parse_content(const char * content)181*0112f8c4SRick Parrish parse_content(const char *content)
182*0112f8c4SRick Parrish {
183*0112f8c4SRick Parrish 	struct element *n1 = malloc_element("content");
184*0112f8c4SRick Parrish 
185*0112f8c4SRick Parrish 	n1->text = content;
186*0112f8c4SRick Parrish 	STAILQ_INSERT_TAIL(&contents, n1, links);
187*0112f8c4SRick Parrish }
188*0112f8c4SRick Parrish 
189*0112f8c4SRick Parrish static void
parse_depth(const char * text)190*0112f8c4SRick Parrish parse_depth(const char *text)
191*0112f8c4SRick Parrish {
192*0112f8c4SRick Parrish 	parse_long(text, &creation.depth, "-d", "depth");
193*0112f8c4SRick Parrish }
194*0112f8c4SRick Parrish 
195*0112f8c4SRick Parrish static void
parse_group(const char * text)196*0112f8c4SRick Parrish parse_group(const char *text)
197*0112f8c4SRick Parrish {
198*0112f8c4SRick Parrish 	struct group *entry = getgrnam(text);
199*0112f8c4SRick Parrish 
200*0112f8c4SRick Parrish 	if (entry == NULL) {
201*0112f8c4SRick Parrish 		parse_unsigned(text, &creation.set_group,
202*0112f8c4SRick Parrish 		    &creation.group, "-g", "group");
203*0112f8c4SRick Parrish 	} else {
204*0112f8c4SRick Parrish 		creation.set_group = true;
205*0112f8c4SRick Parrish 		creation.group = entry->gr_gid;
206*0112f8c4SRick Parrish 	}
207*0112f8c4SRick Parrish }
208*0112f8c4SRick Parrish 
209*0112f8c4SRick Parrish static void
parse_mode(const char * text)210*0112f8c4SRick Parrish parse_mode(const char *text)
211*0112f8c4SRick Parrish {
212*0112f8c4SRick Parrish 	char *cursor = NULL;
213*0112f8c4SRick Parrish 	long value = strtol(text, &cursor, 8);
214*0112f8c4SRick Parrish 
215*0112f8c4SRick Parrish 	// verify only accepted mode bits are set.
216*0112f8c4SRick Parrish 	if (cursor > text && *cursor == 0 && (value & accepted_mode_bits) == value) {
217*0112f8c4SRick Parrish 		creation.set_mode = true;
218*0112f8c4SRick Parrish 		creation.mode = (mode_t)value;
219*0112f8c4SRick Parrish 	} else {
220*0112f8c4SRick Parrish 		warnx("impossible -m mode value [%s] ignored.", text);
221*0112f8c4SRick Parrish 	}
222*0112f8c4SRick Parrish }
223*0112f8c4SRick Parrish 
224*0112f8c4SRick Parrish static void
parse_priority(const char * text)225*0112f8c4SRick Parrish parse_priority(const char *text)
226*0112f8c4SRick Parrish {
227*0112f8c4SRick Parrish 	char *cursor = NULL;
228*0112f8c4SRick Parrish 	long value = strtol(text, &cursor, 10);
229*0112f8c4SRick Parrish 
230*0112f8c4SRick Parrish 	if (cursor > text && *cursor == 0) {
231*0112f8c4SRick Parrish 		if (value >= 0 && value < MQ_PRIO_MAX) {
232*0112f8c4SRick Parrish 			priority = value;
233*0112f8c4SRick Parrish 		} else {
234*0112f8c4SRick Parrish 			warnx("bad -p priority range [%s] ignored.", text);
235*0112f8c4SRick Parrish 		}
236*0112f8c4SRick Parrish 	} else {
237*0112f8c4SRick Parrish 		warnx("bad -p priority format [%s] ignored.", text);
238*0112f8c4SRick Parrish 	}
239*0112f8c4SRick Parrish }
240*0112f8c4SRick Parrish 
241*0112f8c4SRick Parrish static void
parse_queue(const char * queue)242*0112f8c4SRick Parrish parse_queue(const char *queue)
243*0112f8c4SRick Parrish {
244*0112f8c4SRick Parrish 	if (sane_queue(queue)) {
245*0112f8c4SRick Parrish 		struct element *n1 = malloc_element("queue name");
246*0112f8c4SRick Parrish 
247*0112f8c4SRick Parrish 		n1->text = queue;
248*0112f8c4SRick Parrish 		STAILQ_INSERT_TAIL(&queues, n1, links);
249*0112f8c4SRick Parrish 	}
250*0112f8c4SRick Parrish }
251*0112f8c4SRick Parrish 
252*0112f8c4SRick Parrish static void
parse_single_queue(const char * queue)253*0112f8c4SRick Parrish parse_single_queue(const char *queue)
254*0112f8c4SRick Parrish {
255*0112f8c4SRick Parrish 	if (sane_queue(queue)) {
256*0112f8c4SRick Parrish 		if (STAILQ_EMPTY(&queues)) {
257*0112f8c4SRick Parrish 			struct element *n1 = malloc_element("queue name");
258*0112f8c4SRick Parrish 
259*0112f8c4SRick Parrish 			n1->text = queue;
260*0112f8c4SRick Parrish 			STAILQ_INSERT_TAIL(&queues, n1, links);
261*0112f8c4SRick Parrish 		} else
262*0112f8c4SRick Parrish 			warnx("ignoring extra -q queue [%s].", queue);
263*0112f8c4SRick Parrish 	}
264*0112f8c4SRick Parrish }
265*0112f8c4SRick Parrish 
266*0112f8c4SRick Parrish static void
parse_size(const char * text)267*0112f8c4SRick Parrish parse_size(const char *text)
268*0112f8c4SRick Parrish {
269*0112f8c4SRick Parrish 	parse_long(text, &creation.size, "-s", "size");
270*0112f8c4SRick Parrish }
271*0112f8c4SRick Parrish 
272*0112f8c4SRick Parrish static void
parse_user(const char * text)273*0112f8c4SRick Parrish parse_user(const char *text)
274*0112f8c4SRick Parrish {
275*0112f8c4SRick Parrish 	struct passwd *entry = getpwnam(text);
276*0112f8c4SRick Parrish 	if (entry == NULL) {
277*0112f8c4SRick Parrish 		parse_unsigned(text, &creation.set_user,
278*0112f8c4SRick Parrish 		    &creation.user, "-u", "user");
279*0112f8c4SRick Parrish 	} else {
280*0112f8c4SRick Parrish 		creation.set_user = true;
281*0112f8c4SRick Parrish 		creation.user = entry->pw_uid;
282*0112f8c4SRick Parrish 	}
283*0112f8c4SRick Parrish }
284*0112f8c4SRick Parrish 
285*0112f8c4SRick Parrish /* OPTIONS validators */
286*0112f8c4SRick Parrish 
287*0112f8c4SRick Parrish static bool
validate_always_true(void)288*0112f8c4SRick Parrish validate_always_true(void)
289*0112f8c4SRick Parrish {
290*0112f8c4SRick Parrish 	return (true);
291*0112f8c4SRick Parrish }
292*0112f8c4SRick Parrish 
293*0112f8c4SRick Parrish static bool
validate_content(void)294*0112f8c4SRick Parrish validate_content(void)
295*0112f8c4SRick Parrish {
296*0112f8c4SRick Parrish 	bool valid = !STAILQ_EMPTY(&contents);
297*0112f8c4SRick Parrish 
298*0112f8c4SRick Parrish 	if (!valid)
299*0112f8c4SRick Parrish 		warnx("no content to send.");
300*0112f8c4SRick Parrish 	return (valid);
301*0112f8c4SRick Parrish }
302*0112f8c4SRick Parrish 
303*0112f8c4SRick Parrish static bool
validate_depth(void)304*0112f8c4SRick Parrish validate_depth(void)
305*0112f8c4SRick Parrish {
306*0112f8c4SRick Parrish 	bool valid = creation.exists || creation.depth > 0;
307*0112f8c4SRick Parrish 
308*0112f8c4SRick Parrish 	if (!valid)
309*0112f8c4SRick Parrish 		warnx("-d maximum queue depth not provided.");
310*0112f8c4SRick Parrish 	return (valid);
311*0112f8c4SRick Parrish }
312*0112f8c4SRick Parrish 
313*0112f8c4SRick Parrish static bool
validate_queue(void)314*0112f8c4SRick Parrish validate_queue(void)
315*0112f8c4SRick Parrish {
316*0112f8c4SRick Parrish 	bool valid = !STAILQ_EMPTY(&queues);
317*0112f8c4SRick Parrish 
318*0112f8c4SRick Parrish 	if (!valid)
319*0112f8c4SRick Parrish 		warnx("missing -q, or no sane queue name given.");
320*0112f8c4SRick Parrish 	return (valid);
321*0112f8c4SRick Parrish }
322*0112f8c4SRick Parrish 
323*0112f8c4SRick Parrish static bool
validate_single_queue(void)324*0112f8c4SRick Parrish validate_single_queue(void)
325*0112f8c4SRick Parrish {
326*0112f8c4SRick Parrish 	bool valid = !STAILQ_EMPTY(&queues) &&
327*0112f8c4SRick Parrish 	    STAILQ_NEXT(STAILQ_FIRST(&queues), links) == NULL;
328*0112f8c4SRick Parrish 
329*0112f8c4SRick Parrish 	if (!valid)
330*0112f8c4SRick Parrish 		warnx("expected one queue.");
331*0112f8c4SRick Parrish 	return (valid);
332*0112f8c4SRick Parrish }
333*0112f8c4SRick Parrish 
334*0112f8c4SRick Parrish static bool
validate_size(void)335*0112f8c4SRick Parrish validate_size(void)
336*0112f8c4SRick Parrish {
337*0112f8c4SRick Parrish 	bool valid = creation.exists || creation.size > 0;
338*0112f8c4SRick Parrish 
339*0112f8c4SRick Parrish 	if (!valid)
340*0112f8c4SRick Parrish 		warnx("-s maximum message size not provided.");
341*0112f8c4SRick Parrish 	return (valid);
342*0112f8c4SRick Parrish }
343*0112f8c4SRick Parrish 
344*0112f8c4SRick Parrish /* OPTIONS table handling. */
345*0112f8c4SRick Parrish 
346*0112f8c4SRick Parrish struct Option {
347*0112f8c4SRick Parrish 	/* points to array of string pointers terminated by a null pointer. */
348*0112f8c4SRick Parrish 	const char **pattern;
349*0112f8c4SRick Parrish 	/* parse argument. */
350*0112f8c4SRick Parrish 	void (*parse)(const char *);
351*0112f8c4SRick Parrish 	/*
352*0112f8c4SRick Parrish 	 * displays an error and returns false if this parameter is not valid.
353*0112f8c4SRick Parrish 	 * returns true otherwise.
354*0112f8c4SRick Parrish 	 */
355*0112f8c4SRick Parrish 	bool (*validate)(void);
356*0112f8c4SRick Parrish };
357*0112f8c4SRick Parrish 
358*0112f8c4SRick Parrish /*
359*0112f8c4SRick Parrish  * parse options by table.
360*0112f8c4SRick Parrish  * index - current index into argv list.
361*0112f8c4SRick Parrish  * argc, argv - command line parameters.
362*0112f8c4SRick Parrish  * options - null terminated list of pointers to options.
363*0112f8c4SRick Parrish  */
364*0112f8c4SRick Parrish static void
parse_options(int index,int argc,const char * argv[],const struct Option ** options)365*0112f8c4SRick Parrish parse_options(int index, int argc,
366*0112f8c4SRick Parrish     const char *argv[], const struct Option **options)
367*0112f8c4SRick Parrish {
368*0112f8c4SRick Parrish 	while ((index + 1) < argc) {
369*0112f8c4SRick Parrish 		const struct Option **cursor = options;
370*0112f8c4SRick Parrish 		bool match = false;
371*0112f8c4SRick Parrish 		while (*cursor != NULL && !match) {
372*0112f8c4SRick Parrish 			const struct Option *option = cursor[0];
373*0112f8c4SRick Parrish 			const char **pattern = option->pattern;
374*0112f8c4SRick Parrish 
375*0112f8c4SRick Parrish 			while (*pattern != NULL && !match) {
376*0112f8c4SRick Parrish 				const char *knob = *pattern;
377*0112f8c4SRick Parrish 
378*0112f8c4SRick Parrish 				match = strcmp(knob, argv[index]) == 0;
379*0112f8c4SRick Parrish 				if (!match)
380*0112f8c4SRick Parrish 					pattern++;
381*0112f8c4SRick Parrish 			}
382*0112f8c4SRick Parrish 
383*0112f8c4SRick Parrish 			if (match) {
384*0112f8c4SRick Parrish 				option->parse(argv[index + 1]);
385*0112f8c4SRick Parrish 				index += 2;
386*0112f8c4SRick Parrish 				break;
387*0112f8c4SRick Parrish 			}
388*0112f8c4SRick Parrish 			cursor++;
389*0112f8c4SRick Parrish 		}
390*0112f8c4SRick Parrish 
391*0112f8c4SRick Parrish 		if (!match && index < argc) {
392*0112f8c4SRick Parrish 			warnx("skipping [%s].", argv[index]);
393*0112f8c4SRick Parrish 			index++;
394*0112f8c4SRick Parrish 		}
395*0112f8c4SRick Parrish 	}
396*0112f8c4SRick Parrish 
397*0112f8c4SRick Parrish 	if (index < argc) {
398*0112f8c4SRick Parrish 		warnx("skipping [%s].", argv[index]);
399*0112f8c4SRick Parrish 	}
400*0112f8c4SRick Parrish }
401*0112f8c4SRick Parrish 
402*0112f8c4SRick Parrish /* options - null terminated list of pointers to options. */
403*0112f8c4SRick Parrish static bool
validate_options(const struct Option ** options)404*0112f8c4SRick Parrish validate_options(const struct Option **options)
405*0112f8c4SRick Parrish {
406*0112f8c4SRick Parrish 	bool valid = true;
407*0112f8c4SRick Parrish 
408*0112f8c4SRick Parrish 	while (*options != NULL) {
409*0112f8c4SRick Parrish 		const struct Option *option = options[0];
410*0112f8c4SRick Parrish 
411*0112f8c4SRick Parrish 		if (!option->validate())
412*0112f8c4SRick Parrish 			valid = false;
413*0112f8c4SRick Parrish 		options++;
414*0112f8c4SRick Parrish 	}
415*0112f8c4SRick Parrish 	return (valid);
416*0112f8c4SRick Parrish }
417*0112f8c4SRick Parrish 
418*0112f8c4SRick Parrish /* SUBCOMMANDS */
419*0112f8c4SRick Parrish 
420*0112f8c4SRick Parrish /*
421*0112f8c4SRick Parrish  * queue: name of queue to be created.
422*0112f8c4SRick Parrish  * q_creation: creation parameters (copied by value).
423*0112f8c4SRick Parrish  */
424*0112f8c4SRick Parrish static int
create(const char * queue,struct Creation q_creation)425*0112f8c4SRick Parrish create(const char *queue, struct Creation q_creation)
426*0112f8c4SRick Parrish {
427*0112f8c4SRick Parrish 	int flags = O_RDWR;
428*0112f8c4SRick Parrish 	struct mq_attr stuff = {
429*0112f8c4SRick Parrish 		.mq_curmsgs = 0,
430*0112f8c4SRick Parrish 		.mq_maxmsg = q_creation.depth,
431*0112f8c4SRick Parrish 		.mq_msgsize = q_creation.size,
432*0112f8c4SRick Parrish 		.mq_flags = 0
433*0112f8c4SRick Parrish 	};
434*0112f8c4SRick Parrish 
435*0112f8c4SRick Parrish 	if (!q_creation.block) {
436*0112f8c4SRick Parrish 		flags |= O_NONBLOCK;
437*0112f8c4SRick Parrish 		stuff.mq_flags |= O_NONBLOCK;
438*0112f8c4SRick Parrish 	}
439*0112f8c4SRick Parrish 
440*0112f8c4SRick Parrish 	mqd_t handle = mq_open(queue, flags);
441*0112f8c4SRick Parrish 	q_creation.exists = handle != fail;
442*0112f8c4SRick Parrish 	if (!q_creation.exists) {
443*0112f8c4SRick Parrish 		/*
444*0112f8c4SRick Parrish 		 * apply size and depth checks here.
445*0112f8c4SRick Parrish 		 * if queue exists, we can default to existing depth and size.
446*0112f8c4SRick Parrish 		 * but for a new queue, we require that input.
447*0112f8c4SRick Parrish 		 */
448*0112f8c4SRick Parrish 		if (validate_size() && validate_depth()) {
449*0112f8c4SRick Parrish 			/* no need to re-apply mode. */
450*0112f8c4SRick Parrish 			q_creation.set_mode = false;
451*0112f8c4SRick Parrish 			flags |= O_CREAT;
452*0112f8c4SRick Parrish 			handle = mq_open(queue, flags, q_creation.mode, &stuff);
453*0112f8c4SRick Parrish 		}
454*0112f8c4SRick Parrish 	}
455*0112f8c4SRick Parrish 
456*0112f8c4SRick Parrish 	if (handle == fail) {
457*0112f8c4SRick Parrish 		errno_t what = errno;
458*0112f8c4SRick Parrish 
459*0112f8c4SRick Parrish 		warnc(what, "mq_open(create)");
460*0112f8c4SRick Parrish 		return (what);
461*0112f8c4SRick Parrish 	}
462*0112f8c4SRick Parrish 
463*0112f8c4SRick Parrish #ifdef __FreeBSD__
464*0112f8c4SRick Parrish 	/*
465*0112f8c4SRick Parrish 	 * undocumented.
466*0112f8c4SRick Parrish 	 * See https://bugs.freebsd.org/bugzilla//show_bug.cgi?id=273230
467*0112f8c4SRick Parrish 	 */
468*0112f8c4SRick Parrish 	int fd = mq_getfd_np(handle);
469*0112f8c4SRick Parrish 
470*0112f8c4SRick Parrish 	if (fd < 0) {
471*0112f8c4SRick Parrish 		errno_t what = errno;
472*0112f8c4SRick Parrish 
473*0112f8c4SRick Parrish 		warnc(what, "mq_getfd_np(create)");
474*0112f8c4SRick Parrish 		mq_close(handle);
475*0112f8c4SRick Parrish 		return (what);
476*0112f8c4SRick Parrish 	}
477*0112f8c4SRick Parrish 	struct stat status = {0};
478*0112f8c4SRick Parrish 	int result = fstat(fd, &status);
479*0112f8c4SRick Parrish 	if (result != 0) {
480*0112f8c4SRick Parrish 		errno_t what = errno;
481*0112f8c4SRick Parrish 
482*0112f8c4SRick Parrish 		warnc(what, "fstat(create)");
483*0112f8c4SRick Parrish 		mq_close(handle);
484*0112f8c4SRick Parrish 		return (what);
485*0112f8c4SRick Parrish 	}
486*0112f8c4SRick Parrish 
487*0112f8c4SRick Parrish 	/* do this only if group and / or user given. */
488*0112f8c4SRick Parrish 	if (q_creation.set_group || q_creation.set_user) {
489*0112f8c4SRick Parrish 		q_creation.user =
490*0112f8c4SRick Parrish 		    q_creation.set_user ? q_creation.user : status.st_uid;
491*0112f8c4SRick Parrish 		q_creation.group =
492*0112f8c4SRick Parrish 		    q_creation.set_group ? q_creation.group : status.st_gid;
493*0112f8c4SRick Parrish 		result = fchown(fd, q_creation.user, q_creation.group);
494*0112f8c4SRick Parrish 		if (result != 0) {
495*0112f8c4SRick Parrish 			errno_t what = errno;
496*0112f8c4SRick Parrish 
497*0112f8c4SRick Parrish 			warnc(what, "fchown(create)");
498*0112f8c4SRick Parrish 			mq_close(handle);
499*0112f8c4SRick Parrish 			return (what);
500*0112f8c4SRick Parrish 		}
501*0112f8c4SRick Parrish 	}
502*0112f8c4SRick Parrish 
503*0112f8c4SRick Parrish 	/* do this only if altering mode of an existing queue. */
504*0112f8c4SRick Parrish 	if (q_creation.exists && q_creation.set_mode &&
505*0112f8c4SRick Parrish 	    q_creation.mode != (status.st_mode & accepted_mode_bits)) {
506*0112f8c4SRick Parrish 		result = fchmod(fd, q_creation.mode);
507*0112f8c4SRick Parrish 		if (result != 0) {
508*0112f8c4SRick Parrish 			errno_t what = errno;
509*0112f8c4SRick Parrish 
510*0112f8c4SRick Parrish 			warnc(what, "fchmod(create)");
511*0112f8c4SRick Parrish 			mq_close(handle);
512*0112f8c4SRick Parrish 			return (what);
513*0112f8c4SRick Parrish 		}
514*0112f8c4SRick Parrish 	}
515*0112f8c4SRick Parrish #endif /* __FreeBSD__ */
516*0112f8c4SRick Parrish 
517*0112f8c4SRick Parrish 	return (mq_close(handle));
518*0112f8c4SRick Parrish }
519*0112f8c4SRick Parrish 
520*0112f8c4SRick Parrish /* queue: name of queue to be removed. */
521*0112f8c4SRick Parrish static int
rm(const char * queue)522*0112f8c4SRick Parrish rm(const char *queue)
523*0112f8c4SRick Parrish {
524*0112f8c4SRick Parrish 	int result = mq_unlink(queue);
525*0112f8c4SRick Parrish 
526*0112f8c4SRick Parrish 	if (result != 0) {
527*0112f8c4SRick Parrish 		errno_t what = errno;
528*0112f8c4SRick Parrish 
529*0112f8c4SRick Parrish 		warnc(what, "mq_unlink");
530*0112f8c4SRick Parrish 		return (what);
531*0112f8c4SRick Parrish 	}
532*0112f8c4SRick Parrish 
533*0112f8c4SRick Parrish 	return (result);
534*0112f8c4SRick Parrish }
535*0112f8c4SRick Parrish 
536*0112f8c4SRick Parrish /* Return the display character for non-zero mode. */
537*0112f8c4SRick Parrish static char
dual(mode_t mode,char display)538*0112f8c4SRick Parrish dual(mode_t mode, char display)
539*0112f8c4SRick Parrish {
540*0112f8c4SRick Parrish 	return (mode != 0 ? display : '-');
541*0112f8c4SRick Parrish }
542*0112f8c4SRick Parrish 
543*0112f8c4SRick Parrish /* Select one of four display characters based on mode and modifier. */
544*0112f8c4SRick Parrish static char
quad(mode_t mode,mode_t modifier)545*0112f8c4SRick Parrish quad(mode_t mode, mode_t modifier)
546*0112f8c4SRick Parrish {
547*0112f8c4SRick Parrish 	static const char display[] = "-xSs";
548*0112f8c4SRick Parrish 	unsigned index = 0;
549*0112f8c4SRick Parrish 	if (mode != 0)
550*0112f8c4SRick Parrish 		index += 1;
551*0112f8c4SRick Parrish 	if (modifier)
552*0112f8c4SRick Parrish 		index += 2;
553*0112f8c4SRick Parrish 	return (display[index]);
554*0112f8c4SRick Parrish }
555*0112f8c4SRick Parrish 
556*0112f8c4SRick Parrish /* queue: name of queue to be inspected. */
557*0112f8c4SRick Parrish static int
info(const char * queue)558*0112f8c4SRick Parrish info(const char *queue)
559*0112f8c4SRick Parrish {
560*0112f8c4SRick Parrish 	mqd_t handle = mq_open(queue, O_RDONLY);
561*0112f8c4SRick Parrish 
562*0112f8c4SRick Parrish 	if (handle == fail) {
563*0112f8c4SRick Parrish 		errno_t what = errno;
564*0112f8c4SRick Parrish 
565*0112f8c4SRick Parrish 		warnc(what, "mq_open(info)");
566*0112f8c4SRick Parrish 		return (what);
567*0112f8c4SRick Parrish 	}
568*0112f8c4SRick Parrish 
569*0112f8c4SRick Parrish 	struct mq_attr actual;
570*0112f8c4SRick Parrish 
571*0112f8c4SRick Parrish 	int result = mq_getattr(handle, &actual);
572*0112f8c4SRick Parrish 	if (result != 0) {
573*0112f8c4SRick Parrish 		errno_t what = errno;
574*0112f8c4SRick Parrish 
575*0112f8c4SRick Parrish 		warnc(what, "mq_getattr(info)");
576*0112f8c4SRick Parrish 		return (what);
577*0112f8c4SRick Parrish 	}
578*0112f8c4SRick Parrish 
579*0112f8c4SRick Parrish 	fprintf(stdout,
580*0112f8c4SRick Parrish 	    "queue: '%s'\nQSIZE: %lu\nMSGSIZE: %ld\nMAXMSG: %ld\n"
581*0112f8c4SRick Parrish 	    "CURMSG: %ld\nflags: %03ld\n",
582*0112f8c4SRick Parrish 	    queue, actual.mq_msgsize * actual.mq_curmsgs, actual.mq_msgsize,
583*0112f8c4SRick Parrish 	    actual.mq_maxmsg, actual.mq_curmsgs, actual.mq_flags);
584*0112f8c4SRick Parrish #ifdef __FreeBSD__
585*0112f8c4SRick Parrish 
586*0112f8c4SRick Parrish 	int fd = mq_getfd_np(handle);
587*0112f8c4SRick Parrish 	struct stat status;
588*0112f8c4SRick Parrish 
589*0112f8c4SRick Parrish 	result = fstat(fd, &status);
590*0112f8c4SRick Parrish 	if (result != 0) {
591*0112f8c4SRick Parrish 		warn("fstat(info)");
592*0112f8c4SRick Parrish 	} else {
593*0112f8c4SRick Parrish 		mode_t mode = status.st_mode;
594*0112f8c4SRick Parrish 
595*0112f8c4SRick Parrish 		fprintf(stdout, "UID: %u\nGID: %u\n", status.st_uid, status.st_gid);
596*0112f8c4SRick Parrish 		fprintf(stdout, "MODE: %c%c%c%c%c%c%c%c%c%c\n",
597*0112f8c4SRick Parrish 		    dual(mode & S_ISVTX, 's'),
598*0112f8c4SRick Parrish 		    dual(mode & S_IRUSR, 'r'),
599*0112f8c4SRick Parrish 		    dual(mode & S_IWUSR, 'w'),
600*0112f8c4SRick Parrish 		    quad(mode & S_IXUSR, mode & S_ISUID),
601*0112f8c4SRick Parrish 		    dual(mode & S_IRGRP, 'r'),
602*0112f8c4SRick Parrish 		    dual(mode & S_IWGRP, 'w'),
603*0112f8c4SRick Parrish 		    quad(mode & S_IXGRP, mode & S_ISGID),
604*0112f8c4SRick Parrish 		    dual(mode & S_IROTH, 'r'),
605*0112f8c4SRick Parrish 		    dual(mode & S_IWOTH, 'w'),
606*0112f8c4SRick Parrish 		    dual(mode & S_IXOTH, 'x'));
607*0112f8c4SRick Parrish 	}
608*0112f8c4SRick Parrish #endif /* __FreeBSD__ */
609*0112f8c4SRick Parrish 
610*0112f8c4SRick Parrish 	return (mq_close(handle));
611*0112f8c4SRick Parrish }
612*0112f8c4SRick Parrish 
613*0112f8c4SRick Parrish /* queue: name of queue to drain one message. */
614*0112f8c4SRick Parrish static int
recv(const char * queue)615*0112f8c4SRick Parrish recv(const char *queue)
616*0112f8c4SRick Parrish {
617*0112f8c4SRick Parrish 	mqd_t handle = mq_open(queue, O_RDONLY);
618*0112f8c4SRick Parrish 
619*0112f8c4SRick Parrish 	if (handle == fail) {
620*0112f8c4SRick Parrish 		errno_t what = errno;
621*0112f8c4SRick Parrish 
622*0112f8c4SRick Parrish 		warnc(what, "mq_open(recv)");
623*0112f8c4SRick Parrish 		return (what);
624*0112f8c4SRick Parrish 	}
625*0112f8c4SRick Parrish 
626*0112f8c4SRick Parrish 	struct mq_attr actual;
627*0112f8c4SRick Parrish 
628*0112f8c4SRick Parrish 	int result = mq_getattr(handle, &actual);
629*0112f8c4SRick Parrish 
630*0112f8c4SRick Parrish 	if (result != 0) {
631*0112f8c4SRick Parrish 		errno_t what = errno;
632*0112f8c4SRick Parrish 
633*0112f8c4SRick Parrish 		warnc(what, "mq_attr(recv)");
634*0112f8c4SRick Parrish 		mq_close(handle);
635*0112f8c4SRick Parrish 		return (what);
636*0112f8c4SRick Parrish 	}
637*0112f8c4SRick Parrish 
638*0112f8c4SRick Parrish 	char *text = malloc(actual.mq_msgsize + 1);
639*0112f8c4SRick Parrish 	unsigned q_priority = 0;
640*0112f8c4SRick Parrish 
641*0112f8c4SRick Parrish 	memset(text, 0, actual.mq_msgsize + 1);
642*0112f8c4SRick Parrish 	result = mq_receive(handle, text, actual.mq_msgsize, &q_priority);
643*0112f8c4SRick Parrish 	if (result < 0) {
644*0112f8c4SRick Parrish 		errno_t what = errno;
645*0112f8c4SRick Parrish 
646*0112f8c4SRick Parrish 		warnc(what, "mq_receive");
647*0112f8c4SRick Parrish 		mq_close(handle);
648*0112f8c4SRick Parrish 		return (what);
649*0112f8c4SRick Parrish 	}
650*0112f8c4SRick Parrish 
651*0112f8c4SRick Parrish 	fprintf(stdout, "[%u]: %-*.*s\n", q_priority, result, result, text);
652*0112f8c4SRick Parrish 	return (mq_close(handle));
653*0112f8c4SRick Parrish }
654*0112f8c4SRick Parrish 
655*0112f8c4SRick Parrish /*
656*0112f8c4SRick Parrish  * queue: name of queue to send one message.
657*0112f8c4SRick Parrish  * text: message text.
658*0112f8c4SRick Parrish  * q_priority: message priority in range of 0 to 63.
659*0112f8c4SRick Parrish  */
660*0112f8c4SRick Parrish static int
send(const char * queue,const char * text,unsigned q_priority)661*0112f8c4SRick Parrish send(const char *queue, const char *text, unsigned q_priority)
662*0112f8c4SRick Parrish {
663*0112f8c4SRick Parrish 	mqd_t handle = mq_open(queue, O_WRONLY);
664*0112f8c4SRick Parrish 
665*0112f8c4SRick Parrish 	if (handle == fail) {
666*0112f8c4SRick Parrish 		errno_t what = errno;
667*0112f8c4SRick Parrish 
668*0112f8c4SRick Parrish 		warnc(what, "mq_open(send)");
669*0112f8c4SRick Parrish 		return (what);
670*0112f8c4SRick Parrish 	}
671*0112f8c4SRick Parrish 
672*0112f8c4SRick Parrish 	struct mq_attr actual;
673*0112f8c4SRick Parrish 
674*0112f8c4SRick Parrish 	int result = mq_getattr(handle, &actual);
675*0112f8c4SRick Parrish 
676*0112f8c4SRick Parrish 	if (result != 0) {
677*0112f8c4SRick Parrish 		errno_t what = errno;
678*0112f8c4SRick Parrish 
679*0112f8c4SRick Parrish 		warnc(what, "mq_attr(send)");
680*0112f8c4SRick Parrish 		mq_close(handle);
681*0112f8c4SRick Parrish 		return (what);
682*0112f8c4SRick Parrish 	}
683*0112f8c4SRick Parrish 
684*0112f8c4SRick Parrish 	int size = strlen(text);
685*0112f8c4SRick Parrish 
686*0112f8c4SRick Parrish 	if (size > actual.mq_msgsize) {
687*0112f8c4SRick Parrish 		warnx("truncating message to %ld characters.\n", actual.mq_msgsize);
688*0112f8c4SRick Parrish 		size = actual.mq_msgsize;
689*0112f8c4SRick Parrish 	}
690*0112f8c4SRick Parrish 
691*0112f8c4SRick Parrish 	result = mq_send(handle, text, size, q_priority);
692*0112f8c4SRick Parrish 
693*0112f8c4SRick Parrish 	if (result != 0) {
694*0112f8c4SRick Parrish 		errno_t what = errno;
695*0112f8c4SRick Parrish 
696*0112f8c4SRick Parrish 		warnc(what, "mq_send");
697*0112f8c4SRick Parrish 		mq_close(handle);
698*0112f8c4SRick Parrish 		return (what);
699*0112f8c4SRick Parrish 	}
700*0112f8c4SRick Parrish 
701*0112f8c4SRick Parrish 	return (mq_close(handle));
702*0112f8c4SRick Parrish }
703*0112f8c4SRick Parrish 
704*0112f8c4SRick Parrish static void
usage(FILE * file)705*0112f8c4SRick Parrish usage(FILE *file)
706*0112f8c4SRick Parrish {
707*0112f8c4SRick Parrish 	fprintf(file,
708*0112f8c4SRick Parrish 	    "usage:\n\tposixmqcontrol [rm|info|recv] -q <queue>\n"
709*0112f8c4SRick Parrish 	    "\tposixmqcontrol create -q <queue> -s <maxsize> -d <maxdepth> "
710*0112f8c4SRick Parrish 	    "[ -m <mode> ] [ -b <block> ] [-u <uid> ] [ -g <gid> ]\n"
711*0112f8c4SRick Parrish 	    "\tposixmqcontrol send -q <queue> -c <content> "
712*0112f8c4SRick Parrish 	    "[-p <priority> ]\n");
713*0112f8c4SRick Parrish }
714*0112f8c4SRick Parrish 
715*0112f8c4SRick Parrish /* end of SUBCOMMANDS */
716*0112f8c4SRick Parrish 
717*0112f8c4SRick Parrish #define _countof(arg) ((sizeof(arg)) / (sizeof((arg)[0])))
718*0112f8c4SRick Parrish 
719*0112f8c4SRick Parrish /* convert an errno style error code to a sysexits code. */
720*0112f8c4SRick Parrish static int
grace(int err_number)721*0112f8c4SRick Parrish grace(int err_number)
722*0112f8c4SRick Parrish {
723*0112f8c4SRick Parrish 	static const int xlat[][2] = {
724*0112f8c4SRick Parrish 		/* generally means the mqueuefs driver is not loaded. */
725*0112f8c4SRick Parrish 		{ENOSYS, EX_UNAVAILABLE},
726*0112f8c4SRick Parrish 		/* no such queue name. */
727*0112f8c4SRick Parrish 		{ENOENT, EX_OSFILE},
728*0112f8c4SRick Parrish 		{EIO, EX_IOERR},
729*0112f8c4SRick Parrish 		{ENODEV, EX_IOERR},
730*0112f8c4SRick Parrish 		{ENOTSUP, EX_TEMPFAIL},
731*0112f8c4SRick Parrish 		{EAGAIN, EX_IOERR},
732*0112f8c4SRick Parrish 		{EPERM, EX_NOPERM},
733*0112f8c4SRick Parrish 		{EACCES, EX_NOPERM},
734*0112f8c4SRick Parrish 		{0, EX_OK}
735*0112f8c4SRick Parrish 	};
736*0112f8c4SRick Parrish 
737*0112f8c4SRick Parrish 	for (unsigned i = 0; i < _countof(xlat); i++) {
738*0112f8c4SRick Parrish 		if (xlat[i][0] == err_number)
739*0112f8c4SRick Parrish 			return (xlat[i][1]);
740*0112f8c4SRick Parrish 	}
741*0112f8c4SRick Parrish 
742*0112f8c4SRick Parrish 	return (EX_OSERR);
743*0112f8c4SRick Parrish }
744*0112f8c4SRick Parrish 
745*0112f8c4SRick Parrish /* OPTIONS tables */
746*0112f8c4SRick Parrish 
747*0112f8c4SRick Parrish /* careful: these 'names' arrays must be terminated by a null pointer. */
748*0112f8c4SRick Parrish static const char *names_queue[] = {"-q", "--queue", "-t", "--topic", NULL};
749*0112f8c4SRick Parrish static const struct Option option_queue = {
750*0112f8c4SRick Parrish 	.pattern = names_queue,
751*0112f8c4SRick Parrish 	.parse = parse_queue,
752*0112f8c4SRick Parrish 	.validate = validate_queue};
753*0112f8c4SRick Parrish static const struct Option option_single_queue = {
754*0112f8c4SRick Parrish 	.pattern = names_queue,
755*0112f8c4SRick Parrish 	.parse = parse_single_queue,
756*0112f8c4SRick Parrish 	.validate = validate_single_queue};
757*0112f8c4SRick Parrish static const char *names_depth[] = {"-d", "--depth", "--maxmsg", NULL};
758*0112f8c4SRick Parrish static const struct Option option_depth = {
759*0112f8c4SRick Parrish 	.pattern = names_depth,
760*0112f8c4SRick Parrish 	.parse = parse_depth,
761*0112f8c4SRick Parrish 	.validate = validate_always_true};
762*0112f8c4SRick Parrish static const char *names_size[] = {"-s", "--size", "--msgsize", NULL};
763*0112f8c4SRick Parrish static const struct Option option_size = {
764*0112f8c4SRick Parrish 	.pattern = names_size,
765*0112f8c4SRick Parrish 	.parse = parse_size,
766*0112f8c4SRick Parrish 	.validate = validate_always_true};
767*0112f8c4SRick Parrish static const char *names_block[] = {"-b", "--block", NULL};
768*0112f8c4SRick Parrish static const struct Option option_block = {
769*0112f8c4SRick Parrish 	.pattern = names_block,
770*0112f8c4SRick Parrish 	.parse = parse_block,
771*0112f8c4SRick Parrish 	.validate = validate_always_true};
772*0112f8c4SRick Parrish static const char *names_content[] = {
773*0112f8c4SRick Parrish 	"-c", "--content", "--data", "--message", NULL};
774*0112f8c4SRick Parrish static const struct Option option_content = {
775*0112f8c4SRick Parrish 	.pattern = names_content,
776*0112f8c4SRick Parrish 	.parse = parse_content,
777*0112f8c4SRick Parrish 	.validate = validate_content};
778*0112f8c4SRick Parrish static const char *names_priority[] = {"-p", "--priority", NULL};
779*0112f8c4SRick Parrish static const struct Option option_priority = {
780*0112f8c4SRick Parrish 	.pattern = names_priority,
781*0112f8c4SRick Parrish 	.parse = parse_priority,
782*0112f8c4SRick Parrish 	.validate = validate_always_true};
783*0112f8c4SRick Parrish static const char *names_mode[] = {"-m", "--mode", NULL};
784*0112f8c4SRick Parrish static const struct Option option_mode = {
785*0112f8c4SRick Parrish 	.pattern = names_mode,
786*0112f8c4SRick Parrish 	.parse = parse_mode,
787*0112f8c4SRick Parrish 	.validate = validate_always_true};
788*0112f8c4SRick Parrish static const char *names_group[] = {"-g", "--gid", NULL};
789*0112f8c4SRick Parrish static const struct Option option_group = {
790*0112f8c4SRick Parrish 	.pattern = names_group,
791*0112f8c4SRick Parrish 	.parse = parse_group,
792*0112f8c4SRick Parrish 	.validate = validate_always_true};
793*0112f8c4SRick Parrish static const char *names_user[] = {"-u", "--uid", NULL};
794*0112f8c4SRick Parrish static const struct Option option_user = {
795*0112f8c4SRick Parrish 	.pattern = names_user,
796*0112f8c4SRick Parrish 	.parse = parse_user,
797*0112f8c4SRick Parrish 	.validate = validate_always_true};
798*0112f8c4SRick Parrish 
799*0112f8c4SRick Parrish /* careful: these arrays must be terminated by a null pointer. */
800*0112f8c4SRick Parrish #ifdef __FreeBSD__
801*0112f8c4SRick Parrish static const struct Option *create_options[] = {
802*0112f8c4SRick Parrish 	&option_queue, &option_depth, &option_size, &option_block,
803*0112f8c4SRick Parrish 	&option_mode, &option_group, &option_user, NULL};
804*0112f8c4SRick Parrish #else  /* !__FreeBSD__ */
805*0112f8c4SRick Parrish static const struct Option *create_options[] = {
806*0112f8c4SRick Parrish 	&option_queue, &option_depth, &option_size, &option_block,
807*0112f8c4SRick Parrish 	&option_mode, NULL};
808*0112f8c4SRick Parrish #endif /* __FreeBSD__ */
809*0112f8c4SRick Parrish static const struct Option *info_options[] = {&option_queue, NULL};
810*0112f8c4SRick Parrish static const struct Option *unlink_options[] = {&option_queue, NULL};
811*0112f8c4SRick Parrish static const struct Option *recv_options[] = {&option_single_queue, NULL};
812*0112f8c4SRick Parrish static const struct Option *send_options[] = {
813*0112f8c4SRick Parrish 	&option_queue, &option_content, &option_priority, NULL};
814*0112f8c4SRick Parrish 
815*0112f8c4SRick Parrish int
main(int argc,const char * argv[])816*0112f8c4SRick Parrish main(int argc, const char *argv[])
817*0112f8c4SRick Parrish {
818*0112f8c4SRick Parrish 	STAILQ_INIT(&queues);
819*0112f8c4SRick Parrish 	STAILQ_INIT(&contents);
820*0112f8c4SRick Parrish 
821*0112f8c4SRick Parrish 	if (argc > 1) {
822*0112f8c4SRick Parrish 		const char *verb = argv[1];
823*0112f8c4SRick Parrish 		int index = 2;
824*0112f8c4SRick Parrish 
825*0112f8c4SRick Parrish 		if (strcmp("create", verb) == 0 || strcmp("attr", verb) == 0) {
826*0112f8c4SRick Parrish 			parse_options(index, argc, argv, create_options);
827*0112f8c4SRick Parrish 			if (validate_options(create_options)) {
828*0112f8c4SRick Parrish 				int worst = 0;
829*0112f8c4SRick Parrish 				struct element *itq;
830*0112f8c4SRick Parrish 
831*0112f8c4SRick Parrish 				STAILQ_FOREACH(itq, &queues, links) {
832*0112f8c4SRick Parrish 					const char *queue = itq->text;
833*0112f8c4SRick Parrish 
834*0112f8c4SRick Parrish 					int result = create(queue, creation);
835*0112f8c4SRick Parrish 					if (result != 0)
836*0112f8c4SRick Parrish 						worst = result;
837*0112f8c4SRick Parrish 				}
838*0112f8c4SRick Parrish 
839*0112f8c4SRick Parrish 				return (grace(worst));
840*0112f8c4SRick Parrish 			}
841*0112f8c4SRick Parrish 
842*0112f8c4SRick Parrish 			return (EX_USAGE);
843*0112f8c4SRick Parrish 		} else if (strcmp("info", verb) == 0 || strcmp("cat", verb) == 0) {
844*0112f8c4SRick Parrish 			parse_options(index, argc, argv, info_options);
845*0112f8c4SRick Parrish 			if (validate_options(info_options)) {
846*0112f8c4SRick Parrish 				int worst = 0;
847*0112f8c4SRick Parrish 				struct element *itq;
848*0112f8c4SRick Parrish 
849*0112f8c4SRick Parrish 				STAILQ_FOREACH(itq, &queues, links) {
850*0112f8c4SRick Parrish 					const char *queue = itq->text;
851*0112f8c4SRick Parrish 					int result = info(queue);
852*0112f8c4SRick Parrish 
853*0112f8c4SRick Parrish 					if (result != 0)
854*0112f8c4SRick Parrish 						worst = result;
855*0112f8c4SRick Parrish 				}
856*0112f8c4SRick Parrish 
857*0112f8c4SRick Parrish 				return (grace(worst));
858*0112f8c4SRick Parrish 			}
859*0112f8c4SRick Parrish 
860*0112f8c4SRick Parrish 			return (EX_USAGE);
861*0112f8c4SRick Parrish 		} else if (strcmp("send", verb) == 0) {
862*0112f8c4SRick Parrish 			parse_options(index, argc, argv, send_options);
863*0112f8c4SRick Parrish 			if (validate_options(send_options)) {
864*0112f8c4SRick Parrish 				int worst = 0;
865*0112f8c4SRick Parrish 				struct element *itq;
866*0112f8c4SRick Parrish 
867*0112f8c4SRick Parrish 				STAILQ_FOREACH(itq, &queues, links) {
868*0112f8c4SRick Parrish 					const char *queue = itq->text;
869*0112f8c4SRick Parrish 					struct element *itc;
870*0112f8c4SRick Parrish 
871*0112f8c4SRick Parrish 					STAILQ_FOREACH(itc, &contents, links) {
872*0112f8c4SRick Parrish 						const char *content = itc->text;
873*0112f8c4SRick Parrish 						int result = send(queue, content, priority);
874*0112f8c4SRick Parrish 
875*0112f8c4SRick Parrish 						if (result != 0)
876*0112f8c4SRick Parrish 							worst = result;
877*0112f8c4SRick Parrish 					}
878*0112f8c4SRick Parrish 				}
879*0112f8c4SRick Parrish 
880*0112f8c4SRick Parrish 				return (grace(worst));
881*0112f8c4SRick Parrish 			}
882*0112f8c4SRick Parrish 			return (EX_USAGE);
883*0112f8c4SRick Parrish 		} else if (strcmp("recv", verb) == 0 ||
884*0112f8c4SRick Parrish 		    strcmp("receive", verb) == 0) {
885*0112f8c4SRick Parrish 			parse_options(index, argc, argv, recv_options);
886*0112f8c4SRick Parrish 			if (validate_options(recv_options)) {
887*0112f8c4SRick Parrish 				const char *queue = STAILQ_FIRST(&queues)->text;
888*0112f8c4SRick Parrish 				int worst = recv(queue);
889*0112f8c4SRick Parrish 
890*0112f8c4SRick Parrish 				return (grace(worst));
891*0112f8c4SRick Parrish 			}
892*0112f8c4SRick Parrish 
893*0112f8c4SRick Parrish 			return (EX_USAGE);
894*0112f8c4SRick Parrish 		} else if (strcmp("unlink", verb) == 0 ||
895*0112f8c4SRick Parrish 		    strcmp("rm", verb) == 0) {
896*0112f8c4SRick Parrish 			parse_options(index, argc, argv, unlink_options);
897*0112f8c4SRick Parrish 			if (validate_options(unlink_options)) {
898*0112f8c4SRick Parrish 				int worst = 0;
899*0112f8c4SRick Parrish 				struct element *itq;
900*0112f8c4SRick Parrish 
901*0112f8c4SRick Parrish 				STAILQ_FOREACH(itq, &queues, links) {
902*0112f8c4SRick Parrish 					const char *queue = itq->text;
903*0112f8c4SRick Parrish 					int result = rm(queue);
904*0112f8c4SRick Parrish 
905*0112f8c4SRick Parrish 					if (result != 0)
906*0112f8c4SRick Parrish 						worst = result;
907*0112f8c4SRick Parrish 				}
908*0112f8c4SRick Parrish 
909*0112f8c4SRick Parrish 				return (grace(worst));
910*0112f8c4SRick Parrish 			}
911*0112f8c4SRick Parrish 
912*0112f8c4SRick Parrish 			return (EX_USAGE);
913*0112f8c4SRick Parrish 		} else if (strcmp("help", verb) == 0) {
914*0112f8c4SRick Parrish 			usage(stdout);
915*0112f8c4SRick Parrish 			return (EX_OK);
916*0112f8c4SRick Parrish 		} else {
917*0112f8c4SRick Parrish 			warnx("Unknown verb [%s]", verb);
918*0112f8c4SRick Parrish 			return (EX_USAGE);
919*0112f8c4SRick Parrish 		}
920*0112f8c4SRick Parrish 	}
921*0112f8c4SRick Parrish 
922*0112f8c4SRick Parrish 	usage(stdout);
923*0112f8c4SRick Parrish 	return (EX_OK);
924*0112f8c4SRick Parrish }
925