1 /*
2 * Copyright 2020 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 "test/headless/get_options.h"
18
19 #include <base/logging.h>
20 #include <getopt.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <list>
24 #include <string>
25 #include "gd/os/log.h"
26
27 namespace {
28 enum OptionType {
29 kOptionDevice = 0,
30 kOptionLoop = 1,
31 kOptionUuid = 2,
32 kOptionMsleep = 3,
33 kOptionStdErr = 4,
34 kOptionFlags = 5,
35 kOptionClear = 6,
36 };
37
38 constexpr struct option long_options[] = {
39 {"device", required_argument, 0, 0}, // kOptionDevice
40 {"loop", required_argument, 0, 0}, // kOptionLoop/
41 {"uuid", required_argument, 0, 0}, // kOptionUuid
42 {"msleep", required_argument, 0, 0}, // kOptionMsleep
43 {"stderr", no_argument, 0, 0}, // kOptionStdErr
44 {"flags", required_argument, 0, 0}, // kOptionFlags
45 {"clear", no_argument, 0, 0}, // kOptionDevice
46 {0, 0, 0, 0}};
47
48 const char* kShortArgs = "cd:l:u:";
49
50 } // namespace
51
Usage() const52 void bluetooth::test::headless::GetOpt::Usage() const {
53 fprintf(stdout, "%s: Usage:\n", name_);
54 fprintf(stdout, "%s -c Clear logcat logs\n", name_);
55 fprintf(stdout,
56 "%s --device=<device,> Comma separated list of remote devices\n",
57 name_);
58 fprintf(stdout,
59 "%s --flags=<flags,> Comma separated list of gd init flags\n",
60 name_);
61 fprintf(stdout, "%s --uuid=<uuid,> Comma separated list of uuids\n",
62 name_);
63 fprintf(stdout, "%s --loop=<loop> Number of loops\n", name_);
64 fprintf(stdout, "%s --msleep=<msecs> Sleep msec between loops\n", name_);
65 fprintf(stdout, "%s --stderr Dump stderr to stdout\n", name_);
66 fflush(nullptr);
67 }
68
ParseValue(char * optarg,std::list<std::string> & string_list)69 void bluetooth::test::headless::GetOpt::ParseValue(
70 char* optarg, std::list<std::string>& string_list) {
71 CHECK(optarg != nullptr);
72 char* p = optarg;
73 char* pp = optarg;
74 while (*p != '\0') {
75 if (*p == ',') {
76 *p = 0;
77 string_list.push_back(std::string(pp));
78 pp = p + 1;
79 }
80 p++;
81 }
82 if (pp != p) string_list.push_back(std::string(pp));
83 }
84
Split(std::string s)85 std::vector<std::string> bluetooth::test::headless::GetOpt::Split(
86 std::string s) {
87 std::stringstream ss(s);
88 std::vector<std::string> values;
89 std::string item;
90 while (std::getline(ss, item, '=')) {
91 values.push_back(item);
92 }
93 return values;
94 }
95
ProcessOption(int option_index,char * optarg)96 void bluetooth::test::headless::GetOpt::ProcessOption(int option_index,
97 char* optarg) {
98 std::list<std::string> string_list;
99 OptionType option_type = static_cast<OptionType>(option_index);
100
101 switch (option_type) {
102 case kOptionDevice:
103 if (!optarg) return;
104 ParseValue(optarg, string_list);
105 for (auto& entry : string_list) {
106 if (RawAddress::IsValidAddress(entry)) {
107 RawAddress address;
108 RawAddress::FromString(entry, address);
109 device_.push_back(address);
110 }
111 }
112 break;
113 case kOptionLoop:
114 loop_ = std::stoul(optarg, nullptr, 0);
115 break;
116 case kOptionUuid:
117 if (!optarg) return;
118 ParseValue(optarg, string_list);
119 for (auto& entry : string_list) {
120 uuid_.push_back(
121 bluetooth::Uuid::From16Bit(std::stoul(entry.c_str(), nullptr, 0)));
122 }
123 break;
124 case kOptionMsleep:
125 if (!optarg) return;
126 msec_ = std::stoul(optarg, nullptr, 0);
127 break;
128 case kOptionStdErr:
129 close_stderr_ = false;
130 break;
131 case kOptionFlags:
132 if (!optarg) return;
133 ParseValue(optarg, string_list);
134 for (auto& flag : string_list) {
135 init_flags_.push_back(flag);
136 }
137 break;
138 case kOptionClear:
139 clear_logcat_ = true;
140 break;
141 default:
142 fflush(nullptr);
143 valid_ = false;
144 return;
145 break;
146 }
147 }
148
ParseStackInitFlags()149 void bluetooth::test::headless::GetOpt::ParseStackInitFlags() {
150 if (init_flags_.size() == 0) return;
151
152 ASSERT(stack_init_flags_ == nullptr);
153
154 unsigned idx = 0;
155 stack_init_flags_ = (const char**)calloc(sizeof(char*), init_flags_.size());
156 for (const std::string& flag : init_flags_)
157 stack_init_flags_[idx++] = flag.c_str();
158 stack_init_flags_[idx] = nullptr;
159 }
160
StackInitFlags() const161 const char** bluetooth::test::headless::GetOpt::StackInitFlags() const {
162 return stack_init_flags_;
163 }
164
GetOpt(int argc,char ** argv)165 bluetooth::test::headless::GetOpt::GetOpt(int argc, char** argv)
166 : name_(argv[0]) {
167 while (1) {
168 int option_index = 0;
169 int c =
170 getopt_long_only(argc, argv, kShortArgs, long_options, &option_index);
171 if (c == -1) break;
172
173 switch (c) {
174 case 0:
175 ProcessOption(static_cast<OptionType>(option_index), optarg);
176 break;
177 case '?':
178 Usage();
179 valid_ = false;
180 return;
181 default:
182 printf("?? getopt returned character code 0%o ??\n", c);
183 }
184 }
185
186 while (optind < argc) {
187 non_options_.push_back(argv[optind++]);
188 }
189
190 ParseStackInitFlags();
191
192 fflush(nullptr);
193 }
194
~GetOpt()195 bluetooth::test::headless::GetOpt::~GetOpt() { free(stack_init_flags_); }
196