how to get Ethernet adapter name from IP in c in centos - c++

have two ethernet adapters, so i have two different ip addresses. Now I ant to find the name of the adapter with the respective ip. Like, I have intel card with ip 192.168.10.1. How to retrieve this adapter name in centos(linux) using C or C++ without any third party installation?
I need to find the manufacturer name( not eth0,etc..). This manufacturer list is in "/usr/share/hwdata/pci.ids", but i'm unable to map that name with the ip address. I could get the list of adapter name using 'lscpu | grep "Ethernet"'. But again the question arises to mapping the names with ip address.

There is getifaddrs function in standard libc. I modified an example from manual page.
You can't get names from the kernel, but it provides PCI IDs in /sys file systems. You can use libpci to resolve these numbers into filenames. Current code doesn't support USB devices and subdevice numbers.
#define _GNU_SOURCE /* To get defns of NI_MAXSERV and NI_MAXHOST */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/if_link.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <pci/pci.h>
/* PCI IDs are contained in /sys filesystem. */
unsigned long read_sysfs_uint(const char* ifa_name, const char* info) {
char path[PATH_MAX];
char buf[12];
int fd;
snprintf(path, PATH_MAX, "/sys/class/net/%s/device/%s", ifa_name, info);
fd = open(path, O_RDONLY);
if(fd == -1)
return 0;
if(read(fd, buf, 12) == -1) {
close(fd);
return 0;
}
close(fd);
return strtoul(buf, NULL, 16);
}
/* Try to get PCI IDs and get PCI device name for it.
XXX: doesn't check for subsystem's numbers */
void print_pci_ids(const char* ifa_name) {
int vendor = (int) read_sysfs_uint(ifa_name, "vendor");
int device = (int) read_sysfs_uint(ifa_name, "device");
int subsystem_vendor = (int) read_sysfs_uint(ifa_name, "subsystem_vendor");
int subsystem_device = (int) read_sysfs_uint(ifa_name, "subsystem_device");
struct pci_access *pacc = pci_alloc();
char namebuf[256];
printf("PCI IDs: %x %x %x %x\n", vendor, device, subsystem_device, subsystem_vendor);
pci_init(pacc);
if(pci_lookup_name(pacc, namebuf, 256,
PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
vendor, device)) {
printf("PCI Name: %s\n", namebuf);
}
pci_cleanup(pacc);
}
int main(int argc, char *argv[])
{
struct ifaddrs *ifaddr, *ifa;
struct in_addr* ifa_inaddr;
struct in_addr addr;
int family, s, n;
if(argc != 2) {
fprintf(stderr, "Usage: getifaddr <IP>\n");
return EXIT_FAILURE;
}
if (inet_aton(argv[1], &addr) == 0) {
perror("inet_aton");
return EXIT_FAILURE;
}
if (getifaddrs(&ifaddr) == -1) {
perror("getifaddrs");
return EXIT_FAILURE;
}
/* Walk through linked list, maintaining head pointer so we
can free list later */
for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
if (ifa->ifa_addr == NULL)
continue;
/* We seek only for IPv4 addresses */
if(ifa->ifa_addr->sa_family != AF_INET)
continue;
ifa_inaddr = &(((struct sockaddr_in*) ifa->ifa_addr)->sin_addr);
if(memcmp(ifa_inaddr, &addr, sizeof(struct in_addr)) == 0) {
printf("Interface: %s\n", ifa->ifa_name);
print_pci_ids(ifa->ifa_name);
}
}
freeifaddrs(ifaddr);
return EXIT_SUCCESS;
}
Compile it with libpci (you'll need to install corresponding devel package):
$ gcc getifname.c -lpci -o ./getifname
Here are examples of its usage:
$ ./getifname
Usage: getifaddr <IP>
$ ./getifname dlks
inet_aton: Success
$ ./getifname 127.0.0.1
Interface: lo
PCI IDs: 0 0 0 0
PCI Name: Device 0000:0000
$ ./getifname 192.168.13.144
Interface: wlan0
PCI IDs: 8086 88e 4060 8086
PCI Name: Intel Corporation Centrino Advanced-N 6235

Im assuming by adapter name you mean eth0/eth1/etc. and not Manufacturer/Model. If so, one possible solution (a little convoluted but it works) would be to perform an ifconfig sys call and pipe it to a text file. From there you can perform a search of the text file to look for the IP address and then from there since the output is constant you can just use the starting location of the IP as the basis of getting to the adapter name.

That is actually somewhat tricky since linux does not have a common, generic driver stack API like windows - basically it boils down to 3 options :
read the special files which are exported by the kernel : https://stackoverflow.com/a/5611176/351861
call lspci and parse its output : http://prefetch.net/articles/linuxpci.html
copy the functionality of lspci and actually write your own app, as you can see you will need several kernel data structures like pcimap_entry and whatnot, but it should be straighforward since you can literally syphon the knowledge of ye olde kernel grandmasters : https://github.com/gittup/pciutils/blob/gittup/ls-kernel.c

Related

Linux - How to get system DNS server IP on a system with systemd-resolved

Generally, on Linux, one would use the res_init/res_ninit functions to get the system nameserver. I have tried this, but the resulting data gives me a DNS server ip of 127.0.0.53, which is the loopback IP of the systemd-resolved stub resolver. Needless to say, this is not the IP I'm looking for.
More specifically, I tried the following code:
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
int main(int argc, char **argv)
{
struct __res_state dns;
res_ninit(&dns);
uint32_t dnsSrv = dns.nsaddr_list[0].sin_addr.s_addr;
uint8_t *dnsOct = (uint8_t*) &dnsSrv;
printf("DNS: %u.%u.%u.%u\n", dnsOct[0], dnsOct[1], dnsOct[2], dnsOct[3]);
return 0;
}
Which gave me the following output:
DNS: 127.0.0.53
How can I go about getting the actual nameserver IP on a system using systemd-resolved in C/C++?
systemd-resolved is a D-Bus service. You can reach it via D-Bus and there's a DNS property which contains the list of current DNS servers.
To test:
$ busctl get-property org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager DNS
a(iiay) 2 0 2 4 1 1 1 1 0 2 4 8 8 8 8
The data format is described in the org.freedesktop.resolve1 API spec:
Each structure in the array consists of a numeric network interface index, an address family, and a byte array containing the DNS server address (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6).
So a(iiay) 2 0 2 4 1 1 1 1 0 2 4 8 8 8 8 would mean 2 entries, AF_INET 1.1.1.1, AF_INET 8.8.8.8.
Here's a demo in C++ using systemd's built-in sd-bus API:
#include <iostream>
#include <stdexcept>
#include <string>
#include <systemd/sd-bus.h>
#include <arpa/inet.h>
using namespace std::literals;
int main(int argc, char **argv)
{
sd_bus_error dbusErr{};
sd_bus_message *msg{};
sd_bus *dbus{};
try {
int err = sd_bus_open_system(&dbus);
if (err < 0) {
throw std::system_error(-err, std::system_category(), "can't connect to system D-Bus");
}
err = sd_bus_get_property(
dbus,
"org.freedesktop.resolve1",
"/org/freedesktop/resolve1",
"org.freedesktop.resolve1.Manager",
"DNS",
&dbusErr,
&msg,
"a(iiay)");
if (err < 0) {
throw std::runtime_error("can't connect to systemd-resolved: "s + dbusErr.message);
}
err = sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, "(iiay)");
if (err < 0) {
throw std::system_error(-err, std::system_category());
}
int32_t netif;
int32_t af;
size_t n;
const void *addr;
char buf[64];
while (sd_bus_message_enter_container(msg, SD_BUS_TYPE_STRUCT, "iiay") > 0) {
err = sd_bus_message_read(msg, "ii", &netif, &af);
if (err < 0) {
throw std::system_error(-err, std::system_category());
}
err = sd_bus_message_read_array(msg, 'y', &addr, &n);
if (err < 0) {
throw std::system_error(-err, std::system_category());
}
sd_bus_message_exit_container(msg);
inet_ntop(af, addr, buf, sizeof(buf));
std::cout << buf << "\n";
}
sd_bus_message_exit_container(msg);
} catch (std::exception const& e) {
std::cerr << e.what() << "\n";
}
sd_bus_error_free(&dbusErr);
sd_bus_message_unref(msg);
sd_bus_unref(dbus);
}
You'll need to compile and link with libsystemd for the above to work:
cmake_minimum_required(VERSION 3.20)
project(ctest)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDBUS REQUIRED systemd)
add_executable(main main.cpp)
target_link_libraries(main PRIVATE systemd)
After doing some more research, I have found another way to determine the actual DNS servers being queried by systemd-resolved. It turns out that systemd-resolved stores the IP addresses of the downstream DNS servers in the file /run/systemd/resolve/resolv.conf. On my system, the file looks like this:
# This is /run/systemd/resolve/resolv.conf managed by man:systemd-resolved(8).
# Do not edit.
#
# This file might be symlinked as /etc/resolv.conf. If you're looking at
# /etc/resolv.conf and seeing this text, you have followed the symlink.
#
# This is a dynamic resolv.conf file for connecting local clients directly to
# all known uplink DNS servers. This file lists all configured search domains.
#
# Third party programs should typically not access this file directly, but only
# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
# different way, replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.
nameserver 192.168.1.1
search .
It would be fairly straightforward to parse this file to grab the nameserver IP address:
#include <cstdint>
#include <cstdio>
#include <string>
#include <fstream>
int main(int argc, char **argv)
{
char lineBuf[1024] = {0};
std::ifstream sdResolv("/run/systemd/resolve/resolv.conf", std::ios::binary);
uint8_t dnsIP[4] = {0};
if(sdResolv.is_open() == true)
{
while(sdResolv.eof() == false)
{
sdResolv.getline(lineBuf, 1024);
std::string line(lineBuf);
size_t nsPos = line.find("nameserver ");
if(nsPos == 0)
{
sscanf(lineBuf, "nameserver %hhu.%hhu.%hhu.%hhu", &dnsIP[0], &dnsIP[1], &dnsIP[2], &dnsIP[3]);
break;
}
}
sdResolv.close();
}
printf("DNS: %u.%u.%u.%u\n", dnsIP[0], dnsIP[1], dnsIP[2], dnsIP[3]);
return 0;
}
Note: While this is a valid solution, I have accepted the answer submitted by rustyx, as I feel that using DBUS is really the proper way to obtain the DNS server IP addresses in this case.

