blob: bddc08e38d5953bc40991ebed0f2f0deff7871e7 [file] [log] [blame]
Artem Senicheve8837d52020-06-07 11:59:04 +03001// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2020 YADRO
3
4#include "host_console.hpp"
5
6#include <sys/ioctl.h>
7#include <sys/socket.h>
8#include <sys/un.h>
9#include <unistd.h>
10
11#include <cstring>
12#include <system_error>
13
14/**
15 * @brief Base path to the console's socket.
16 * See obmc-console for details.
17 */
18static constexpr char socketPath[] = "\0obmc-console";
19
20HostConsole::HostConsole(const std::string& socketId) :
21 socketId(socketId), socketFd(-1)
22{}
23
24HostConsole::~HostConsole()
25{
26 if (socketFd != -1)
27 {
28 close(socketFd);
29 }
30}
31
32void HostConsole::connect()
33{
34 if (socketFd != -1)
35 {
36 throw std::runtime_error("Socket already opened");
37 }
38
39 // Create socket
40 socketFd = socket(AF_UNIX, SOCK_STREAM, 0);
41 if (socketFd == -1)
42 {
43 std::error_code ec(errno ? errno : EIO, std::generic_category());
44 throw std::system_error(ec, "Unable to create socket");
45 }
46
47 // Set non-blocking mode for socket
48 int opt = 1;
49 if (ioctl(socketFd, FIONBIO, &opt))
50 {
51 std::error_code ec(errno ? errno : EIO, std::generic_category());
52 throw std::system_error(ec, "Unable to set non-blocking mode");
53 }
54
55 // Construct path to the socket file (see obmc-console for details)
56 std::string path(socketPath, socketPath + sizeof(socketPath) - 1);
57 if (!socketId.empty())
58 {
59 path += '.';
60 path += socketId;
61 }
62 if (path.length() > sizeof(sockaddr_un::sun_path))
63 {
64 throw std::invalid_argument("Invalid socket ID");
65 }
66
67 sockaddr_un sa;
68 sa.sun_family = AF_UNIX;
69 memcpy(&sa.sun_path, path.c_str(), path.length());
70
71 // Connect to host's log stream via socket.
72 // The owner of the socket (server) is obmc-console service and
73 // we have a dependency on it written in the systemd unit file, but
74 // we can't guarantee that the socket is initialized at the moment.
75 size_t connectAttempts = 60; // Number of attempts
76 const socklen_t len = sizeof(sa) - sizeof(sa.sun_path) + path.length();
77 int rc = -1;
78 while (connectAttempts--)
79 {
80 rc = ::connect(socketFd, reinterpret_cast<const sockaddr*>(&sa), len);
81 if (!rc)
82 {
83 break;
84 }
85 else
86 {
87 sleep(1); // Make 1 second pause between attempts
88 }
89 }
90 if (rc)
91 {
92 std::string err = "Unable to connect to console";
93 if (!socketId.empty())
94 {
95 err += ' ';
96 err += socketId;
97 }
98 std::error_code ec(errno ? errno : EIO, std::generic_category());
99 throw std::system_error(ec, err);
100 }
101}
102
103size_t HostConsole::read(char* buf, size_t sz) const
104{
105 ssize_t rsz = ::read(socketFd, buf, sz);
106 if (rsz < 0)
107 {
108 if (errno == EAGAIN || errno == EWOULDBLOCK)
109 {
110 // We are in non-blocking mode, so ignore these codes
111 rsz = 0;
112 }
113 else
114 {
115 std::string err = "Unable to read socket";
116 if (!socketId.empty())
117 {
118 err += ' ';
119 err += socketId;
120 }
121 std::error_code ec(errno ? errno : EIO, std::generic_category());
122 throw std::system_error(ec, err);
123 }
124 }
125
126 return static_cast<size_t>(rsz);
127}
128
129HostConsole::operator int() const
130{
131 return socketFd;
132}