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 57static int need_exit; 58static __u32 seq; 59 60static 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 = NLMSG_LENGTH(size - sizeof(*nlh)); 75 nlh->nlmsg_flags = 0; 76 77 m = NLMSG_DATA(nlh); 78 memcpy(m, msg, sizeof(*m) + msg->len); 79 80 err = send(s, nlh, size, 0); 81 if (err == -1) 82 ulog("Failed to send: %s [%d].\n", 83 strerror(errno), errno); 84 85 return err; 86} 87 88static void usage(void) 89{ 90 printf( 91 "Usage: ucon [options] [output file]\n" 92 "\n" 93 "\t-h\tthis help screen\n" 94 "\t-s\tsend buffers to the test module\n" 95 "\n" 96 "The default behavior of ucon is to subscribe to the test module\n" 97 "and wait for state messages. Any ones received are dumped to the\n" 98 "specified output file (or stdout). The test module is assumed to\n" 99 "have an id of {%u.%u}\n" 100 "\n" 101 "If you get no output, then verify the cn_test module id matches\n" 102 "the expected id above.\n" 103 , CN_TEST_IDX, CN_TEST_VAL 104 ); 105} 106 107int main(int argc, char *argv[]) 108{ 109 int s; 110 char buf[1024]; 111 int len; 112 struct nlmsghdr *reply; 113 struct sockaddr_nl l_local; 114 struct cn_msg *data; 115 FILE *out; 116 time_t tm; 117 struct pollfd pfd; 118 bool send_msgs = false; 119 120 while ((s = getopt(argc, argv, "hs")) != -1) { 121 switch (s) { 122 case 's': 123 send_msgs = true; 124 break; 125 126 case 'h': 127 usage(); 128 return 0; 129 130 default: 131 /* getopt() outputs an error for us */ 132 usage(); 133 return 1; 134 } 135 } 136 137 if (argc != optind) { 138 out = fopen(argv[optind], "a+"); 139 if (!out) { 140 ulog("Unable to open %s for writing: %s\n", 141 argv[1], strerror(errno)); 142 out = stdout; 143 } 144 } else 145 out = stdout; 146 147 memset(buf, 0, sizeof(buf)); 148 149 s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); 150 if (s == -1) { 151 perror("socket"); 152 return -1; 153 } 154 155 l_local.nl_family = AF_NETLINK; 156 l_local.nl_groups = -1; /* bitmask of requested groups */ 157 l_local.nl_pid = 0; 158 159 ulog("subscribing to %u.%u\n", CN_TEST_IDX, CN_TEST_VAL); 160 161 if (bind(s, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)) == -1) { 162 perror("bind"); 163 close(s); 164 return -1; 165 } 166 167 if (send_msgs) { 168 int i, j; 169 170 memset(buf, 0, sizeof(buf)); 171 172 data = (struct cn_msg *)buf; 173 174 data->id.idx = CN_TEST_IDX; 175 data->id.val = CN_TEST_VAL; 176 data->seq = seq++; 177 data->ack = 0; 178 data->len = 0; 179 180 for (j=0; j<10; ++j) { 181 for (i=0; i<1000; ++i) { 182 len = netlink_send(s, data); 183 } 184 185 ulog("%d messages have been sent to %08x.%08x.\n", i, data->id.idx, data->id.val); 186 } 187 188 return 0; 189 } 190 191 192 pfd.fd = s; 193 194 while (!need_exit) { 195 pfd.events = POLLIN; 196 pfd.revents = 0; 197 switch (poll(&pfd, 1, -1)) { 198 case 0: 199 need_exit = 1; 200 break; 201 case -1: 202 if (errno != EINTR) { 203 need_exit = 1; 204 break; 205 } 206 continue; 207 } 208 if (need_exit) 209 break; 210 211 memset(buf, 0, sizeof(buf)); 212 len = recv(s, buf, sizeof(buf), 0); 213 if (len == -1) { 214 perror("recv buf"); 215 close(s); 216 return -1; 217 } 218 reply = (struct nlmsghdr *)buf; 219 220 switch (reply->nlmsg_type) { 221 case NLMSG_ERROR: 222 fprintf(out, "Error message received.\n"); 223 fflush(out); 224 break; 225 case NLMSG_DONE: 226 data = (struct cn_msg *)NLMSG_DATA(reply); 227 228 time(&tm); 229 fprintf(out, "%.24s : [%x.%x] [%08u.%08u].\n", 230 ctime(&tm), data->id.idx, data->id.val, data->seq, data->ack); 231 fflush(out); 232 break; 233 default: 234 break; 235 } 236 } 237 238 close(s); 239 return 0; 240} 241