SocketCAN: Proper error reporting from openSocket()

This commit is contained in:
Pavel Kirienko
2016-01-28 00:50:28 +03:00
parent 587088bb18
commit 597e0b4356
2 changed files with 49 additions and 19 deletions
@@ -5,6 +5,7 @@
#pragma once #pragma once
#include <cerrno> #include <cerrno>
#include <cstring>
#include <stdexcept> #include <stdexcept>
namespace uavcan_linux namespace uavcan_linux
@@ -16,9 +17,14 @@ class Exception : public std::runtime_error
{ {
const int errno_; const int errno_;
static std::string makeErrorString(const std::string& descr)
{
return descr + " [errno " + std::to_string(errno) + " \"" + std::strerror(errno) + "\"]";
}
public: public:
explicit Exception(const std::string& descr) explicit Exception(const std::string& descr)
: std::runtime_error(descr) : std::runtime_error(makeErrorString(descr))
, errno_(errno) , errno_(errno)
{ } { }
@@ -381,6 +381,7 @@ public:
*/ */
virtual ~SocketCanIface() virtual ~SocketCanIface()
{ {
UAVCAN_TRACE("SocketCAN", "SocketCanIface: Closing fd %d", fd_);
(void)::close(fd_); (void)::close(fd_);
} }
@@ -515,68 +516,91 @@ public:
*/ */
static int openSocket(const std::string& iface_name) static int openSocket(const std::string& iface_name)
{ {
errno = 0;
const int s = ::socket(PF_CAN, SOCK_RAW, CAN_RAW); const int s = ::socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (s < 0) if (s < 0)
{ {
return s; return s;
} }
class RaiiCloser
{
int fd_;
public:
RaiiCloser(int filedesc) : fd_(filedesc)
{
assert(fd_ >= 0);
}
~RaiiCloser()
{
if (fd_ >= 0)
{
UAVCAN_TRACE("SocketCAN", "RaiiCloser: Closing fd %d", fd_);
(void)::close(fd_);
}
}
void disarm() { fd_ = -1; }
} raii_closer(s);
// Detect the iface index // Detect the iface index
auto ifr = ::ifreq(); auto ifr = ::ifreq();
if (iface_name.length() >= IFNAMSIZ) if (iface_name.length() >= IFNAMSIZ)
{ {
goto fail; errno = ENAMETOOLONG;
return -1;
} }
(void)std::strncpy(ifr.ifr_name, iface_name.c_str(), iface_name.length()); (void)std::strncpy(ifr.ifr_name, iface_name.c_str(), iface_name.length());
if (::ioctl(s, SIOCGIFINDEX, &ifr) < 0 || ifr.ifr_ifindex < 0) if (::ioctl(s, SIOCGIFINDEX, &ifr) < 0 || ifr.ifr_ifindex < 0)
{ {
goto fail; return -1;
} }
// Bind to a CAN iface
// Bind to the specified CAN iface
{ {
auto addr = ::sockaddr_can(); auto addr = ::sockaddr_can();
addr.can_family = AF_CAN; addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex; addr.can_ifindex = ifr.ifr_ifindex;
if (::bind(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) if (::bind(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0)
{ {
goto fail; return -1;
} }
} }
// Configure // Configure
{ {
const int on = 1; const int on = 1;
// Timestamping // Timestamping
if (::setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)) < 0) if (::setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)) < 0)
{ {
goto fail; return -1;
} }
// Socket loopback // Socket loopback
if (::setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &on, sizeof(on)) < 0) if (::setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &on, sizeof(on)) < 0)
{ {
goto fail; return -1;
} }
// Non-blocking // Non-blocking
if (::fcntl(s, F_SETFL, O_NONBLOCK) < 0) if (::fcntl(s, F_SETFL, O_NONBLOCK) < 0)
{ {
goto fail; return -1;
} }
} }
// Validate the resulting socket // Validate the resulting socket
{ {
int error = 0; int socket_error = 0;
::socklen_t errlen = sizeof(error); ::socklen_t errlen = sizeof(socket_error);
(void)::getsockopt(s, SOL_SOCKET, SO_ERROR, reinterpret_cast<void*>(&error), &errlen); (void)::getsockopt(s, SOL_SOCKET, SO_ERROR, reinterpret_cast<void*>(&socket_error), &errlen);
if (error != 0) if (socket_error != 0)
{ {
goto fail; errno = socket_error;
UAVCAN_TRACE("SocketCAN", "Socket error: iface='%s' errno=%d error=%d", return -1;
iface_name.c_str(), errno, error);
} }
} }
return s;
fail: raii_closer.disarm();
(void)::close(s); return s;
return -1;
} }
}; };