5 kx /*
5 kx * Wireless Tools
5 kx *
5 kx * Jean II - HPL 99->04
5 kx *
5 kx * Main code for "iwevent". This listent for wireless events on rtnetlink.
5 kx * You need to link this code against "iwcommon.c" and "-lm".
5 kx *
5 kx * Part of this code is from Alexey Kuznetsov, part is from Casey Carter,
5 kx * I've just put the pieces together...
5 kx * By the way, if you know a way to remove the root restrictions, tell me
5 kx * about it...
5 kx *
5 kx * This file is released under the GPL license.
5 kx * Copyright (c) 1997-2004 Jean Tourrilhes <jt@hpl.hp.com>
5 kx */
5 kx
5 kx /***************************** INCLUDES *****************************/
5 kx
5 kx #include "iwlib-private.h" /* Private header */
5 kx
5 kx #include <linux/netlink.h>
5 kx #include <linux/rtnetlink.h>
5 kx
5 kx #include <getopt.h>
5 kx #include <time.h>
5 kx #include <sys/time.h>
5 kx
5 kx /* Ugly backward compatibility :-( */
5 kx #ifndef IFLA_WIRELESS
5 kx #define IFLA_WIRELESS (IFLA_MASTER + 1)
5 kx #endif /* IFLA_WIRELESS */
5 kx
5 kx /****************************** TYPES ******************************/
5 kx
5 kx /*
5 kx * Static information about wireless interface.
5 kx * We cache this info for performance reason.
5 kx */
5 kx typedef struct wireless_iface
5 kx {
5 kx /* Linked list */
5 kx struct wireless_iface * next;
5 kx
5 kx /* Interface identification */
5 kx int ifindex; /* Interface index == black magic */
5 kx
5 kx /* Interface data */
5 kx char ifname[IFNAMSIZ + 1]; /* Interface name */
5 kx struct iw_range range; /* Wireless static data */
5 kx int has_range;
5 kx } wireless_iface;
5 kx
5 kx /**************************** VARIABLES ****************************/
5 kx
5 kx /* Cache of wireless interfaces */
5 kx struct wireless_iface * interface_cache = NULL;
5 kx
5 kx /************************ RTNETLINK HELPERS ************************/
5 kx /*
5 kx * The following code is extracted from :
5 kx * ----------------------------------------------
5 kx * libnetlink.c RTnetlink service routines.
5 kx *
5 kx * This program is free software; you can redistribute it and/or
5 kx * modify it under the terms of the GNU General Public License
5 kx * as published by the Free Software Foundation; either version
5 kx * 2 of the License, or (at your option) any later version.
5 kx *
5 kx * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
5 kx * -----------------------------------------------
5 kx */
5 kx
5 kx struct rtnl_handle
5 kx {
5 kx int fd;
5 kx struct sockaddr_nl local;
5 kx struct sockaddr_nl peer;
5 kx __u32 seq;
5 kx __u32 dump;
5 kx };
5 kx
5 kx static inline void rtnl_close(struct rtnl_handle *rth)
5 kx {
5 kx close(rth->fd);
5 kx }
5 kx
5 kx static inline int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
5 kx {
5 kx int addr_len;
5 kx
5 kx addr_len = sizeof(rth);
5 kx memset(rth, 0, addr_len);
5 kx
5 kx rth->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
5 kx if (rth->fd < 0) {
5 kx perror("Cannot open netlink socket");
5 kx return -1;
5 kx }
5 kx
5 kx memset(&rth->local, 0, sizeof(rth->local));
5 kx rth->local.nl_family = AF_NETLINK;
5 kx rth->local.nl_groups = subscriptions;
5 kx
5 kx if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
5 kx perror("Cannot bind netlink socket");
5 kx return -1;
5 kx }
5 kx addr_len = sizeof(rth->local);
5 kx if (getsockname(rth->fd, (struct sockaddr*)&rth->local,
5 kx (socklen_t *) &addr_len) < 0) {
5 kx perror("Cannot getsockname");
5 kx return -1;
5 kx }
5 kx if (addr_len != sizeof(rth->local)) {
5 kx fprintf(stderr, "Wrong address length %d\n", addr_len);
5 kx return -1;
5 kx }
5 kx if (rth->local.nl_family != AF_NETLINK) {
5 kx fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
5 kx return -1;
5 kx }
5 kx rth->seq = time(NULL);
5 kx return 0;
5 kx }
5 kx
5 kx /******************* WIRELESS INTERFACE DATABASE *******************/
5 kx /*
5 kx * We keep a few information about each wireless interface on the
5 kx * system. This avoid to query this info at each event, therefore
5 kx * reducing overhead.
5 kx *
5 kx * Each interface is indexed by the 'ifindex'. As opposed to interface
5 kx * names, 'ifindex' are never reused (even if you reactivate the same
5 kx * hardware), so the data we cache will never apply to the wrong
5 kx * interface.
5 kx * Because of that, we are pretty lazy when it come to purging the
5 kx * cache...
5 kx */
5 kx
5 kx /*------------------------------------------------------------------*/
5 kx /*
5 kx * Get name of interface based on interface index...
5 kx */
5 kx static inline int
5 kx index2name(int skfd,
5 kx int ifindex,
5 kx char * name)
5 kx {
5 kx struct ifreq irq;
5 kx int ret = 0;
5 kx
5 kx memset(name, 0, IFNAMSIZ + 1);
5 kx
5 kx /* Get interface name */
5 kx irq.ifr_ifindex = ifindex;
5 kx if(ioctl(skfd, SIOCGIFNAME, &irq) < 0)
5 kx ret = -1;
5 kx else
5 kx strncpy(name, irq.ifr_name, IFNAMSIZ);
5 kx
5 kx return(ret);
5 kx }
5 kx
5 kx /*------------------------------------------------------------------*/
5 kx /*
5 kx * Get interface data from cache or live interface
5 kx */
5 kx static struct wireless_iface *
5 kx iw_get_interface_data(int ifindex)
5 kx {
5 kx struct wireless_iface * curr;
5 kx int skfd = -1; /* ioctl socket */
5 kx
5 kx /* Search for it in the database */
5 kx curr = interface_cache;
5 kx while(curr != NULL)
5 kx {
5 kx /* Match ? */
5 kx if(curr->ifindex == ifindex)
5 kx {
5 kx //printf("Cache : found %d-%s\n", curr->ifindex, curr->ifname);
5 kx
5 kx /* Return */
5 kx return(curr);
5 kx }
5 kx /* Next entry */
5 kx curr = curr->next;
5 kx }
5 kx
5 kx /* Create a channel to the NET kernel. Doesn't happen too often, so
5 kx * socket creation overhead is minimal... */
5 kx if((skfd = iw_sockets_open()) < 0)
5 kx {
5 kx perror("iw_sockets_open");
5 kx return(NULL);
5 kx }
5 kx
5 kx /* Create new entry, zero, init */
5 kx curr = calloc(1, sizeof(struct wireless_iface));
5 kx if(!curr)
5 kx {
5 kx fprintf(stderr, "Malloc failed\n");
5 kx return(NULL);
5 kx }
5 kx curr->ifindex = ifindex;
5 kx
5 kx /* Extract static data */
5 kx if(index2name(skfd, ifindex, curr->ifname) < 0)
5 kx {
5 kx perror("index2name");
5 kx free(curr);
5 kx return(NULL);
5 kx }
5 kx curr->has_range = (iw_get_range_info(skfd, curr->ifname, &curr->range) >= 0);
5 kx //printf("Cache : create %d-%s\n", curr->ifindex, curr->ifname);
5 kx
5 kx /* Done */
5 kx iw_sockets_close(skfd);
5 kx
5 kx /* Link it */
5 kx curr->next = interface_cache;
5 kx interface_cache = curr;
5 kx
5 kx return(curr);
5 kx }
5 kx
5 kx /*------------------------------------------------------------------*/
5 kx /*
5 kx * Remove interface data from cache (if it exist)
5 kx */
5 kx static void
5 kx iw_del_interface_data(int ifindex)
5 kx {
5 kx struct wireless_iface * curr;
5 kx struct wireless_iface * prev = NULL;
5 kx struct wireless_iface * next;
5 kx
5 kx /* Go through the list, find the interface, kills it */
5 kx curr = interface_cache;
5 kx while(curr)
5 kx {
5 kx next = curr->next;
5 kx
5 kx /* Got a match ? */
5 kx if(curr->ifindex == ifindex)
5 kx {
5 kx /* Unlink. Root ? */
5 kx if(!prev)
5 kx interface_cache = next;
5 kx else
5 kx prev->next = next;
5 kx //printf("Cache : purge %d-%s\n", curr->ifindex, curr->ifname);
5 kx
5 kx /* Destroy */
5 kx free(curr);
5 kx }
5 kx else
5 kx {
5 kx /* Keep as previous */
5 kx prev = curr;
5 kx }
5 kx
5 kx /* Next entry */
5 kx curr = next;
5 kx }
5 kx }
5 kx
5 kx /********************* WIRELESS EVENT DECODING *********************/
5 kx /*
5 kx * Parse the Wireless Event and print it out
5 kx */
5 kx
5 kx /*------------------------------------------------------------------*/
5 kx /*
5 kx * Dump a buffer as a serie of hex
5 kx * Maybe should go in iwlib...
5 kx * Maybe we should have better formatting like iw_print_key...
5 kx */
5 kx static char *
5 kx iw_hexdump(char * buf,
5 kx size_t buflen,
5 kx const unsigned char *data,
5 kx size_t datalen)
5 kx {
5 kx size_t i;
5 kx char * pos = buf;
5 kx
5 kx for(i = 0; i < datalen; i++)
5 kx pos += snprintf(pos, buf + buflen - pos, "%02X", data[i]);
5 kx return buf;
5 kx }
5 kx
5 kx /*------------------------------------------------------------------*/
5 kx /*
5 kx * Print one element from the scanning results
5 kx */
5 kx static inline int
5 kx print_event_token(struct iw_event * event, /* Extracted token */
5 kx struct iw_range * iw_range, /* Range info */
5 kx int has_range)
5 kx {
5 kx char buffer[128]; /* Temporary buffer */
5 kx char buffer2[30]; /* Temporary buffer */
5 kx char * prefix = (IW_IS_GET(event->cmd) ? "New" : "Set");
5 kx
5 kx /* Now, let's decode the event */
5 kx switch(event->cmd)
5 kx {
5 kx /* ----- set events ----- */
5 kx /* Events that result from a "SET XXX" operation by the user */
5 kx case SIOCSIWNWID:
5 kx if(event->u.nwid.disabled)
5 kx printf("Set NWID:off/any\n");
5 kx else
5 kx printf("Set NWID:%X\n", event->u.nwid.value);
5 kx break;
5 kx case SIOCSIWFREQ:
5 kx case SIOCGIWFREQ:
5 kx {
5 kx double freq; /* Frequency/channel */
5 kx int channel = -1; /* Converted to channel */
5 kx freq = iw_freq2float(&(event->u.freq));
5 kx if(has_range)
5 kx {
5 kx if(freq < KILO)
5 kx /* Convert channel to frequency if possible */
5 kx channel = iw_channel_to_freq((int) freq, &freq, iw_range);
5 kx else
5 kx /* Convert frequency to channel if possible */
5 kx channel = iw_freq_to_channel(freq, iw_range);
5 kx }
5 kx iw_print_freq(buffer, sizeof(buffer),
5 kx freq, channel, event->u.freq.flags);
5 kx printf("%s %s\n", prefix, buffer);
5 kx }
5 kx break;
5 kx case SIOCSIWMODE:
5 kx printf("Set Mode:%s\n",
5 kx iw_operation_mode[event->u.mode]);
5 kx break;
5 kx case SIOCSIWESSID:
5 kx case SIOCGIWESSID:
5 kx {
5 kx char essid[4*IW_ESSID_MAX_SIZE + 1];
5 kx memset(essid, '\0', sizeof(essid));
5 kx if((event->u.essid.pointer) && (event->u.essid.length))
5 kx iw_essid_escape(essid,
5 kx event->u.essid.pointer, event->u.essid.length);
5 kx if(event->u.essid.flags)
5 kx {
5 kx /* Does it have an ESSID index ? */
5 kx if((event->u.essid.flags & IW_ENCODE_INDEX) > 1)
5 kx printf("%s ESSID:\"%s\" [%d]\n", prefix, essid,
5 kx (event->u.essid.flags & IW_ENCODE_INDEX));
5 kx else
5 kx printf("%s ESSID:\"%s\"\n", prefix, essid);
5 kx }
5 kx else
5 kx printf("%s ESSID:off/any\n", prefix);
5 kx }
5 kx break;
5 kx case SIOCSIWENCODE:
5 kx {
5 kx unsigned char key[IW_ENCODING_TOKEN_MAX];
5 kx if(event->u.data.pointer)
5 kx memcpy(key, event->u.data.pointer, event->u.data.length);
5 kx else
5 kx event->u.data.flags |= IW_ENCODE_NOKEY;
5 kx printf("Set Encryption key:{%X}", event->u.data.flags);
5 kx if(event->u.data.flags & IW_ENCODE_DISABLED)
5 kx printf("off\n");
5 kx else
5 kx {
5 kx /* Display the key */
5 kx iw_print_key(buffer, sizeof(buffer), key, event->u.data.length,
5 kx event->u.data.flags);
5 kx printf("%s", buffer);
5 kx
5 kx /* Other info... */
5 kx if((event->u.data.flags & IW_ENCODE_INDEX) > 1)
5 kx printf(" [%d]", event->u.data.flags & IW_ENCODE_INDEX);
5 kx if(event->u.data.flags & IW_ENCODE_RESTRICTED)
5 kx printf(" Security mode:restricted");
5 kx if(event->u.data.flags & IW_ENCODE_OPEN)
5 kx printf(" Security mode:open");
5 kx printf("\n");
5 kx }
5 kx }
5 kx break;
5 kx /* ----- driver events ----- */
5 kx /* Events generated by the driver when something important happens */
5 kx case SIOCGIWAP:
5 kx printf("New Access Point/Cell address:%s\n",
5 kx iw_sawap_ntop(&event->u.ap_addr, buffer));
5 kx break;
5 kx case SIOCGIWSCAN:
5 kx printf("Scan request completed\n");
5 kx break;
5 kx case IWEVTXDROP:
5 kx printf("Tx packet dropped:%s\n",
5 kx iw_saether_ntop(&event->u.addr, buffer));
5 kx break;
5 kx case IWEVCUSTOM:
5 kx {
5 kx char custom[IW_CUSTOM_MAX+1];
5 kx memset(custom, '\0', sizeof(custom));
5 kx if((event->u.data.pointer) && (event->u.data.length))
5 kx memcpy(custom, event->u.data.pointer, event->u.data.length);
5 kx printf("Custom driver event:%s\n", custom);
5 kx }
5 kx break;
5 kx case IWEVREGISTERED:
5 kx printf("Registered node:%s\n",
5 kx iw_saether_ntop(&event->u.addr, buffer));
5 kx break;
5 kx case IWEVEXPIRED:
5 kx printf("Expired node:%s\n",
5 kx iw_saether_ntop(&event->u.addr, buffer));
5 kx break;
5 kx case SIOCGIWTHRSPY:
5 kx {
5 kx struct iw_thrspy threshold;
5 kx if((event->u.data.pointer) && (event->u.data.length))
5 kx {
5 kx memcpy(&threshold, event->u.data.pointer,
5 kx sizeof(struct iw_thrspy));
5 kx printf("Spy threshold crossed on address:%s\n",
5 kx iw_saether_ntop(&threshold.addr, buffer));
5 kx iw_print_stats(buffer, sizeof(buffer),
5 kx &threshold.qual, iw_range, has_range);
5 kx printf(" Link %s\n", buffer);
5 kx }
5 kx else
5 kx printf("Invalid Spy Threshold event\n");
5 kx }
5 kx break;
5 kx /* ----- driver WPA events ----- */
5 kx /* Events generated by the driver, used for WPA operation */
5 kx case IWEVMICHAELMICFAILURE:
5 kx if(event->u.data.length >= sizeof(struct iw_michaelmicfailure))
5 kx {
5 kx struct iw_michaelmicfailure mf;
5 kx memcpy(&mf, event->u.data.pointer, sizeof(mf));
5 kx printf("Michael MIC failure flags:0x%X src_addr:%s tsc:%s\n",
5 kx mf.flags,
5 kx iw_saether_ntop(&mf.src_addr, buffer2),
5 kx iw_hexdump(buffer, sizeof(buffer),
5 kx mf.tsc, IW_ENCODE_SEQ_MAX_SIZE));
5 kx }
5 kx break;
5 kx case IWEVASSOCREQIE:
5 kx printf("Association Request IEs:%s\n",
5 kx iw_hexdump(buffer, sizeof(buffer),
5 kx event->u.data.pointer, event->u.data.length));
5 kx break;
5 kx case IWEVASSOCRESPIE:
5 kx printf("Association Response IEs:%s\n",
5 kx iw_hexdump(buffer, sizeof(buffer),
5 kx event->u.data.pointer, event->u.data.length));
5 kx break;
5 kx case IWEVPMKIDCAND:
5 kx if(event->u.data.length >= sizeof(struct iw_pmkid_cand))
5 kx {
5 kx struct iw_pmkid_cand cand;
5 kx memcpy(&cand, event->u.data.pointer, sizeof(cand));
5 kx printf("PMKID candidate flags:0x%X index:%d bssid:%s\n",
5 kx cand.flags, cand.index,
5 kx iw_saether_ntop(&cand.bssid, buffer));
5 kx }
5 kx break;
5 kx /* ----- junk ----- */
5 kx /* other junk not currently in use */
5 kx case SIOCGIWRATE:
5 kx iw_print_bitrate(buffer, sizeof(buffer), event->u.bitrate.value);
5 kx printf("New Bit Rate:%s\n", buffer);
5 kx break;
5 kx case SIOCGIWNAME:
5 kx printf("Protocol:%-1.16s\n", event->u.name);
5 kx break;
5 kx case IWEVQUAL:
5 kx {
5 kx event->u.qual.updated = 0x0; /* Not that reliable, disable */
5 kx iw_print_stats(buffer, sizeof(buffer),
5 kx &event->u.qual, iw_range, has_range);
5 kx printf("Link %s\n", buffer);
5 kx break;
5 kx }
5 kx default:
5 kx printf("(Unknown Wireless event 0x%04X)\n", event->cmd);
5 kx } /* switch(event->cmd) */
5 kx
5 kx return(0);
5 kx }
5 kx
5 kx /*------------------------------------------------------------------*/
5 kx /*
5 kx * Print out all Wireless Events part of the RTNetlink message
5 kx * Most often, there will be only one event per message, but
5 kx * just make sure we read everything...
5 kx */
5 kx static inline int
5 kx print_event_stream(int ifindex,
5 kx char * data,
5 kx int len)
5 kx {
5 kx struct iw_event iwe;
5 kx struct stream_descr stream;
5 kx int i = 0;
5 kx int ret;
5 kx char buffer[64];
5 kx struct timeval recv_time;
5 kx struct timezone tz;
5 kx struct wireless_iface * wireless_data;
5 kx
5 kx /* Get data from cache */
5 kx wireless_data = iw_get_interface_data(ifindex);
5 kx if(wireless_data == NULL)
5 kx return(-1);
5 kx
5 kx /* Print received time in readable form */
5 kx gettimeofday(&recv_time, &tz);
5 kx iw_print_timeval(buffer, sizeof(buffer), &recv_time, &tz);
5 kx
5 kx iw_init_event_stream(&stream, data, len);
5 kx do
5 kx {
5 kx /* Extract an event and print it */
5 kx ret = iw_extract_event_stream(&stream, &iwe,
5 kx wireless_data->range.we_version_compiled);
5 kx if(ret != 0)
5 kx {
5 kx if(i++ == 0)
5 kx printf("%s %-8.16s ", buffer, wireless_data->ifname);
5 kx else
5 kx printf(" ");
5 kx if(ret > 0)
5 kx print_event_token(&iwe,
5 kx &wireless_data->range, wireless_data->has_range);
5 kx else
5 kx printf("(Invalid event)\n");
5 kx /* Push data out *now*, in case we are redirected to a pipe */
5 kx fflush(stdout);
5 kx }
5 kx }
5 kx while(ret > 0);
5 kx
5 kx return(0);
5 kx }
5 kx
5 kx /*********************** RTNETLINK EVENT DUMP***********************/
5 kx /*
5 kx * Dump the events we receive from rtnetlink
5 kx * This code is mostly from Casey
5 kx */
5 kx
5 kx /*------------------------------------------------------------------*/
5 kx /*
5 kx * Respond to a single RTM_NEWLINK event from the rtnetlink socket.
5 kx */
5 kx static int
5 kx LinkCatcher(struct nlmsghdr *nlh)
5 kx {
5 kx struct ifinfomsg* ifi;
5 kx
5 kx #if 0
5 kx fprintf(stderr, "nlmsg_type = %d.\n", nlh->nlmsg_type);
5 kx #endif
5 kx
5 kx ifi = NLMSG_DATA(nlh);
5 kx
5 kx /* Code is ugly, but sort of works - Jean II */
5 kx
5 kx /* If interface is getting destoyed */
5 kx if(nlh->nlmsg_type == RTM_DELLINK)
5 kx {
5 kx /* Remove from cache (if in cache) */
5 kx iw_del_interface_data(ifi->ifi_index);
5 kx return 0;
5 kx }
5 kx
5 kx /* Only keep add/change events */
5 kx if(nlh->nlmsg_type != RTM_NEWLINK)
5 kx return 0;
5 kx
5 kx /* Check for attributes */
5 kx if (nlh->nlmsg_len > NLMSG_ALIGN(sizeof(struct ifinfomsg)))
5 kx {
5 kx int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct ifinfomsg));
5 kx struct rtattr *attr = (void *) ((char *) ifi +
5 kx NLMSG_ALIGN(sizeof(struct ifinfomsg)));
5 kx
5 kx while (RTA_OK(attr, attrlen))
5 kx {
5 kx /* Check if the Wireless kind */
5 kx if(attr->rta_type == IFLA_WIRELESS)
5 kx {
5 kx /* Go to display it */
5 kx print_event_stream(ifi->ifi_index,
5 kx (char *) attr + RTA_ALIGN(sizeof(struct rtattr)),
5 kx attr->rta_len - RTA_ALIGN(sizeof(struct rtattr)));
5 kx }
5 kx attr = RTA_NEXT(attr, attrlen);
5 kx }
5 kx }
5 kx
5 kx return 0;
5 kx }
5 kx
5 kx /* ---------------------------------------------------------------- */
5 kx /*
5 kx * We must watch the rtnelink socket for events.
5 kx * This routine handles those events (i.e., call this when rth.fd
5 kx * is ready to read).
5 kx */
5 kx static inline void
5 kx handle_netlink_events(struct rtnl_handle * rth)
5 kx {
5 kx while(1)
5 kx {
5 kx struct sockaddr_nl sanl;
5 kx socklen_t sanllen = sizeof(struct sockaddr_nl);
5 kx
5 kx struct nlmsghdr *h;
5 kx int amt;
5 kx char buf[8192];
5 kx
5 kx amt = recvfrom(rth->fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&sanl, &sanllen);
5 kx if(amt < 0)
5 kx {
5 kx if(errno != EINTR && errno != EAGAIN)
5 kx {
5 kx fprintf(stderr, "%s: error reading netlink: %s.\n",
5 kx __PRETTY_FUNCTION__, strerror(errno));
5 kx }
5 kx return;
5 kx }
5 kx
5 kx if(amt == 0)
5 kx {
5 kx fprintf(stderr, "%s: EOF on netlink??\n", __PRETTY_FUNCTION__);
5 kx return;
5 kx }
5 kx
5 kx h = (struct nlmsghdr*)buf;
5 kx while(amt >= (int)sizeof(*h))
5 kx {
5 kx int len = h->nlmsg_len;
5 kx int l = len - sizeof(*h);
5 kx
5 kx if(l < 0 || len > amt)
5 kx {
5 kx fprintf(stderr, "%s: malformed netlink message: len=%d\n", __PRETTY_FUNCTION__, len);
5 kx break;
5 kx }
5 kx
5 kx switch(h->nlmsg_type)
5 kx {
5 kx case RTM_NEWLINK:
5 kx case RTM_DELLINK:
5 kx LinkCatcher(h);
5 kx break;
5 kx default:
5 kx #if 0
5 kx fprintf(stderr, "%s: got nlmsg of type %#x.\n", __PRETTY_FUNCTION__, h->nlmsg_type);
5 kx #endif
5 kx break;
5 kx }
5 kx
5 kx len = NLMSG_ALIGN(len);
5 kx amt -= len;
5 kx h = (struct nlmsghdr*)((char*)h + len);
5 kx }
5 kx
5 kx if(amt > 0)
5 kx fprintf(stderr, "%s: remnant of size %d on netlink\n", __PRETTY_FUNCTION__, amt);
5 kx }
5 kx }
5 kx
5 kx /**************************** MAIN LOOP ****************************/
5 kx
5 kx /* ---------------------------------------------------------------- */
5 kx /*
5 kx * Wait until we get an event
5 kx */
5 kx static inline int
5 kx wait_for_event(struct rtnl_handle * rth)
5 kx {
5 kx #if 0
5 kx struct timeval tv; /* Select timeout */
5 kx #endif
5 kx
5 kx /* Forever */
5 kx while(1)
5 kx {
5 kx fd_set rfds; /* File descriptors for select */
5 kx int last_fd; /* Last fd */
5 kx int ret;
5 kx
5 kx /* Guess what ? We must re-generate rfds each time */
5 kx FD_ZERO(&rfds);
5 kx FD_SET(rth->fd, &rfds);
5 kx last_fd = rth->fd;
5 kx
5 kx /* Wait until something happens */
5 kx ret = select(last_fd + 1, &rfds, NULL, NULL, NULL);
5 kx
5 kx /* Check if there was an error */
5 kx if(ret < 0)
5 kx {
5 kx if(errno == EAGAIN || errno == EINTR)
5 kx continue;
5 kx fprintf(stderr, "Unhandled signal - exiting...\n");
5 kx break;
5 kx }
5 kx
5 kx /* Check if there was a timeout */
5 kx if(ret == 0)
5 kx {
5 kx continue;
5 kx }
5 kx
5 kx /* Check for interface discovery events. */
5 kx if(FD_ISSET(rth->fd, &rfds))
5 kx handle_netlink_events(rth);
5 kx }
5 kx
5 kx return(0);
5 kx }
5 kx
5 kx /******************************* MAIN *******************************/
5 kx
5 kx /* ---------------------------------------------------------------- */
5 kx /*
5 kx * helper ;-)
5 kx */
5 kx static void
5 kx iw_usage(int status)
5 kx {
5 kx fputs("Usage: iwevent [OPTIONS]\n"
5 kx " Monitors and displays Wireless Events.\n"
5 kx " Options are:\n"
5 kx " -h,--help Print this message.\n"
5 kx " -v,--version Show version of this program.\n",
5 kx status ? stderr : stdout);
5 kx exit(status);
5 kx }
5 kx /* Command line options */
5 kx static const struct option long_opts[] = {
5 kx { "help", no_argument, NULL, 'h' },
5 kx { "version", no_argument, NULL, 'v' },
5 kx { NULL, 0, NULL, 0 }
5 kx };
5 kx
5 kx /* ---------------------------------------------------------------- */
5 kx /*
5 kx * main body of the program
5 kx */
5 kx int
5 kx main(int argc,
5 kx char * argv[])
5 kx {
5 kx struct rtnl_handle rth;
5 kx int opt;
5 kx
5 kx /* Check command line options */
5 kx while((opt = getopt_long(argc, argv, "hv", long_opts, NULL)) > 0)
5 kx {
5 kx switch(opt)
5 kx {
5 kx case 'h':
5 kx iw_usage(0);
5 kx break;
5 kx
5 kx case 'v':
5 kx return(iw_print_version_info("iwevent"));
5 kx break;
5 kx
5 kx default:
5 kx iw_usage(1);
5 kx break;
5 kx }
5 kx }
5 kx if(optind < argc)
5 kx {
5 kx fputs("Too many arguments.\n", stderr);
5 kx iw_usage(1);
5 kx }
5 kx
5 kx /* Open netlink channel */
5 kx if(rtnl_open(&rth, RTMGRP_LINK) < 0)
5 kx {
5 kx perror("Can't initialize rtnetlink socket");
5 kx return(1);
5 kx }
5 kx
5 kx fprintf(stderr, "Waiting for Wireless Events from interfaces...\n");
5 kx
5 kx /* Do what we have to do */
5 kx wait_for_event(&rth);
5 kx
5 kx /* Cleanup - only if you are pedantic */
5 kx rtnl_close(&rth);
5 kx
5 kx return(0);
5 kx }