/* 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>
#include "libradharc.h"
USAGE("[-R rule] (pid | @name | &fd | address) ...");
struct message {
void *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, *socks;
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;
sock = socket(PF_UNIX, SOCK_DGRAM, 0);
if (sock < 0)
goto fail;
/* we need an address, otherwise the peer will not get an address to send the response to */
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
if (bind(sock, (void *)&addr, (socklen_t)offsetof(struct sockaddr_un, sun_path)))
goto fail;
socks = calloc((size_t)argc, sizeof(int));
if (!socks)
goto fail;
for (i = 0; i < (size_t)argc; i++) {
const char *arg = argv[i];
uintmax_t num;
socks[i] = sock;
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] = NULL;
socks[i] = (int)num;
} 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;
}
}
for (j = 0; j < nmessages; j++) {
for (i = 0; i < (size_t)argc; i++) {
size_t len;
if (socks[i] < 0)
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 (socks[i] != sock) {
if (send(socks[i], messages[j].text, messages[j].len, MSG_NOSIGNAL) < 0)
goto send_failed;
} else if (sendto(sock, messages[j].text, messages[j].len, MSG_NOSIGNAL,
(void *)&addr, (socklen_t)addrlen) < 0) {
send_failed:
fprintf(stderr, "%s: error while sending to '%s': %s\n",
argv0, argv[i], strerror(errno));
ret = 1;
socks[i] = -1;
}
}
free(messages[j].text);
for (i = 0; i < (size_t)argc; i++) {
if (socks[i] < 0)
continue;
/* TODO get response */
}
}
close(sock);
for (i = 0; i < (size_t)argc; i++)
free(addresses[i]);
free(addresses);
free(messages);
free(class);
free(socks);
return ret;
fail:
perror(argv0);
return 1;
}