aboutsummaryrefslogblamecommitdiffstats
path: root/radharc-ipc.c
blob: e2463754f8566d170f3026488ce4d6dead96b917 (plain) (tree)


















































































































































                                                                                                            
/* 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;
}