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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <assert.h>
28#include <stdlib.h>
29#include <strings.h>
30#include <string.h>
31
32#include "libnwam_impl.h"
33#include <libintl.h>
34#include <libnwam.h>
35
36/*
37 * Generic object manipulation functions. Given an object handle and
38 * other parameters, create/destroy objects, walk them, walk their
39 * properties, modify/retrieve/delete properties, enable/disable them,
40 * etc. All object handles are "struct nwam_handle *" objects, sharing
41 * the same description based on the object type, name, original name
42 * (used in renaming) and associated data representing properties.
43 */
44
45nwam_error_t
46nwam_handle_create(nwam_object_type_t type, const char *name,
47    struct nwam_handle **hpp)
48{
49
50	assert(name != NULL && hpp != NULL);
51
52	if (strnlen(name, NWAM_MAX_NAME_LEN) > NWAM_MAX_NAME_LEN) {
53		*hpp = NULL;
54		return (NWAM_INVALID_ARG);
55	}
56
57	if ((*hpp = calloc(1, sizeof (struct nwam_handle))) == NULL)
58		return (NWAM_NO_MEMORY);
59
60	(*hpp)->nwh_object_type = type;
61	(void) strlcpy((*hpp)->nwh_name, name, strlen(name) + 1);
62	(*hpp)->nwh_committed = B_FALSE;
63	(*hpp)->nwh_data = NULL;
64
65	return (NWAM_SUCCESS);
66}
67
68/*
69 * Read object of specified type from dbname.
70 */
71nwam_error_t
72nwam_read(nwam_object_type_t type, const char *dbname, const char *name,
73    uint64_t flags, struct nwam_handle **hpp)
74{
75	nwam_error_t err;
76	char dbname_copy[MAXPATHLEN];
77
78	assert(name != NULL && hpp != NULL);
79
80	if (dbname != NULL)
81		(void) strlcpy(dbname_copy, dbname, sizeof (dbname_copy));
82
83	if ((err = nwam_valid_flags(flags, NWAM_FLAG_BLOCKING)) != NWAM_SUCCESS)
84		return (err);
85	if ((err = nwam_handle_create(type, name, hpp)) != NWAM_SUCCESS)
86		return (err);
87
88	if ((err = nwam_read_object_from_backend
89	    (dbname != NULL ? dbname_copy : NULL,
90	    type == NWAM_OBJECT_TYPE_NCP ? NULL : (*hpp)->nwh_name, flags,
91	    &(*hpp)->nwh_data)) != NWAM_SUCCESS) {
92		free(*hpp);
93		*hpp = NULL;
94		return (err);
95	}
96	if (type == NWAM_OBJECT_TYPE_NCP && dbname != NULL) {
97		char *ncpname;
98
99		/*
100		 * dbname_copy may have been changed due to case-insensitive
101		 * match against the actual NCP configuration file.
102		 */
103		if (nwam_ncp_file_to_name(dbname_copy, &ncpname)
104		    == NWAM_SUCCESS) {
105			(void) strlcpy((*hpp)->nwh_name, ncpname,
106			    sizeof ((*hpp)->nwh_name));
107			free(ncpname);
108		}
109	}
110
111	(*hpp)->nwh_committed = B_TRUE;
112
113	return (NWAM_SUCCESS);
114}
115
116/*
117 * Create simply creates the handle - the object-specific function must
118 * then fill in property values.
119 */
120nwam_error_t
121nwam_create(nwam_object_type_t type, const char *dbname, const char *name,
122    struct nwam_handle **hpp)
123{
124	struct nwam_handle *hp;
125
126	assert(hpp != NULL && name != NULL);
127
128	if (nwam_read(type, dbname, name, 0, &hp) == NWAM_SUCCESS) {
129		nwam_free(hp);
130		return (NWAM_ENTITY_EXISTS);
131	}
132	/* Create handle */
133	return (nwam_handle_create(type, name, hpp));
134}
135
136nwam_error_t
137nwam_get_name(struct nwam_handle *hp, char **namep)
138{
139	assert(hp != NULL && namep != NULL);
140
141	if ((*namep = strdup(hp->nwh_name)) == NULL) {
142		*namep = NULL;
143		return (NWAM_NO_MEMORY);
144	}
145	return (NWAM_SUCCESS);
146}
147
148nwam_error_t
149nwam_set_name(struct nwam_handle *hp, const char *name)
150{
151	assert(hp != NULL && name != NULL);
152
153	if (hp->nwh_committed)
154		return (NWAM_ENTITY_READ_ONLY);
155
156	if (strlen(name) >= sizeof (hp->nwh_name))
157		return (NWAM_INVALID_ARG);
158
159	(void) strcpy(hp->nwh_name, name);
160
161	return (NWAM_SUCCESS);
162}
163
164/* Compare object names c1 and c2 using strcasecmp() */
165static int
166name_cmp(const void *c1, const void *c2)
167{
168	nwam_ncu_type_t t1, t2;
169	char		*n1, *n2;
170
171	/* If c1 and c2 are typed NCU names, compare names without the types */
172	if (nwam_ncu_typed_name_to_name(*(const char **)c1, &t1, &n1)
173	    == NWAM_SUCCESS &&
174	    nwam_ncu_typed_name_to_name(*(const char **)c2, &t2, &n2)
175	    == NWAM_SUCCESS) {
176		int ret = strcasecmp(n1, n2);
177		free(n1);
178		free(n2);
179
180		/* For NCUs with the same name, compare their types */
181		if (ret == 0) {
182			if (t1 < t2)
183				ret = -1;
184			else if (t1 > t2)
185				ret = 1;
186		}
187		return (ret);
188	}
189
190	return (strcasecmp(*(const char **)c1, *(const char **)c2));
191}
192
193/*
194 * Generic walk function takes the standard walk arguments, and in addition
195 * takes a selection callback that is object-specific. If this returns
196 * 0, the object is a valid selection for the walk and the callback is called.
197 * Otherwise, it is skipped.
198 */
199nwam_error_t
200nwam_walk(nwam_object_type_t type, const char *dbname,
201    int(*cb)(struct nwam_handle *, void *),
202    void *data, uint64_t flags, int *retp,
203    int(*selectcb)(struct nwam_handle *, uint64_t, void *))
204{
205	void *objlist;
206	nwam_value_t value;
207	char **object_names;
208	uint_t i, num_objects = 0;
209	struct nwam_handle *hp;
210	nwam_error_t err;
211	int ret = 0;
212
213	assert(cb != NULL);
214
215	/*
216	 * To walk a set of objects, call nwam_read_object_from_backend()
217	 * with a "dbname" argument set to the container db name and
218	 * the object name set to NULL. This returns an nvlist with one
219	 * member - the NWAM_OBJECT_NAMES_STRING - and the values it contains
220	 * represent the names of the objects.  Read each in turn, calling
221	 * the callback function.
222	 */
223	if ((err = nwam_read_object_from_backend((char *)dbname, NULL, flags,
224	    &objlist)) != NWAM_SUCCESS) {
225		if (err == NWAM_ENTITY_NOT_FOUND) {
226			/*
227			 * This indicates the dbname container is not present.
228			 * Do not pass back an error in this case, since it is
229			 * valid for a container not to exist.
230			 */
231			return (NWAM_SUCCESS);
232		}
233		return (err);
234	}
235
236	if ((err = nwam_get_prop_value(objlist, NWAM_OBJECT_NAMES_STRING,
237	    &value)) != NWAM_SUCCESS) {
238		nwam_free_object_list(objlist);
239		return (err);
240	}
241	err = nwam_value_get_string_array(value, &object_names, &num_objects);
242	nwam_free_object_list(objlist);
243	if (err != NWAM_SUCCESS) {
244		nwam_value_free(value);
245		return (err);
246	}
247
248	/* sort the object names alphabetically */
249	qsort(object_names, num_objects, sizeof (char *), name_cmp);
250
251	for (i = 0; i < num_objects; i++) {
252		err = nwam_read(type, dbname, object_names[i],
253		    flags & NWAM_FLAG_GLOBAL_MASK, &hp);
254		/* An object may have disappeared.  If so, skip it. */
255		if (err == NWAM_ENTITY_NOT_FOUND)
256			continue;
257		if (err != NWAM_SUCCESS) {
258			nwam_value_free(value);
259			return (err);
260		}
261		if ((selectcb == NULL) || (selectcb(hp, flags, data) == 0)) {
262			ret = cb(hp, data);
263			if (ret != 0) {
264				nwam_free(hp);
265				nwam_value_free(value);
266				if (retp != NULL)
267					*retp = ret;
268				return (NWAM_WALK_HALTED);
269			}
270		}
271		nwam_free(hp);
272	}
273	nwam_value_free(value);
274
275	if (retp != NULL)
276		*retp = ret;
277	return (err);
278}
279
280void
281nwam_free(struct nwam_handle *hp)
282{
283	if (hp != NULL) {
284		if (hp->nwh_data != NULL)
285			nwam_free_object_list(hp->nwh_data);
286		free(hp);
287	}
288}
289
290/*
291 * Copy object represented by oldhp to an object newname, all in container
292 * dbname.
293 */
294nwam_error_t
295nwam_copy(const char *dbname, struct nwam_handle *oldhp, const char *newname,
296    struct nwam_handle **newhpp)
297{
298	nwam_error_t err;
299	struct nwam_handle *hp;
300
301	assert(oldhp != NULL && newname != NULL && newhpp != NULL);
302
303	if (nwam_read(oldhp->nwh_object_type, dbname, newname, 0, &hp)
304	    == NWAM_SUCCESS) {
305		nwam_free(hp);
306		return (NWAM_ENTITY_EXISTS);
307	}
308
309	if ((err = nwam_handle_create(oldhp->nwh_object_type, newname, newhpp))
310	    != NWAM_SUCCESS)
311		return (err);
312	if ((err = nwam_dup_object_list(oldhp->nwh_data,
313	    &((*newhpp)->nwh_data))) != NWAM_SUCCESS) {
314		nwam_free(*newhpp);
315		*newhpp = NULL;
316		return (err);
317	}
318
319	return (NWAM_SUCCESS);
320}
321
322/* ARGSUSED3 */
323nwam_error_t
324nwam_walk_props(struct nwam_handle *hp,
325    int (*cb)(const char *, nwam_value_t, void *),
326    void *data, uint64_t flags, int *retp)
327{
328	char *lastpropname = NULL, *propname;
329	nwam_value_t value;
330	nwam_error_t err;
331	int ret = 0;
332
333	assert(hp != NULL && hp->nwh_data != NULL && cb != NULL);
334
335	if ((err = nwam_valid_flags(flags, 0)) != NWAM_SUCCESS)
336		return (err);
337	while ((err = nwam_next_object_prop(hp->nwh_data, lastpropname,
338	    &propname, &value)) == NWAM_SUCCESS) {
339
340		ret = cb(propname, value, data);
341		if (ret != 0)
342			err = NWAM_WALK_HALTED;
343
344		/* Free value */
345		nwam_value_free(value);
346
347		if (err != NWAM_SUCCESS)
348			break;
349
350		lastpropname = propname;
351	}
352
353	if (retp != NULL)
354		*retp = ret;
355	if (err == NWAM_SUCCESS || err == NWAM_LIST_END)
356		return (NWAM_SUCCESS);
357	return (err);
358}
359
360/*
361 * Note that prior to calling the generic commit function, object-specific
362 * validation should be carried out.
363 */
364nwam_error_t
365nwam_commit(const char *dbname, struct nwam_handle *hp, uint64_t flags)
366{
367	nwam_error_t err;
368	uint64_t iflags = flags;
369	boolean_t is_ncu;
370	struct nwam_handle *testhp;
371	nwam_action_t action;
372
373	assert(hp != NULL);
374
375	/*
376	 * NWAM_FLAG_ENTITY_KNOWN_WLAN is only used for Known WLANs and
377	 * NWAM_FLAG_ENTITY_ENABLE is used for other objects (during enable
378	 * and disable).
379	 */
380	if ((err = nwam_valid_flags(flags,
381	    NWAM_FLAG_BLOCKING | NWAM_FLAG_CREATE |
382	    (hp->nwh_object_type == NWAM_OBJECT_TYPE_KNOWN_WLAN ?
383	    NWAM_FLAG_ENTITY_KNOWN_WLAN : NWAM_FLAG_ENTITY_ENABLE)))
384	    != NWAM_SUCCESS)
385		return (err);
386
387	is_ncu = (hp->nwh_object_type == NWAM_OBJECT_TYPE_NCU);
388
389	/*
390	 * Does object already exist? If not, action is ADD, otherwise REFRESH.
391	 */
392	switch (nwam_read(hp->nwh_object_type, (char *)dbname, hp->nwh_name, 0,
393	    &testhp)) {
394	case NWAM_ENTITY_NOT_FOUND:
395		action = NWAM_ACTION_ADD;
396		break;
397	case NWAM_SUCCESS:
398		nwam_free(testhp);
399		if (hp->nwh_object_type == NWAM_OBJECT_TYPE_NCP)
400			return (NWAM_ENTITY_EXISTS);
401		/* FALLTHRU */
402	default:
403		action = NWAM_ACTION_REFRESH;
404		break;
405	}
406
407	err = nwam_update_object_in_backend((char *)dbname,
408	    hp->nwh_object_type == NWAM_OBJECT_TYPE_NCP ? NULL : hp->nwh_name,
409	    iflags, hp->nwh_data);
410	if (err != NWAM_SUCCESS)
411		return (err);
412
413	hp->nwh_committed = B_TRUE;
414
415	/*
416	 * Tell nwamd to reread this object.  For NCUs, we need to convert
417	 * the dbname to the NCP name in order to pass it to nwamd.
418	 */
419	if (is_ncu) {
420		char *ncpname;
421
422		if (nwam_ncp_file_to_name(dbname, &ncpname) == NWAM_SUCCESS) {
423			(void) nwam_request_action(hp->nwh_object_type,
424			    hp->nwh_name, ncpname, action);
425			free(ncpname);
426		}
427	} else {
428		(void) nwam_request_action(hp->nwh_object_type, hp->nwh_name,
429		    NULL, action);
430	}
431	return (NWAM_SUCCESS);
432}
433
434static boolean_t
435nwam_is_active(struct nwam_handle *hp)
436{
437	nwam_state_t state;
438	nwam_aux_state_t aux;
439
440	return ((nwam_get_state(NULL, hp, &state, &aux) == NWAM_SUCCESS &&
441	    state == NWAM_STATE_ONLINE));
442}
443
444nwam_error_t
445nwam_destroy(const char *dbname, struct nwam_handle *hp, uint64_t flags)
446{
447	nwam_error_t err;
448	char *name;
449	boolean_t is_ncp, is_ncu;
450
451	assert(hp != NULL);
452
453	/* NWAM_FLAG_ENTITY_KNOWN_WLAN is only used for Known WLANs */
454	if ((err = nwam_valid_flags(flags,
455	    NWAM_FLAG_BLOCKING | NWAM_FLAG_DO_NOT_FREE |
456	    (hp->nwh_object_type == NWAM_OBJECT_TYPE_KNOWN_WLAN ?
457	    NWAM_FLAG_ENTITY_KNOWN_WLAN : 0))) != NWAM_SUCCESS)
458		return (err);
459
460	is_ncp = hp->nwh_object_type == NWAM_OBJECT_TYPE_NCP;
461	is_ncu = hp->nwh_object_type == NWAM_OBJECT_TYPE_NCU;
462	name = hp->nwh_name;
463
464	/* Check if object is active */
465	if (!is_ncp && !is_ncu && nwam_is_active(hp))
466		return (NWAM_ENTITY_IN_USE);
467
468	/* For NCPs, just remove the dbname file, otherwise remove the object */
469	err = nwam_remove_object_from_backend((char *)dbname,
470	    is_ncp ? NULL : name, flags);
471
472	/*
473	 * Tell nwamd to remove this object.  For NCUs, we need to convert the
474	 * dbname filename to the NCP name to pass it to nwamd.
475	 */
476	if (is_ncu) {
477		char *ncpname;
478
479		if (nwam_ncp_file_to_name(dbname, &ncpname) == NWAM_SUCCESS) {
480			(void) nwam_request_action(hp->nwh_object_type, name,
481			    ncpname, NWAM_ACTION_DESTROY);
482			free(ncpname);
483		}
484	} else {
485		(void) nwam_request_action(hp->nwh_object_type, name, NULL,
486		    NWAM_ACTION_DESTROY);
487	}
488
489	if ((err == NWAM_SUCCESS) && !(flags & NWAM_FLAG_DO_NOT_FREE))
490		nwam_free(hp);
491
492	return (err);
493}
494
495/*
496 * Enable/disable functions assume prior checking of activation mode
497 * to ensure an enable/disable action is valid for the object. "parent" in these
498 * functions specifies the NCP for NCUs.
499 */
500nwam_error_t
501nwam_enable(const char *parent, struct nwam_handle *hp)
502{
503	return (nwam_request_action(hp->nwh_object_type, hp->nwh_name,
504	    parent, NWAM_ACTION_ENABLE));
505}
506
507nwam_error_t
508nwam_disable(const char *parent, struct nwam_handle *hp)
509{
510	return (nwam_request_action(hp->nwh_object_type, hp->nwh_name,
511	    parent, NWAM_ACTION_DISABLE));
512}
513
514nwam_error_t
515nwam_get_state(const char *parent, struct nwam_handle *hp, nwam_state_t *statep,
516    nwam_aux_state_t *auxp)
517{
518	return (nwam_request_state(hp->nwh_object_type, hp->nwh_name, parent,
519	    statep, auxp));
520}
521
522struct nwam_prop_table_entry *
523nwam_get_prop_table_entry(struct nwam_prop_table table, const char *propname)
524{
525	struct nwam_prop_table_entry *cur = table.entries;
526	struct nwam_prop_table_entry *end = cur + table.num_entries;
527
528	assert(propname != NULL);
529
530	for (; cur < end; cur++) {
531		if (strcmp(propname, cur->prop_name) == 0)
532			return (cur);
533	}
534	return (NULL);
535}
536
537nwam_error_t
538nwam_get_prop_description(struct nwam_prop_table table, const char *propname,
539    const char **descriptionp)
540{
541	struct nwam_prop_table_entry *pte;
542
543	assert(propname != NULL && descriptionp != NULL);
544
545	if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL) {
546		*descriptionp = NULL;
547		return (NWAM_INVALID_ARG);
548	}
549
550	*descriptionp = dgettext(TEXT_DOMAIN, pte->prop_description);
551	return (NWAM_SUCCESS);
552}
553
554nwam_error_t
555nwam_get_prop_type(struct nwam_prop_table table, const char *propname,
556    nwam_value_type_t *typep)
557{
558	struct nwam_prop_table_entry *pte;
559
560	assert(propname != NULL && typep != NULL);
561
562	if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL)
563		return (NWAM_INVALID_ARG);
564
565	*typep = pte->prop_type;
566
567	return (NWAM_SUCCESS);
568}
569
570nwam_error_t
571nwam_prop_multivalued(struct nwam_prop_table table, const char *propname,
572    boolean_t *multip)
573{
574	struct nwam_prop_table_entry *pte;
575
576	assert(propname != NULL && multip != NULL);
577
578	if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL)
579		return (NWAM_INVALID_ARG);
580
581	if (pte->prop_max_numvalues > 1)
582		*multip = B_TRUE;
583	else
584		*multip = B_FALSE;
585
586	return (NWAM_SUCCESS);
587}
588
589nwam_error_t
590nwam_prop_read_only(struct nwam_prop_table table, const char *propname,
591    boolean_t *readp)
592{
593	struct nwam_prop_table_entry *pte;
594
595	assert(propname != NULL && readp != NULL);
596
597	if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL)
598		return (NWAM_INVALID_ARG);
599
600	*readp = (pte->prop_is_readonly && !nwam_uid_is_special());
601
602	return (NWAM_SUCCESS);
603}
604
605/*
606 * Structure used to pass in prop table and errprop string pointer to internal
607 * validate function.
608 */
609struct validate_internal_arg {
610	struct nwam_prop_table table;
611	const char **errpropp;
612};
613
614/*
615 * Callback used by nwam_walk_props() in nwam_validate(), and
616 * by nwam_validate_prop() to determine that the number, type and
617 * range of values are correct, and that validation function (if present)
618 * succeeds.
619 */
620static int
621nwam_validate_prop_internal(const char *propname, nwam_value_t value,
622    void *arg)
623{
624	struct validate_internal_arg *via = arg;
625	struct nwam_prop_table table = via->table;
626	const char **errpropp = via->errpropp;
627	struct nwam_prop_table_entry *pte;
628	nwam_error_t err;
629	nwam_value_type_t type;
630	uint_t numvalues;
631	int i;
632
633	if ((err = nwam_value_get_numvalues(value, &numvalues))
634	    != NWAM_SUCCESS ||
635	    (err = nwam_value_get_type(value, &type)) != NWAM_SUCCESS) {
636		if (errpropp != NULL)
637			*errpropp = propname;
638		return (err);
639	}
640	if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL)
641		return (NWAM_INVALID_ARG);
642
643	/* have we get expected number of values? */
644	if (numvalues < pte->prop_min_numvalues ||
645	    numvalues > pte->prop_max_numvalues) {
646		if (errpropp != NULL)
647			*errpropp = propname;
648		if (numvalues < 1)
649			return (NWAM_ENTITY_NO_VALUE);
650		else
651			return (NWAM_ENTITY_INVALID_VALUE);
652	}
653	/* Ensure type matches */
654	if (numvalues > 0) {
655		for (i = 0; i < numvalues; i++) {
656			if (pte->prop_type != type) {
657				if (errpropp != NULL)
658					*errpropp = propname;
659				return (NWAM_ENTITY_TYPE_MISMATCH);
660
661			}
662		}
663	}
664	/* Call property-specific validation function */
665	if (pte->prop_validate != NULL) {
666		err = pte->prop_validate(value);
667		if (err != NWAM_SUCCESS && errpropp != NULL)
668			*errpropp = propname;
669		return (err);
670	}
671
672	return (NWAM_SUCCESS);
673}
674
675nwam_error_t
676nwam_validate_prop(struct nwam_prop_table table, struct nwam_handle *hp,
677    const char *propname, nwam_value_t value)
678{
679	struct validate_internal_arg via;
680
681	assert(hp != NULL && propname != NULL);
682
683	via.table = table;
684	via.errpropp = NULL;
685
686	return ((nwam_error_t)nwam_validate_prop_internal(propname,
687	    value, &via));
688}
689
690nwam_error_t
691nwam_validate(struct nwam_prop_table table, struct nwam_handle *hp,
692    const char **errpropp)
693{
694	struct validate_internal_arg via;
695	nwam_error_t err1, err2;
696
697	assert(hp != NULL);
698
699	via.table = table;
700	via.errpropp = errpropp;
701
702	err1 = nwam_walk_props(hp, nwam_validate_prop_internal, &via,
703	    0, (int *)&err2);
704	if (err1 != NWAM_SUCCESS)
705		return (err2);
706	return (NWAM_SUCCESS);
707}
708
709/*
710 * Given the type and class flag representations, return the list of properties
711 * that can be set for that type/class combination. Note this list is a complete
712 * property list that includes both the required and the optional properties.
713 * The type and class flags are only used for NCU objects at present.
714 *
715 * Caller needs to free prop_list.
716 */
717nwam_error_t
718nwam_get_default_proplist(struct nwam_prop_table table,
719    uint64_t type, uint64_t class, const char ***prop_list, uint_t *numvalues)
720{
721	struct nwam_prop_table_entry *cur = table.entries;
722	struct nwam_prop_table_entry *end = cur + table.num_entries;
723	int i = 0;
724	const char **list = NULL;
725
726	assert(prop_list != NULL && numvalues != NULL);
727
728	/* Construct a list of all properties for required type/class */
729	list = calloc(table.num_entries, sizeof (char *));
730	if (list == NULL) {
731		*prop_list = NULL;
732		*numvalues = 0;
733		return (NWAM_NO_MEMORY);
734	}
735	for (; cur < end; cur++) {
736		if (((type & cur->prop_type_membership) == 0) ||
737		    ((class & cur->prop_class_membership) == 0))
738			continue;
739		list[i++] = cur->prop_name;
740	}
741	*numvalues = i;
742	*prop_list = list;
743	return (NWAM_SUCCESS);
744}
745