1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2025 Oxide Computer Company
26 */
27
28 #include <mdb/mdb_types.h>
29 #include <mdb/mdb_argvec.h>
30 #include <mdb/mdb_string.h>
31 #include <mdb/mdb_err.h>
32 #include <mdb/mdb_stdlib.h>
33 #include <mdb/mdb_modapi.h>
34 #include <mdb/mdb_frame.h>
35 #include <mdb/mdb.h>
36
37 #include <alloca.h>
38
39 #define AV_DEFSZ 16 /* Initial size of argument vector */
40 #define AV_GROW 2 /* Multiplier for growing argument vector */
41
42 void
mdb_argvec_create(mdb_argvec_t * vec)43 mdb_argvec_create(mdb_argvec_t *vec)
44 {
45 vec->a_data = NULL;
46 vec->a_nelems = 0;
47 vec->a_size = 0;
48 }
49
50 void
mdb_argvec_destroy(mdb_argvec_t * vec)51 mdb_argvec_destroy(mdb_argvec_t *vec)
52 {
53 if (vec->a_data != NULL) {
54 mdb_argvec_reset(vec);
55 mdb_free(vec->a_data, sizeof (mdb_arg_t) * vec->a_size);
56 }
57 }
58
59 void
mdb_argvec_append(mdb_argvec_t * vec,const mdb_arg_t * arg)60 mdb_argvec_append(mdb_argvec_t *vec, const mdb_arg_t *arg)
61 {
62 if (vec->a_nelems >= vec->a_size) {
63 size_t size = vec->a_size ? vec->a_size * AV_GROW : AV_DEFSZ;
64 void *data = mdb_alloc(sizeof (mdb_arg_t) * size, UM_NOSLEEP);
65
66 if (data == NULL) {
67 warn("failed to grow argument vector");
68 longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM);
69 }
70
71 bcopy(vec->a_data, data, sizeof (mdb_arg_t) * vec->a_size);
72 mdb_free(vec->a_data, sizeof (mdb_arg_t) * vec->a_size);
73
74 vec->a_data = data;
75 vec->a_size = size;
76 }
77
78 bcopy(arg, &vec->a_data[vec->a_nelems++], sizeof (mdb_arg_t));
79 }
80
81 void
mdb_argvec_reset(mdb_argvec_t * vec)82 mdb_argvec_reset(mdb_argvec_t *vec)
83 {
84 size_t nelems = vec->a_nelems;
85 mdb_arg_t *arg;
86
87 for (arg = vec->a_data; nelems != 0; nelems--, arg++) {
88 if (arg->a_type == MDB_TYPE_STRING && arg->a_un.a_str != NULL)
89 strfree((char *)arg->a_un.a_str);
90 }
91
92 vec->a_nelems = 0;
93 }
94
95 void
mdb_argvec_zero(mdb_argvec_t * vec)96 mdb_argvec_zero(mdb_argvec_t *vec)
97 {
98 #ifdef DEBUG
99 size_t i;
100
101 for (i = 0; i < vec->a_size; i++) {
102 vec->a_data[i].a_type = UMEM_UNINITIALIZED_PATTERN;
103 vec->a_data[i].a_un.a_val =
104 ((u_longlong_t)UMEM_UNINITIALIZED_PATTERN << 32) |
105 ((u_longlong_t)UMEM_UNINITIALIZED_PATTERN);
106 }
107 #endif
108 vec->a_nelems = 0;
109 }
110
111 void
mdb_argvec_copy(mdb_argvec_t * dst,const mdb_argvec_t * src)112 mdb_argvec_copy(mdb_argvec_t *dst, const mdb_argvec_t *src)
113 {
114 if (src->a_nelems > dst->a_size) {
115 mdb_arg_t *data =
116 mdb_alloc(sizeof (mdb_arg_t) * src->a_nelems, UM_NOSLEEP);
117
118 if (data == NULL) {
119 warn("failed to grow argument vector");
120 longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM);
121 }
122
123 if (dst->a_data != NULL)
124 mdb_free(dst->a_data, sizeof (mdb_arg_t) * dst->a_size);
125
126 dst->a_data = data;
127 dst->a_size = src->a_nelems;
128 }
129
130 bcopy(src->a_data, dst->a_data, sizeof (mdb_arg_t) * src->a_nelems);
131 dst->a_nelems = src->a_nelems;
132 }
133
134 static int
argvec_process_subopt(const mdb_opt_t * opt,const mdb_arg_t * arg)135 argvec_process_subopt(const mdb_opt_t *opt, const mdb_arg_t *arg)
136 {
137 mdb_subopt_t *sop;
138 const char *start;
139 const char *next;
140 char error[32];
141 size_t len;
142 uint_t value = 0;
143 uint_t i;
144
145 start = arg->a_un.a_str;
146
147 for (i = 0; ; i++) {
148 next = strchr(start, ',');
149
150 if (next == NULL)
151 len = strlen(start);
152 else
153 len = next - start;
154
155 /*
156 * Record the index of the subopt if a match if found.
157 */
158 for (sop = opt->opt_subopts; sop->sop_flag; sop++) {
159 if (strlen(sop->sop_str) == len &&
160 strncmp(sop->sop_str, start, len) == 0) {
161 value |= sop->sop_flag;
162 sop->sop_index = i;
163 goto found;
164 }
165 }
166 (void) mdb_snprintf(error, len + 1, "%s", start);
167 warn("invalid option for -%c: \"%s\"\n", opt->opt_char, error);
168
169 return (-1);
170
171 found:
172 if (next == NULL)
173 break;
174 start = next + 1;
175 }
176
177 *((uint_t *)opt->opt_valp) = value;
178
179 return (0);
180 }
181
182
183 static int
argvec_process_opt(const mdb_opt_t * opt,const mdb_arg_t * arg)184 argvec_process_opt(const mdb_opt_t *opt, const mdb_arg_t *arg)
185 {
186 uint64_t ui64;
187 uintptr_t uip;
188
189 switch (opt->opt_type) {
190 case MDB_OPT_SETBITS:
191 *((uint_t *)opt->opt_valp) |= opt->opt_bits;
192 break;
193
194 case MDB_OPT_CLRBITS:
195 *((uint_t *)opt->opt_valp) &= ~opt->opt_bits;
196 break;
197
198 case MDB_OPT_STR:
199 if (arg->a_type != MDB_TYPE_STRING) {
200 warn("string argument required for -%c\n",
201 opt->opt_char);
202 return (-1);
203 }
204 *((const char **)opt->opt_valp) = arg->a_un.a_str;
205 break;
206
207 case MDB_OPT_UINTPTR_SET:
208 *opt->opt_flag = TRUE;
209 /* FALLTHROUGH */
210 case MDB_OPT_UINTPTR:
211 uip = (uintptr_t)mdb_argtoull(arg);
212 *((uintptr_t *)opt->opt_valp) = uip;
213 break;
214
215 case MDB_OPT_UINT64:
216 ui64 = (uint64_t)mdb_argtoull(arg);
217 *((uint64_t *)opt->opt_valp) = ui64;
218 break;
219
220 case MDB_OPT_SUBOPTS:
221 if (arg->a_type != MDB_TYPE_STRING) {
222 warn("string argument required for -%c\n",
223 opt->opt_char);
224 return (-1);
225 }
226 return (argvec_process_subopt(opt, arg));
227
228 default:
229 warn("internal: bad opt=%p type=%hx\n",
230 (void *)opt, opt->opt_type);
231 return (-1);
232 }
233
234 return (0);
235 }
236
237 static const mdb_opt_t *
argvec_findopt(const mdb_opt_t * opts,char c)238 argvec_findopt(const mdb_opt_t *opts, char c)
239 {
240 const mdb_opt_t *optp;
241
242 for (optp = opts; optp->opt_char != 0; optp++) {
243 if (optp->opt_char == c)
244 return (optp);
245 }
246
247 return (NULL);
248 }
249
250 static int
argvec_getopts(const mdb_opt_t * opts,const mdb_arg_t * argv,int argc)251 argvec_getopts(const mdb_opt_t *opts, const mdb_arg_t *argv, int argc)
252 {
253 const mdb_opt_t *optp;
254 const mdb_arg_t *argp;
255
256 mdb_arg_t arg;
257
258 const char *p;
259 int i;
260 int nargs; /* Number of arguments consumed in an iteration */
261
262 for (i = 0; i < argc; i++, argv++) {
263 /*
264 * Each option must begin with a string argument whose first
265 * character is '-' and has additional characters afterward.
266 */
267 if (argv->a_type != MDB_TYPE_STRING ||
268 argv->a_un.a_str[0] != '-' || argv->a_un.a_str[1] == '\0')
269 return (i);
270
271 /*
272 * The special prefix '--' ends option processing.
273 */
274 if (strncmp(argv->a_un.a_str, "--", 2) == 0)
275 return (i);
276
277 for (p = &argv->a_un.a_str[1]; *p != '\0'; p++) {
278 /*
279 * Locate an option struct whose opt_char field
280 * matches the current option letter.
281 */
282 if ((optp = argvec_findopt(opts, *p)) == NULL) {
283 warn("illegal option -- %c\n", *p);
284 return (i);
285 }
286
287 /*
288 * Require an argument for strings, immediate
289 * values, subopt-lists and callback functions
290 * which require arguments.
291 */
292 if (optp->opt_type == MDB_OPT_STR ||
293 optp->opt_type == MDB_OPT_UINTPTR ||
294 optp->opt_type == MDB_OPT_UINTPTR_SET ||
295 optp->opt_type == MDB_OPT_SUBOPTS ||
296 optp->opt_type == MDB_OPT_UINT64) {
297 /*
298 * More text after the option letter:
299 * forge a string argument from remainder.
300 */
301 if (p[1] != '\0') {
302 arg.a_type = MDB_TYPE_STRING;
303 arg.a_un.a_str = ++p;
304 argp = &arg;
305 p += strlen(p) - 1;
306
307 nargs = 0;
308 /*
309 * Otherwise use the next argv element as
310 * the argument if there is one.
311 */
312 } else if (++i == argc) {
313 warn("option requires an "
314 "argument -- %c\n", *p);
315 return (i - 1);
316 } else {
317 argp = ++argv;
318 nargs = 1;
319 }
320 } else {
321 argp = NULL;
322 nargs = 0;
323 }
324
325 /*
326 * Perform type-specific handling for this option.
327 */
328 if (argvec_process_opt(optp, argp) == -1)
329 return (i - nargs);
330 }
331 }
332
333 return (i);
334 }
335
336 int
mdb_getopts(int argc,const mdb_arg_t * argv,...)337 mdb_getopts(int argc, const mdb_arg_t *argv, ...)
338 {
339 /*
340 * For simplicity just declare enough options on the stack to handle
341 * a-z and A-Z and an extra terminator.
342 */
343 mdb_opt_t opts[53], *op = &opts[0];
344 va_list alist;
345 int c, i = 0;
346 mdb_subopt_t *sop;
347
348 va_start(alist, argv);
349
350 for (i = 0; i < (sizeof (opts) / sizeof (opts[0]) - 1); i++, op++) {
351 if ((c = va_arg(alist, int)) == 0)
352 break; /* end of options */
353
354 op->opt_char = (char)c;
355 op->opt_type = va_arg(alist, uint_t);
356
357 if (op->opt_type == MDB_OPT_SETBITS ||
358 op->opt_type == MDB_OPT_CLRBITS) {
359 op->opt_bits = va_arg(alist, uint_t);
360 } else if (op->opt_type == MDB_OPT_UINTPTR_SET) {
361 op->opt_flag = va_arg(alist, boolean_t *);
362 } else if (op->opt_type == MDB_OPT_SUBOPTS) {
363 op->opt_subopts = va_arg(alist, mdb_subopt_t *);
364
365 for (sop = op->opt_subopts; sop->sop_flag; sop++)
366 sop->sop_index = -1;
367 }
368
369 op->opt_valp = va_arg(alist, void *);
370 }
371
372 bzero(&opts[i], sizeof (mdb_opt_t));
373 va_end(alist);
374
375 return (argvec_getopts(opts, argv, argc));
376 }
377
378 u_longlong_t
mdb_argtoull(const mdb_arg_t * arg)379 mdb_argtoull(const mdb_arg_t *arg)
380 {
381 switch (arg->a_type) {
382 case MDB_TYPE_STRING:
383 return (mdb_strtoull(arg->a_un.a_str));
384 case MDB_TYPE_IMMEDIATE:
385 return (arg->a_un.a_val);
386 case MDB_TYPE_CHAR:
387 return (arg->a_un.a_char);
388 }
389 /* NOTREACHED */
390 return (0);
391 }
392
393 /*
394 * The old adb breakpoint and watchpoint routines did not accept any arguments;
395 * all characters after the verb were concatenated to form the string callback.
396 * This utility function concatenates all arguments in argv[] into a single
397 * string to simplify the implementation of these legacy routines.
398 */
399 char *
mdb_argv_to_str(int argc,const mdb_arg_t * argv)400 mdb_argv_to_str(int argc, const mdb_arg_t *argv)
401 {
402 char *s = NULL;
403 size_t n = 0;
404 int i;
405
406 for (i = 0; i < argc; i++) {
407 if (argv[i].a_type == MDB_TYPE_STRING)
408 n += strlen(argv[i].a_un.a_str);
409 }
410
411 if (n != 0) {
412 s = mdb_zalloc(n + argc, UM_SLEEP);
413
414 for (i = 0; i < argc - 1; i++, argv++) {
415 (void) strcat(s, argv->a_un.a_str);
416 (void) strcat(s, " ");
417 }
418
419 (void) strcat(s, argv->a_un.a_str);
420 }
421
422 return (s);
423 }
424