OSDN Git Service

Add Android.mk
[android-x86/external-wireless-tools.git] / wireless_tools / iwevent.c
1 /*
2  *      Wireless Tools
3  *
4  *              Jean II - HPL 99->04
5  *
6  * Main code for "iwevent". This listent for wireless events on rtnetlink.
7  * You need to link this code against "iwcommon.c" and "-lm".
8  *
9  * Part of this code is from Alexey Kuznetsov, part is from Casey Carter,
10  * I've just put the pieces together...
11  * By the way, if you know a way to remove the root restrictions, tell me
12  * about it...
13  *
14  * This file is released under the GPL license.
15  *     Copyright (c) 1997-2004 Jean Tourrilhes <jt@hpl.hp.com>
16  */
17
18 /***************************** INCLUDES *****************************/
19
20 #include "iwlib-private.h"              /* Private header */
21
22 #include <linux/netlink.h>
23 #include <linux/rtnetlink.h>
24
25 #include <getopt.h>
26 #include <time.h>
27 #include <sys/time.h>
28
29 /* Ugly backward compatibility :-( */
30 #ifndef IFLA_WIRELESS
31 #define IFLA_WIRELESS   (IFLA_MASTER + 1)
32 #endif /* IFLA_WIRELESS */
33
34 /****************************** TYPES ******************************/
35
36 /*
37  * Static information about wireless interface.
38  * We cache this info for performance reason.
39  */
40 typedef struct wireless_iface
41 {
42   /* Linked list */
43   struct wireless_iface *       next;
44
45   /* Interface identification */
46   int           ifindex;                /* Interface index == black magic */
47
48   /* Interface data */
49   char                  ifname[IFNAMSIZ + 1];   /* Interface name */
50   struct iw_range       range;                  /* Wireless static data */
51   int                   has_range;
52 } wireless_iface;
53
54 /**************************** VARIABLES ****************************/
55
56 /* Cache of wireless interfaces */
57 struct wireless_iface * interface_cache = NULL;
58
59 /************************ RTNETLINK HELPERS ************************/
60 /*
61  * The following code is extracted from :
62  * ----------------------------------------------
63  * libnetlink.c RTnetlink service routines.
64  *
65  *              This program is free software; you can redistribute it and/or
66  *              modify it under the terms of the GNU General Public License
67  *              as published by the Free Software Foundation; either version
68  *              2 of the License, or (at your option) any later version.
69  *
70  * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
71  * -----------------------------------------------
72  */
73
74 struct rtnl_handle
75 {
76         int                     fd;
77         struct sockaddr_nl      local;
78         struct sockaddr_nl      peer;
79         __u32                   seq;
80         __u32                   dump;
81 };
82
83 static inline void rtnl_close(struct rtnl_handle *rth)
84 {
85         close(rth->fd);
86 }
87
88 static inline int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
89 {
90         int addr_len;
91
92         memset(rth, 0, sizeof(rth));
93
94         rth->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
95         if (rth->fd < 0) {
96                 perror("Cannot open netlink socket");
97                 return -1;
98         }
99
100         memset(&rth->local, 0, sizeof(rth->local));
101         rth->local.nl_family = AF_NETLINK;
102         rth->local.nl_groups = subscriptions;
103
104         if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
105                 perror("Cannot bind netlink socket");
106                 return -1;
107         }
108         addr_len = sizeof(rth->local);
109         if (getsockname(rth->fd, (struct sockaddr*)&rth->local,
110                         (socklen_t *) &addr_len) < 0) {
111                 perror("Cannot getsockname");
112                 return -1;
113         }
114         if (addr_len != sizeof(rth->local)) {
115                 fprintf(stderr, "Wrong address length %d\n", addr_len);
116                 return -1;
117         }
118         if (rth->local.nl_family != AF_NETLINK) {
119                 fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
120                 return -1;
121         }
122         rth->seq = time(NULL);
123         return 0;
124 }
125
126 /******************* WIRELESS INTERFACE DATABASE *******************/
127 /*
128  * We keep a few information about each wireless interface on the
129  * system. This avoid to query this info at each event, therefore
130  * reducing overhead.
131  *
132  * Each interface is indexed by the 'ifindex'. As opposed to interface
133  * names, 'ifindex' are never reused (even if you reactivate the same
134  * hardware), so the data we cache will never apply to the wrong
135  * interface.
136  * Because of that, we are pretty lazy when it come to purging the
137  * cache...
138  */
139
140 /*------------------------------------------------------------------*/
141 /*
142  * Get name of interface based on interface index...
143  */
144 static inline int
145 index2name(int          skfd,
146            int          ifindex,
147            char *       name)
148 {
149   struct ifreq  irq;
150   int           ret = 0;
151
152   memset(name, 0, IFNAMSIZ + 1);
153
154   /* Get interface name */
155   irq.ifr_ifindex = ifindex;
156   if(ioctl(skfd, SIOCGIFNAME, &irq) < 0)
157     ret = -1;
158   else
159     strncpy(name, irq.ifr_name, IFNAMSIZ);
160
161   return(ret);
162 }
163
164 /*------------------------------------------------------------------*/
165 /*
166  * Get interface data from cache or live interface
167  */
168 static struct wireless_iface *
169 iw_get_interface_data(int       ifindex)
170 {
171   struct wireless_iface *       curr;
172   int                           skfd = -1;      /* ioctl socket */
173
174   /* Search for it in the database */
175   curr = interface_cache;
176   while(curr != NULL)
177     {
178       /* Match ? */
179       if(curr->ifindex == ifindex)
180         {
181           //printf("Cache : found %d-%s\n", curr->ifindex, curr->ifname);
182
183           /* Return */
184           return(curr);
185         }
186       /* Next entry */
187       curr = curr->next;
188     }
189
190   /* Create a channel to the NET kernel. Doesn't happen too often, so
191    * socket creation overhead is minimal... */
192   if((skfd = iw_sockets_open()) < 0)
193     {
194       perror("iw_sockets_open");
195       return(NULL);
196     }
197
198   /* Create new entry, zero, init */
199   curr = calloc(1, sizeof(struct wireless_iface));
200   if(!curr)
201     {
202       fprintf(stderr, "Malloc failed\n");
203       return(NULL);
204     }
205   curr->ifindex = ifindex;
206
207   /* Extract static data */
208   if(index2name(skfd, ifindex, curr->ifname) < 0)
209     {
210       perror("index2name");
211       free(curr);
212       return(NULL);
213     }
214   curr->has_range = (iw_get_range_info(skfd, curr->ifname, &curr->range) >= 0);
215   //printf("Cache : create %d-%s\n", curr->ifindex, curr->ifname);
216
217   /* Done */
218   iw_sockets_close(skfd);
219
220   /* Link it */
221   curr->next = interface_cache;
222   interface_cache = curr;
223
224   return(curr);
225 }
226
227 /*------------------------------------------------------------------*/
228 /*
229  * Remove interface data from cache (if it exist)
230  */
231 static void
232 iw_del_interface_data(int       ifindex)
233 {
234   struct wireless_iface *       curr;
235   struct wireless_iface *       prev = NULL;
236   struct wireless_iface *       next;
237
238   /* Go through the list, find the interface, kills it */
239   curr = interface_cache;
240   while(curr)
241     {
242       next = curr->next;
243
244       /* Got a match ? */
245       if(curr->ifindex == ifindex)
246         {
247           /* Unlink. Root ? */
248           if(!prev)
249             interface_cache = next;
250           else
251             prev->next = next;
252           //printf("Cache : purge %d-%s\n", curr->ifindex, curr->ifname);
253
254           /* Destroy */
255           free(curr);
256         }
257       else
258         {
259           /* Keep as previous */
260           prev = curr;
261         }
262
263       /* Next entry */
264       curr = next;
265     }
266 }
267
268 /********************* WIRELESS EVENT DECODING *********************/
269 /*
270  * Parse the Wireless Event and print it out
271  */
272
273 /*------------------------------------------------------------------*/
274 /*
275  * Dump a buffer as a serie of hex
276  * Maybe should go in iwlib...
277  * Maybe we should have better formatting like iw_print_key...
278  */
279 static char *
280 iw_hexdump(char *               buf,
281            size_t               buflen,
282            const unsigned char *data,
283            size_t               datalen)
284 {
285   size_t        i;
286   char *        pos = buf;
287
288   for(i = 0; i < datalen; i++)
289     pos += snprintf(pos, buf + buflen - pos, "%02X", data[i]);
290   return buf;
291 }
292
293 /*------------------------------------------------------------------*/
294 /*
295  * Print one element from the scanning results
296  */
297 static inline int
298 print_event_token(struct iw_event *     event,          /* Extracted token */
299                   struct iw_range *     iw_range,       /* Range info */
300                   int                   has_range)
301 {
302   char          buffer[128];    /* Temporary buffer */
303   char          buffer2[30];    /* Temporary buffer */
304   char *        prefix = (IW_IS_GET(event->cmd) ? "New" : "Set");
305
306   /* Now, let's decode the event */
307   switch(event->cmd)
308     {
309       /* ----- set events ----- */
310       /* Events that result from a "SET XXX" operation by the user */
311     case SIOCSIWNWID:
312       if(event->u.nwid.disabled)
313         printf("Set NWID:off/any\n");
314       else
315         printf("Set NWID:%X\n", event->u.nwid.value);
316       break;
317     case SIOCSIWFREQ:
318     case SIOCGIWFREQ:
319       {
320         double          freq;                   /* Frequency/channel */
321         int             channel = -1;           /* Converted to channel */
322         freq = iw_freq2float(&(event->u.freq));
323         if(has_range)
324           {
325             if(freq < KILO)
326               /* Convert channel to frequency if possible */
327               channel = iw_channel_to_freq((int) freq, &freq, iw_range);
328             else
329               /* Convert frequency to channel if possible */
330               channel = iw_freq_to_channel(freq, iw_range);
331           }
332         iw_print_freq(buffer, sizeof(buffer),
333                       freq, channel, event->u.freq.flags);
334         printf("%s %s\n", prefix, buffer);
335       }
336       break;
337     case SIOCSIWMODE:
338       printf("Set Mode:%s\n",
339              iw_operation_mode[event->u.mode]);
340       break;
341     case SIOCSIWESSID:
342     case SIOCGIWESSID:
343       {
344         char essid[4*IW_ESSID_MAX_SIZE + 1];
345         memset(essid, '\0', sizeof(essid));
346         if((event->u.essid.pointer) && (event->u.essid.length))
347           iw_essid_escape(essid,
348                           event->u.essid.pointer, event->u.essid.length);
349         if(event->u.essid.flags)
350           {
351             /* Does it have an ESSID index ? */
352             if((event->u.essid.flags & IW_ENCODE_INDEX) > 1)
353               printf("%s ESSID:\"%s\" [%d]\n", prefix, essid,
354                      (event->u.essid.flags & IW_ENCODE_INDEX));
355             else
356               printf("%s ESSID:\"%s\"\n", prefix, essid);
357           }
358         else
359           printf("%s ESSID:off/any\n", prefix);
360       }
361       break;
362     case SIOCSIWENCODE:
363       {
364         unsigned char   key[IW_ENCODING_TOKEN_MAX];
365         if(event->u.data.pointer)
366           memcpy(key, event->u.data.pointer, event->u.data.length);
367         else
368           event->u.data.flags |= IW_ENCODE_NOKEY;
369         printf("Set Encryption key:{%X}", event->u.data.flags);
370         if(event->u.data.flags & IW_ENCODE_DISABLED)
371           printf("off\n");
372         else
373           {
374             /* Display the key */
375             iw_print_key(buffer, sizeof(buffer), key, event->u.data.length,
376                          event->u.data.flags);
377             printf("%s", buffer);
378
379             /* Other info... */
380             if((event->u.data.flags & IW_ENCODE_INDEX) > 1)
381               printf(" [%d]", event->u.data.flags & IW_ENCODE_INDEX);
382             if(event->u.data.flags & IW_ENCODE_RESTRICTED)
383               printf("   Security mode:restricted");
384             if(event->u.data.flags & IW_ENCODE_OPEN)
385               printf("   Security mode:open");
386             printf("\n");
387           }
388       }
389       break;
390       /* ----- driver events ----- */
391       /* Events generated by the driver when something important happens */
392     case SIOCGIWAP:
393       printf("New Access Point/Cell address:%s\n",
394              iw_sawap_ntop(&event->u.ap_addr, buffer));
395       break;
396     case SIOCGIWSCAN:
397       printf("Scan request completed\n");
398       break;
399     case IWEVTXDROP:
400       printf("Tx packet dropped:%s\n",
401              iw_saether_ntop(&event->u.addr, buffer));
402       break;
403     case IWEVCUSTOM:
404       {
405         char custom[IW_CUSTOM_MAX+1];
406         memset(custom, '\0', sizeof(custom));
407         if((event->u.data.pointer) && (event->u.data.length))
408           memcpy(custom, event->u.data.pointer, event->u.data.length);
409         printf("Custom driver event:%s\n", custom);
410       }
411       break;
412     case IWEVREGISTERED:
413       printf("Registered node:%s\n",
414              iw_saether_ntop(&event->u.addr, buffer));
415       break;
416     case IWEVEXPIRED:
417       printf("Expired node:%s\n",
418              iw_saether_ntop(&event->u.addr, buffer));
419       break;
420     case SIOCGIWTHRSPY:
421       {
422         struct iw_thrspy        threshold;
423         if((event->u.data.pointer) && (event->u.data.length))
424           {
425             memcpy(&threshold, event->u.data.pointer,
426                    sizeof(struct iw_thrspy));
427             printf("Spy threshold crossed on address:%s\n",
428                    iw_saether_ntop(&threshold.addr, buffer));
429             iw_print_stats(buffer, sizeof(buffer),
430                            &threshold.qual, iw_range, has_range);
431             printf("                            Link %s\n", buffer);
432           }
433         else
434           printf("Invalid Spy Threshold event\n");
435       }
436       break;
437       /* ----- driver WPA events ----- */
438       /* Events generated by the driver, used for WPA operation */
439     case IWEVMICHAELMICFAILURE:
440       if(event->u.data.length >= sizeof(struct iw_michaelmicfailure))
441         {
442           struct iw_michaelmicfailure mf;
443           memcpy(&mf, event->u.data.pointer, sizeof(mf));
444           printf("Michael MIC failure flags:0x%X src_addr:%s tsc:%s\n",
445                  mf.flags,
446                  iw_saether_ntop(&mf.src_addr, buffer2),
447                  iw_hexdump(buffer, sizeof(buffer),
448                             mf.tsc, IW_ENCODE_SEQ_MAX_SIZE));
449         }
450       break;
451     case IWEVASSOCREQIE:
452       printf("Association Request IEs:%s\n",
453              iw_hexdump(buffer, sizeof(buffer),
454                         event->u.data.pointer, event->u.data.length));
455       break;
456     case IWEVASSOCRESPIE:
457       printf("Association Response IEs:%s\n",
458              iw_hexdump(buffer, sizeof(buffer),
459                         event->u.data.pointer, event->u.data.length));
460       break;
461     case IWEVPMKIDCAND:
462       if(event->u.data.length >= sizeof(struct iw_pmkid_cand))
463         {
464           struct iw_pmkid_cand cand;
465           memcpy(&cand, event->u.data.pointer, sizeof(cand));
466           printf("PMKID candidate flags:0x%X index:%d bssid:%s\n",
467                  cand.flags, cand.index,
468                  iw_saether_ntop(&cand.bssid, buffer));
469         }
470       break;
471       /* ----- junk ----- */
472       /* other junk not currently in use */
473     case SIOCGIWRATE:
474       iw_print_bitrate(buffer, sizeof(buffer), event->u.bitrate.value);
475       printf("New Bit Rate:%s\n", buffer);
476       break;
477     case SIOCGIWNAME:
478       printf("Protocol:%-1.16s\n", event->u.name);
479       break;
480     case IWEVQUAL:
481       {
482         event->u.qual.updated = 0x0;    /* Not that reliable, disable */
483         iw_print_stats(buffer, sizeof(buffer),
484                        &event->u.qual, iw_range, has_range);
485         printf("Link %s\n", buffer);
486         break;
487       }
488     default:
489       printf("(Unknown Wireless event 0x%04X)\n", event->cmd);
490     }   /* switch(event->cmd) */
491
492   return(0);
493 }
494
495 /*------------------------------------------------------------------*/
496 /*
497  * Print out all Wireless Events part of the RTNetlink message
498  * Most often, there will be only one event per message, but
499  * just make sure we read everything...
500  */
501 static inline int
502 print_event_stream(int          ifindex,
503                    char *       data,
504                    int          len)
505 {
506   struct iw_event       iwe;
507   struct stream_descr   stream;
508   int                   i = 0;
509   int                   ret;
510   char                  buffer[64];
511   struct timeval        recv_time;
512   struct timezone       tz;
513   struct wireless_iface *       wireless_data;
514
515   /* Get data from cache */
516   wireless_data = iw_get_interface_data(ifindex);
517   if(wireless_data == NULL)
518     return(-1);
519
520   /* Print received time in readable form */
521   gettimeofday(&recv_time, &tz);
522   iw_print_timeval(buffer, sizeof(buffer), &recv_time, &tz);
523
524   iw_init_event_stream(&stream, data, len);
525   do
526     {
527       /* Extract an event and print it */
528       ret = iw_extract_event_stream(&stream, &iwe,
529                                     wireless_data->range.we_version_compiled);
530       if(ret != 0)
531         {
532           if(i++ == 0)
533             printf("%s   %-8.16s ", buffer, wireless_data->ifname);
534           else
535             printf("                           ");
536           if(ret > 0)
537             print_event_token(&iwe,
538                               &wireless_data->range, wireless_data->has_range);
539           else
540             printf("(Invalid event)\n");
541           /* Push data out *now*, in case we are redirected to a pipe */
542           fflush(stdout);
543         }
544     }
545   while(ret > 0);
546
547   return(0);
548 }
549
550 /*********************** RTNETLINK EVENT DUMP***********************/
551 /*
552  * Dump the events we receive from rtnetlink
553  * This code is mostly from Casey
554  */
555
556 /*------------------------------------------------------------------*/
557 /*
558  * Respond to a single RTM_NEWLINK event from the rtnetlink socket.
559  */
560 static int
561 LinkCatcher(struct nlmsghdr *nlh)
562 {
563   struct ifinfomsg* ifi;
564
565 #if 0
566   fprintf(stderr, "nlmsg_type = %d.\n", nlh->nlmsg_type);
567 #endif
568
569   ifi = NLMSG_DATA(nlh);
570
571   /* Code is ugly, but sort of works - Jean II */
572
573   /* If interface is getting destoyed */
574   if(nlh->nlmsg_type == RTM_DELLINK)
575     {
576       /* Remove from cache (if in cache) */
577       iw_del_interface_data(ifi->ifi_index);
578       return 0;
579     }
580
581   /* Only keep add/change events */
582   if(nlh->nlmsg_type != RTM_NEWLINK)
583     return 0;
584
585   /* Check for attributes */
586   if (nlh->nlmsg_len > NLMSG_ALIGN(sizeof(struct ifinfomsg)))
587     {
588       int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct ifinfomsg));
589       struct rtattr *attr = (void *) ((char *) ifi +
590                                       NLMSG_ALIGN(sizeof(struct ifinfomsg)));
591
592       while (RTA_OK(attr, attrlen))
593         {
594           /* Check if the Wireless kind */
595           if(attr->rta_type == IFLA_WIRELESS)
596             {
597               /* Go to display it */
598               print_event_stream(ifi->ifi_index,
599                                  (char *) attr + RTA_ALIGN(sizeof(struct rtattr)),
600                                  attr->rta_len - RTA_ALIGN(sizeof(struct rtattr)));
601             }
602           attr = RTA_NEXT(attr, attrlen);
603         }
604     }
605
606   return 0;
607 }
608
609 /* ---------------------------------------------------------------- */
610 /*
611  * We must watch the rtnelink socket for events.
612  * This routine handles those events (i.e., call this when rth.fd
613  * is ready to read).
614  */
615 static inline void
616 handle_netlink_events(struct rtnl_handle *      rth)
617 {
618   while(1)
619     {
620       struct sockaddr_nl sanl;
621       socklen_t sanllen = sizeof(struct sockaddr_nl);
622
623       struct nlmsghdr *h;
624       int amt;
625       char buf[8192];
626
627       amt = recvfrom(rth->fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&sanl, &sanllen);
628       if(amt < 0)
629         {
630           if(errno != EINTR && errno != EAGAIN)
631             {
632               fprintf(stderr, "%s: error reading netlink: %s.\n",
633                       __PRETTY_FUNCTION__, strerror(errno));
634             }
635           return;
636         }
637
638       if(amt == 0)
639         {
640           fprintf(stderr, "%s: EOF on netlink??\n", __PRETTY_FUNCTION__);
641           return;
642         }
643
644       h = (struct nlmsghdr*)buf;
645       while(amt >= (int)sizeof(*h))
646         {
647           int len = h->nlmsg_len;
648           int l = len - sizeof(*h);
649
650           if(l < 0 || len > amt)
651             {
652               fprintf(stderr, "%s: malformed netlink message: len=%d\n", __PRETTY_FUNCTION__, len);
653               break;
654             }
655
656           switch(h->nlmsg_type)
657             {
658             case RTM_NEWLINK:
659             case RTM_DELLINK:
660               LinkCatcher(h);
661               break;
662             default:
663 #if 0
664               fprintf(stderr, "%s: got nlmsg of type %#x.\n", __PRETTY_FUNCTION__, h->nlmsg_type);
665 #endif
666               break;
667             }
668
669           len = NLMSG_ALIGN(len);
670           amt -= len;
671           h = (struct nlmsghdr*)((char*)h + len);
672         }
673
674       if(amt > 0)
675         fprintf(stderr, "%s: remnant of size %d on netlink\n", __PRETTY_FUNCTION__, amt);
676     }
677 }
678
679 /**************************** MAIN LOOP ****************************/
680
681 /* ---------------------------------------------------------------- */
682 /*
683  * Wait until we get an event
684  */
685 static inline int
686 wait_for_event(struct rtnl_handle *     rth)
687 {
688 #if 0
689   struct timeval        tv;     /* Select timeout */
690 #endif
691
692   /* Forever */
693   while(1)
694     {
695       fd_set            rfds;           /* File descriptors for select */
696       int               last_fd;        /* Last fd */
697       int               ret;
698
699       /* Guess what ? We must re-generate rfds each time */
700       FD_ZERO(&rfds);
701       FD_SET(rth->fd, &rfds);
702       last_fd = rth->fd;
703
704       /* Wait until something happens */
705       ret = select(last_fd + 1, &rfds, NULL, NULL, NULL);
706
707       /* Check if there was an error */
708       if(ret < 0)
709         {
710           if(errno == EAGAIN || errno == EINTR)
711             continue;
712           fprintf(stderr, "Unhandled signal - exiting...\n");
713           break;
714         }
715
716       /* Check if there was a timeout */
717       if(ret == 0)
718         {
719           continue;
720         }
721
722       /* Check for interface discovery events. */
723       if(FD_ISSET(rth->fd, &rfds))
724         handle_netlink_events(rth);
725     }
726
727   return(0);
728 }
729
730 /******************************* MAIN *******************************/
731
732 /* ---------------------------------------------------------------- */
733 /*
734  * helper ;-)
735  */
736 static void
737 iw_usage(int status)
738 {
739   fputs("Usage: iwevent [OPTIONS]\n"
740         "   Monitors and displays Wireless Events.\n"
741         "   Options are:\n"
742         "     -h,--help     Print this message.\n"
743         "     -v,--version  Show version of this program.\n",
744         status ? stderr : stdout);
745   exit(status);
746 }
747 /* Command line options */
748 static const struct option long_opts[] = {
749   { "help", no_argument, NULL, 'h' },
750   { "version", no_argument, NULL, 'v' },
751   { NULL, 0, NULL, 0 }
752 };
753
754 /* ---------------------------------------------------------------- */
755 /*
756  * main body of the program
757  */
758 int
759 main(int        argc,
760      char *     argv[])
761 {
762   struct rtnl_handle    rth;
763   int opt;
764
765   /* Check command line options */
766   while((opt = getopt_long(argc, argv, "hv", long_opts, NULL)) > 0)
767     {
768       switch(opt)
769         {
770         case 'h':
771           iw_usage(0);
772           break;
773
774         case 'v':
775           return(iw_print_version_info("iwevent"));
776           break;
777
778         default:
779           iw_usage(1);
780           break;
781         }
782     }
783   if(optind < argc)
784     {
785       fputs("Too many arguments.\n", stderr);
786       iw_usage(1);
787     }
788
789   /* Open netlink channel */
790   if(rtnl_open(&rth, RTMGRP_LINK) < 0)
791     {
792       perror("Can't initialize rtnetlink socket");
793       return(1);
794     }
795
796   fprintf(stderr, "Waiting for Wireless Events from interfaces...\n");
797
798   /* Do what we have to do */
799   wait_for_event(&rth);
800
801   /* Cleanup - only if you are pedantic */
802   rtnl_close(&rth);
803
804   return(0);
805 }