/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include #include 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(); /* TODO whoops, this should be send with that fd, not to it */ 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; }