How to connect BLE devices using Linux bluetooth C library

Description of the problem
I am trying to connect my Bluetooth devices with BLE to a Linux system using the Bluetooth C libraries (I am programming using C++), so here is the code I am currently using:
#include <string>
#include <iostream>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#define CONNECT_TIMEOUT 60 /* sec */
#define BLE_ERROR(e) std::cout << "Error: " << e << ": " << strerror(errno) << std::endl;
void connect(std::string addr){
uint16_t handle = 0;
uint8_t initiator_filter = 0;
uint8_t peer_type = LE_PUBLIC_ADDRESS;
uint8_t own_type = LE_PUBLIC_ADDRESS;
uint16_t interval = htobs(0x0005);
uint16_t window = htobs(0x0005);
uint16_t min_interval = 15;
uint16_t max_interval = 15;
uint16_t latency = 0;
uint16_t timeout = 0;
uint16_t min_ce_length = 1;
uint16_t max_ce_length = 1;
bdaddr_t bdaddr;
int r, dev_id, dd = -1;
dev_id = hci_get_route(NULL);
if (dev_id < 0)
{
BLE_ERROR("No local device");
goto finish;
}
dd = hci_open_dev(dev_id);
if (dd < 0)
{
BLE_ERROR("Cannot open socket");
goto finish;
}
r = str2ba(addr.c_str(), &bdaddr);
if(r < 0){
BLE_ERROR("Getting baddr");
goto finish;
}
r = hci_le_create_conn(dd,interval,window,initiator_filter,peer_type,
bdaddr,own_type,min_interval,max_interval,latency,timeout,min_ce_length,max_ce_length,
&handle,CONNECT_TIMEOUT * 1000000);
if(r < 0){
BLE_ERROR("Connecting device");
goto finish;
}
printf("\tHandle: %d (0x%04x)\n", handle, handle);
finish:
hci_close_dev(dd);
}
int main(){
connect(""); //TODO Complete with a functional MAC Address
return 0;
}
NOTE: You need to set a specific MAC in connect function parameter. I am also compiling with g++ using the following command:
/usr/bin/g++ -g /home/maria/projects/TestStackOverBLE/main.cpp -o /home/maria/projects/TestStackOverBLE/main -lbluetooth
Here are more information about my hci0 device using hciconfig -a command:
hci0: Type: Primary Bus: USB
BD Address: 24:4B:FE:3A:1A:B6 ACL MTU: 1021:6 SCO MTU: 255:12
UP RUNNING PSCAN
RX bytes:141559 acl:0 sco:0 events:5409 errors:0
TX bytes:59986 acl:0 sco:0 commands:2084 errors:0
Features: 0xff 0xff 0xff 0xfe 0xdb 0xfd 0x7b 0x87
Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
Link policy: RSWITCH HOLD SNIFF PARK
Link mode: SLAVE ACCEPT
Name: 'maria'
Class: 0x3c010c
Service Classes: Rendering, Capturing, Object Transfer, Audio
Device Class: Computer, Laptop
HCI Version: (0xa) Revision: 0x999
LMP Version: (0xa) Subversion: 0x646b
Manufacturer: Realtek Semiconductor Corporation (93)
I am also using Ubuntu 18.04 with:
bluetoothctl v5.64
dbus v1.12.2-1ubuntu1
bluez v5.48-0ubuntu3.
And the result I am getting when I run the application is the following:
Error: Connecting device: Input/output error
Solution attempts
I first tried to document myself about how the bluetooth C libraries for Linux works (itis difficult to find any documentation and less for BLE), I need to connect using BLE since my device does not allow communication using Bluetooth classic and I cannot find the reason for the problem I am having. I also have tried to restart both the bluetooth service (using sudo service bluetooth restart) and the hci0 device (using sudo hciconfig hci0 reset) several times but none worked, I also tried restarting the PC and neither, I even tried the steps mentioned here that are quite similar to my case since previously the "Connection timeout" error has also happened to me but it didn't work either, it just returned the following code in case it was useful: Executing this command: hcitool cmd 0x08 0x000EI received the following output:
< HCI Command: ogf 0x08, ocf 0x000e, plen 0
> HCI Event: 0x0e plen 4
02 0E 20 0C
What can I do about this problem?
EDIT: I was researching and found that making dbus calls to the bluetooth service using C/C++ could work for me but it is quite difficult to find good documentation or concrete connection examples despite having reviewed lots of github projects as most of the code is too convoluted, included in many files and/or consecutive calls for what I am trying to find/understand
This is what the old hci_xxx bluetooth C functions are doing at the lowest level. They probably don't work now because bluez/dbus is getting in the way. The following code works on a Raspberry Pi because it disables bluez first, and could be the basis of a C program - but it would be much easier to use one of the github libraries mentioned in the comments.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
struct sockaddr_hci
{
unsigned short hci_family;
unsigned short hci_dev;
unsigned short hci_channel;
};
struct hci_filter
{
unsigned long type_mask;
unsigned long event_mask[2];
unsigned short opcode;
};
#define BTPROTO_HCI 1
#define SOL_HCI 0
#define HCI_FILTER 2
#define HCIDEVDOWN 0x400448CA
unsigned char eventmask[16] = { 1,1,0x0C,8,0xFF,0xFF,0xFB,0xFF,0x07,0xF8,0xBF,0x3D };
unsigned char lemask[16] = { 0x01,0x01,0x20,0x08,0xBF,0x05,0,0,0,0,0,0 };
unsigned char leopen[30] = {1,0x0D,0x20,0x19,0x60,0,0x60,0,0,0,0x66,0x55,0x44,0x33,0x22,0x11,0,0x18,0,0x28,0,0,0,0x11,0x01,0,0,0,0};
int main()
{
int n,len,dd;
struct sockaddr_hci sa;
struct hci_filter flt;
char buf[256];
// set board address 00:1E:C0:2D:17:7C
leopen[15] = 0x00;
leopen[14] = 0x1E;
leopen[13] = 0xC0;
leopen[12] = 0x2D;
leopen[11] = 0x17;
leopen[10] = 0x7C;
dd = socket(31, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI);
if(dd < 0)
{
printf("Socket open error\n");
return(0);
}
ioctl(dd,HCIDEVDOWN,0); // hci0
close(dd);
// AF_BLUETOOTH=31
dd = socket(31, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI);
if(dd < 0)
{
printf("Socket open error\n");
return(0);
}
sa.hci_family = 31; // AF_BLUETOOTH;
sa.hci_dev = 0; // hci0/1/2...
sa.hci_channel = 1; // HCI_CHANNEL_USER
if(bind(dd,(struct sockaddr *)&sa,sizeof(sa)) < 0)
{
printf("Bind failed\n");
close(dd);
return(0);
}
write(dd,eventmask,12);
write(dd,lemask,12);
printf("Send hci LE connect\n");
write(dd,leopen,29);
printf("If get reply = 04 3E 13 01 00.. then has connected OK\n");
printf("REPLY =");
for(n = 0 ; n < 10 ; ++ n)
{
len = read(dd,buf,sizeof(buf));
for(n = 0 ; n < len ; ++n)
printf(" %02X",buf[n]);
printf("\n");
sleep(1);
}
printf("\nExit and disconnect\n");
}

