blob: f6a44d8b7f7118f4a2f91bb2964a8f5c124fac2c [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>
31
Benjamin Fair30d09a32019-10-11 16:57:47 -070032#include <cstdint>
33#include <cstring>
Benjamin Fair30d09a32019-10-11 16:57:47 -070034#include <memory>
Benjamin Fair4152f092019-11-25 19:07:31 -080035#include <optional>
Benjamin Fair30d09a32019-10-11 16:57:47 -070036#include <string>
37#include <vector>
38
39namespace
40{
41
42void closefd(int&& fd, const internal::Sys*& sys)
43{
44 sys->close(fd);
45}
46using Fd = stdplus::Managed<int, const internal::Sys*>::Handle<closefd>;
47
48} // namespace
49
50namespace host_tool
51{
52
53bool NetDataHandler::sendContents(const std::string& input,
54 std::uint16_t session)
55{
56 constexpr size_t blockSize = 64 * 1024;
57 Fd inputFd(std::nullopt, sys);
58
59 {
60 inputFd.reset(sys->open(input.c_str(), O_RDONLY));
61 if (*inputFd < 0)
62 {
63 (void)inputFd.release();
64 std::fprintf(stderr, "Unable to open file: '%s'\n", input.c_str());
65 return false;
66 }
67
68 std::int64_t fileSize = sys->getSize(input.c_str());
69 if (fileSize == 0)
70 {
71 std::fprintf(stderr,
72 "Zero-length file, or other file access error\n");
73 return false;
74 }
75
76 progress->start(fileSize);
77 }
78
79 Fd connFd(std::nullopt, sys);
80
81 {
82 struct addrinfo hints;
83 std::memset(&hints, 0, sizeof(hints));
84 hints.ai_flags = AI_NUMERICHOST;
Benjamin Fair2aa55342019-12-04 16:39:04 -080085 hints.ai_family = AF_UNSPEC;
Benjamin Fair30d09a32019-10-11 16:57:47 -070086 hints.ai_socktype = SOCK_STREAM;
87
88 struct addrinfo *addrs, *addr;
89 int ret = sys->getaddrinfo(host.c_str(), port.c_str(), &hints, &addrs);
90 if (ret < 0)
91 {
92 std::fprintf(stderr, "Couldn't parse address %s with port %s: %s\n",
93 host.c_str(), port.c_str(), gai_strerror(ret));
William A. Kennington III0d5bb782021-01-19 15:50:42 -080094 progress->abort();
Benjamin Fair30d09a32019-10-11 16:57:47 -070095 return false;
96 }
97
98 for (addr = addrs; addr != nullptr; addr = addr->ai_next)
99 {
100 connFd.reset(sys->socket(addr->ai_family, addr->ai_socktype,
101 addr->ai_protocol));
102 if (*connFd == -1)
103 continue;
104
105 if (sys->connect(*connFd, addr->ai_addr, addr->ai_addrlen) != -1)
106 break;
107 }
108
109 // TODO: use stdplus Managed for the addrinfo structs
110 sys->freeaddrinfo(addrs);
111
112 if (addr == nullptr)
113 {
114 std::fprintf(stderr, "Failed to connect\n");
William A. Kennington III0d5bb782021-01-19 15:50:42 -0800115 progress->abort();
Benjamin Fair30d09a32019-10-11 16:57:47 -0700116 return false;
117 }
118 }
119
120 try
121 {
122 int bytesSent = 0;
123 off_t offset = 0;
124
125 do
126 {
127 bytesSent = sys->sendfile(*connFd, *inputFd, &offset, blockSize);
128 if (bytesSent < 0)
129 {
130 std::fprintf(stderr, "Failed to send data to BMC: %s\n",
131 strerror(errno));
William A. Kennington III0d5bb782021-01-19 15:50:42 -0800132 progress->abort();
Benjamin Fair30d09a32019-10-11 16:57:47 -0700133 return false;
134 }
135 else if (bytesSent > 0)
136 {
137 /* Ok, so the data is staged, now send the blob write with
138 * the details.
139 */
140 struct ipmi_flash::ExtChunkHdr chunk;
141 chunk.length = bytesSent;
142 std::vector<std::uint8_t> chunkBytes(sizeof(chunk));
143 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
144
145 /* This doesn't return anything on success. */
146 blob->writeBytes(session, offset - bytesSent, chunkBytes);
147 progress->updateProgress(bytesSent);
148 }
149 } while (bytesSent > 0);
150 }
151 catch (const ipmiblob::BlobException& b)
152 {
William A. Kennington III0d5bb782021-01-19 15:50:42 -0800153 progress->abort();
Benjamin Fair30d09a32019-10-11 16:57:47 -0700154 return false;
155 }
156
William A. Kennington III0d5bb782021-01-19 15:50:42 -0800157 progress->finish();
Benjamin Fair30d09a32019-10-11 16:57:47 -0700158 return true;
159}
160
161} // namespace host_tool