1 /*
2 * Copyright (C) 2021 The Android Open Source Project
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 <android-base/logging.h>
18 #include <android-base/parsedouble.h>
19 #include <android-base/parseint.h>
20 #include <gflags/gflags.h>
21
22 #include <cstdlib>
23 #include <functional>
24 #include <iomanip>
25 #include <iostream>
26 #include <string>
27 #include <unordered_map>
28 #include <vector>
29
30 #include "host/libs/config/cuttlefish_config.h"
31 #include "host/libs/wmediumd_controller/wmediumd_api_protocol.h"
32 #include "host/libs/wmediumd_controller/wmediumd_controller.h"
33
34 const std::string usageMessage =
35 "wmediumd control commandline utility\n\n"
36 " Usage: wmediumd_control [option] command [args...]\n\n"
37 " Commands:\n\n"
38 " set_snr mac1 mac2 snr\n"
39 " set SNR between two nodes. (0 <= snr <= 255)\n\n"
40 " reload_config [path]\n"
41 " force reload wmediumd configuration file\n\n"
42 " if path is not specified, reload current configuration file\n\n"
43 " start_pcap path\n"
44 " start packet capture and save capture result to file.\n"
45 " file format is pcap capture format.\n\n"
46 " stop_pcap\n"
47 " stop packet capture\n\n"
48 " list_stations\n"
49 " listing stations connected to wmediumd\n\n"
50 " set_position mac xpos ypos\n"
51 " set X, Y positions of specific station\n"
52 " use -- before set_position if you want to set the position with "
53 "negative values\n"
54 " e.g. wmediumd_control -- set_position 42:00:00:00:00:00 -1.0 "
55 "-2.0\n\n"
56 " set_lci mac lci\n"
57 " set LCI (latitude, longitude, altitude) of the specific station\n"
58 " it's free-form string and may not match with other location nor"
59 "position information\n\n"
60 " set_civicloc mac civicloc\n"
61 " set CIVIC location (e.g. postal address) of the specific station\n"
62 " it's free-form string and may not match with other location nor"
63 "position information\n";
64
65 DEFINE_string(wmediumd_api_server, "",
66 "Unix socket path of wmediumd api server");
67
HandleSetSnrCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)68 bool HandleSetSnrCommand(cuttlefish::WmediumdController& client,
69 const std::vector<std::string>& args) {
70 if (args.size() != 4) {
71 LOG(ERROR) << "error: set_snr must provide 3 options";
72 return false;
73 }
74
75 if (!cuttlefish::ValidMacAddr(args[1])) {
76 LOG(ERROR) << "error: invalid mac address " << args[1];
77 return false;
78 }
79
80 if (!cuttlefish::ValidMacAddr(args[2])) {
81 LOG(ERROR) << "error: invalid mac address " << args[2];
82 return false;
83 }
84
85 uint8_t snr = 0;
86
87 auto parseResult =
88 android::base::ParseUint<decltype(snr)>(args[3].c_str(), &snr);
89
90 if (!parseResult) {
91 if (errno == EINVAL) {
92 LOG(ERROR) << "error: cannot parse snr: " << args[3];
93 } else if (errno == ERANGE) {
94 LOG(ERROR) << "error: snr exceeded range: " << args[3];
95 }
96
97 return false;
98 }
99
100 if (!client.SetSnr(args[1], args[2], snr)) {
101 return false;
102 }
103
104 return true;
105 }
106
HandleReloadConfigCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)107 bool HandleReloadConfigCommand(cuttlefish::WmediumdController& client,
108 const std::vector<std::string>& args) {
109 if (args.size() > 2) {
110 LOG(ERROR) << "error: reload_config must provide 0 or 1 option";
111 return false;
112 }
113
114 if (args.size() == 2) {
115 return client.ReloadConfig(args[1]);
116 } else {
117 return client.ReloadCurrentConfig();
118 }
119 }
120
HandleStartPcapCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)121 bool HandleStartPcapCommand(cuttlefish::WmediumdController& client,
122 const std::vector<std::string>& args) {
123 if (args.size() != 2) {
124 LOG(ERROR) << "error: you must provide only 1 option(path)";
125 return false;
126 }
127
128 return client.StartPcap(args[1]);
129 }
130
HandleStopPcapCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)131 bool HandleStopPcapCommand(cuttlefish::WmediumdController& client,
132 const std::vector<std::string>& args) {
133 if (args.size() != 1) {
134 LOG(ERROR) << "error: you must not provide option";
135 return false;
136 }
137
138 return client.StopPcap();
139 }
140
HandleListStationsCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)141 bool HandleListStationsCommand(cuttlefish::WmediumdController& client,
142 const std::vector<std::string>& args) {
143 if (args.size() != 1) {
144 LOG(ERROR) << "error: you must not provide option";
145 return false;
146 }
147
148 auto result = client.GetStations();
149
150 if (!result) {
151 LOG(ERROR) << "error: failed to get stations";
152 return false;
153 }
154
155 auto stationList = result->GetStations();
156
157 std::cout << "Total stations : " << stationList.size() << std::endl
158 << std::endl;
159 std::cout << "Mac Address "
160 << "\t"
161 << "X Pos"
162 << "\t"
163 << "Y Pos"
164 << "\t"
165 << "LCI"
166 << "\t"
167 << "CIVICLOC"
168 << "\t"
169 << "TX Power" << std::endl;
170
171 for (auto& station : stationList) {
172 std::cout << cuttlefish::MacToString(station.addr) << "\t"
173 << std::setprecision(1) << std::fixed << station.x << "\t"
174 << std::setprecision(1) << std::fixed << station.y << "\t\""
175 << station.lci << "\"\t\"" << station.civicloc << "\"\t"
176 << station.tx_power << std::endl;
177 }
178
179 std::cout << std::endl;
180
181 return true;
182 }
183
HandleSetPositionCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)184 bool HandleSetPositionCommand(cuttlefish::WmediumdController& client,
185 const std::vector<std::string>& args) {
186 if (args.size() != 4) {
187 LOG(ERROR) << "error: set_position must provide 3 options";
188 return false;
189 }
190
191 if (!cuttlefish::ValidMacAddr(args[1])) {
192 LOG(ERROR) << "error: invalid mac address " << args[1];
193 return false;
194 }
195
196 double x = 0;
197 double y = 0;
198
199 auto parseResultX = android::base::ParseDouble(args[2].c_str(), &x);
200 auto parseResultY = android::base::ParseDouble(args[3].c_str(), &y);
201
202 if (!parseResultX) {
203 LOG(ERROR) << "error: cannot parse X: " << args[2];
204 return false;
205 }
206
207 if (!parseResultY) {
208 LOG(ERROR) << "error: cannot parse Y: " << args[3];
209 return false;
210 }
211
212 if (!client.SetPosition(args[1], x, y)) {
213 return false;
214 }
215
216 return true;
217 }
218
HandleSetLciCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)219 bool HandleSetLciCommand(cuttlefish::WmediumdController& client,
220 const std::vector<std::string>& args) {
221 if (args.size() != 3) {
222 LOG(ERROR) << "error: set_lci must provide 2 options";
223 return false;
224 }
225
226 if (!cuttlefish::ValidMacAddr(args[1])) {
227 LOG(ERROR) << "error: invalid mac address " << args[1];
228 return false;
229 }
230
231 if (!client.SetLci(args[1], args[2])) {
232 return false;
233 }
234
235 return true;
236 }
237
HandleSetCiviclocCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)238 bool HandleSetCiviclocCommand(cuttlefish::WmediumdController& client,
239 const std::vector<std::string>& args) {
240 if (args.size() != 3) {
241 LOG(ERROR) << "error: set_civicloc must provide 2 options";
242 return false;
243 }
244
245 if (!cuttlefish::ValidMacAddr(args[1])) {
246 LOG(ERROR) << "error: invalid mac address " << args[1];
247 return false;
248 }
249
250 if (!client.SetCivicloc(args[1], args[2])) {
251 return false;
252 }
253
254 return true;
255 }
256
main(int argc,char ** argv)257 int main(int argc, char** argv) {
258 gflags::SetUsageMessage(usageMessage);
259 gflags::ParseCommandLineFlags(&argc, &argv, true);
260
261 std::vector<std::string> args;
262
263 for (int i = 1; i < argc; ++i) {
264 args.push_back(argv[i]);
265 }
266
267 if (args.size() == 0) {
268 LOG(ERROR) << "error: you must provide at least 1 argument";
269 gflags::ShowUsageWithFlags(argv[0]);
270 return -1;
271 }
272
273 std::string wmediumdApiServerPath(FLAGS_wmediumd_api_server);
274
275 if (wmediumdApiServerPath == "") {
276 const auto cuttlefishConfig = cuttlefish::CuttlefishConfig::Get();
277
278 if (!cuttlefishConfig) {
279 LOG(ERROR) << "error: cannot get global cuttlefish config";
280 return -1;
281 }
282
283 wmediumdApiServerPath = cuttlefishConfig->wmediumd_api_server_socket();
284 }
285
286 auto client = cuttlefish::WmediumdController::New(wmediumdApiServerPath);
287
288 if (!client) {
289 LOG(ERROR) << "error: cannot connect to " << wmediumdApiServerPath;
290 return -1;
291 }
292
293 auto commandMap =
294 std::unordered_map<std::string,
295 std::function<bool(cuttlefish::WmediumdController&,
296 const std::vector<std::string>&)>>{
297 {{"set_snr", HandleSetSnrCommand},
298 {"reload_config", HandleReloadConfigCommand},
299 {"start_pcap", HandleStartPcapCommand},
300 {"stop_pcap", HandleStopPcapCommand},
301 {"list_stations", HandleListStationsCommand},
302 {"set_position", HandleSetPositionCommand},
303 {"set_lci", HandleSetLciCommand},
304 {"set_civicloc", HandleSetCiviclocCommand}}};
305
306 if (commandMap.find(args[0]) == std::end(commandMap)) {
307 LOG(ERROR) << "error: command " << args[0] << " does not exist";
308 gflags::ShowUsageWithFlags(argv[0]);
309 return -1;
310 }
311
312 if (!commandMap[args[0]](*client, args)) {
313 LOG(ERROR) << "error: failed to execute command " << args[0];
314 return -1;
315 }
316
317 return 0;
318 }
319