xref: /openbmc/phosphor-ipmi-flash/tools/net.cpp (revision bec23189399cf5524352a4a42debd9ed43b31bfa)
1 /*
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 <ipmiblob/blob_errors.hpp>
30 #include <stdplus/handle/managed.hpp>
31 
32 #include <cstdint>
33 #include <cstring>
34 #include <memory>
35 #include <optional>
36 #include <string>
37 #include <vector>
38 
39 namespace
40 {
41 
42 void closefd(int&& fd, const internal::Sys*& sys)
43 {
44     sys->close(fd);
45 }
46 using Fd = stdplus::Managed<int, const internal::Sys*>::Handle<closefd>;
47 
48 } // namespace
49 
50 namespace host_tool
51 {
52 
53 bool 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;
85         hints.ai_family = AF_UNSPEC;
86         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));
94             progress->abort();
95             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");
115             progress->abort();
116             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));
132                 progress->abort();
133                 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     {
153         progress->abort();
154         return false;
155     }
156 
157     progress->finish();
158     return true;
159 }
160 
161 } // namespace host_tool
162