blob: 8b0663ec3aa1bc79fad7da4669847e240d3d8f2d [file] [log] [blame]
Benjamin Fair30d09a32019-10-11 16:57:47 -07001/*
2 * Copyright 2019 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "net.hpp"
18
19#include "data.hpp"
20#include "flags.hpp"
21
22#include <errno.h>
23#include <fcntl.h>
24#include <netdb.h>
25#include <sys/sendfile.h>
26#include <sys/socket.h>
27#include <sys/types.h>
28
Patrick Venture9b37b092020-05-28 20:58:57 -070029#include <ipmiblob/blob_errors.hpp>
30#include <stdplus/handle/managed.hpp>
William A. Kennington III9bb21e32022-04-08 14:28:50 -070031#include <stdplus/util/cexec.hpp>
Patrick Venture9b37b092020-05-28 20:58:57 -070032
Benjamin Fair30d09a32019-10-11 16:57:47 -070033#include <cstdint>
34#include <cstring>
Benjamin Fair4152f092019-11-25 19:07:31 -080035#include <optional>
Benjamin Fair30d09a32019-10-11 16:57:47 -070036#include <vector>
37
38namespace
39{
40
41void closefd(int&& fd, const internal::Sys*& sys)
42{
43 sys->close(fd);
44}
45using Fd = stdplus::Managed<int, const internal::Sys*>::Handle<closefd>;
46
47} // namespace
48
49namespace host_tool
50{
51
52bool NetDataHandler::sendContents(const std::string& input,
53 std::uint16_t session)
54{
55 constexpr size_t blockSize = 64 * 1024;
56 Fd inputFd(std::nullopt, sys);
57
William A. Kennington III53ecd292021-01-25 15:54:08 -080058 inputFd.reset(sys->open(input.c_str(), O_RDONLY));
59 if (*inputFd < 0)
Benjamin Fair30d09a32019-10-11 16:57:47 -070060 {
William A. Kennington III53ecd292021-01-25 15:54:08 -080061 (void)inputFd.release();
62 std::fprintf(stderr, "Unable to open file: '%s'\n", input.c_str());
63 return false;
64 }
Benjamin Fair30d09a32019-10-11 16:57:47 -070065
William A. Kennington III53ecd292021-01-25 15:54:08 -080066 std::int64_t fileSize = sys->getSize(input.c_str());
Benjamin Fair30d09a32019-10-11 16:57:47 -070067 Fd connFd(std::nullopt, sys);
68
69 {
70 struct addrinfo hints;
71 std::memset(&hints, 0, sizeof(hints));
72 hints.ai_flags = AI_NUMERICHOST;
Benjamin Fair2aa55342019-12-04 16:39:04 -080073 hints.ai_family = AF_UNSPEC;
Benjamin Fair30d09a32019-10-11 16:57:47 -070074 hints.ai_socktype = SOCK_STREAM;
75
76 struct addrinfo *addrs, *addr;
77 int ret = sys->getaddrinfo(host.c_str(), port.c_str(), &hints, &addrs);
78 if (ret < 0)
79 {
80 std::fprintf(stderr, "Couldn't parse address %s with port %s: %s\n",
81 host.c_str(), port.c_str(), gai_strerror(ret));
82 return false;
83 }
84
85 for (addr = addrs; addr != nullptr; addr = addr->ai_next)
86 {
87 connFd.reset(sys->socket(addr->ai_family, addr->ai_socktype,
88 addr->ai_protocol));
89 if (*connFd == -1)
90 continue;
91
92 if (sys->connect(*connFd, addr->ai_addr, addr->ai_addrlen) != -1)
93 break;
94 }
95
96 // TODO: use stdplus Managed for the addrinfo structs
97 sys->freeaddrinfo(addrs);
98
99 if (addr == nullptr)
100 {
101 std::fprintf(stderr, "Failed to connect\n");
102 return false;
103 }
104 }
105
106 try
107 {
108 int bytesSent = 0;
109 off_t offset = 0;
110
William A. Kennington III53ecd292021-01-25 15:54:08 -0800111 progress->start(fileSize);
William A. Kennington III9bb21e32022-04-08 14:28:50 -0700112 auto confirmSend = [&]() {
113 if (bytesSent == 0)
114 {
115 return;
116 }
117 struct ipmi_flash::ExtChunkHdr chunk;
118 chunk.length = bytesSent;
119 std::vector<uint8_t> chunkBytes(sizeof(chunk));
120 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
121 /* This doesn't return anything on success. */
122 blob->writeBytes(session, offset - bytesSent, chunkBytes);
123 progress->updateProgress(bytesSent);
124 };
William A. Kennington III53ecd292021-01-25 15:54:08 -0800125
Benjamin Fair30d09a32019-10-11 16:57:47 -0700126 do
127 {
128 bytesSent = sys->sendfile(*connFd, *inputFd, &offset, blockSize);
129 if (bytesSent < 0)
130 {
William A. Kennington III9bb21e32022-04-08 14:28:50 -0700131 // Not all input files support sendfile, fall back to a simple
132 // buffered mechanism if unsupported.
133 if (errno != EINVAL)
134 {
135 CHECK_ERRNO(-1, "Sending data to BMC");
136 }
137 std::array<uint8_t, 4096> buf;
138 size_t left = 0;
139 do
140 {
141 left += CHECK_ERRNO(sys->read(*inputFd, buf.data() + left,
142 buf.size() - left),
143 "Reading data for BMC");
144 bytesSent =
145 CHECK_ERRNO(sys->send(*connFd, buf.data(), left, 0),
146 "Sending data to BMC");
147 std::memmove(buf.data(), buf.data() + bytesSent,
148 left - bytesSent);
149 left -= bytesSent;
150 offset += bytesSent;
151 confirmSend();
152 } while (bytesSent > 0);
153 break;
Benjamin Fair30d09a32019-10-11 16:57:47 -0700154 }
William A. Kennington III9bb21e32022-04-08 14:28:50 -0700155 confirmSend();
Benjamin Fair30d09a32019-10-11 16:57:47 -0700156 } while (bytesSent > 0);
157 }
William A. Kennington III9bb21e32022-04-08 14:28:50 -0700158 catch (const std::system_error& e)
159 {
160 std::fprintf(stderr, "%s\n", e.what());
161 progress->abort();
162 return false;
163 }
Benjamin Fair30d09a32019-10-11 16:57:47 -0700164 catch (const ipmiblob::BlobException& b)
165 {
William A. Kennington III0d5bb782021-01-19 15:50:42 -0800166 progress->abort();
Benjamin Fair30d09a32019-10-11 16:57:47 -0700167 return false;
168 }
169
William A. Kennington III0d5bb782021-01-19 15:50:42 -0800170 progress->finish();
Benjamin Fair30d09a32019-10-11 16:57:47 -0700171 return true;
172}
173
174} // namespace host_tool