Soft Reset USB Device Without Unplugging in Linux

Problem:
I have USB devices connected to a remote linux machine. I would like to reset them using file descriptors.
I connect to a port using generic port names in /dev/usbTTYXX. Unfortunately, I quickly found out this was a bad approach as these are generated in order. If I plugged in a tty device first it will generate ttyUSB0 and if I plugin a second tty device it will generate TTYUSB1 and so on. Unfortunately I have too many different ttyUSB devices and others that break this order. In short its just not user friendly and is a nightmare. I found that it is better to open a port using specific simlink file descriptors in /dev/serial/by-id. This is best preferred since they seem very unique and do not change even after restart, reboot despite order of USB plugged in.
So I can open a port in a more robust way like so. (I even have a json for this)
// old
int serial_port = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY);
// new
int serial_port = open("/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AC00MET6-if00-port0", O_RDWR | O_NOCTTY);
No matter how much I reconnect, the USB device will not change (unlike /dev/ttyUSB0). Anyways I found this code that potentially performs a soft-reset to a device so I do not need to replug it in. Here is the source:
If I use this file descriptor /dev/bus/usb/001/010 it works. /dev/bus/usb/XXX/YYY where XXX is the bus number (nearly always 001 on the Pi) and YYY is the device number (get both of these from lsusb). Again lsusb also seems to change quite often. (Even more troublesome, I have similar device adapters for the USB which use the same Product and Vendor ID!)
However it will not work if I use /dev/ttyUSB0 or by id with /dev/serial/by-id/. The error I get is Error in ioctl: Inappropriate ioctl for device..
Question: How can I use the ioctrl USBDEVFS_RESET method with the /dev/serial/by-id/ file simlinks?
/* usbreset -- send a USB port reset to a USB device */
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>
int main(int argc, char **argv)
{
const char *filename;
int fd;
int rc;
if (argc != 2) {
fprintf(stderr, "Usage: usbreset device-filename\n");
return 1;
}
filename = argv[1];
fd = open(filename, O_WRONLY);
if (fd < 0) {
perror("Error opening output file");
return 1;
}
printf("Resetting USB device %s\n", filename);
rc = ioctl(fd, USBDEVFS_RESET, 0);
if (rc < 0) {
perror("Error in ioctl");
return 1;
}
printf("Reset successful\n");
close(fd);
return 0;
}

