nlmon.c (2840B)
1 /* 2 * Copy me if you can. 3 * by 20h 4 */ 5 6 #include <unistd.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <stdarg.h> 10 #include <poll.h> 11 #include <ctype.h> 12 #include <string.h> 13 #include <errno.h> 14 #include <sys/socket.h> 15 #include <sys/types.h> 16 #include <linux/types.h> 17 #include <linux/netlink.h> 18 19 #include "arg.h" 20 21 char *argv0; 22 23 void 24 edie(char *fmt, ...) 25 { 26 va_list fmtargs; 27 28 va_start(fmtargs, fmt); 29 vfprintf(stderr, fmt, fmtargs); 30 va_end(fmtargs); 31 fprintf(stderr, ": "); 32 33 perror(NULL); 34 35 exit(1); 36 } 37 38 void 39 die(char *fmt, ...) 40 { 41 va_list fmtargs; 42 43 va_start(fmtargs, fmt); 44 vfprintf(stderr, fmt, fmtargs); 45 va_end(fmtargs); 46 47 exit(1); 48 } 49 50 void 51 usage(void) 52 { 53 die("usage: %s [-h] [-kl] [-f subsystem]\n", argv0); 54 } 55 56 int 57 main(int argc, char *argv[]) 58 { 59 struct sockaddr_nl nls, cnls; 60 struct pollfd fds; 61 struct msghdr hdr; 62 struct iovec iov; 63 char buf[4097], obuf[4098], *subsystem; 64 int i, len, olen, slen, showudev, showkernel; 65 66 showkernel = 1; 67 showudev = 1; 68 subsystem = NULL; 69 70 ARGBEGIN { 71 case 'f': 72 subsystem = EARGF(usage()); 73 break; 74 case 'k': 75 showudev = 0; 76 break; 77 case 'l': 78 showkernel = 0; 79 break; 80 default: 81 usage(); 82 } ARGEND; 83 84 memset(&nls, 0, sizeof(nls)); 85 nls.nl_family = AF_NETLINK; 86 nls.nl_pid = getpid(); 87 nls.nl_groups = -1; 88 89 /* 90 * The whole process of decoding the kernel messages is 91 * kept simple to avoid clashes with root rights. One 92 * goal of nlmon is to be able to decode the kernel 93 * messages in userspace. 94 */ 95 96 fds.events = POLLIN; 97 fds.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 98 if (fds.fd < 0) 99 edie("socket"); 100 101 /* 102 * Setting the sockopts here will require root rights. 103 * Just keep it that way and pretend to have a big 104 * enough buffer. 105 */ 106 107 if (bind(fds.fd, (void *)&nls, sizeof(nls))) 108 edie("bind"); 109 110 buf[sizeof(buf)-1] = '\0'; 111 while (poll(&fds, 1, -1) > -1) { 112 iov.iov_base = &buf; 113 iov.iov_len = sizeof(buf); 114 memset(&hdr, 0, sizeof(hdr)); 115 hdr.msg_iov = &iov; 116 hdr.msg_iovlen = 1; 117 hdr.msg_name = &cnls; 118 hdr.msg_namelen = sizeof(cnls); 119 120 len = recvmsg(fds.fd, &hdr, 0); 121 if (len < 0) { 122 if (errno == EINTR) 123 continue; 124 edie("recvmsg"); 125 } 126 if (len < 32 || len >= sizeof(buf)) 127 continue; 128 129 if (!memcmp(buf, "libudev", 8)) { 130 if (!showudev) 131 continue; 132 } else { 133 if (!showkernel) 134 continue; 135 136 /* 137 * Kernel messages should be from root. 138 */ 139 if (cnls.nl_pid > 0) 140 continue; 141 } 142 143 for (i = 0, olen = 0; i < len; i += slen + 1) { 144 slen = strlen(buf+i); 145 if (!slen || !strchr(buf+i, '=')) 146 continue; 147 if (subsystem && !strncmp(buf+i, "SUBSYSTEM=", 10) 148 && !strstr(buf+i+10, subsystem)) { 149 olen = 0; 150 break; 151 } 152 153 snprintf(obuf+olen, sizeof(obuf)-olen-2, 154 "%s\n", buf+i); 155 olen += slen + 1; 156 } 157 if (olen > 0) { 158 obuf[olen] = '\n'; 159 write(1, obuf, olen+1); 160 } 161 } 162 163 return 0; 164 } 165