LCOV - code coverage report
Current view: top level - ipc/common/source - NamedSocket.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 72.3 % 155 112
Test Date: 2026-06-17 06:35:35 Functions: 100.0 % 15 15

            Line data    Source code
       1              : /*
       2              :  * If not stated otherwise in this file or this component's LICENSE file the
       3              :  * following copyright and licenses apply:
       4              :  *
       5              :  * Copyright 2025 Sky UK
       6              :  *
       7              :  * Licensed under the Apache License, Version 2.0 (the "License");
       8              :  * you may not use this file except in compliance with the License.
       9              :  * You may obtain a copy of the License at
      10              :  *
      11              :  * http://www.apache.org/licenses/LICENSE-2.0
      12              :  *
      13              :  * Unless required by applicable law or agreed to in writing, software
      14              :  * distributed under the License is distributed on an "AS IS" BASIS,
      15              :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      16              :  * See the License for the specific language governing permissions and
      17              :  * limitations under the License.
      18              :  */
      19              : 
      20              : #include "NamedSocket.h"
      21              : #include "IpcLogging.h"
      22              : #include <cstdint>
      23              : #include <grp.h>
      24              : #include <pwd.h>
      25              : #include <stdexcept>
      26              : #include <sys/file.h>
      27              : #include <sys/socket.h>
      28              : #include <sys/stat.h>
      29              : #include <sys/un.h>
      30              : #include <unistd.h>
      31              : #include <utility>
      32              : #include <vector>
      33              : 
      34              : namespace
      35              : {
      36              : constexpr uid_t kNoOwnerChange = -1;        // -1 means chown() won't change the owner
      37              : constexpr gid_t kNoGroupChange = -1;        // -1 means chown() won't change the group
      38              : constexpr size_t kDefaultBufferSize = 4096; // Fallback buffer size
      39              : } // namespace
      40              : 
      41              : namespace firebolt::rialto::ipc
      42              : {
      43            6 : INamedSocketFactory &INamedSocketFactory::getFactory()
      44              : {
      45            6 :     static NamedSocketFactory factory;
      46            6 :     return factory;
      47              : }
      48              : 
      49            3 : std::unique_ptr<INamedSocket> NamedSocketFactory::createNamedSocket() const
      50              : try
      51              : {
      52            3 :     return std::make_unique<NamedSocket>();
      53              : }
      54            0 : catch (const std::runtime_error &error)
      55              : {
      56            0 :     RIALTO_IPC_LOG_ERROR("Failed to create named socket: %s", error.what());
      57            0 :     return nullptr;
      58              : }
      59              : 
      60            3 : std::unique_ptr<INamedSocket> NamedSocketFactory::createNamedSocket(const std::string &socketPath) const
      61              : try
      62              : {
      63            3 :     return std::make_unique<NamedSocket>(socketPath);
      64              : }
      65            0 : catch (const std::runtime_error &error)
      66              : {
      67            0 :     RIALTO_IPC_LOG_ERROR("Failed to create named socket: %s", error.what());
      68            0 :     return nullptr;
      69              : }
      70              : 
      71            3 : NamedSocket::NamedSocket()
      72              : {
      73            3 :     RIALTO_IPC_LOG_MIL("Creating new socket without binding");
      74              : 
      75              :     // Create the socket
      76            3 :     m_sockFd = ::socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0);
      77            3 :     if (m_sockFd == -1)
      78              :     {
      79            0 :         RIALTO_IPC_LOG_SYS_ERROR(errno, "socket error");
      80            0 :         throw std::runtime_error("socket error");
      81              :     }
      82              : 
      83            3 :     RIALTO_IPC_LOG_MIL("Socket created, fd: %d", m_sockFd);
      84              : }
      85              : 
      86            3 : NamedSocket::NamedSocket(const std::string &socketPath)
      87              : {
      88            3 :     RIALTO_IPC_LOG_MIL("Creating named socket with name: %s", socketPath.c_str());
      89            3 :     m_sockPath = socketPath;
      90              : 
      91              :     // Create the socket
      92            3 :     m_sockFd = ::socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0);
      93            3 :     if (m_sockFd == -1)
      94              :     {
      95            0 :         RIALTO_IPC_LOG_SYS_ERROR(errno, "socket error");
      96            0 :         throw std::runtime_error("socket error");
      97              :     }
      98              : 
      99              :     // get the socket lock
     100            3 :     if (!getSocketLock())
     101              :     {
     102            0 :         closeListeningSocket();
     103            0 :         throw std::runtime_error("lock error");
     104              :     }
     105              : 
     106              :     // bind to the given path
     107            3 :     struct sockaddr_un addr = {0};
     108            3 :     memset(&addr, 0x00, sizeof(addr));
     109            3 :     addr.sun_family = AF_UNIX;
     110            3 :     strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
     111              : 
     112            3 :     if (::bind(m_sockFd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) == -1)
     113              :     {
     114            0 :         RIALTO_IPC_LOG_SYS_ERROR(errno, "bind error");
     115              : 
     116            0 :         closeListeningSocket();
     117            0 :         throw std::runtime_error("bind error");
     118              :     }
     119              : 
     120            3 :     RIALTO_IPC_LOG_MIL("Named socket with name: %s created, fd: %d", m_sockPath.c_str(), m_sockFd);
     121              : }
     122              : 
     123           12 : NamedSocket::~NamedSocket()
     124              : {
     125            6 :     RIALTO_IPC_LOG_MIL("Close named socket with name: %s, fd: %d", m_sockPath.c_str(), m_sockFd);
     126            6 :     closeListeningSocket();
     127           12 : }
     128              : 
     129            4 : int NamedSocket::getFd() const
     130              : {
     131            4 :     return m_sockFd;
     132              : }
     133              : 
     134            1 : bool NamedSocket::setSocketPermissions(unsigned int socketPermissions) const
     135              : {
     136            1 :     errno = 0;
     137            1 :     if (chmod(m_sockPath.c_str(), socketPermissions) != 0)
     138              :     {
     139            0 :         RIALTO_IPC_LOG_SYS_WARN(errno, "Failed to change the permissions on the IPC socket");
     140            0 :         return false;
     141              :     }
     142            1 :     return true;
     143              : }
     144              : 
     145            1 : bool NamedSocket::setSocketOwnership(const std::string &socketOwner, const std::string &socketGroup) const
     146              : {
     147            1 :     uid_t ownerId = getSocketOwnerId(socketOwner);
     148            1 :     gid_t groupId = getSocketGroupId(socketGroup);
     149              : 
     150            1 :     if (ownerId != kNoOwnerChange || groupId != kNoGroupChange)
     151              :     {
     152            1 :         errno = 0;
     153            1 :         if (chown(m_sockPath.c_str(), ownerId, groupId) != 0)
     154              :         {
     155            0 :             RIALTO_IPC_LOG_SYS_WARN(errno, "Failed to change the owner/group for the IPC socket");
     156              :         }
     157              :     }
     158            1 :     return true;
     159              : }
     160              : 
     161            2 : bool NamedSocket::blockNewConnections() const
     162              : {
     163            2 :     if (m_sockPath.empty())
     164              :     {
     165            1 :         RIALTO_IPC_LOG_DEBUG("No need to block new connections - socket not configured");
     166            1 :         return true;
     167              :     }
     168            1 :     RIALTO_IPC_LOG_INFO("Block new connections for: %s", m_sockPath.c_str());
     169            1 :     if (listen(m_sockFd, 0) == -1)
     170              :     {
     171            0 :         RIALTO_IPC_LOG_SYS_ERROR(errno, "blockNewConnections: listen error");
     172            0 :         return false;
     173              :     }
     174            1 :     return true;
     175              : }
     176              : 
     177            3 : bool NamedSocket::bind(const std::string &socketPath)
     178              : {
     179            3 :     if (!m_sockPath.empty())
     180              :     {
     181            1 :         RIALTO_IPC_LOG_DEBUG("no need to bind again");
     182            1 :         return true;
     183              :     }
     184            2 :     RIALTO_IPC_LOG_MIL("Binding socket with fd: %d with name: %s", m_sockFd, socketPath.c_str());
     185            2 :     m_sockPath = socketPath;
     186              : 
     187              :     // get the socket lock
     188            2 :     if (!getSocketLock())
     189              :     {
     190            0 :         closeListeningSocket();
     191            0 :         return false;
     192              :     }
     193              : 
     194              :     // bind to the given path
     195            2 :     struct sockaddr_un addr = {0};
     196            2 :     memset(&addr, 0x00, sizeof(addr));
     197            2 :     addr.sun_family = AF_UNIX;
     198            2 :     strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
     199              : 
     200            2 :     if (::bind(m_sockFd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) == -1)
     201              :     {
     202            0 :         RIALTO_IPC_LOG_SYS_ERROR(errno, "bind error");
     203              : 
     204            0 :         closeListeningSocket();
     205            0 :         return false;
     206              :     }
     207              : 
     208            2 :     RIALTO_IPC_LOG_MIL("Named socket with fd: %d bound with path: %s", m_sockFd, m_sockPath.c_str());
     209              : 
     210            2 :     return true;
     211              : }
     212              : 
     213            6 : void NamedSocket::closeListeningSocket()
     214              : {
     215            6 :     if (!m_sockPath.empty() && (unlink(m_sockPath.c_str()) != 0) && (errno != ENOENT))
     216            0 :         RIALTO_IPC_LOG_SYS_ERROR(errno, "failed to remove socket @ '%s'", m_sockPath.c_str());
     217            6 :     if ((m_sockFd >= 0) && (close(m_sockFd) != 0))
     218            0 :         RIALTO_IPC_LOG_SYS_ERROR(errno, "failed to close listening socket");
     219              : 
     220            6 :     if (!m_lockPath.empty() && (unlink(m_lockPath.c_str()) != 0) && (errno != ENOENT))
     221            0 :         RIALTO_IPC_LOG_SYS_ERROR(errno, "failed to remove socket lock file @ '%s'", m_lockPath.c_str());
     222            6 :     if ((m_lockFd >= 0) && (close(m_lockFd) != 0))
     223            0 :         RIALTO_IPC_LOG_SYS_ERROR(errno, "failed to close socket lock file");
     224              : 
     225            6 :     m_sockFd = -1;
     226            6 :     m_sockPath.clear();
     227              : 
     228            6 :     m_lockFd = -1;
     229            6 :     m_lockPath.clear();
     230              : }
     231              : 
     232            5 : bool NamedSocket::getSocketLock()
     233              : {
     234            5 :     std::string lockPath = m_sockPath + ".lock";
     235            5 :     int fd = open(lockPath.c_str(), O_CREAT | O_CLOEXEC, (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
     236            5 :     if (fd < 0)
     237              :     {
     238            0 :         RIALTO_IPC_LOG_SYS_ERROR(errno, "failed to create / open lockfile @ '%s' (check permissions)", lockPath.c_str());
     239            0 :         return false;
     240              :     }
     241              : 
     242            5 :     if (flock(fd, LOCK_EX | LOCK_NB) < 0)
     243              :     {
     244            0 :         RIALTO_IPC_LOG_SYS_ERROR(errno, "failed to lock lockfile @ '%s', maybe another server is running",
     245              :                                  lockPath.c_str());
     246            0 :         close(fd);
     247            0 :         return false;
     248              :     }
     249              : 
     250            5 :     struct stat sbuf = {0};
     251            5 :     if (stat(m_sockPath.c_str(), &sbuf) < 0)
     252              :     {
     253            5 :         if (errno != ENOENT)
     254              :         {
     255            0 :             RIALTO_IPC_LOG_SYS_ERROR(errno, "did not manage to stat existing socket @ '%s'", m_sockPath.c_str());
     256            0 :             close(fd);
     257            0 :             return false;
     258              :         }
     259              :     }
     260            0 :     else if ((sbuf.st_mode & S_IWUSR) || (sbuf.st_mode & S_IWGRP))
     261              :     {
     262            0 :         unlink(m_sockPath.c_str());
     263              :     }
     264              : 
     265            5 :     m_lockFd = fd;
     266            5 :     m_lockPath = std::move(lockPath);
     267              : 
     268            5 :     return true;
     269              : }
     270              : 
     271            1 : uid_t NamedSocket::getSocketOwnerId(const std::string &socketOwner) const
     272              : {
     273            1 :     uid_t ownerId = kNoOwnerChange;
     274            1 :     if (!socketOwner.empty())
     275              :     {
     276            1 :         int64_t bufferSizeLong = sysconf(_SC_GETPW_R_SIZE_MAX);
     277            1 :         const size_t kBufferSize = (bufferSizeLong > 0) ? static_cast<size_t>(bufferSizeLong) : kDefaultBufferSize;
     278              : 
     279            1 :         errno = 0;
     280            1 :         passwd passwordStruct{};
     281            1 :         passwd *passwordResult = nullptr;
     282            1 :         std::vector<char> buffer(kBufferSize);
     283            1 :         int result = getpwnam_r(socketOwner.c_str(), &passwordStruct, buffer.data(), buffer.size(), &passwordResult);
     284            1 :         if (result == 0)
     285              :         {
     286            1 :             if (passwordResult)
     287              :             {
     288            1 :                 ownerId = passwordResult->pw_uid;
     289              :             }
     290              :             else
     291              :             {
     292            0 :                 RIALTO_IPC_LOG_WARN("Owner name '%s' not found", socketOwner.c_str());
     293              :             }
     294              :         }
     295              :         else
     296              :         {
     297            0 :             RIALTO_IPC_LOG_SYS_WARN(result, "Failed to lookup ownerId for '%s'", socketOwner.c_str());
     298              :         }
     299            1 :     }
     300            1 :     return ownerId;
     301              : }
     302              : 
     303            1 : gid_t NamedSocket::getSocketGroupId(const std::string &socketGroup) const
     304              : {
     305            1 :     gid_t groupId = kNoGroupChange;
     306            1 :     if (!socketGroup.empty())
     307              :     {
     308            1 :         int64_t bufferSizeLong = sysconf(_SC_GETGR_R_SIZE_MAX);
     309            1 :         const size_t kBufferSize = (bufferSizeLong > 0) ? static_cast<size_t>(bufferSizeLong) : kDefaultBufferSize;
     310              : 
     311            1 :         errno = 0;
     312            1 :         group groupStruct{};
     313            1 :         group *groupResult = nullptr;
     314            1 :         std::vector<char> buffer(kBufferSize);
     315            1 :         int result = getgrnam_r(socketGroup.c_str(), &groupStruct, buffer.data(), buffer.size(), &groupResult);
     316            1 :         if (result == 0)
     317              :         {
     318            1 :             if (groupResult)
     319              :             {
     320            1 :                 groupId = groupResult->gr_gid;
     321              :             }
     322              :             else
     323              :             {
     324            0 :                 RIALTO_IPC_LOG_WARN("Group name '%s' not found", socketGroup.c_str());
     325              :             }
     326              :         }
     327              :         else
     328              :         {
     329            0 :             RIALTO_IPC_LOG_SYS_WARN(result, "Failed to lookup groupId for '%s'", socketGroup.c_str());
     330              :         }
     331            1 :     }
     332            1 :     return groupId;
     333              : }
     334              : } // namespace firebolt::rialto::ipc
        

Generated by: LCOV version 2.0-1