void Netlink::open(int netNsPid) { auto fdFactory = []{ return socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); }; assert(mFd == -1); if (netNsPid == 0 || netNsPid == 1 || netNsPid == ::getpid()) { mFd = fdFactory(); if (mFd == -1) { LOGE("Can't open socket: " << getSystemErrorMessage()); } } else { mFd = utils::passNamespacedFd(netNsPid, CLONE_NEWNET, fdFactory); } if (mFd == -1) { throw VasumException("Can't open netlink connection (zone not running)"); } sockaddr_nl local = utils::make_clean<sockaddr_nl>(); local.nl_family = AF_NETLINK; if (bind(mFd, (struct sockaddr *)&local, sizeof(local)) < 0) { int err = errno; close(); const std::string msg = "Can't bind to socket: " + getSystemErrorMessage(err); LOGE(msg); throw VasumException(msg); } }
Socket::Type Socket::getType() const { int family; socklen_t length = sizeof(family); if (::getsockopt(mFD, SOL_SOCKET, SO_DOMAIN, &family, &length)) { if (errno == EBADF) { return Type::INVALID; } else { const std::string msg = "Error getting socket type: " + getSystemErrorMessage(); LOGE(msg); throw IPCException(msg); } } if (family == AF_UNIX || family == AF_LOCAL) { return Type::UNIX; } if (family == AF_INET || family == AF_INET6) { return Type::INET; } return Type::INVALID; }
std::shared_ptr<Socket> Socket::accept() { int sockfd = ::accept(mFD, nullptr, nullptr); if (sockfd == -1) { const std::string msg = "Error in accept: " + getSystemErrorMessage(); LOGE(msg); throw IPCException(msg); } setFdOptions(sockfd); return std::make_shared<Socket>(sockfd); }
bool waitPid(pid_t pid, int& status) { LOGD("Wait pid " << pid); while (::waitpid(pid, &status, 0) == -1) { if (errno != EINTR) { LOGE("waitpid() failed: " << getSystemErrorMessage()); return false; } } return true; }
bool executeAndWait(const uid_t uid, const char* fname, const char* const* argv, int& status) { LOGD("Execute " << (uid == UNSPEC_UID ? "" : "as " + std::to_string(uid) + " ") << fname << argv); pid_t pid = ::fork(); if (pid == -1) { LOGE("Fork failed: " << getSystemErrorMessage()); return false; } if (pid == 0) { if (uid != UNSPEC_UID && ::setuid(uid) < 0) { LOGW("Failed to become uid(" << uid << "): " << getSystemErrorMessage()); ::_exit(EXIT_FAILURE); } ::execv(fname, const_cast<char* const*>(argv)); LOGE("execv(" << fname << ") failed: " << getSystemErrorMessage()); ::_exit(EXIT_FAILURE); } return waitPid(pid, status); }
std::unique_ptr<std::vector<char>> Netlink::rcv(unsigned int nlmsgSeq) { std::unique_ptr<std::vector<char>> buf(new std::vector<char>()); msghdr msg = utils::make_clean<msghdr>(); sockaddr_nl nladdr = utils::make_clean<sockaddr_nl>(); iovec iov = utils::make_clean<iovec>(); msg.msg_name = &nladdr; msg.msg_namelen = sizeof(nladdr); msg.msg_iov = &iov; msg.msg_iovlen = 1; nladdr.nl_family = AF_NETLINK; nlmsghdr* answer; nlmsghdr* lastOk = NULL; size_t offset = 0; do { buf->resize(offset + NLMSG_RCV_GOOD_SIZE); answer = reinterpret_cast<nlmsghdr*>(buf->data() + offset); iov.iov_base = answer; iov.iov_len = buf->size() - offset; unsigned int ret = vsm_recvmsg(mFd, &msg, 0); for (unsigned int len = ret; NLMSG_OK(answer, len); answer = NLMSG_NEXT(answer, len)) { lastOk = answer; if (answer->nlmsg_type == NLMSG_ERROR) { // It is NACK/ACK message nlmsgerr *err = reinterpret_cast<nlmsgerr*>(NLMSG_DATA(answer)); if (answer->nlmsg_seq != nlmsgSeq) { throw VasumException("Receive failed: answer message was mismatched"); } if (err->error) { throw VasumException("Receive failed: " + getSystemErrorMessage(-err->error)); } } else if (answer->nlmsg_type == NLMSG_OVERRUN) { throw VasumException("Receive failed: data lost"); } } if (lastOk == NULL) { const std::string msg = "Can't receive data from the system"; LOGE(msg); throw VasumException(msg); } offset += NLMSG_ALIGN(ret); } while (lastOk->nlmsg_type != NLMSG_DONE && lastOk->nlmsg_flags & NLM_F_MULTI); buf->resize(offset); return buf; }
unsigned short Socket::getPort() const { ::sockaddr_storage address = {0, 0, {0}}; ::socklen_t length = sizeof(address); if (::getsockname(mFD, reinterpret_cast<sockaddr*>(&address), &length) != 0) { const std::string msg = "Failed to get socked address: " + getSystemErrorMessage(); LOGE(msg); throw IPCException(msg); } if (length == sizeof(sockaddr_in)) { return ntohs(reinterpret_cast<const sockaddr_in*>(&address)->sin_port); } else { return ntohs(reinterpret_cast<const sockaddr_in6*>(&address)->sin6_port); } }
void close(int fd) noexcept { if (fd < 0) { return; } for (;;) { if (-1 == ::close(fd)) { if (errno == EINTR) { LOGT("close() interrupted by a signal, retrying"); continue; } LOGE("Error in close: " << getSystemErrorMessage()); } break; } }
int Socket::getSystemdSocketInternal(const std::string& path) { int n = ::sd_listen_fds(-1 /*Block further calls to sd_listen_fds*/); if (n < 0) { const std::string msg = "sd_listen_fds failed: " + getSystemErrorMessage(-n); LOGE(msg); throw IPCException(msg); } for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; ++fd) { if (0 < ::sd_is_socket_unix(fd, SOCK_STREAM, 1, path.c_str(), 0)) { setFdOptions(fd); return fd; } } LOGW("No usable sockets were passed by systemd."); return -1; }
bool umountImage(const std::string& path, const std::string& loopdev) { if (::umount(path.c_str()) != 0) { LOGD("Umount failed for '" << path << "': " << getSystemErrorMessage()); return false; } // clear loop device int loopFD = ::open(loopdev.c_str(), O_RDWR); if (loopFD < 0) { LOGD("Failed to open fd for loop device 0"); return false; } if (::ioctl(loopFD, LOOP_CLR_FD, 0) < 0) { LOGD("Failed to clear loop device."); close(loopFD); return false; } close(loopFD); return true; }
std::string getSystemErrorMessage() { return getSystemErrorMessage(errno); }