1 /* 2 * ucon.c 3 * 4 * Copyright (c) 2004+ Evgeniy Polyakov <zbr@ioremap.net> 5 * 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22 #include <asm/types.h> 23 24 #include <sys/types.h> 25 #include <sys/socket.h> 26 #include <sys/poll.h> 27 28 #include <linux/netlink.h> 29 #include <linux/rtnetlink.h> 30 31 #include <arpa/inet.h> 32 33 #include <stdbool.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <unistd.h> 37 #include <string.h> 38 #include <errno.h> 39 #include <time.h> 40 #include <getopt.h> 41 42 #include <linux/connector.h> 43 44 #define DEBUG 45 #define NETLINK_CONNECTOR 11 46 47 /* Hopefully your userspace connector.h matches this kernel */ 48 #define CN_TEST_IDX CN_NETLINK_USERS + 3 49 #define CN_TEST_VAL 0x456 50 51 #ifdef DEBUG 52 #define ulog(f, a...) fprintf(stdout, f, ##a) 53 #else 54 #define ulog(f, a...) do {} while (0) 55 #endif 56 57 static int need_exit; 58 static __u32 seq; 59 60 static int netlink_send(int s, struct cn_msg *msg) 61 { 62 struct nlmsghdr *nlh; 63 unsigned int size; 64 int err; 65 char buf[128]; 66 struct cn_msg *m; 67 68 size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len); 69 70 nlh = (struct nlmsghdr *)buf; 71 nlh->nlmsg_seq = seq++; 72 nlh->nlmsg_pid = getpid(); 73 nlh->nlmsg_type = NLMSG_DONE; 74 nlh->nlmsg_len = size; 75 nlh->nlmsg_flags = 0; 76 77 m = NLMSG_DATA(nlh); 78 #if 0 79 ulog("%s: [%08x.%08x] len=%u, seq=%u, ack=%u.\n", 80 __func__, msg->id.idx, msg->id.val, msg->len, msg->seq, msg->ack); 81 #endif 82 memcpy(m, msg, sizeof(*m) + msg->len); 83 84 err = send(s, nlh, size, 0); 85 if (err == -1) 86 ulog("Failed to send: %s [%d].\n", 87 strerror(errno), errno); 88 89 return err; 90 } 91 92 static void usage(void) 93 { 94 printf( 95 "Usage: ucon [options] [output file]\n" 96 "\n" 97 "\t-h\tthis help screen\n" 98 "\t-s\tsend buffers to the test module\n" 99 "\n" 100 "The default behavior of ucon is to subscribe to the test module\n" 101 "and wait for state messages. Any ones received are dumped to the\n" 102 "specified output file (or stdout). The test module is assumed to\n" 103 "have an id of {%u.%u}\n" 104 "\n" 105 "If you get no output, then verify the cn_test module id matches\n" 106 "the expected id above.\n" 107 , CN_TEST_IDX, CN_TEST_VAL 108 ); 109 } 110 111 int main(int argc, char *argv[]) 112 { 113 int s; 114 char buf[1024]; 115 int len; 116 struct nlmsghdr *reply; 117 struct sockaddr_nl l_local; 118 struct cn_msg *data; 119 FILE *out; 120 time_t tm; 121 struct pollfd pfd; 122 bool send_msgs = false; 123 124 while ((s = getopt(argc, argv, "hs")) != -1) { 125 switch (s) { 126 case 's': 127 send_msgs = true; 128 break; 129 130 case 'h': 131 usage(); 132 return 0; 133 134 default: 135 /* getopt() outputs an error for us */ 136 usage(); 137 return 1; 138 } 139 } 140 141 if (argc != optind) { 142 out = fopen(argv[optind], "a+"); 143 if (!out) { 144 ulog("Unable to open %s for writing: %s\n", 145 argv[1], strerror(errno)); 146 out = stdout; 147 } 148 } else 149 out = stdout; 150 151 memset(buf, 0, sizeof(buf)); 152 153 s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); 154 if (s == -1) { 155 perror("socket"); 156 return -1; 157 } 158 159 l_local.nl_family = AF_NETLINK; 160 l_local.nl_groups = -1; /* bitmask of requested groups */ 161 l_local.nl_pid = 0; 162 163 ulog("subscribing to %u.%u\n", CN_TEST_IDX, CN_TEST_VAL); 164 165 if (bind(s, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)) == -1) { 166 perror("bind"); 167 close(s); 168 return -1; 169 } 170 171 #if 0 172 { 173 int on = 0x57; /* Additional group number */ 174 setsockopt(s, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &on, sizeof(on)); 175 } 176 #endif 177 if (send_msgs) { 178 int i, j; 179 180 memset(buf, 0, sizeof(buf)); 181 182 data = (struct cn_msg *)buf; 183 184 data->id.idx = CN_TEST_IDX; 185 data->id.val = CN_TEST_VAL; 186 data->seq = seq++; 187 data->ack = 0; 188 data->len = 0; 189 190 for (j=0; j<10; ++j) { 191 for (i=0; i<1000; ++i) { 192 len = netlink_send(s, data); 193 } 194 195 ulog("%d messages have been sent to %08x.%08x.\n", i, data->id.idx, data->id.val); 196 } 197 198 return 0; 199 } 200 201 202 pfd.fd = s; 203 204 while (!need_exit) { 205 pfd.events = POLLIN; 206 pfd.revents = 0; 207 switch (poll(&pfd, 1, -1)) { 208 case 0: 209 need_exit = 1; 210 break; 211 case -1: 212 if (errno != EINTR) { 213 need_exit = 1; 214 break; 215 } 216 continue; 217 } 218 if (need_exit) 219 break; 220 221 memset(buf, 0, sizeof(buf)); 222 len = recv(s, buf, sizeof(buf), 0); 223 if (len == -1) { 224 perror("recv buf"); 225 close(s); 226 return -1; 227 } 228 reply = (struct nlmsghdr *)buf; 229 230 switch (reply->nlmsg_type) { 231 case NLMSG_ERROR: 232 fprintf(out, "Error message received.\n"); 233 fflush(out); 234 break; 235 case NLMSG_DONE: 236 data = (struct cn_msg *)NLMSG_DATA(reply); 237 238 time(&tm); 239 fprintf(out, "%.24s : [%x.%x] [%08u.%08u].\n", 240 ctime(&tm), data->id.idx, data->id.val, data->seq, data->ack); 241 fflush(out); 242 break; 243 default: 244 break; 245 } 246 } 247 248 close(s); 249 return 0; 250 } 251