xref: /openbmc/phosphor-networkd/src/ncsi_netlink_main.cpp (revision 3c4f35ebe7ee527608bd57a2c51cccfa3f2ee836)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2018 IBM Corporation
3 
4 #include "argument.hpp"
5 #include "ncsi_util.hpp"
6 
7 #include <string.h>
8 #include <unistd.h>
9 
10 #include <phosphor-logging/lg2.hpp>
11 #include <stdplus/numeric/str.hpp>
12 #include <stdplus/str/buf.hpp>
13 
14 #include <string>
15 #include <vector>
16 
17 static void exitWithError(const char* err, char** argv)
18 {
19     phosphor::network::ncsi::ArgumentParser::usage(argv);
20     lg2::error("ERROR: {ERROR}", "ERROR", err);
21     exit(EXIT_FAILURE);
22 }
23 
24 static void printInfo(phosphor::network::ncsi::InterfaceInfo& info)
25 {
26     using namespace phosphor::network::ncsi;
27 
28     for (PackageInfo& pkg : info.packages)
29     {
30         lg2::debug("Package id : {ID}", "ID", pkg.id);
31         if (pkg.forced)
32         {
33             lg2::debug("  package is forced");
34         }
35         for (ChannelInfo& chan : pkg.channels)
36         {
37             lg2::debug("    Channel id : {ID}", "ID", chan.id);
38             if (chan.forced)
39             {
40                 lg2::debug("    channel is forced");
41             }
42             if (chan.active)
43             {
44                 lg2::debug("    channel is active");
45             }
46 
47             lg2::debug("      version {MAJOR}.{MINOR} ({STR})", "MAJOR",
48                        chan.version_major, "MINOR", chan.version_minor, "STR",
49                        chan.version);
50 
51             lg2::debug("      link state {LINK}", "LINK", lg2::hex,
52                        chan.link_state);
53 
54             auto& vlans = chan.vlan_ids;
55 
56             if (!vlans.empty())
57             {
58                 lg2::debug("      Actve VLAN IDs:");
59                 for (uint16_t vlan : vlans)
60                 {
61                     lg2::debug("        VID: {VLAN_ID}", "VLAN_ID", vlan);
62                 }
63             }
64         }
65     }
66 }
67 
68 int main(int argc, char** argv)
69 {
70     using namespace phosphor::network;
71     using namespace phosphor::network::ncsi;
72     // Read arguments.
73     auto options = ArgumentParser(argc, argv);
74     int packageInt{};
75     int channelInt{};
76     int indexInt{};
77 
78     // Parse out interface argument.
79     auto ifIndex = (options)["index"];
80     try
81     {
82         indexInt = stoi(ifIndex, nullptr);
83     }
84     catch (const std::exception& e)
85     {
86         exitWithError("Interface not specified.", argv);
87     }
88 
89     if (indexInt < 0)
90     {
91         exitWithError("Interface value should be greater than equal to 0",
92                       argv);
93     }
94 
95     NetlinkInterface interface(indexInt);
96 
97     // Parse out package argument.
98     auto package = (options)["package"];
99     try
100     {
101         packageInt = stoi(package, nullptr);
102     }
103     catch (const std::exception& e)
104     {
105         packageInt = DEFAULT_VALUE;
106     }
107 
108     if (packageInt < 0)
109     {
110         packageInt = DEFAULT_VALUE;
111     }
112 
113     // Parse out channel argument.
114     auto channel = (options)["channel"];
115     try
116     {
117         channelInt = stoi(channel, nullptr);
118     }
119     catch (const std::exception& e)
120     {
121         channelInt = DEFAULT_VALUE;
122     }
123 
124     if (channelInt < 0)
125     {
126         channelInt = DEFAULT_VALUE;
127     }
128 
129     auto payloadStr = (options)["oem-payload"];
130     if (!payloadStr.empty())
131     {
132         if (payloadStr.size() % 2 || payloadStr.size() < 2)
133             exitWithError("Payload invalid: specify two hex digits per byte.",
134                           argv);
135 
136         // Payload string is in the format <type>[<payload>]
137         // (e.g. "50000001572100"), where the first two characters (i.e. "50")
138         // represent the command type, and the rest the payload. Split this
139         // up for the ncsi-cmd operation, which has these as separate arguments.
140         std::string typeStr(payloadStr.substr(0, 2));
141         std::string dataStr(payloadStr.substr(2));
142 
143         if (packageInt == DEFAULT_VALUE)
144         {
145             exitWithError("Package not specified.", argv);
146         }
147 
148         std::vector<std::string> args = {
149             "ncsi-cmd",
150             "-i",
151             std::to_string(indexInt),
152             "-p",
153             std::to_string(packageInt),
154         };
155 
156         if (channelInt != DEFAULT_VALUE)
157         {
158             args.push_back("-c");
159             args.push_back(std::to_string(channelInt));
160         }
161 
162         args.push_back("raw");
163         args.push_back(typeStr);
164         args.push_back(dataStr);
165 
166         /* Convert to C argv array. execvp()'s argv argument is not const,
167          * whereas .c_str() is, so we need to strdup here.
168          */
169         char** argv = new char*[args.size() + 1]();
170         for (size_t i = 0; i < args.size(); i++)
171         {
172             argv[i] = strdup(args[i].c_str());
173         }
174         argv[args.size()] = NULL;
175 
176         lg2::debug("ncsi-netlink [..] -o is deprecated by ncsi-cmd");
177         execvp(argv[0], argv);
178         lg2::error("exec failed; use ncsi-cmd directly");
179 
180         for (size_t i = 0; i < args.size(); i++)
181         {
182             free(argv[i]);
183         }
184         delete[] argv;
185         return EXIT_FAILURE;
186     }
187     else if ((options)["set"] == "true")
188     {
189         // Can not perform set operation without package.
190         if (packageInt == DEFAULT_VALUE)
191         {
192             exitWithError("Package not specified.", argv);
193         }
194         return interface.setChannel(packageInt, channelInt);
195     }
196     else if ((options)["info"] == "true")
197     {
198         auto info = interface.getInfo(packageInt);
199         if (!info)
200         {
201             return EXIT_FAILURE;
202         }
203         printInfo(*info);
204     }
205     else if ((options)["clear"] == "true")
206     {
207         return interface.clearInterface();
208     }
209     else if (!(options)["pmask"].empty())
210     {
211         unsigned int mask{};
212         try
213         {
214             size_t lastChar{};
215             mask = std::stoul((options)["pmask"], &lastChar, 0);
216             if (lastChar < (options["pmask"].size()))
217             {
218                 exitWithError("Package mask value is not valid", argv);
219             }
220         }
221         catch (const std::exception& e)
222         {
223             exitWithError("Package mask value is not valid", argv);
224         }
225         return interface.setPackageMask(mask);
226     }
227     else if (!(options)["cmask"].empty())
228     {
229         if (packageInt == DEFAULT_VALUE)
230         {
231             exitWithError("Package is not specified", argv);
232         }
233         unsigned int mask{};
234         try
235         {
236             size_t lastChar{};
237             mask = stoul((options)["cmask"], &lastChar, 0);
238             if (lastChar < (options["cmask"].size()))
239             {
240                 exitWithError("Channel mask value is not valid", argv);
241             }
242         }
243         catch (const std::exception& e)
244         {
245             exitWithError("Channel mask value is not valid", argv);
246         }
247         return interface.setChannelMask(packageInt, mask);
248     }
249     else
250     {
251         exitWithError("No Command specified", argv);
252     }
253     return 0;
254 }
255