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