How get information from LIBPCAP?

I need to get information about internet package and I was trying with the following code but I don't have experience with C++.
I executed this code from this tutorial http://yuba.stanford.edu/~casado/pcap/section1.html
#include <stdio.h>
#include <stdlib.h>
#include <pcap.h> /* GIMME a libpcap plz! */
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char **argv)
{
char *dev; /* name of the device to use */
char *net; /* dot notation of the network address */
char *mask;/* dot notation of the network mask */
int ret; /* return code */
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 netp; /* ip */
bpf_u_int32 maskp;/* subnet mask */
struct in_addr addr;
/* ask pcap to find a valid device for use to sniff on */
dev = pcap_lookupdev(errbuf);
/* error checking */
if(dev == NULL)
{
printf("%s\n",errbuf);
exit(1);
}
/* print out device name */
printf("DEV: %s\n",dev);
/* ask pcap for the network address and mask of the device */
ret = pcap_lookupnet(dev,&netp,&maskp,errbuf);
if(ret == -1)
{
printf("%s\n",errbuf);
exit(1);
}
/* get the network address in a human readable form */
addr.s_addr = netp;
net = inet_ntoa(addr);
if(net == NULL)/* thanks Scott :-P */
{
perror("inet_ntoa");
exit(1);
}
printf("NET: %s\n",net);
/* do the same as above for the device's mask */
addr.s_addr = maskp;
mask = inet_ntoa(addr);
if(mask == NULL)
{
perror("inet_ntoa");
exit(1);
}
printf("MASK: %s\n",mask);
return 0;
}
When I execute this code, I get a file a.out but I cannot open the file, why? How can i pass my information to .csv?
I don't have experience with C++
Although the code may compile using a C++ compiler it looks more like C code. The name of the file, ldev.c, and the compiler used also says that it's a C program.
When I execute this code, I get a file a.out but I cannot open the file, why?
The last step in the tutorial tells you how to compile the program, not how to execute it:
gcc ldev.c -lpcap
That step produces a.out which is the program you are supposed to execute.
To actually execute the program, run the command:
./a.out
Then, if everything works, the output should be something similar to:
DEV: eth0
NET: 192.168.12.0
MASK: 255.255.255.0
Writing to a .csv file is not very different from the basic writing to any other file.
We need to open the file using the library
#include <fstream>
Then we create our file to write to
std::ofstream outputFile;
we open it using
outputFile.open("random.csv");
and start writing using commas as separators
outputFile << "a,b,c,\n";
outputFile << mask << "\n"; // in your case
Then don't forget to close it!
outputFile.close();

