mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-04 15:40:44 +00:00
posixmqcontrol(1): manage posix message queues
Reviewed by: kib, paumma MFC after: 1 week Differential revision: https://reviews.freebsd.org/D43845
This commit is contained in:
parent
c5698afcd5
commit
0112f8c4a8
|
@ -110,6 +110,7 @@ SUBDIR= alias \
|
|||
patch \
|
||||
pathchk \
|
||||
perror \
|
||||
posixmqcontrol \
|
||||
posixshmcontrol \
|
||||
pr \
|
||||
printenv \
|
||||
|
|
4
usr.bin/posixmqcontrol/Makefile
Normal file
4
usr.bin/posixmqcontrol/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
PROG= posixmqcontrol
|
||||
LIBADD= rt
|
||||
|
||||
.include <bsd.prog.mk>
|
180
usr.bin/posixmqcontrol/posixmqcontrol.1
Normal file
180
usr.bin/posixmqcontrol/posixmqcontrol.1
Normal file
|
@ -0,0 +1,180 @@
|
|||
.\"-
|
||||
.\" SPDX-License-Identifier: BSD-2-Clause
|
||||
.\"
|
||||
.\" Copyright (c) 2024 Rick Parrish <unitrunker@unitrunker.net>.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd February 19, 2024
|
||||
.Dt POSIXMQCONTROL 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm posixmqcontrol
|
||||
.Nd Control POSIX mqueuefs message queues
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Ar create
|
||||
.Fl q Ar queue
|
||||
.Fl s Ar size
|
||||
.Fl d Ar depth
|
||||
.Op Fl m Ar mode
|
||||
.Op Fl g Ar group
|
||||
.Op Fl u Ar user
|
||||
.Nm
|
||||
.Ar info
|
||||
.Fl q Ar queue
|
||||
.Nm
|
||||
.Ar recv
|
||||
.Fl q Ar queue
|
||||
.Nm
|
||||
.Ar rm
|
||||
.Fl q Ar queue
|
||||
.Nm
|
||||
.Ar send
|
||||
.Fl q Ar queue
|
||||
.Fl c Ar content
|
||||
.Op Fl p Ar priority
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
command allows separating POSIX message queue administration from application
|
||||
stack.
|
||||
Defining and adjusting queue attributes can be done without touching
|
||||
application code.
|
||||
It allows creating queues, inspecting queue metadata, altering group and user
|
||||
access to queues, dumping queue contents, and unlinking queues.
|
||||
.Pp
|
||||
Unlinking removes the name from the system and frees underlying memory.
|
||||
.Pp
|
||||
The maximum message size, maximum queue size, and current queue size are
|
||||
displayed by the
|
||||
.Ic info
|
||||
subcommand. This output is similar to running
|
||||
.Ic cat
|
||||
on a mqueuefs queue mounted under a mount point.
|
||||
This utility requires the
|
||||
.Ic mqueuefs
|
||||
kernel module to be loaded but does not require
|
||||
.Ic mqueuefs
|
||||
to be mounted as a file system.
|
||||
.Pp
|
||||
The following subcommands are provided:
|
||||
.Bl -tag -width truncate
|
||||
.It Ic create
|
||||
Create the named queues, if they do not already exist.
|
||||
More than one queue name may be created. The same maximum queue depth and
|
||||
maximum message size are used to create all queues.
|
||||
If a queue exists, then depth and size are optional.
|
||||
.Pp
|
||||
The required
|
||||
.Ar size
|
||||
and
|
||||
.Ar depth
|
||||
arguments specify the maximum message size (bytes per message) and maximum queue
|
||||
size (depth or number of messages in the queue).
|
||||
The optional numerical
|
||||
.Ar mode
|
||||
argument specifies the initial access mode.
|
||||
If the queue exists but does not match the requested size and depth, this
|
||||
utility will attempt to recreate the queue by first unlinking and then creating
|
||||
it.
|
||||
This will fail if the queue is not empty or is opened by other processes.
|
||||
.It Ic rm
|
||||
Unlink the queues specified - one attempt per queue.
|
||||
Failure to unlink one queue does not stop this sub-command from attempting to
|
||||
unlink the others.
|
||||
.It Ic info
|
||||
For each named queue, dispay the maximum message size, maximum queue size,
|
||||
current queue depth, user owner id, group owner id, and mode permission bits.
|
||||
.It Ic recv
|
||||
Wait for a message from a single named queue and display the message to
|
||||
standard output.
|
||||
.It Ic send
|
||||
Send messages to one or more named queues.
|
||||
If multiple messages and multiple queues are specified, the utility attempts to
|
||||
send all messages to all queues.
|
||||
The optional -p priority, if omitted, defaults to MQ_PRIO_MAX / 2 or medium
|
||||
priority.
|
||||
.El
|
||||
.Sh NOTES
|
||||
A change of queue geometry (maximum message size and/or maximum number of
|
||||
messages) requires destroying and re-creating the queue.
|
||||
As a safety feature,
|
||||
the create subcommand refuses to destroy a non-empty queue.
|
||||
If you use the rm subcommand to destroy a queue, any queued messages are lost.
|
||||
To avoid down-time when altering queue attributes, consider creating a new
|
||||
queue and configure reading applications to drain both new and old queues.
|
||||
Retire the old queue once all writers have been updated to write to the new
|
||||
queue.
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
.Bl -bullet
|
||||
.It
|
||||
EX_NOTAVAILABLE usually means the mqueuefs kernel module is not loaded.
|
||||
.It
|
||||
EX_USAGE reports one or more incorrect parameters.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
.Bl -bullet
|
||||
.It
|
||||
To retrieve the current message from a named queue,
|
||||
.Pa /1 ,
|
||||
use the command
|
||||
.Dl "posixmqcontrol recv -q /1"
|
||||
.It
|
||||
To create a queue with the name
|
||||
.Pa /2
|
||||
with maximum message size 100 and maximum queue depth 10,
|
||||
use the command
|
||||
.Dl "posixmqcontrol create -q /2 -s 100 -d 10"
|
||||
.It
|
||||
To send a message to a queue with the name
|
||||
.Pa /3
|
||||
use the command
|
||||
.Dl "posixmqcontrol send -q /3 -c 'some choice words.'"
|
||||
.It
|
||||
To examine attributes of a queue named
|
||||
.Pa /4
|
||||
use the command
|
||||
.Dl "posixmqcontrol info -q /4"
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr mq_open 2 ,
|
||||
.Xr mq_getattr 2 ,
|
||||
.Xr mq_receive 2 ,
|
||||
.Xr mq_send 2 ,
|
||||
.Xr mq_setattr 2 ,
|
||||
.Xr mq_unlink 2 ,
|
||||
.Xr mqueuefs 5
|
||||
.Sh BUGS
|
||||
mq_timedsend and mq_timedrecv are not implemented.
|
||||
info reports a worst-case estimate for QSIZE.
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
command appeared in
|
||||
.Fx 15.0 .
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
command and this manual page were written by
|
||||
.An Rick Parrish Aq Mt unitrunker@unitrunker.net.
|
924
usr.bin/posixmqcontrol/posixmqcontrol.c
Normal file
924
usr.bin/posixmqcontrol/posixmqcontrol.c
Normal file
|
@ -0,0 +1,924 @@
|
|||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2024 Rick Parrish <unitrunker@unitrunker.net>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/stat.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <limits.h>
|
||||
#include <mqueue.h>
|
||||
#include <pwd.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct Creation {
|
||||
/* true if the queue exists. */
|
||||
bool exists;
|
||||
/* true if a mode value was specified. */
|
||||
bool set_mode;
|
||||
/* access mode with rwx permission bits. */
|
||||
mode_t mode;
|
||||
/* maximum queue depth. default to an invalid depth. */
|
||||
long depth;
|
||||
/* maximum message size. default to an invalid size. */
|
||||
long size;
|
||||
/* true for blocking I/O and false for non-blocking I/O. */
|
||||
bool block;
|
||||
/* true if a group ID was specified. */
|
||||
bool set_group;
|
||||
/* group ID. */
|
||||
gid_t group;
|
||||
/* true if a user ID was specified. */
|
||||
bool set_user;
|
||||
/* user ID. */
|
||||
uid_t user;
|
||||
};
|
||||
|
||||
struct element {
|
||||
STAILQ_ENTRY(element) links;
|
||||
const char *text;
|
||||
};
|
||||
|
||||
static struct element *
|
||||
malloc_element(const char *context)
|
||||
{
|
||||
struct element *item = malloc(sizeof(struct element));
|
||||
|
||||
if (item == NULL)
|
||||
/* the only non-EX_* prefixed exit code. */
|
||||
err(1, "malloc(%s)", context);
|
||||
return (item);
|
||||
}
|
||||
|
||||
static STAILQ_HEAD(tqh, element)
|
||||
queues = STAILQ_HEAD_INITIALIZER(queues),
|
||||
contents = STAILQ_HEAD_INITIALIZER(contents);
|
||||
/* send defaults to medium priority. */
|
||||
static long priority = MQ_PRIO_MAX / 2;
|
||||
static struct Creation creation = {
|
||||
.exists = false,
|
||||
.set_mode = false,
|
||||
.mode = 0755,
|
||||
.depth = -1,
|
||||
.size = -1,
|
||||
.block = true,
|
||||
.set_group = false,
|
||||
.group = 0,
|
||||
.set_user = false,
|
||||
.user = 0
|
||||
};
|
||||
static const mqd_t fail = (mqd_t)-1;
|
||||
static const mode_t accepted_mode_bits =
|
||||
S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISTXT;
|
||||
|
||||
/* OPTIONS parsing utilitarian */
|
||||
|
||||
static void
|
||||
parse_long(const char *text, long *capture, const char *knob, const char *name)
|
||||
{
|
||||
char *cursor = NULL;
|
||||
long value = strtol(text, &cursor, 10);
|
||||
|
||||
if (cursor > text && *cursor == 0) {
|
||||
*capture = value;
|
||||
} else {
|
||||
warnx("%s %s invalid format [%s].", knob, name, text);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parse_unsigned(const char *text, bool *set,
|
||||
unsigned *capture, const char *knob, const char *name)
|
||||
{
|
||||
char *cursor = NULL;
|
||||
unsigned value = strtoul(text, &cursor, 8);
|
||||
|
||||
if (cursor > text && *cursor == 0) {
|
||||
*set = true;
|
||||
*capture = value;
|
||||
} else {
|
||||
warnx("%s %s format [%s] ignored.", knob, name, text);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
sane_queue(const char *queue)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
if (queue[size] != '/') {
|
||||
warnx("queue name [%-.*s] must start with '/'.", NAME_MAX, queue);
|
||||
return (false);
|
||||
}
|
||||
|
||||
for (size++; queue[size] != 0 && size < NAME_MAX; size++) {
|
||||
if (queue[size] == '/') {
|
||||
warnx("queue name [%-.*s] - only one '/' permitted.",
|
||||
NAME_MAX, queue);
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
if (size == NAME_MAX && queue[size] != 0) {
|
||||
warnx("queue name [%-.*s...] may not be longer than %d.",
|
||||
NAME_MAX, queue, NAME_MAX);
|
||||
return (false);
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
/* OPTIONS parsers */
|
||||
|
||||
static void
|
||||
parse_block(const char *text)
|
||||
{
|
||||
if (strcmp(text, "true") == 0 || strcmp(text, "yes") == 0) {
|
||||
creation.block = true;
|
||||
} else if (strcmp(text, "false") == 0 || strcmp(text, "no") == 0) {
|
||||
creation.block = false;
|
||||
} else {
|
||||
char *cursor = NULL;
|
||||
long value = strtol(text, &cursor, 10);
|
||||
if (cursor > text) {
|
||||
creation.block = value != 0;
|
||||
} else {
|
||||
warnx("bad -b block format [%s] ignored.", text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parse_content(const char *content)
|
||||
{
|
||||
struct element *n1 = malloc_element("content");
|
||||
|
||||
n1->text = content;
|
||||
STAILQ_INSERT_TAIL(&contents, n1, links);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_depth(const char *text)
|
||||
{
|
||||
parse_long(text, &creation.depth, "-d", "depth");
|
||||
}
|
||||
|
||||
static void
|
||||
parse_group(const char *text)
|
||||
{
|
||||
struct group *entry = getgrnam(text);
|
||||
|
||||
if (entry == NULL) {
|
||||
parse_unsigned(text, &creation.set_group,
|
||||
&creation.group, "-g", "group");
|
||||
} else {
|
||||
creation.set_group = true;
|
||||
creation.group = entry->gr_gid;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parse_mode(const char *text)
|
||||
{
|
||||
char *cursor = NULL;
|
||||
long value = strtol(text, &cursor, 8);
|
||||
|
||||
// verify only accepted mode bits are set.
|
||||
if (cursor > text && *cursor == 0 && (value & accepted_mode_bits) == value) {
|
||||
creation.set_mode = true;
|
||||
creation.mode = (mode_t)value;
|
||||
} else {
|
||||
warnx("impossible -m mode value [%s] ignored.", text);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parse_priority(const char *text)
|
||||
{
|
||||
char *cursor = NULL;
|
||||
long value = strtol(text, &cursor, 10);
|
||||
|
||||
if (cursor > text && *cursor == 0) {
|
||||
if (value >= 0 && value < MQ_PRIO_MAX) {
|
||||
priority = value;
|
||||
} else {
|
||||
warnx("bad -p priority range [%s] ignored.", text);
|
||||
}
|
||||
} else {
|
||||
warnx("bad -p priority format [%s] ignored.", text);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parse_queue(const char *queue)
|
||||
{
|
||||
if (sane_queue(queue)) {
|
||||
struct element *n1 = malloc_element("queue name");
|
||||
|
||||
n1->text = queue;
|
||||
STAILQ_INSERT_TAIL(&queues, n1, links);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parse_single_queue(const char *queue)
|
||||
{
|
||||
if (sane_queue(queue)) {
|
||||
if (STAILQ_EMPTY(&queues)) {
|
||||
struct element *n1 = malloc_element("queue name");
|
||||
|
||||
n1->text = queue;
|
||||
STAILQ_INSERT_TAIL(&queues, n1, links);
|
||||
} else
|
||||
warnx("ignoring extra -q queue [%s].", queue);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parse_size(const char *text)
|
||||
{
|
||||
parse_long(text, &creation.size, "-s", "size");
|
||||
}
|
||||
|
||||
static void
|
||||
parse_user(const char *text)
|
||||
{
|
||||
struct passwd *entry = getpwnam(text);
|
||||
if (entry == NULL) {
|
||||
parse_unsigned(text, &creation.set_user,
|
||||
&creation.user, "-u", "user");
|
||||
} else {
|
||||
creation.set_user = true;
|
||||
creation.user = entry->pw_uid;
|
||||
}
|
||||
}
|
||||
|
||||
/* OPTIONS validators */
|
||||
|
||||
static bool
|
||||
validate_always_true(void)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
static bool
|
||||
validate_content(void)
|
||||
{
|
||||
bool valid = !STAILQ_EMPTY(&contents);
|
||||
|
||||
if (!valid)
|
||||
warnx("no content to send.");
|
||||
return (valid);
|
||||
}
|
||||
|
||||
static bool
|
||||
validate_depth(void)
|
||||
{
|
||||
bool valid = creation.exists || creation.depth > 0;
|
||||
|
||||
if (!valid)
|
||||
warnx("-d maximum queue depth not provided.");
|
||||
return (valid);
|
||||
}
|
||||
|
||||
static bool
|
||||
validate_queue(void)
|
||||
{
|
||||
bool valid = !STAILQ_EMPTY(&queues);
|
||||
|
||||
if (!valid)
|
||||
warnx("missing -q, or no sane queue name given.");
|
||||
return (valid);
|
||||
}
|
||||
|
||||
static bool
|
||||
validate_single_queue(void)
|
||||
{
|
||||
bool valid = !STAILQ_EMPTY(&queues) &&
|
||||
STAILQ_NEXT(STAILQ_FIRST(&queues), links) == NULL;
|
||||
|
||||
if (!valid)
|
||||
warnx("expected one queue.");
|
||||
return (valid);
|
||||
}
|
||||
|
||||
static bool
|
||||
validate_size(void)
|
||||
{
|
||||
bool valid = creation.exists || creation.size > 0;
|
||||
|
||||
if (!valid)
|
||||
warnx("-s maximum message size not provided.");
|
||||
return (valid);
|
||||
}
|
||||
|
||||
/* OPTIONS table handling. */
|
||||
|
||||
struct Option {
|
||||
/* points to array of string pointers terminated by a null pointer. */
|
||||
const char **pattern;
|
||||
/* parse argument. */
|
||||
void (*parse)(const char *);
|
||||
/*
|
||||
* displays an error and returns false if this parameter is not valid.
|
||||
* returns true otherwise.
|
||||
*/
|
||||
bool (*validate)(void);
|
||||
};
|
||||
|
||||
/*
|
||||
* parse options by table.
|
||||
* index - current index into argv list.
|
||||
* argc, argv - command line parameters.
|
||||
* options - null terminated list of pointers to options.
|
||||
*/
|
||||
static void
|
||||
parse_options(int index, int argc,
|
||||
const char *argv[], const struct Option **options)
|
||||
{
|
||||
while ((index + 1) < argc) {
|
||||
const struct Option **cursor = options;
|
||||
bool match = false;
|
||||
while (*cursor != NULL && !match) {
|
||||
const struct Option *option = cursor[0];
|
||||
const char **pattern = option->pattern;
|
||||
|
||||
while (*pattern != NULL && !match) {
|
||||
const char *knob = *pattern;
|
||||
|
||||
match = strcmp(knob, argv[index]) == 0;
|
||||
if (!match)
|
||||
pattern++;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
option->parse(argv[index + 1]);
|
||||
index += 2;
|
||||
break;
|
||||
}
|
||||
cursor++;
|
||||
}
|
||||
|
||||
if (!match && index < argc) {
|
||||
warnx("skipping [%s].", argv[index]);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (index < argc) {
|
||||
warnx("skipping [%s].", argv[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/* options - null terminated list of pointers to options. */
|
||||
static bool
|
||||
validate_options(const struct Option **options)
|
||||
{
|
||||
bool valid = true;
|
||||
|
||||
while (*options != NULL) {
|
||||
const struct Option *option = options[0];
|
||||
|
||||
if (!option->validate())
|
||||
valid = false;
|
||||
options++;
|
||||
}
|
||||
return (valid);
|
||||
}
|
||||
|
||||
/* SUBCOMMANDS */
|
||||
|
||||
/*
|
||||
* queue: name of queue to be created.
|
||||
* q_creation: creation parameters (copied by value).
|
||||
*/
|
||||
static int
|
||||
create(const char *queue, struct Creation q_creation)
|
||||
{
|
||||
int flags = O_RDWR;
|
||||
struct mq_attr stuff = {
|
||||
.mq_curmsgs = 0,
|
||||
.mq_maxmsg = q_creation.depth,
|
||||
.mq_msgsize = q_creation.size,
|
||||
.mq_flags = 0
|
||||
};
|
||||
|
||||
if (!q_creation.block) {
|
||||
flags |= O_NONBLOCK;
|
||||
stuff.mq_flags |= O_NONBLOCK;
|
||||
}
|
||||
|
||||
mqd_t handle = mq_open(queue, flags);
|
||||
q_creation.exists = handle != fail;
|
||||
if (!q_creation.exists) {
|
||||
/*
|
||||
* apply size and depth checks here.
|
||||
* if queue exists, we can default to existing depth and size.
|
||||
* but for a new queue, we require that input.
|
||||
*/
|
||||
if (validate_size() && validate_depth()) {
|
||||
/* no need to re-apply mode. */
|
||||
q_creation.set_mode = false;
|
||||
flags |= O_CREAT;
|
||||
handle = mq_open(queue, flags, q_creation.mode, &stuff);
|
||||
}
|
||||
}
|
||||
|
||||
if (handle == fail) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "mq_open(create)");
|
||||
return (what);
|
||||
}
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
/*
|
||||
* undocumented.
|
||||
* See https://bugs.freebsd.org/bugzilla//show_bug.cgi?id=273230
|
||||
*/
|
||||
int fd = mq_getfd_np(handle);
|
||||
|
||||
if (fd < 0) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "mq_getfd_np(create)");
|
||||
mq_close(handle);
|
||||
return (what);
|
||||
}
|
||||
struct stat status = {0};
|
||||
int result = fstat(fd, &status);
|
||||
if (result != 0) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "fstat(create)");
|
||||
mq_close(handle);
|
||||
return (what);
|
||||
}
|
||||
|
||||
/* do this only if group and / or user given. */
|
||||
if (q_creation.set_group || q_creation.set_user) {
|
||||
q_creation.user =
|
||||
q_creation.set_user ? q_creation.user : status.st_uid;
|
||||
q_creation.group =
|
||||
q_creation.set_group ? q_creation.group : status.st_gid;
|
||||
result = fchown(fd, q_creation.user, q_creation.group);
|
||||
if (result != 0) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "fchown(create)");
|
||||
mq_close(handle);
|
||||
return (what);
|
||||
}
|
||||
}
|
||||
|
||||
/* do this only if altering mode of an existing queue. */
|
||||
if (q_creation.exists && q_creation.set_mode &&
|
||||
q_creation.mode != (status.st_mode & accepted_mode_bits)) {
|
||||
result = fchmod(fd, q_creation.mode);
|
||||
if (result != 0) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "fchmod(create)");
|
||||
mq_close(handle);
|
||||
return (what);
|
||||
}
|
||||
}
|
||||
#endif /* __FreeBSD__ */
|
||||
|
||||
return (mq_close(handle));
|
||||
}
|
||||
|
||||
/* queue: name of queue to be removed. */
|
||||
static int
|
||||
rm(const char *queue)
|
||||
{
|
||||
int result = mq_unlink(queue);
|
||||
|
||||
if (result != 0) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "mq_unlink");
|
||||
return (what);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/* Return the display character for non-zero mode. */
|
||||
static char
|
||||
dual(mode_t mode, char display)
|
||||
{
|
||||
return (mode != 0 ? display : '-');
|
||||
}
|
||||
|
||||
/* Select one of four display characters based on mode and modifier. */
|
||||
static char
|
||||
quad(mode_t mode, mode_t modifier)
|
||||
{
|
||||
static const char display[] = "-xSs";
|
||||
unsigned index = 0;
|
||||
if (mode != 0)
|
||||
index += 1;
|
||||
if (modifier)
|
||||
index += 2;
|
||||
return (display[index]);
|
||||
}
|
||||
|
||||
/* queue: name of queue to be inspected. */
|
||||
static int
|
||||
info(const char *queue)
|
||||
{
|
||||
mqd_t handle = mq_open(queue, O_RDONLY);
|
||||
|
||||
if (handle == fail) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "mq_open(info)");
|
||||
return (what);
|
||||
}
|
||||
|
||||
struct mq_attr actual;
|
||||
|
||||
int result = mq_getattr(handle, &actual);
|
||||
if (result != 0) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "mq_getattr(info)");
|
||||
return (what);
|
||||
}
|
||||
|
||||
fprintf(stdout,
|
||||
"queue: '%s'\nQSIZE: %lu\nMSGSIZE: %ld\nMAXMSG: %ld\n"
|
||||
"CURMSG: %ld\nflags: %03ld\n",
|
||||
queue, actual.mq_msgsize * actual.mq_curmsgs, actual.mq_msgsize,
|
||||
actual.mq_maxmsg, actual.mq_curmsgs, actual.mq_flags);
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
int fd = mq_getfd_np(handle);
|
||||
struct stat status;
|
||||
|
||||
result = fstat(fd, &status);
|
||||
if (result != 0) {
|
||||
warn("fstat(info)");
|
||||
} else {
|
||||
mode_t mode = status.st_mode;
|
||||
|
||||
fprintf(stdout, "UID: %u\nGID: %u\n", status.st_uid, status.st_gid);
|
||||
fprintf(stdout, "MODE: %c%c%c%c%c%c%c%c%c%c\n",
|
||||
dual(mode & S_ISVTX, 's'),
|
||||
dual(mode & S_IRUSR, 'r'),
|
||||
dual(mode & S_IWUSR, 'w'),
|
||||
quad(mode & S_IXUSR, mode & S_ISUID),
|
||||
dual(mode & S_IRGRP, 'r'),
|
||||
dual(mode & S_IWGRP, 'w'),
|
||||
quad(mode & S_IXGRP, mode & S_ISGID),
|
||||
dual(mode & S_IROTH, 'r'),
|
||||
dual(mode & S_IWOTH, 'w'),
|
||||
dual(mode & S_IXOTH, 'x'));
|
||||
}
|
||||
#endif /* __FreeBSD__ */
|
||||
|
||||
return (mq_close(handle));
|
||||
}
|
||||
|
||||
/* queue: name of queue to drain one message. */
|
||||
static int
|
||||
recv(const char *queue)
|
||||
{
|
||||
mqd_t handle = mq_open(queue, O_RDONLY);
|
||||
|
||||
if (handle == fail) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "mq_open(recv)");
|
||||
return (what);
|
||||
}
|
||||
|
||||
struct mq_attr actual;
|
||||
|
||||
int result = mq_getattr(handle, &actual);
|
||||
|
||||
if (result != 0) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "mq_attr(recv)");
|
||||
mq_close(handle);
|
||||
return (what);
|
||||
}
|
||||
|
||||
char *text = malloc(actual.mq_msgsize + 1);
|
||||
unsigned q_priority = 0;
|
||||
|
||||
memset(text, 0, actual.mq_msgsize + 1);
|
||||
result = mq_receive(handle, text, actual.mq_msgsize, &q_priority);
|
||||
if (result < 0) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "mq_receive");
|
||||
mq_close(handle);
|
||||
return (what);
|
||||
}
|
||||
|
||||
fprintf(stdout, "[%u]: %-*.*s\n", q_priority, result, result, text);
|
||||
return (mq_close(handle));
|
||||
}
|
||||
|
||||
/*
|
||||
* queue: name of queue to send one message.
|
||||
* text: message text.
|
||||
* q_priority: message priority in range of 0 to 63.
|
||||
*/
|
||||
static int
|
||||
send(const char *queue, const char *text, unsigned q_priority)
|
||||
{
|
||||
mqd_t handle = mq_open(queue, O_WRONLY);
|
||||
|
||||
if (handle == fail) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "mq_open(send)");
|
||||
return (what);
|
||||
}
|
||||
|
||||
struct mq_attr actual;
|
||||
|
||||
int result = mq_getattr(handle, &actual);
|
||||
|
||||
if (result != 0) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "mq_attr(send)");
|
||||
mq_close(handle);
|
||||
return (what);
|
||||
}
|
||||
|
||||
int size = strlen(text);
|
||||
|
||||
if (size > actual.mq_msgsize) {
|
||||
warnx("truncating message to %ld characters.\n", actual.mq_msgsize);
|
||||
size = actual.mq_msgsize;
|
||||
}
|
||||
|
||||
result = mq_send(handle, text, size, q_priority);
|
||||
|
||||
if (result != 0) {
|
||||
errno_t what = errno;
|
||||
|
||||
warnc(what, "mq_send");
|
||||
mq_close(handle);
|
||||
return (what);
|
||||
}
|
||||
|
||||
return (mq_close(handle));
|
||||
}
|
||||
|
||||
static void
|
||||
usage(FILE *file)
|
||||
{
|
||||
fprintf(file,
|
||||
"usage:\n\tposixmqcontrol [rm|info|recv] -q <queue>\n"
|
||||
"\tposixmqcontrol create -q <queue> -s <maxsize> -d <maxdepth> "
|
||||
"[ -m <mode> ] [ -b <block> ] [-u <uid> ] [ -g <gid> ]\n"
|
||||
"\tposixmqcontrol send -q <queue> -c <content> "
|
||||
"[-p <priority> ]\n");
|
||||
}
|
||||
|
||||
/* end of SUBCOMMANDS */
|
||||
|
||||
#define _countof(arg) ((sizeof(arg)) / (sizeof((arg)[0])))
|
||||
|
||||
/* convert an errno style error code to a sysexits code. */
|
||||
static int
|
||||
grace(int err_number)
|
||||
{
|
||||
static const int xlat[][2] = {
|
||||
/* generally means the mqueuefs driver is not loaded. */
|
||||
{ENOSYS, EX_UNAVAILABLE},
|
||||
/* no such queue name. */
|
||||
{ENOENT, EX_OSFILE},
|
||||
{EIO, EX_IOERR},
|
||||
{ENODEV, EX_IOERR},
|
||||
{ENOTSUP, EX_TEMPFAIL},
|
||||
{EAGAIN, EX_IOERR},
|
||||
{EPERM, EX_NOPERM},
|
||||
{EACCES, EX_NOPERM},
|
||||
{0, EX_OK}
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < _countof(xlat); i++) {
|
||||
if (xlat[i][0] == err_number)
|
||||
return (xlat[i][1]);
|
||||
}
|
||||
|
||||
return (EX_OSERR);
|
||||
}
|
||||
|
||||
/* OPTIONS tables */
|
||||
|
||||
/* careful: these 'names' arrays must be terminated by a null pointer. */
|
||||
static const char *names_queue[] = {"-q", "--queue", "-t", "--topic", NULL};
|
||||
static const struct Option option_queue = {
|
||||
.pattern = names_queue,
|
||||
.parse = parse_queue,
|
||||
.validate = validate_queue};
|
||||
static const struct Option option_single_queue = {
|
||||
.pattern = names_queue,
|
||||
.parse = parse_single_queue,
|
||||
.validate = validate_single_queue};
|
||||
static const char *names_depth[] = {"-d", "--depth", "--maxmsg", NULL};
|
||||
static const struct Option option_depth = {
|
||||
.pattern = names_depth,
|
||||
.parse = parse_depth,
|
||||
.validate = validate_always_true};
|
||||
static const char *names_size[] = {"-s", "--size", "--msgsize", NULL};
|
||||
static const struct Option option_size = {
|
||||
.pattern = names_size,
|
||||
.parse = parse_size,
|
||||
.validate = validate_always_true};
|
||||
static const char *names_block[] = {"-b", "--block", NULL};
|
||||
static const struct Option option_block = {
|
||||
.pattern = names_block,
|
||||
.parse = parse_block,
|
||||
.validate = validate_always_true};
|
||||
static const char *names_content[] = {
|
||||
"-c", "--content", "--data", "--message", NULL};
|
||||
static const struct Option option_content = {
|
||||
.pattern = names_content,
|
||||
.parse = parse_content,
|
||||
.validate = validate_content};
|
||||
static const char *names_priority[] = {"-p", "--priority", NULL};
|
||||
static const struct Option option_priority = {
|
||||
.pattern = names_priority,
|
||||
.parse = parse_priority,
|
||||
.validate = validate_always_true};
|
||||
static const char *names_mode[] = {"-m", "--mode", NULL};
|
||||
static const struct Option option_mode = {
|
||||
.pattern = names_mode,
|
||||
.parse = parse_mode,
|
||||
.validate = validate_always_true};
|
||||
static const char *names_group[] = {"-g", "--gid", NULL};
|
||||
static const struct Option option_group = {
|
||||
.pattern = names_group,
|
||||
.parse = parse_group,
|
||||
.validate = validate_always_true};
|
||||
static const char *names_user[] = {"-u", "--uid", NULL};
|
||||
static const struct Option option_user = {
|
||||
.pattern = names_user,
|
||||
.parse = parse_user,
|
||||
.validate = validate_always_true};
|
||||
|
||||
/* careful: these arrays must be terminated by a null pointer. */
|
||||
#ifdef __FreeBSD__
|
||||
static const struct Option *create_options[] = {
|
||||
&option_queue, &option_depth, &option_size, &option_block,
|
||||
&option_mode, &option_group, &option_user, NULL};
|
||||
#else /* !__FreeBSD__ */
|
||||
static const struct Option *create_options[] = {
|
||||
&option_queue, &option_depth, &option_size, &option_block,
|
||||
&option_mode, NULL};
|
||||
#endif /* __FreeBSD__ */
|
||||
static const struct Option *info_options[] = {&option_queue, NULL};
|
||||
static const struct Option *unlink_options[] = {&option_queue, NULL};
|
||||
static const struct Option *recv_options[] = {&option_single_queue, NULL};
|
||||
static const struct Option *send_options[] = {
|
||||
&option_queue, &option_content, &option_priority, NULL};
|
||||
|
||||
int
|
||||
main(int argc, const char *argv[])
|
||||
{
|
||||
STAILQ_INIT(&queues);
|
||||
STAILQ_INIT(&contents);
|
||||
|
||||
if (argc > 1) {
|
||||
const char *verb = argv[1];
|
||||
int index = 2;
|
||||
|
||||
if (strcmp("create", verb) == 0 || strcmp("attr", verb) == 0) {
|
||||
parse_options(index, argc, argv, create_options);
|
||||
if (validate_options(create_options)) {
|
||||
int worst = 0;
|
||||
struct element *itq;
|
||||
|
||||
STAILQ_FOREACH(itq, &queues, links) {
|
||||
const char *queue = itq->text;
|
||||
|
||||
int result = create(queue, creation);
|
||||
if (result != 0)
|
||||
worst = result;
|
||||
}
|
||||
|
||||
return (grace(worst));
|
||||
}
|
||||
|
||||
return (EX_USAGE);
|
||||
} else if (strcmp("info", verb) == 0 || strcmp("cat", verb) == 0) {
|
||||
parse_options(index, argc, argv, info_options);
|
||||
if (validate_options(info_options)) {
|
||||
int worst = 0;
|
||||
struct element *itq;
|
||||
|
||||
STAILQ_FOREACH(itq, &queues, links) {
|
||||
const char *queue = itq->text;
|
||||
int result = info(queue);
|
||||
|
||||
if (result != 0)
|
||||
worst = result;
|
||||
}
|
||||
|
||||
return (grace(worst));
|
||||
}
|
||||
|
||||
return (EX_USAGE);
|
||||
} else if (strcmp("send", verb) == 0) {
|
||||
parse_options(index, argc, argv, send_options);
|
||||
if (validate_options(send_options)) {
|
||||
int worst = 0;
|
||||
struct element *itq;
|
||||
|
||||
STAILQ_FOREACH(itq, &queues, links) {
|
||||
const char *queue = itq->text;
|
||||
struct element *itc;
|
||||
|
||||
STAILQ_FOREACH(itc, &contents, links) {
|
||||
const char *content = itc->text;
|
||||
int result = send(queue, content, priority);
|
||||
|
||||
if (result != 0)
|
||||
worst = result;
|
||||
}
|
||||
}
|
||||
|
||||
return (grace(worst));
|
||||
}
|
||||
return (EX_USAGE);
|
||||
} else if (strcmp("recv", verb) == 0 ||
|
||||
strcmp("receive", verb) == 0) {
|
||||
parse_options(index, argc, argv, recv_options);
|
||||
if (validate_options(recv_options)) {
|
||||
const char *queue = STAILQ_FIRST(&queues)->text;
|
||||
int worst = recv(queue);
|
||||
|
||||
return (grace(worst));
|
||||
}
|
||||
|
||||
return (EX_USAGE);
|
||||
} else if (strcmp("unlink", verb) == 0 ||
|
||||
strcmp("rm", verb) == 0) {
|
||||
parse_options(index, argc, argv, unlink_options);
|
||||
if (validate_options(unlink_options)) {
|
||||
int worst = 0;
|
||||
struct element *itq;
|
||||
|
||||
STAILQ_FOREACH(itq, &queues, links) {
|
||||
const char *queue = itq->text;
|
||||
int result = rm(queue);
|
||||
|
||||
if (result != 0)
|
||||
worst = result;
|
||||
}
|
||||
|
||||
return (grace(worst));
|
||||
}
|
||||
|
||||
return (EX_USAGE);
|
||||
} else if (strcmp("help", verb) == 0) {
|
||||
usage(stdout);
|
||||
return (EX_OK);
|
||||
} else {
|
||||
warnx("Unknown verb [%s]", verb);
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
usage(stdout);
|
||||
return (EX_OK);
|
||||
}
|
50
usr.bin/posixmqcontrol/posixmqcontroltest8qs.sh
Normal file
50
usr.bin/posixmqcontrol/posixmqcontroltest8qs.sh
Normal file
|
@ -0,0 +1,50 @@
|
|||
#!/bin/sh
|
||||
# testing create, info, and send operations applied to multiple queue names at once.
|
||||
# recv accepts a single queue name so draining is done one queue at a time.
|
||||
subject='posixmqcontrol'
|
||||
prefix='/posixmqcontroltest'
|
||||
|
||||
list=
|
||||
for i in 1 2 3 4 5 6 7 8
|
||||
do
|
||||
topic="${prefix}${i}"
|
||||
${subject} info -q "${topic}" 2>/dev/null
|
||||
if [ $? == 0 ]; then
|
||||
echo "sorry, $topic exists."
|
||||
exit 1
|
||||
fi
|
||||
list="${list} -q ${topic}"
|
||||
done
|
||||
|
||||
${subject} create -d 2 -s 64 ${list}
|
||||
if [ $? != 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ignore=$( ${subject} info ${list} )
|
||||
if [ $? != 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
${subject} send -c 'this message sent to all listed queues.' ${list}
|
||||
if [ $? != 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# we can only drain one message at a time.
|
||||
for i in 1 2 3 4 5 6 7 8
|
||||
do
|
||||
topic="${prefix}${i}"
|
||||
ignore=$( ${subject} recv -q "${topic}" )
|
||||
if [ $? != 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
${subject} rm ${list}
|
||||
if [ $? == 0 ]; then
|
||||
echo "Pass!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exit 1
|
99
usr.bin/posixmqcontrol/posixmqcontroltest8x64.sh
Normal file
99
usr.bin/posixmqcontrol/posixmqcontroltest8x64.sh
Normal file
|
@ -0,0 +1,99 @@
|
|||
#!/bin/sh
|
||||
# exercises create, info, send and recv subcommands.
|
||||
|
||||
subject='posixmqcontrol'
|
||||
topic='/test123'
|
||||
|
||||
${subject} info -q "$topic" 2>/dev/null
|
||||
if [ $? == 0 ]; then
|
||||
echo "sorry, $topic exists."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# create trivial queue that can hold 8 messages of 64 bytes each.
|
||||
${subject} create -q "$topic" -s 64 -d 8
|
||||
if [ $? != 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
info=$(${subject} info -q "$topic")
|
||||
if [ $? != 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
expected='MSGSIZE: 64'
|
||||
actual=$(echo "${info}" | grep 'MSGSIZE: ')
|
||||
if [ "$expected" != "$actual" ]; then
|
||||
echo "EXPECTED: $expected"
|
||||
echo " ACTUAL: $actual"
|
||||
exit 1
|
||||
fi
|
||||
expected='MAXMSG: 8'
|
||||
actual=$(echo "${info}" | grep 'MAXMSG: ')
|
||||
if [ "$expected" != "$actual" ]; then
|
||||
echo "EXPECTED: $expected"
|
||||
echo " ACTUAL: $actual"
|
||||
exit 1
|
||||
fi
|
||||
expected='CURMSG: 0'
|
||||
actual=$(echo "${info}" | grep 'CURMSG: ')
|
||||
if [ "$expected" != "$actual" ]; then
|
||||
echo "EXPECTED: $expected"
|
||||
echo " ACTUAL: $actual"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# write eight messages of increasing priority.
|
||||
for i in 1 2 3 4 5 6 7 8
|
||||
do
|
||||
${subject} send -q "$topic" -c "message $i" -p "$i"
|
||||
if [ $? != 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
info=$(${subject} info -q "$topic")
|
||||
if [ $? != 0 ]; then
|
||||
exit
|
||||
fi
|
||||
expected='CURMSG: 8'
|
||||
actual=$(echo "${info}" | grep 'CURMSG: ')
|
||||
if [ "$expected" != "$actual" ]; then
|
||||
echo "EXPECTED: $expected"
|
||||
echo " ACTUAL: $actual"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# expect the eight messages to appear in priority order.
|
||||
for i in 8 7 6 5 4 3 2 1
|
||||
do
|
||||
expected='['"$i"']: message '"$i"
|
||||
actual=$(${subject} recv -q "$topic")
|
||||
if [ $? != 0 ]; then
|
||||
exit
|
||||
fi
|
||||
if [ "$expected" != "$actual" ]; then
|
||||
echo "EXPECTED: $expected"
|
||||
echo " ACTUAL: $actual"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
info=$(${subject} info -q "$topic")
|
||||
if [ $? != 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
expected='CURMSG: 0'
|
||||
actual=$(echo "${info}" | grep 'CURMSG: ')
|
||||
if [ "$expected" != "$actual" ]; then
|
||||
echo "EXPECTED: $expected"
|
||||
echo " ACTUAL: $actual"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
${subject} rm -q "$topic"
|
||||
if [ $? == 0 ]; then
|
||||
echo "Pass!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exit 1
|
28
usr.bin/posixmqcontrol/posixmqcontroltestsane.sh
Normal file
28
usr.bin/posixmqcontrol/posixmqcontroltestsane.sh
Normal file
|
@ -0,0 +1,28 @@
|
|||
#!/bin/sh
|
||||
# test for 'insane' queue names.
|
||||
|
||||
subject='posixmqcontrol'
|
||||
|
||||
# does sanity check enforce leading slash?
|
||||
${subject} info -q missing.leading.slash 2>/dev/null
|
||||
code=$?
|
||||
if [ $code != 64 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# does sanity check enforce one and only one slash?
|
||||
${subject} info -q /to/many/slashes 2>/dev/null
|
||||
code=$?
|
||||
if [ $code != 64 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# does sanity check enforce length limit?
|
||||
${subject} info -q /this.queue.name.is.way.too.long.at.more.than.one.thousand.and.twenty.four.characters.long.because.nobody.needs.to.type.out.something.this.ridiculously.long.than.just.goes.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on.and.on 2>/dev/null
|
||||
code=$?
|
||||
if [ $code != 64 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Pass!"
|
||||
exit 0
|
Loading…
Reference in a new issue