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