1 /*
2  * Copyright 2018 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 "blob_handler.hpp"
18 #include "bt.hpp"
19 #include "ipmi_handler.hpp"
20 #include "lpc.hpp"
21 #include "tool_errors.hpp"
22 #include "updater.hpp"
23 
24 /* Use CLI11 argument parser once in openbmc/meta-oe or whatever. */
25 #include <getopt.h>
26 
27 #include <algorithm>
28 #include <cstdio>
29 #include <iostream>
30 #include <iterator>
31 #include <memory>
32 #include <string>
33 #include <vector>
34 
35 #define IPMILPC "ipmilpc"
36 #define IPMIBT "ipmibt"
37 
38 namespace
39 {
40 const std::vector<std::string> interfaceList = {IPMIBT, IPMILPC};
41 }
42 
43 void usage(const char* program)
44 {
45     std::fprintf(
46         stderr,
47         "Usage: %s --command <command> --interface <interface> --image "
48         "<image file> --sig <signature file>\n",
49         program);
50 
51     std::fprintf(stderr, "interfaces: ");
52     std::copy(interfaceList.begin(), interfaceList.end(),
53               std::ostream_iterator<std::string>(std::cerr, ", "));
54     std::fprintf(stderr, "\n");
55 }
56 
57 bool checkCommand(const std::string& command)
58 {
59     return (command == "update");
60 }
61 
62 bool checkInterface(const std::string& interface)
63 {
64     auto intf =
65         std::find(interfaceList.begin(), interfaceList.end(), interface);
66     return (intf != interfaceList.end());
67 }
68 
69 int main(int argc, char* argv[])
70 {
71     std::string command, interface, imagePath, signaturePath;
72 
73     while (1)
74     {
75         // clang-format off
76         static struct option long_options[] = {
77             {"command", required_argument, 0, 'c'},
78             {"interface", required_argument, 0, 'i'},
79             {"image", required_argument, 0, 'm'},
80             {"sig", required_argument, 0, 's'},
81             {0, 0, 0, 0}
82         };
83         // clang-format on
84 
85         int option_index = 0;
86         int c =
87             getopt_long(argc, argv, "c:i:m:s:", long_options, &option_index);
88         if (c == -1)
89         {
90             break;
91         }
92 
93         switch (c)
94         {
95             case 'c':
96                 command = std::string{optarg};
97                 if (!checkCommand(command))
98                 {
99                     usage(argv[0]);
100                     exit(EXIT_FAILURE);
101                 }
102 
103                 break;
104             case 'i':
105                 interface = std::string{optarg};
106                 if (!checkInterface(interface))
107                 {
108                     usage(argv[0]);
109                     exit(EXIT_FAILURE);
110                 }
111                 break;
112             case 'm':
113                 imagePath = std::string{optarg};
114                 break;
115             case 's':
116                 signaturePath = std::string{optarg};
117                 break;
118             default:
119                 usage(argv[0]);
120                 exit(EXIT_FAILURE);
121         }
122     }
123 
124     if (command.empty())
125     {
126         usage(argv[0]);
127         exit(EXIT_FAILURE);
128     }
129 
130     /* They want to update the firmware. */
131     if (command == "update")
132     {
133         if (interface.empty() || imagePath.empty() || signaturePath.empty())
134         {
135             usage(argv[0]);
136             exit(EXIT_FAILURE);
137         }
138 
139         host_tool::IpmiHandler ipmi;
140         host_tool::BlobHandler blob(&ipmi);
141 
142         std::unique_ptr<host_tool::DataInterface> handler;
143 
144         /* Input has already been validated in this case. */
145         if (interface == IPMIBT)
146         {
147             handler = std::make_unique<host_tool::BtDataHandler>(&blob);
148         }
149         else if (interface == IPMILPC)
150         {
151             handler = std::make_unique<host_tool::LpcDataHandler>(&blob);
152         }
153 
154         if (!handler)
155         {
156             /* TODO(venture): use a custom exception. */
157             std::fprintf(stderr, "Interface %s is unavailable\n",
158                          interface.c_str());
159             exit(EXIT_FAILURE);
160         }
161 
162         /* The parameters are all filled out. */
163         try
164         {
165             host_tool::updaterMain(&blob, handler.get(), imagePath,
166                                    signaturePath);
167         }
168         catch (const host_tool::ToolException& e)
169         {
170             std::fprintf(stderr, "Exception received: %s\n", e.what());
171             return -1;
172         }
173     }
174 
175     return 0;
176 }
177