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