blob: c3a6902c9da3d332b2939c29c3fd1cb6a4e06907 [file] [log] [blame]
Nan Zhou042b5ba2021-06-18 09:32:45 -07001// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2021 Google
3
4#include "stream_service.hpp"
5
6#include <sys/socket.h>
7#include <sys/un.h>
8#include <unistd.h>
9
10#include <phosphor-logging/log.hpp>
11
12#include <vector>
13
14using namespace phosphor::logging;
15
16StreamService::StreamService(const char* streamDestination, DbusLoop& dbusLoop,
17 HostConsole& hostConsole) :
Patrick Williams87c333e2024-08-16 15:20:11 -040018 destinationPath(streamDestination), dbusLoop(&dbusLoop),
19 hostConsole(&hostConsole), outputSocketFd(-1), destination()
Nan Zhou042b5ba2021-06-18 09:32:45 -070020{}
21
22StreamService::~StreamService()
23{
24 if (outputSocketFd != -1)
25 {
26 close(outputSocketFd);
27 }
28}
29
30void StreamService::run()
31{
32 setStreamSocket();
33 hostConsole->connect();
34 // Add SIGTERM signal handler for service shutdown
35 dbusLoop->addSignalHandler(SIGTERM, [this]() { this->dbusLoop->stop(0); });
36 // Register callback for socket IO
37 dbusLoop->addIoHandler(*hostConsole, [this]() { this->readConsole(); });
38
39 // Run D-Bus event loop
40 const int rc = dbusLoop->run();
41 if (rc < 0)
42 {
43 std::error_code ec(-rc, std::generic_category());
44 throw std::system_error(ec, "Error in event loop");
45 }
46}
47
48void StreamService::readConsole()
49{
50 constexpr size_t bufSize = 128; // enough for most line-oriented output
51 std::vector<char> bufData(bufSize);
52 char* buf = bufData.data();
53
54 try
55 {
56 while (const size_t rsz = hostConsole->read(buf, bufSize))
57 {
58 streamConsole(buf, rsz);
59 }
60 }
61 catch (const std::system_error& ex)
62 {
63 log<level::ERR>(ex.what());
64 }
65}
66
67void StreamService::streamConsole(const char* data, size_t len)
68{
69 // Send all received characters in a blocking manner.
70 size_t sent = 0;
71 while (sent < len)
72 {
73 // Datagram sockets preserve message boundaries. Furthermore,
74 // In most implementation, UNIX domain datagram sockets are
75 // always reliable and don't reorder datagrams.
76 ssize_t curr_sent =
77 sendto(outputSocketFd, data + sent, len - sent, 0,
78 reinterpret_cast<const sockaddr*>(&destination),
79 sizeof(destination) - sizeof(destination.sun_path) +
80 strlen(destinationPath + 1) + 1);
81 if (curr_sent == -1)
82 {
83 std::string error = "Unable to send to the destination ";
84 error += destinationPath;
85 std::error_code ec(errno ? errno : EIO, std::generic_category());
86 throw std::system_error(ec, error);
87 }
88 sent += curr_sent;
89 }
90}
91
92void StreamService::setStreamSocket()
93{
94 destination.sun_family = AF_UNIX;
95 // To deal with abstract namespace unix socket.
96 size_t len = strlen(destinationPath + 1) + 1;
97 memcpy(destination.sun_path, destinationPath, len);
98 destination.sun_path[len] = '\0';
99 outputSocketFd = socket(AF_UNIX, SOCK_DGRAM, 0);
100 if (outputSocketFd == -1)
101 {
102 std::error_code ec(errno ? errno : EIO, std::generic_category());
103 throw std::system_error(ec, "Unable to create output socket.");
104 }
105}