I am constantly learning new things about operating systems, and I had never heard of ioctl before. It only took a quick Google search to realize how immensely powerful it is, and how well it suited my needs. Ioctl is a means of interacting with device drivers. You pass it a request code along with a pointer to memory.
Since I don't consider the automated gathering of Wifi signal strength to be that obscure of a task, I've decided to post my solution here on my blog. I'm surprised that I was unable to find it documented elsewhere.
There are a few libraries that you need to be sure to include. I believe I have read that wireless.h may exist elsewhere on other distributions, but every machine that my code is running on uses Ubuntu so I didn't look any further in to that.
//libraries necessary for wifi ioctl communication #include <linux/wireless.h> #include <sys/ioctl.h> //struct to hold collected information struct signalInfo { char mac[18]; char ssid[33]; int bitrate; int level; };
For clarity, I also have a struct that I'm using to hold all of the data I collect.
Here is the function in which the actual collection occurs. The iwname parameter is the name of the wireless network interface.
int getSignalInfo(signalInfo *sigInfo, char *iwname){ iwreq req; strcpy(req.ifr_name, iwname); iw_statistics *stats; //have to use a socket for ioctl int sockfd = socket(AF_INET, SOCK_DGRAM, 0); //make room for the iw_statistics object req.u.data.pointer = (iw_statistics *)malloc(sizeof(iw_statistics)); req.u.data.length = sizeof(iw_statistics); //this will gather the signal strength if(ioctl(sockfd, SIOCGIWSTATS, &req) == -1){ //die with error, invalid interface fprintf(stderr, "Invalid interface.\n"); return(-1); } else if(((iw_statistics *)req.u.data.pointer)->qual.updated & IW_QUAL_DBM){ //signal is measured in dBm and is valid for us to use sigInfo->level=((iw_statistics *)req.u.data.pointer)->qual.level - 256; } //SIOCGIWESSID for ssid char buffer[32]; memset(buffer, 0, 32); req.u.essid.pointer = buffer; req.u.essid.length = 32; //this will gather the SSID of the connected network if(ioctl(sockfd, SIOCGIWESSID, &req) == -1){ //die with error, invalid interface return(-1); } else{ memcpy(&sigInfo->ssid, req.u.essid.pointer, req.u.essid.length); memset(&sigInfo->ssid[req.u.essid.length],0,1); } //SIOCGIWRATE for bits/sec (convert to mbit) int bitrate=-1; //this will get the bitrate of the link if(ioctl(sockfd, SIOCGIWRATE, &req) == -1){ fprintf(stderr, "bitratefail"); return(-1); }else{ memcpy(&bitrate, &req.u.bitrate, sizeof(int)); sigInfo->bitrate=bitrate/1000000; } //SIOCGIFHWADDR for mac addr ifreq req2; strcpy(req2.ifr_name, iwname); //this will get the mac address of the interface if(ioctl(sockfd, SIOCGIFHWADDR, &req2) == -1){ fprintf(stderr, "mac error"); return(-1); } else{ sprintf(sigInfo->mac, "%.2X", (unsigned char)req2.ifr_hwaddr.sa_data[0]); for(int s=1; s<6; s++){ sprintf(sigInfo->mac+strlen(sigInfo->mac), ":%.2X", (unsigned char)req2.ifr_hwaddr.sa_data[s]); } } close(sockfd); }
This seems to work very reliably for me. Hopefully it will save some poor soul from having to parse iwconfig in the future. Happy programming!