AIO on OS X vs Linux - why it doesn't work on Mac OS X 10.6

My question is really simple. Why the code below does work on Linux, and doesn't on Mac OS X 10.6.2 Snow Leopard.
To compile save the file to aio.cc, and compile with g++ aio.cc -o aio -lrt on Linux, and g++ aio.cc -o aio on Mac OS X. I'm using Mac OS X 10.6.2 for testing on a Mac, and Linux kernel 2.6 for testing on Linux.
The failure I see on OS X is aio_write fails with -1 and sets errno to EAGAIN, which simply means "Resource temporarily unavailable". Why is that?
extern "C" {
#include <aio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <signal.h>
}
#include <cassert>
#include <string>
#include <iostream>
using namespace std;
static void
aio_completion_handler(int signo, siginfo_t *info, void *context)
{
using namespace std;
cout << "BLAH" << endl;
}
int main()
{
int err;
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_port = htons(1234);
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
sin.sin_family = PF_INET;
int sd = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sd == -1) {
assert(!"socket() failed");
}
const struct sockaddr *saddr = reinterpret_cast<const struct sockaddr *>(&sin);
err = ::connect(sd, saddr, sizeof(struct sockaddr));
if (err == -1) {
perror(NULL);
assert(!"connect() failed");
}
struct aiocb *aio = new aiocb();
memset(aio, 0, sizeof(struct aiocb));
char *buf = new char[3];
buf[0] = 'a';
buf[1] = 'b';
buf[2] = 'c';
aio->aio_fildes = sd;
aio->aio_buf = buf;
aio->aio_nbytes = 3;
aio->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
aio->aio_sigevent.sigev_signo = SIGIO;
aio->aio_sigevent.sigev_value.sival_ptr = &aio;
struct sigaction sig_act;
sigemptyset(&sig_act.sa_mask);
sig_act.sa_flags = SA_SIGINFO;
sig_act.sa_sigaction = aio_completion_handler;
sigaction(SIGIO, &sig_act, NULL);
errno = 0;
int ret = aio_write(aio);
if (ret == -1) {
perror(NULL);
}
assert(ret != -1);
}
UPDATE (Feb 2010): OSX does not support AIO on sockets at all. Bummer!
The presented code was tested on Mountain Lion 10.8.2. It works with a small correction.
The line
"aio->aio_fildes = sd;"
should be changed for example to:
aio->aio_fildes = open( "/dev/null", O_RDWR);
to get the expected result.
see manual. "The aio_write() function allows the calling process to perform an asynchronous write to a previously opened file."
I have code very similar to yours on 10.6.2 (but writing to a file) working without any problems - so it is possible to do what you're trying.
Just out of curiosity, what value are you using for the SIGIO constant ?
I found that an invalid value here in OS X would casue aio_write to fail - so
I always pass SIGUSR1.
Maybe check the return value of sigaction() to verify the signal details?
The points raised in your links all point to a different method for raising io completion notifications (e.g. kqueue which is a BSD specific mechanism), but doesn't really answer your question re POSIX methods for async io. and whether they work on Darwin.
The UNIX world really is a mish mash of solutions for this, and it would be really good if there was one tried and tested solutiom that worked across all platforms, alas currently there's not - POSIX being the one that aims for the most consistency.
It's a bit of a stab in the dark, but it might be useful as well to set nonblocking on your socket handle ( i.e. set socket option O_NONBLOCK ) as well as using SIGUSR1
If I get some time I'll work with your socket sample and see if I can get anything out of that too.
Best of luck.
OSX Allows you to use sockets via the (CF)RunLoop. Or getting callbacks from the runloop.
That is the most elegant way I have found to use async IO on mac.
You can use your existing socket and do a CFSocketCreateWithNative. And register callbacks on your runloop.
Here is a small snippet of code that shows how it can be setup, incomplete since I have cut down on a source file...
// This will setup a readCallback
void SocketClass::setupCFCallback() {
CFSocketContext context = { 0, this, NULL, NULL, NULL };
if (CFSocketRef macMulticastSocketRef = CFSocketCreateWithNative(NULL, socketHandle_, kCFSocketReadCallBack,readCallBack, &context)) {
if (CFRunLoopSourceRef macRunLoopSrc = CFSocketCreateRunLoopSource(NULL, macMulticastSocketRef, 0)) {
if (!CFRunLoopContainsSource(CFRunLoopGetCurrent(), macRunLoopSrc, kCFRunLoopDefaultMode)) {
CFRunLoopAddSource(CFRunLoopGetCurrent(), macRunLoopSrc, kCFRunLoopDefaultMode);
macRunLoopSrc_ = macRunLoopSrc;
}
else
CFRelease(macRunLoopSrc);
}
else
CFSocketInvalidate(macMulticastSocketRef);
CFRelease(macMulticastSocketRef);
}
}
void SocketClass::readCallBack(CFSocketRef inref, CFSocketCallBackType type,CFDataRef , const void *, void *info) {
if (SocketClass* socket_ptr = reinterpret_cast<SocketClass*>(info))
socket_ptr->receive(); // do stuff with your socket
}