1

I'm trying to use the Boost.Asio library for C++ to make an ioctl system call.

Here is my equivalent "C style" code, which gets the ESSID for a wireless interface:

int sockfd;
char * id;
struct iwreq wreq;

memset(&wreq, 0, sizeof(struct iwreq));
sprintf(wreq.ifr_name, "wlp3s0");

if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
    fprintf(stderr, "Cannot open socket: %s\n", strerror(errno));
    exit(1);
}

id = malloc(IW_ESSID_MAX_SIZE+1);
wreq.u.essid.pointer = id;
if (ioctl(sockfd, SIOCGIWESSID, &wreq)) {
    fprintf(stderr, "Get ESSID ioctl failed: %s\n", strerror(errno));
    exit(2);
}

printf("ESSID is %s\n", wreq.u.essid.pointer);

I found a relevant example for Boost.Asio, but I'm not clear how to use it correctly. Here is the code that I tried adding to my main function:

boost::asio::ip::udp::socket socket(io_service);
struct iwreq wreq
memset(&wreq, 0, sizeof(struct iwreq));
sprintf(wreq.ifr_name, "wlp3s0");
id = malloc(IW_ESSID_MAX_SIZE+1);
wreq.u.essid.pointer = id;

boost::asio::detail::io_control::myCommand command;
command.set(&wreq);
boost::system::error_code ec;
socket.io_control(command, ec);
if (ec)
{
  // An error occurred.
}

This is the code for my custom command:

#include <boost/asio/detail/config.hpp>
#include <cstddef>
#include <boost/config.hpp>
#include <boost/asio/detail/socket_types.hpp>
#include <boost/asio/detail/push_options.hpp>

namespace boost {
namespace asio {
namespace detail {
namespace io_control {

class myCommand
{
public:
  // Default constructor.
  myCommand()
    : value_(0)
  {
  }

  // Get the name of the IO control command.
  int name() const
  {
    return static_cast<int>(SIOCGIWESSID);
  }

  // Set the value of the I/O control command.
  void set(struct iwreq* value)
  {
    value_ = static_cast<detail::ioctl_arg_type>(value);
  }

  // Get the current value of the I/O control command.
  std::size_t get() const
  {
    return static_cast<struct iwreq*>(value_);
  }

  // Get the address of the command data.
  detail::ioctl_arg_type* data()
  {
    return &value_;
  }

  // Get the address of the command data.
  const detail::ioctl_arg_type* data() const
  {
    return &value_;
  }

private:
  detail::ioctl_arg_type value_;
};

} // namespace io_control
} // namespace detail
} // namespace asio
} // namespace boost

However, the code does not work. If you have any example code or solution, please let me know. Thank you.

1 Answer 1

0

In Asio, an IoControlCommand class provides support for a specific ioctl command. Asio itself includes one such class, bytes_readable, which handles the FIONREAD command. (It is the example mentioned in the question.)

The argument to FIONREAD is a pointer to an integer1, as shown here in C-style code:

int fd, value, ret;
/* ... */
ret = ioctl(fd, FIONREAD, &value);

Because of this, Asio's bytes_readable class has a private member variable that holds an integer, and data() returns a pointer to it.

The argument to SIOCGIWESSID, however, is a pointer to an instance of struct iwreq. The custom IoControlCommand class should store and initialize that instance, and data() should return a pointer to it:

#include <linux/wireless.h>
#include <string>

class myCommand
{
public:
  // Construct with a specific command value.
  myCommand(std::string interface)
    : wreq_(), essid_()
  {
    interface.copy(wreq_.ifr_name, sizeof(wreq_.ifr_name));
    wreq_.u.essid.pointer = essid_;
    wreq_.u.essid.length = sizeof(essid_);
  }

  // Get the name of the IO control command.
  int name() const
  {
    return static_cast<int>(SIOCGIWESSID);
  }

  // Get the current value of the I/O control command.
  std::string essid() const
  {
    return essid_;
  }

  // Get the address of the command data.
  void* data()
  {
    return &wreq_;
  }

  // Get the address of the command data.
  const void* data() const
  {
    return &wreq_;
  }

private:
  struct iwreq wreq_;
  char essid_[IW_ESSID_MAX_SIZE + 1];
};

To use this class:

boost::asio::ip::udp::socket socket(io_service);

myCommand cmd("wlp3s0");

boost::system::error_code ec;
socket.io_control(cmd, ec);
if (ec)
{
  // An error occurred.
}

std::string essid = cmd.essid();
std::cout << "ESSID is " << essid << std::endl;

1 The exact integer type is platform-specific: it is int for Linux, but unsigned long for Windows. For this reason value_ is defined using a type alias, boost::asio::detail::ioctl_arg_type.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.