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/log.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 using namespace phosphor::logging;
28 namespace fs = std::filesystem;
29 
30 void Download::downloadViaTFTP(std::string fileName, std::string serverAddress)
31 {
32     using Argument = xyz::openbmc_project::Common::InvalidArgument;
33 
34     // Sanitize the fileName string
35     if (!fileName.empty())
36     {
37         fileName.erase(std::remove(fileName.begin(), fileName.end(), '/'),
38                        fileName.end());
39         fileName = fileName.substr(fileName.find_first_not_of('.'));
40     }
41 
42     if (fileName.empty())
43     {
44         log<level::ERR>("Error FileName is empty");
45         elog<InvalidArgument>(Argument::ARGUMENT_NAME("FileName"),
46                               Argument::ARGUMENT_VALUE(fileName.c_str()));
47         return;
48     }
49 
50     if (serverAddress.empty())
51     {
52         log<level::ERR>("Error ServerAddress is empty");
53         elog<InvalidArgument>(Argument::ARGUMENT_NAME("ServerAddress"),
54                               Argument::ARGUMENT_VALUE(serverAddress.c_str()));
55         return;
56     }
57 
58     log<level::INFO>("Downloading via TFTP",
59                      entry("FILENAME=%s", fileName.c_str()),
60                      entry("SERVERADDRESS=%s", serverAddress.c_str()));
61 
62     // Check if IMAGE DIR exists
63     fs::path imgDirPath(IMG_UPLOAD_DIR);
64     if (!fs::is_directory(imgDirPath))
65     {
66         log<level::ERR>("Error Image Dir does not exist");
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             log<level::ERR>("Error occurred during the TFTP call");
85             elog<InternalFailure>();
86         }
87         else if (nextPid < 0)
88         {
89             log<level::ERR>("Error occurred during fork");
90             elog<InternalFailure>();
91         }
92         // do nothing as parent if all is going well
93         // when parent exits, child will be reparented under init
94         // and then be reaped properly
95         exit(0);
96     }
97     else if (pid < 0)
98     {
99         log<level::ERR>("Error occurred during fork");
100         elog<InternalFailure>();
101     }
102     else
103     {
104         int status;
105         if (waitpid(pid, &status, 0) < 0)
106         {
107             log<level::ERR>("waitpid error");
108         }
109         else if (WEXITSTATUS(status) != 0)
110         {
111             log<level::ERR>("failed to launch tftp");
112         }
113     }
114 
115     return;
116 }
117 
118 } // namespace manager
119 } // namespace software
120 } // namespace phosphor
121