xref: /openbmc/phosphor-bmc-code-mgmt/download_manager.cpp (revision cc74233cbe5b4d99787eb30d85c34d7d8e7aafb7)
1 #include "config.h"
2 
3 #include "download_manager.hpp"
4 
5 #include "xyz/openbmc_project/Common/error.hpp"
6 
7 #include <sys/wait.h>
8 #include <unistd.h>
9 
10 #include <phosphor-logging/elog-errors.hpp>
11 #include <phosphor-logging/elog.hpp>
12 #include <phosphor-logging/lg2.hpp>
13 
14 #include <algorithm>
15 #include <filesystem>
16 #include <iostream>
17 #include <string>
18 
19 namespace phosphor
20 {
21 namespace software
22 {
23 namespace manager
24 {
25 
26 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
27 PHOSPHOR_LOG2_USING;
28 using namespace phosphor::logging;
29 namespace fs = std::filesystem;
30 
31 void Download::downloadViaTFTP(std::string fileName, std::string serverAddress)
32 {
33     using Argument = xyz::openbmc_project::Common::InvalidArgument;
34 
35     // Sanitize the fileName string
36     if (!fileName.empty())
37     {
38         fileName.erase(std::remove(fileName.begin(), fileName.end(), '/'),
39                        fileName.end());
40         fileName = fileName.substr(fileName.find_first_not_of('.'));
41     }
42 
43     if (fileName.empty())
44     {
45         error("Filename is empty");
46         elog<InvalidArgument>(Argument::ARGUMENT_NAME("FileName"),
47                               Argument::ARGUMENT_VALUE(fileName.c_str()));
48         return;
49     }
50 
51     if (serverAddress.empty())
52     {
53         error("ServerAddress is empty");
54         elog<InvalidArgument>(Argument::ARGUMENT_NAME("ServerAddress"),
55                               Argument::ARGUMENT_VALUE(serverAddress.c_str()));
56         return;
57     }
58 
59     info("Downloading {PATH} via TFTP: {SERVERADDRESS}", "PATH", fileName,
60          "SERVERADDRESS", serverAddress);
61 
62     // Check if IMAGE DIR exists
63     fs::path imgDirPath(IMG_UPLOAD_DIR);
64     if (!fs::is_directory(imgDirPath))
65     {
66         error("Image Dir {PATH} does not exist", "PATH", imgDirPath);
67         elog<InternalFailure>();
68         return;
69     }
70 
71     pid_t pid = fork();
72 
73     if (pid == 0)
74     {
75         pid_t nextPid = fork();
76         if (nextPid == 0)
77         {
78             // child process
79             execl("/usr/bin/tftp", "tftp", "-g", "-r", fileName.c_str(),
80                   serverAddress.c_str(), "-l",
81                   (std::string{IMG_UPLOAD_DIR} + '/' + fileName).c_str(),
82                   (char*)0);
83             // execl only returns on fail
84             error("Error ({ERRNO}) occurred during the TFTP call", "ERRNO",
85                   errno);
86             elog<InternalFailure>();
87         }
88         else if (nextPid < 0)
89         {
90             error("Error ({ERRNO}) occurred during fork", "ERRNO", errno);
91             elog<InternalFailure>();
92         }
93         // do nothing as parent if all is going well
94         // when parent exits, child will be reparented under init
95         // and then be reaped properly
96         exit(0);
97     }
98     else if (pid < 0)
99     {
100         error("Error ({ERRNO}) occurred during fork", "ERRNO", errno);
101         elog<InternalFailure>();
102     }
103     else
104     {
105         int status;
106         if (waitpid(pid, &status, 0) < 0)
107         {
108             error("Error ({ERRNO}) occurred during waitpid", "ERRNO", errno);
109         }
110         else if (WEXITSTATUS(status) != 0)
111         {
112 
113             error("Failed ({STATUS}) to launch tftp", "STATUS", status);
114         }
115     }
116 
117     return;
118 }
119 
120 } // namespace manager
121 } // namespace software
122 } // namespace phosphor
123