static int genetlink_call(__u16 family_id, __u8 cmd, void *header, size_t header_len, void *request, size_t request_len, void *reply, size_t reply_len) { struct msg { struct nlmsghdr n; struct genlmsghdr g; char payload[0]; }; struct msg *request_msg; struct msg *reply_msg; int request_msg_size; int reply_msg_size; struct sockaddr_nl local; struct pollfd pfd; int sndbuf = 32*1024; /* 32k */ int rcvbuf = 32*1024; /* 32k */ int len; int sk; /* * Prepare request/reply messages */ request_msg_size = NLMSG_LENGTH(GENL_HDRLEN + header_len + request_len); request_msg = malloc(request_msg_size); request_msg->n.nlmsg_len = request_msg_size; request_msg->n.nlmsg_type = family_id; request_msg->n.nlmsg_flags = NLM_F_REQUEST; request_msg->n.nlmsg_seq = 0; request_msg->n.nlmsg_pid = getpid(); request_msg->g.cmd = cmd; request_msg->g.version = 0; if (header_len) memcpy(&request_msg->payload[0], header, header_len); if (request_len) memcpy(&request_msg->payload[header_len], request, request_len); reply_msg_size = NLMSG_LENGTH(GENL_HDRLEN + header_len + reply_len); reply_msg = malloc(reply_msg_size); /* * Create socket */ memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; if ((sk = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_GENERIC)) == -1) fatal("error creating Netlink socket\n"); if ((bind(sk, (struct sockaddr*)&local, sizeof(local)) == -1) || (setsockopt(sk, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) == -1) || (setsockopt(sk, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) == -1)) { fatal("error creating Netlink socket\n"); } /* * Send request */ if (write_uninterrupted(sk, (char*)request_msg, request_msg_size) < 0) fatal("error sending message via Netlink\n"); /* * Wait for reply */ pfd.fd = sk; pfd.events = ~POLLOUT; if ((poll(&pfd, 1, 3000) != 1) || !(pfd.revents & POLLIN)) fatal("no reply detected from Netlink\n"); /* * Read reply */ len = recv(sk, (char*)reply_msg, reply_msg_size, 0); if (len < 0) fatal("error receiving reply message via Netlink\n"); close(sk); /* * Validate response */ if (!NLMSG_OK(&reply_msg->n, len)) fatal("invalid reply message received via Netlink\n"); if (reply_msg->n.nlmsg_type == NLMSG_ERROR) { len = -1; goto out; } if ((request_msg->n.nlmsg_type != reply_msg->n.nlmsg_type) || (request_msg->n.nlmsg_seq != reply_msg->n.nlmsg_seq)) fatal("unexpected message received via Netlink\n"); /* * Copy reply header */ len -= NLMSG_LENGTH(GENL_HDRLEN); if (len < header_len) fatal("too small reply message received via Netlink\n"); if (header_len > 0) memcpy(header, &reply_msg->payload[0], header_len); /* * Copy reply payload */ len -= header_len; if (len > reply_len) fatal("reply message too large to copy\n"); if (len > 0) memcpy(reply, &reply_msg->payload[header_len], len); out: free(request_msg); free(reply_msg); return len; }
// entry point int main (int argc, char **argv) { int rc = 0; uint64_t seqnum = 0; int opt_index = 0; int c = 0; int fd = 0; char *tmp = NULL; char event_buf[8192]; char event_buf_tmp[8292]; // has to hold event_buf plus an 8-byte sequence number char event_path[PATH_MAX + 1]; bool have_seqnum = false; ssize_t nr = 0; char const *required_fields[] = { "\nSUBSYSTEM=", "\nDEVPATH=", NULL }; char target_queues[PATH_MAX + 1]; // default event queue memset (g_dev_events, 0, PATH_MAX + 1); memset (target_queues, 0, PATH_MAX + 1); memset (event_path, 0, PATH_MAX + 1); strcpy (g_dev_events, DEFAULT_DEV_EVENTS); static struct option opts[] = { {"source-queue", required_argument, 0, 's'}, {"seqnum", required_argument, 0, 'n'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; char const *optstr = "n:s:h"; while (rc == 0 && c != -1) { c = getopt_long (argc, argv, optstr, opts, &opt_index); if (c == -1) { break; } switch (c) { case 's': { memset (g_dev_events, 0, PATH_MAX); strncpy (g_dev_events, optarg, PATH_MAX); break; } case 'n': { seqnum = (uint64_t) strtoull (optarg, &tmp, 10); if (seqnum == 0 && (tmp == optarg || *tmp != '\0')) { usage (argv[0]); exit (1); } have_seqnum = true; break; } case 'h': { help (argv[0]); exit (0); } default: { fprintf (stderr, "[ERROR] %s: Unrecognized option '%c'\n", argv[0], c); usage (argv[0]); exit (1); } } } // get the event memset (event_buf, 0, 8192); nr = read_uninterrupted (STDIN_FILENO, event_buf, 8192); if (nr <= 0) { rc = -errno; fprintf (stderr, "[ERROR] %s: Failed to read event from stdin: %s\n", argv[0], strerror (-rc)); exit (1); } // simple sanity check for requirements for (int i = 0; required_fields[i] != NULL; i++) { if (strstr (event_buf, required_fields[i]) == NULL) { // head of line? with no leading '\n'? if (strncmp (event_buf, required_fields[i] + 1, strlen (required_fields[i]) - 1) != 0) { fprintf (stderr, "[ERROR] %s: Missing required field '%s'\n", argv[0], required_fields[i] + 1); fprintf (stderr, "[ERROR] %s: Pass -h for a list of required fields\n", argv[0]); exit (1); } } } // do we have a seqnum? if (!have_seqnum) { char *seqnum_str = strstr (event_buf, "SEQNUM="); // go find it in the device event if (seqnum_str == NULL) { fprintf (stderr, "[ERROR] %s: Missing SEQNUM. Pass -n or include SEQNUM= in the input.\n", argv[0]); exit (1); } // is it a valid seqnum? seqnum = (uint64_t) strtoull (seqnum_str + strlen ("SEQNUM="), &tmp, 10); if (seqnum == 0 && (tmp == seqnum_str + strlen ("SEQNUM=") || *tmp != '\n')) { // invalid seqnum fprintf (stderr, "[ERROR] %s: Invalid SEQNUM. Pass -n or include a valid SEQNUM in the input.\n", argv[0]); exit (1); } } // send it off! make_event_path (seqnum, event_path); fd = open_event (event_path); if (fd < 0) { fprintf (stderr, "[ERROR] %s: Failed to open '%s': %s\n", argv[0], event_path, strerror (-fd)); exit (1); } rc = write_uninterrupted (fd, event_buf, nr); if (rc < 0) { fprintf (stderr, "[ERROR] %s: Failed to write '%s': %s\n", argv[0], event_path, strerror (-fd)); clear_event (event_buf); close (fd); exit (1); } // propagate.... rc = multicast_event (seqnum, event_path); if (rc < 0) { fprintf (stderr, "[ERROR] %s: Failed to multicast '%s': %s\n", argv[0], event_path, strerror (-rc)); clear_event (event_buf); close (fd); exit (1); } // done! clear_event (event_path); close (fd); return 0; }