diff options
Diffstat (limited to 'radharc-ipc.c')
-rw-r--r-- | radharc-ipc.c | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/radharc-ipc.c b/radharc-ipc.c new file mode 100644 index 0000000..e246375 --- /dev/null +++ b/radharc-ipc.c @@ -0,0 +1,147 @@ +/* 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; +} |