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