/* See LICENSE file for copyright and license details. */
#include <libsimple-arg.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
USAGE("[-R rule] (pid | @name | &fd | address) ...");
struct message {
char *text;
size_t len;
};
static int
parse_uint(const char *s, uintmax_t *value)
{
uintmax_t digit;
if (!*s)
return 0;
*value = 0;
for (; isdigit(*s); s++) {
digit = (*s & 15);
if (*value > (UINTMAX_MAX - digit) / 10)
return 0;
*value = *value * 10 + digit;
}
return !*s;
}
int
main(int argc, char *argv[])
{
const char *rule = "standard";
char *class, **addresses;
int ret = 0;
size_t i, j;
struct sockaddr_un addr;
size_t addrlen;
int sock;
struct message *messages = NULL;
size_t nmessages = 0;
ARGBEGIN {
case 'R':
rule = ARG();
break;
default:
usage();
} ARGEND;
if (!argc)
usage();
class = malloc(sizeof(PACKAGE_NAME"::"COMMAND_NAME"::") + strlen(rule));
if (!class)
goto fail;
stpcpy(stpcpy(class, PACKAGE_NAME"::"COMMAND_NAME"::"), rule);
addresses = calloc((size_t)argc, sizeof(*addresses));
if (!addresses)
goto fail;
for (i = 0; i < (size_t)argc; i++) {
const char *arg = argv[i];
uintmax_t num;
if (*arg == '@' || strchr(arg, '/')) {
addresses[i] = strdup(arg);
if (addresses[i])
goto fail;
if (*arg == '@')
addresses[i][0] = '\0';
} else if (parse_uint(&arg[*arg == '&'], &num)) {
if (*arg == '&') {
if (num > (uintmax_t)INT_MAX)
usage();
addresses[i] = malloc(sizeof("/proc/self/fd/") + strlen(&arg[1]));
if (!addresses[i])
goto fail;
stpcpy(stpcpy(addresses[i], "/proc/self/fd/"), &arg[1]);
} else {
addresses[i] = malloc(sizeof("@/proc//") + strlen(arg) + strlen(class));
if (!addresses[i])
goto fail;
stpcpy(stpcpy(stpcpy(stpcpy(&addresses[i][1], "/proc/"), arg), "/"), class);
addresses[i][0] = '\0';
}
} else {
usage();
}
if (strlen(&addresses[i][1]) + 1U > sizeof(addr.sun_path)) {
fprintf(stderr, "%s: socket name '%s%s' too long\n", argv0,
addresses[i][0] ? "" : "@", &addresses[i][!addresses[i][0]]);
return 1;
}
}
sock = socket(PF_UNIX, SOCK_DGRAM, 0);
if (sock < 0)
goto fail;
for (j = 0; j < nmessages; j++) {
for (i = 0; i < (size_t)argc; i++) {
size_t len;
if (!addresses[i])
continue;
len = strlen(&addresses[i][1]) + 1U;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, addresses[i], len);
addrlen = offsetof(struct sockaddr_un, sun_path) + len;
if (sendto(sock, messages[j].text, messages[j].len, MSG_NOSIGNAL,
(void *)&addr, (socklen_t)addrlen) < 0) {
fprintf(stderr, "%s: error while sending to '%s': %s\n",
argv0, argv[i], strerror(errno));
ret = 1;
addresses[i] = NULL;
}
}
free(messages[j].text);
}
close(sock);
for (i = 0; i < (size_t)argc; i++)
free(addresses[i]);
free(addresses);
free(messages);
free(class);
return ret;
fail:
perror(argv0);
return 1;
}