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