xref: /titanic_44/usr/src/lib/libvscan/common/libvscan.c (revision 0cd13cbfb4270b840b4bd22ec5f673b2b6a2c02b)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <string.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <math.h>
34 #include <limits.h>
35 #include <libscf.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <door.h>
39 #include <pwd.h>
40 #include <auth_attr.h>
41 #include <secdb.h>
42 #include <sys/socket.h>
43 #include <arpa/inet.h>
44 #include <libintl.h>
45 #include <libvscan.h>
46 
47 #define	VS_INSTANCE_FMRI	"svc:/system/filesystem/vscan:icap"
48 
49 /* SMF property group and property names */
50 #define	VS_PGNAME_GENERAL		"vs_general"
51 #define	VS_PGNAME_ENGINE_PREFIX		"vs_engine_"
52 #define	VS_PGNAME_ENGINE_LEN		VS_SE_NAME_LEN + 16
53 
54 #define	VS_PNAME_MAXSIZE		"maxsize"
55 #define	VS_PNAME_MAXSIZE_ACTION		"maxsize_action"
56 #define	VS_PNAME_TYPES			"types"
57 #define	VS_PNAME_VLOG			"viruslog"
58 
59 #define	VS_PNAME_SE_ENABLE		"enable"
60 #define	VS_PNAME_SE_HOST		"host"
61 #define	VS_PNAME_SE_PORT		"port"
62 #define	VS_PNAME_SE_MAXCONN		"max_connect"
63 #define	VS_PNAME_VAUTH			"value_authorization"
64 
65 
66 /* types string processing */
67 #define	VS_TYPES_SEP		','
68 #define	VS_TYPES_ESCAPE		'\\'
69 #define	VS_TYPES_RULES		"+-"
70 
71 
72 /*
73  * The SCF context enapsulating the SCF objects used in the
74  * repository load and store routines vs_scf_values_get()
75  * and vs_scf_values_set().
76  *
77  * The context is always opened before a get or set, then
78  * closed when finished (or on error); the open does an
79  * initial setup, while inside the get and set functions,
80  * additional objects within the context may be selectively
81  * initialized for use, depending on the actions needed and
82  * the properties being operated on.
83  */
84 typedef struct vs_scfctx {
85 	scf_handle_t *vscf_handle;
86 	scf_instance_t *vscf_inst;
87 	scf_propertygroup_t *vscf_pgroup;
88 	scf_transaction_t *vscf_tx;
89 	scf_iter_t *vscf_iter;
90 	scf_property_t *vscf_prop[VS_NUM_PROPIDS];
91 	scf_transaction_entry_t *vscf_ent[VS_NUM_PROPIDS];
92 	scf_value_t *vscf_val[VS_NUM_PROPIDS];
93 } vs_scfctx_t;
94 
95 /*
96  * The vscan property definition. Maps the property id with the name
97  * and type used to store the property in the repository.
98  * A table of these definitions is defined with a single entry per
99  * property.
100  */
101 typedef struct {
102 	const char *vpd_name;
103 	uint64_t vpd_id;
104 	scf_type_t vpd_type;
105 } vs_propdef_t;
106 
107 typedef enum {
108 	VS_PTYPE_GEN,
109 	VS_PTYPE_SE
110 } vs_prop_type_t;
111 
112 typedef struct vs_prop_hd {
113 	vs_prop_type_t vp_type;
114 	uint64_t vp_ids;
115 	uint64_t vp_all;
116 	union {
117 		vs_props_t vp_gen;
118 		vs_props_se_t vp_se;
119 	} vp_props;
120 } vs_prop_hd_t;
121 
122 #define	vp_gen	vp_props.vp_gen
123 #define	vp_se	vp_props.vp_se
124 
125 /*
126  * Default values - these are used to return valid data
127  * to the caller in cases where invalid or unexpected values
128  * are found in the repository.
129  *
130  * Note: These values must be kept in sync with those defined
131  * in the service manifest.
132  */
133 static const boolean_t vs_dflt_allow = B_TRUE;
134 static const boolean_t vs_dflt_enable = B_TRUE;
135 static const char *vs_dflt_maxsize = "1GB";
136 static const char *vs_dflt_host = "";
137 static const uint16_t vs_dflt_port = 1344;
138 static const uint16_t vs_dflt_maxconn = 32L;
139 static const  char *vs_dflt_types = "+*";
140 static const char *vs_dflt_vlog = "";
141 
142 /* Property definition table */
143 static const vs_propdef_t vs_propdefs[] = {
144 	/* general properties */
145 	{ VS_PNAME_MAXSIZE, VS_PROPID_MAXSIZE, SCF_TYPE_ASTRING },
146 	{ VS_PNAME_MAXSIZE_ACTION, VS_PROPID_MAXSIZE_ACTION, SCF_TYPE_BOOLEAN },
147 	{ VS_PNAME_TYPES, VS_PROPID_TYPES, SCF_TYPE_ASTRING },
148 	{ VS_PNAME_VLOG, VS_PROPID_VLOG, SCF_TYPE_ASTRING },
149 	/* scan engine properties */
150 	{ VS_PNAME_SE_ENABLE, VS_PROPID_SE_ENABLE, SCF_TYPE_BOOLEAN },
151 	{ VS_PNAME_SE_HOST, VS_PROPID_SE_HOST, SCF_TYPE_HOST },
152 	{ VS_PNAME_SE_PORT, VS_PROPID_SE_PORT, SCF_TYPE_INTEGER },
153 	{ VS_PNAME_SE_MAXCONN, VS_PROPID_SE_MAXCONN, SCF_TYPE_INTEGER },
154 	{ VS_PNAME_VAUTH, VS_PROPID_VALUE_AUTH, SCF_TYPE_ASTRING }
155 };
156 
157 static const int vs_npropdefs = sizeof (vs_propdefs)/sizeof (vs_propdef_t);
158 
159 /* Local functions */
160 static const vs_propdef_t *vs_get_propdef(uint64_t);
161 static void vs_default_value(vs_prop_hd_t *, const uint64_t);
162 
163 static int vs_scf_values_get(const char *, vs_prop_hd_t *);
164 static int vs_scf_get(const vs_propdef_t *, vs_prop_hd_t *, vs_scfctx_t *, int);
165 
166 static int vs_scf_values_set(const char *, vs_prop_hd_t *);
167 static int vs_scf_set(const vs_propdef_t *, vs_prop_hd_t *, vs_scfctx_t *, int);
168 static int vs_scf_pg_create(const char *, vs_prop_hd_t *);
169 static int vs_scf_pg_delete(const char *);
170 
171 static int vs_scf_ctx_open(vs_scfctx_t *);
172 static void vs_scf_ctx_close(vs_scfctx_t *);
173 
174 static int vs_validate(const vs_prop_hd_t *, uint64_t);
175 static int vs_is_valid_types(const char *);
176 static int vs_is_valid_host(const char *);
177 static int vs_checkauth(char *);
178 
179 static int vs_props_get_engines(char *[], int *);
180 static void vs_engid_to_pgname(const char *, char [VS_PGNAME_ENGINE_LEN]);
181 static int vs_scf_pg_count(void);
182 static int vs_strtoshift(const char *);
183 
184 
185 /*
186  * vs_props_get_all
187  *
188  * Retrieves the general service properties and all properties
189  * for all scan engines from the repository.
190  *
191  * If invalid property values are found, the values are corrected to
192  * the default value.
193  *
194  * Return codes:
195  *	VS_ERR_VS_ERR_NONE
196  *	VS_ERR_SCF
197  *	VS_ERR_SYS
198  */
199 int
200 vs_props_get_all(vs_props_all_t *va)
201 {
202 	int i, rc, n;
203 	char *engids[VS_SE_MAX];
204 
205 	(void) memset(va, 0, sizeof (vs_props_all_t));
206 	if ((rc = vs_props_get(&va->va_props, VS_PROPID_GEN_ALL))
207 	    != VS_ERR_NONE)
208 		return (rc);
209 
210 	n = VS_SE_MAX;
211 	if ((rc = vs_props_get_engines(engids, &n)) != VS_ERR_NONE)
212 		return (rc);
213 
214 	for (i = 0; i < n; i++) {
215 		if ((rc = vs_props_se_get(engids[i],
216 		    &va->va_se[i], VS_PROPID_SE_ALL)) != VS_ERR_NONE)
217 			break;
218 	}
219 
220 	/* free engids allocated in vs_props_get_engines */
221 	for (i = 0; i < VS_SE_MAX; i++)	{
222 		if (engids[i] != NULL)
223 			free(engids[i]);
224 	}
225 
226 	return (rc);
227 }
228 
229 
230 /*
231  * vs_props_get
232  *
233  * Retrieves values for the specified general service properties from
234  * the repository.
235  *
236  * If invalid property values are found, the values are corrected to
237  * the default value.
238  *
239  * Return codes:
240  *	VS_ERR_VS_ERR_NONE
241  *	VS_ERR_INVALID_PROPERTY
242  *	VS_ERR_SCF
243  *	VS_ERR_SYS
244  */
245 int
246 vs_props_get(vs_props_t *vp, uint64_t propids)
247 {
248 	int  rc;
249 	vs_prop_hd_t prop_hd;
250 
251 	if ((propids & VS_PROPID_GEN_ALL) != propids)
252 		return (VS_ERR_INVALID_PROPERTY);
253 
254 	(void) memset(&prop_hd, 0, sizeof (vs_prop_hd_t));
255 	prop_hd.vp_type = VS_PTYPE_GEN;
256 	prop_hd.vp_ids = propids;
257 	prop_hd.vp_all = VS_PROPID_GEN_ALL;
258 
259 	rc = vs_scf_values_get(VS_PGNAME_GENERAL, &prop_hd);
260 
261 	*vp = prop_hd.vp_gen;
262 	return (rc);
263 }
264 
265 
266 /*
267  * vs_props_set
268  *
269  * Changes values for the specified general service properties
270  * in the repository.
271  *
272  * Return codes:
273  *	VS_ERR_VS_ERR_NONE
274  *	VS_ERR_INVALID_PROPERTY
275  *	VS_ERR_INVALID_VALUE
276  *	VS_ERR_SCF
277  *	VS_ERR_SYS
278  */
279 int
280 vs_props_set(const vs_props_t *vp, uint64_t propids)
281 {
282 	vs_prop_hd_t prop_hd;
283 
284 	if ((propids & VS_PROPID_GEN_ALL) != propids)
285 		return (VS_ERR_INVALID_PROPERTY);
286 
287 	(void) memset(&prop_hd, 0, sizeof (vs_prop_hd_t));
288 	prop_hd.vp_type = VS_PTYPE_GEN;
289 	prop_hd.vp_ids = propids;
290 	prop_hd.vp_all = VS_PROPID_GEN_ALL;
291 	prop_hd.vp_gen = *vp;
292 	return (vs_scf_values_set(VS_PGNAME_GENERAL, &prop_hd));
293 }
294 
295 
296 /*
297  * vs_props_se_get
298  *
299  * Retrieves values for the specified scan engine properties from the
300  * repository.
301  *
302  * If the enable property is set (true), the host property is
303  * checked for validity. If it is not valid, the requested values
304  * are returned with the enable propery set to off (false)
305  *
306  * Return codes:
307  *	VS_ERR_VS_ERR_NONE
308  *	VS_ERR_INVALID_PROPERTY
309  *	VS_ERR_SCF
310  *	VS_ERR_SYS
311  */
312 int
313 vs_props_se_get(char *engid, vs_props_se_t *sep, uint64_t propids)
314 {
315 	int rc;
316 	char pgname[VS_PGNAME_ENGINE_LEN];
317 	vs_prop_hd_t prop_hd;
318 
319 	/* VS_PGNAME_GENERAL is a reserved for GENERAL property group */
320 	if (strcmp(engid, VS_PGNAME_GENERAL) == 0)
321 		return (VS_ERR_INVALID_SE);
322 
323 	if ((propids & VS_PROPID_SE_ALL) != propids)
324 		return (VS_ERR_INVALID_PROPERTY);
325 
326 	(void) memset(&prop_hd, 0, sizeof (vs_prop_hd_t));
327 	prop_hd.vp_type = VS_PTYPE_SE;
328 	prop_hd.vp_ids = propids;
329 	prop_hd.vp_all = VS_PROPID_SE_ALL;
330 	(void) strlcpy(prop_hd.vp_se.vep_engid, engid, VS_SE_NAME_LEN);
331 
332 	/* If getting enable, get the host property too */
333 	if ((propids & VS_PROPID_SE_ENABLE))
334 		prop_hd.vp_ids |= VS_PROPID_SE_HOST;
335 
336 	/* Load values from the repository */
337 	vs_engid_to_pgname(engid, pgname);
338 	rc = vs_scf_values_get(pgname, &prop_hd);
339 	if (rc != VS_ERR_NONE)
340 		return (rc);
341 
342 	/*
343 	 *  If the host is invalid and the enable property is on,
344 	 *  return enable property as off
345 	 */
346 	if ((prop_hd.vp_ids & VS_PROPID_SE_HOST) &&
347 	    (vs_validate(&prop_hd, VS_PROPID_SE_HOST) != VS_ERR_NONE)) {
348 		prop_hd.vp_se.vep_enable = B_FALSE;
349 	}
350 
351 	*sep = prop_hd.vp_se;
352 	return (rc);
353 }
354 
355 
356 
357 /*
358  * vs_props_se_set
359  *
360  * Changes the values for the specified scan engine properties in the
361  * repository.
362  *
363  * If the enable property is being changed to true in this operation,
364  * a host property must also be specified, or already exist in the
365  * repository.
366  *
367  * Return codes:
368  *	VS_ERR_NONE
369  *	VS_ERR_INVALID_PROPERTY
370  *	VS_ERR_INVALID_VALUE
371  *	VS_ERR_SCF
372  *	VS_ERR_SYS
373  */
374 int
375 vs_props_se_set(char *engid, const vs_props_se_t *sep, uint64_t propids)
376 {
377 	int rc;
378 	char pgname[VS_PGNAME_ENGINE_LEN];
379 	vs_prop_hd_t prop_hd;
380 
381 	/* VS_PGNAME_GENERAL is a reserved for GENERAL property group */
382 	if (strcmp(engid, VS_PGNAME_GENERAL) == 0)
383 		return (VS_ERR_INVALID_SE);
384 
385 	if ((propids & VS_PROPID_SE_ALL) != propids)
386 		return (VS_ERR_INVALID_PROPERTY);
387 
388 	(void) memset(&prop_hd, 0, sizeof (vs_prop_hd_t));
389 	prop_hd.vp_type = VS_PTYPE_SE;
390 	prop_hd.vp_all = VS_PROPID_SE_ALL;
391 
392 	vs_engid_to_pgname(engid, pgname);
393 
394 	/*
395 	 * if enabling a scan engine, ensure that a valid host
396 	 * is also being set, or already exists in the repository
397 	 */
398 	if ((propids & VS_PROPID_SE_ENABLE) && (sep->vep_enable == B_TRUE) &&
399 	    !(propids & VS_PROPID_SE_HOST)) {
400 
401 		prop_hd.vp_ids = VS_PROPID_SE_HOST;
402 		if ((rc = vs_scf_values_get(pgname, &prop_hd)) != VS_ERR_NONE)
403 			return (rc);
404 
405 		if (vs_validate(&prop_hd, VS_PROPID_SE_HOST) != VS_ERR_NONE)
406 			return (VS_ERR_INVALID_HOST);
407 	}
408 
409 	prop_hd.vp_ids = propids;
410 	prop_hd.vp_se = *sep;
411 
412 	return (vs_scf_values_set(pgname, &prop_hd));
413 }
414 
415 
416 /*
417  * vs_props_se_create
418  */
419 int
420 vs_props_se_create(char *engid, const vs_props_se_t *sep, uint64_t propids)
421 {
422 	int n;
423 	char pgname[VS_PGNAME_ENGINE_LEN];
424 	vs_prop_hd_t prop_hd;
425 
426 	if ((propids & VS_PROPID_SE_ALL) != propids)
427 		return (VS_ERR_INVALID_PROPERTY);
428 
429 	/* VS_PGNAME_GENERAL is a reserved for GENERAL property group */
430 	if (strcmp(engid, VS_PGNAME_GENERAL) == 0)
431 		return (VS_ERR_INVALID_SE);
432 
433 	if ((n = vs_scf_pg_count()) == -1)
434 		return (VS_ERR_SCF);
435 
436 	if (n == VS_SE_MAX)
437 		return (VS_ERR_MAX_SE);
438 
439 	vs_engid_to_pgname(engid, pgname);
440 
441 	(void) memset(&prop_hd, 0, sizeof (vs_prop_hd_t));
442 	prop_hd.vp_type = VS_PTYPE_SE;
443 	prop_hd.vp_all = VS_PROPID_SE_ALL;
444 	prop_hd.vp_ids = propids | VS_PROPID_VALUE_AUTH;
445 	prop_hd.vp_se = *sep;
446 
447 	/* if hostname not specified, default it to engid */
448 	if ((propids & VS_PROPID_SE_HOST) == 0) {
449 		(void) strlcpy(prop_hd.vp_se.vep_host, engid, MAXHOSTNAMELEN);
450 		prop_hd.vp_ids |= VS_PROPID_SE_HOST;
451 	}
452 
453 	return (vs_scf_pg_create(pgname, &prop_hd));
454 }
455 
456 
457 /*
458  * vs_props_se_delete
459  */
460 int
461 vs_props_se_delete(const char *engid)
462 {
463 	char pgname[VS_PGNAME_ENGINE_LEN];
464 
465 	/* VS_PGNAME_GENERAL is a reserved for GENERAL property group */
466 	if (strcmp(engid, VS_PGNAME_GENERAL) == 0)
467 		return (VS_ERR_INVALID_SE);
468 
469 	vs_engid_to_pgname(engid, pgname);
470 
471 	return (vs_scf_pg_delete(pgname));
472 }
473 
474 
475 /*
476  * vs_strerror
477  */
478 const char *
479 vs_strerror(int error)
480 {
481 	switch (error) {
482 	case VS_ERR_NONE:
483 		return (gettext("no error"));
484 	case VS_ERR_INVALID_PROPERTY:
485 		return (gettext("invalid property id"));
486 	case VS_ERR_INVALID_VALUE:
487 		return (gettext("invalid property value"));
488 	case VS_ERR_INVALID_HOST:
489 		return (gettext("invalid host"));
490 	case VS_ERR_INVALID_SE:
491 		return (gettext("invalid scan engine"));
492 	case VS_ERR_MAX_SE:
493 		return (gettext("max scan engines exceeded"));
494 	case VS_ERR_AUTH:
495 		return (gettext("insufficient privileges for action"));
496 	case VS_ERR_DAEMON_COMM:
497 		return (gettext("unable to contact vscand"));
498 	case VS_ERR_SCF:
499 		return (scf_strerror(scf_error()));
500 	case VS_ERR_SYS:
501 		return (strerror(errno));
502 	default:
503 		return (gettext("unknown error"));
504 	}
505 }
506 
507 
508 /*
509  * vs_get_propdef
510  *
511  * Finds and returns a property definition by property id.
512  */
513 static const vs_propdef_t *
514 vs_get_propdef(uint64_t propid)
515 {
516 	int i;
517 
518 	for (i = 0; i < vs_npropdefs; i++) {
519 		if (propid == vs_propdefs[i].vpd_id)
520 			return (&vs_propdefs[i]);
521 	}
522 
523 	return (NULL);
524 }
525 
526 
527 /*
528  * vs_default_value
529  *
530  * Sets a property value that contains invalid data to its default value.
531  *
532  * Note that this function does not alter any values in the repository
533  * This is only to enable the caller to get valid data.
534  */
535 static void
536 vs_default_value(vs_prop_hd_t *prop_hd, const uint64_t propid)
537 {
538 	vs_props_t *vp = &prop_hd->vp_gen;
539 	vs_props_se_t *vep = &prop_hd->vp_se;
540 
541 	switch (propid) {
542 	case VS_PROPID_MAXSIZE:
543 		(void) strlcpy(vp->vp_maxsize, vs_dflt_maxsize,
544 		    sizeof (vp->vp_maxsize));
545 		break;
546 	case VS_PROPID_MAXSIZE_ACTION:
547 		vp->vp_maxsize_action = vs_dflt_allow;
548 		break;
549 	case VS_PROPID_TYPES:
550 		(void) strlcpy(vp->vp_types, vs_dflt_types,
551 		    sizeof (vp->vp_types));
552 		break;
553 	case VS_PROPID_VLOG:
554 		(void) strlcpy(vp->vp_vlog, vs_dflt_vlog,
555 		    sizeof (vp->vp_vlog));
556 		break;
557 	case VS_PROPID_SE_ENABLE:
558 		vep->vep_enable = vs_dflt_enable;
559 		break;
560 	case VS_PROPID_SE_HOST:
561 		(void) strlcpy(vep->vep_host, vs_dflt_host,
562 		    sizeof (vep->vep_host));
563 		break;
564 	case VS_PROPID_SE_PORT:
565 		vep->vep_port = vs_dflt_port;
566 		break;
567 	case VS_PROPID_SE_MAXCONN:
568 		vep->vep_maxconn = vs_dflt_maxconn;
569 		break;
570 	default:
571 		break;
572 	}
573 }
574 
575 
576 /*
577  * vs_scf_values_get
578  *
579  * Gets property values for one or more properties from the repository.
580  * This is the single entry point for loading SMF values.
581  *
582  * While a transaction is not used for loading property values,
583  * the operation is parameterized by a property group. All properties
584  * retrieved in this function, then, must belong to the same property
585  * group.
586  */
587 int
588 vs_scf_values_get(const char *pgname, vs_prop_hd_t *prop_hd)
589 {
590 	vs_scfctx_t vsc;
591 	int rc, np;
592 	const vs_propdef_t *vpd;
593 	uint64_t propid;
594 
595 	if ((vs_scf_ctx_open(&vsc)) != 0) {
596 		vs_scf_ctx_close(&vsc);
597 		return (VS_ERR_SCF);
598 	}
599 
600 	if (scf_instance_get_pg(vsc.vscf_inst, pgname, vsc.vscf_pgroup) == -1) {
601 		vs_scf_ctx_close(&vsc);
602 		if (strcmp(pgname, "VS_PGNAME_GENERAL") != 0) {
603 			rc = scf_error();
604 			if ((rc == SCF_ERROR_NOT_FOUND) ||
605 			    (rc == SCF_ERROR_INVALID_ARGUMENT))
606 				return (VS_ERR_INVALID_SE);
607 		}
608 		return (VS_ERR_SCF);
609 	}
610 
611 	rc = VS_ERR_NONE;
612 	np = 0;
613 	for (propid = 1LL; propid <= VS_PROPID_MAX; propid <<= 1) {
614 		if ((prop_hd->vp_ids & propid) == 0)
615 			continue;
616 
617 		if ((vpd = vs_get_propdef(propid)) == NULL) {
618 			rc = VS_ERR_INVALID_PROPERTY;
619 			break;
620 		}
621 
622 		vsc.vscf_prop[np] = scf_property_create(vsc.vscf_handle);
623 		vsc.vscf_val[np] = scf_value_create(vsc.vscf_handle);
624 
625 		if (vsc.vscf_prop[np] == NULL || vsc.vscf_val[np] == NULL) {
626 			rc = VS_ERR_SCF;
627 			break;
628 		}
629 
630 		if (scf_pg_get_property(vsc.vscf_pgroup, vpd->vpd_name,
631 		    vsc.vscf_prop[np]) == -1) {
632 			if (scf_error() == SCF_ERROR_NOT_FOUND) {
633 				vs_default_value(prop_hd, vpd->vpd_id);
634 				continue;
635 			}
636 			rc = VS_ERR_SCF;
637 			break;
638 		}
639 
640 		if ((rc = vs_scf_get(vpd, prop_hd, &vsc, np)) != VS_ERR_NONE)
641 			break;
642 
643 		++np;
644 	}
645 
646 
647 	vs_scf_ctx_close(&vsc);
648 
649 	return (rc);
650 }
651 
652 
653 /*
654  * vs_scf_get
655  *
656  * Loads a single values from the repository into the appropriate vscan
657  * property structure member.
658  */
659 static int
660 vs_scf_get(const vs_propdef_t *vpd, vs_prop_hd_t *prop_hd,
661 	vs_scfctx_t *vsc, int idx)
662 {
663 	int rc;
664 	int64_t port;
665 	uint8_t valbool;
666 	vs_props_t *vp = &prop_hd->vp_gen;
667 	vs_props_se_t *vep = &prop_hd->vp_se;
668 
669 	if ((rc = scf_property_get_value(vsc->vscf_prop[idx],
670 	    vsc->vscf_val[idx])) == -1) {
671 		if (rc == SCF_ERROR_CONSTRAINT_VIOLATED ||
672 		    rc == SCF_ERROR_NOT_FOUND) {
673 			vs_default_value(prop_hd, vpd->vpd_id);
674 			return (VS_ERR_NONE);
675 		}
676 		return (VS_ERR_SCF);
677 	}
678 
679 	rc = VS_ERR_NONE;
680 	switch (vpd->vpd_id) {
681 	case VS_PROPID_MAXSIZE:
682 		if ((scf_value_get_astring(vsc->vscf_val[idx],
683 		    vp->vp_maxsize, sizeof (vp->vp_maxsize))) == -1) {
684 			return (VS_ERR_SCF);
685 		}
686 		break;
687 	case VS_PROPID_MAXSIZE_ACTION:
688 		if ((scf_value_get_boolean(vsc->vscf_val[idx],
689 		    &valbool)) == -1) {
690 			return (VS_ERR_SCF);
691 		}
692 		vp->vp_maxsize_action = (valbool == 0) ? B_FALSE : B_TRUE;
693 		break;
694 	case VS_PROPID_TYPES:
695 		if ((scf_value_get_astring(vsc->vscf_val[idx],
696 		    vp->vp_types, sizeof (vp->vp_types))) == -1) {
697 			return (VS_ERR_SCF);
698 		}
699 		break;
700 	case VS_PROPID_VLOG:
701 		if ((scf_value_get_astring(vsc->vscf_val[idx],
702 		    vp->vp_vlog, sizeof (vp->vp_vlog))) == -1) {
703 			return (VS_ERR_SCF);
704 		}
705 		break;
706 	case VS_PROPID_SE_ENABLE:
707 		if ((scf_value_get_boolean(vsc->vscf_val[idx],
708 		    &valbool)) == -1) {
709 			return (VS_ERR_SCF);
710 		}
711 		vep->vep_enable = (valbool == 0) ? B_FALSE : B_TRUE;
712 		break;
713 	case VS_PROPID_SE_HOST:
714 		(void) scf_value_get_as_string_typed(vsc->vscf_val[idx],
715 		    vpd->vpd_type, vep->vep_host, sizeof (vep->vep_host));
716 		break;
717 	case VS_PROPID_SE_PORT:
718 		if ((scf_value_get_integer(vsc->vscf_val[idx], &port)) == -1)
719 			return (VS_ERR_SCF);
720 		if (port <= 0 || port >= UINT16_MAX)
721 			rc = VS_ERR_INVALID_VALUE;
722 		else
723 			vep->vep_port = (uint16_t)port;
724 		break;
725 	case VS_PROPID_SE_MAXCONN:
726 		if ((scf_value_get_integer(vsc->vscf_val[idx],
727 		    (int64_t *)&vep->vep_maxconn)) == -1) {
728 			return (VS_ERR_SCF);
729 		}
730 		break;
731 	default:
732 		break;
733 	}
734 
735 	if ((rc != VS_ERR_NONE) ||
736 	    (vs_validate(prop_hd, vpd->vpd_id) != VS_ERR_NONE)) {
737 		vs_default_value(prop_hd, vpd->vpd_id);
738 	}
739 
740 	return (VS_ERR_NONE);
741 }
742 
743 
744 /*
745  * vs_scf_pg_create
746  */
747 static int
748 vs_scf_pg_create(const char *pgname, vs_prop_hd_t *prop_hd)
749 {
750 	int rc;
751 	uint64_t propid;
752 	vs_scfctx_t vsc;
753 
754 	/* ensure that caller has authorization to refresh service */
755 	if ((rc = vs_checkauth(VS_ACTION_AUTH)) != VS_ERR_NONE)
756 		return (rc);
757 
758 	if (vs_scf_ctx_open(&vsc) != 0) {
759 		vs_scf_ctx_close(&vsc);
760 		return (VS_ERR_SCF);
761 	}
762 
763 	if (scf_instance_add_pg(vsc.vscf_inst, pgname,
764 	    SCF_GROUP_APPLICATION, 0, vsc.vscf_pgroup) == -1) {
765 		vs_scf_ctx_close(&vsc);
766 		if (scf_error() == SCF_ERROR_INVALID_ARGUMENT)
767 			return (VS_ERR_INVALID_SE);
768 		return (VS_ERR_SCF);
769 	}
770 	vs_scf_ctx_close(&vsc);
771 
772 	/* set default values for those not specified */
773 	for (propid = 1LL; propid <= VS_PROPID_MAX; propid <<= 1) {
774 		if ((propid & prop_hd->vp_all) && !(propid & prop_hd->vp_ids))
775 			vs_default_value(prop_hd, propid);
776 	}
777 
778 	prop_hd->vp_ids = prop_hd->vp_all;
779 	prop_hd->vp_ids |= VS_PROPID_VALUE_AUTH;
780 
781 	rc = vs_scf_values_set(pgname, prop_hd);
782 	if (rc != VS_ERR_NONE)
783 		(void) vs_scf_pg_delete(pgname);
784 
785 	return (rc);
786 }
787 
788 
789 /*
790  * vs_scf_pg_delete
791  */
792 static int
793 vs_scf_pg_delete(const char *pgname)
794 {
795 	int rc;
796 	vs_scfctx_t vsc;
797 
798 	/* ensure that caller has authorization to refresh service */
799 	if ((rc = vs_checkauth(VS_ACTION_AUTH)) != VS_ERR_NONE)
800 		return (rc);
801 
802 	if (vs_scf_ctx_open(&vsc) != 0) {
803 		vs_scf_ctx_close(&vsc);
804 		return (VS_ERR_SCF);
805 	}
806 
807 	if (scf_instance_get_pg(vsc.vscf_inst, pgname, vsc.vscf_pgroup) == -1) {
808 		vs_scf_ctx_close(&vsc);
809 		rc = scf_error();
810 		if ((rc == SCF_ERROR_NOT_FOUND) ||
811 		    (rc == SCF_ERROR_INVALID_ARGUMENT))
812 			return (VS_ERR_INVALID_SE);
813 		else
814 			return (VS_ERR_SCF);
815 	}
816 
817 	if (scf_pg_delete(vsc.vscf_pgroup) == -1) {
818 		vs_scf_ctx_close(&vsc);
819 		rc = scf_error();
820 		if ((rc == SCF_ERROR_NOT_FOUND) ||
821 		    (rc == SCF_ERROR_INVALID_ARGUMENT))
822 			return (VS_ERR_INVALID_SE);
823 
824 		return (VS_ERR_SCF);
825 	}
826 
827 	vs_scf_ctx_close(&vsc);
828 
829 	/* Notify the daemon that things have changed */
830 	if ((smf_refresh_instance(VS_INSTANCE_FMRI)) == -1) {
831 		return (VS_ERR_SCF);
832 	}
833 
834 	return (VS_ERR_NONE);
835 }
836 
837 
838 /*
839  * vs_scf_values_set
840  *
841  * Sets property values in the repository.  This is the single
842  * entry point for storing SMF values.
843  *
844  * Like loading values, this is an operation based on a single property
845  * group, so all property values changed in this function must belong
846  * to the same property group. Additionally, this operation is done in
847  * the context of a repository transaction; on any fatal error, the
848  * SCF context will be closed, destroying all SCF objects and aborting
849  * the transaction.
850  */
851 static int
852 vs_scf_values_set(const char *pgname, vs_prop_hd_t *prop_hd)
853 {
854 	int rc, np;
855 	const vs_propdef_t *vpd;
856 	uint64_t propid;
857 	vs_scfctx_t vsc;
858 
859 	/* ensure that caller has authorization to refresh service */
860 	if ((rc = vs_checkauth(VS_ACTION_AUTH)) != VS_ERR_NONE)
861 		return (rc);
862 
863 	if (vs_scf_ctx_open(&vsc) != 0) {
864 		vs_scf_ctx_close(&vsc);
865 		return (VS_ERR_SCF);
866 	}
867 
868 	if (scf_instance_get_pg(vsc.vscf_inst, pgname, vsc.vscf_pgroup) == -1) {
869 		vs_scf_ctx_close(&vsc);
870 		rc = scf_error();
871 		if (strcmp(pgname, "VS_PGNAME_GENERAL") != 0) {
872 			if ((rc == SCF_ERROR_NOT_FOUND) ||
873 			    (rc == SCF_ERROR_INVALID_ARGUMENT))
874 				return (VS_ERR_INVALID_SE);
875 		}
876 		return (VS_ERR_SCF);
877 	}
878 
879 	if (((vsc.vscf_tx = scf_transaction_create(vsc.vscf_handle)) == NULL) ||
880 	    (scf_transaction_start(vsc.vscf_tx, vsc.vscf_pgroup) == -1)) {
881 		vs_scf_ctx_close(&vsc);
882 		return (VS_ERR_SCF);
883 	}
884 
885 	/* Process the value change for each specified property */
886 	rc = 0;
887 	np = 0;
888 	for (propid = 1LL; propid <= VS_PROPID_MAX; propid <<= 1) {
889 		if ((prop_hd->vp_ids & propid) == 0)
890 			continue;
891 
892 		if ((vpd = vs_get_propdef(propid)) == NULL) {
893 			rc = VS_ERR_INVALID_PROPERTY;
894 			break;
895 		}
896 
897 		vsc.vscf_val[np] = scf_value_create(vsc.vscf_handle);
898 		vsc.vscf_ent[np] = scf_entry_create(vsc.vscf_handle);
899 
900 		if (vsc.vscf_val[np] == NULL || vsc.vscf_ent[np] == NULL) {
901 			rc = VS_ERR_SCF;
902 			break;
903 		}
904 
905 		if ((rc = scf_transaction_property_change(vsc.vscf_tx,
906 		    vsc.vscf_ent[np], vpd->vpd_name, vpd->vpd_type)) == -1) {
907 			rc = scf_transaction_property_new(vsc.vscf_tx,
908 			    vsc.vscf_ent[np], vpd->vpd_name, vpd->vpd_type);
909 		}
910 		if (rc == -1) {
911 			rc = VS_ERR_SCF;
912 			break;
913 		}
914 
915 		if ((rc = vs_scf_set(vpd, prop_hd, &vsc, np)) != VS_ERR_NONE)
916 			break;
917 
918 		++np;
919 	}
920 
921 	if (rc != VS_ERR_NONE) {
922 		vs_scf_ctx_close(&vsc);
923 		return (rc);
924 	}
925 
926 	/* Commit the transaction */
927 	if (scf_transaction_commit(vsc.vscf_tx) == -1) {
928 		vs_scf_ctx_close(&vsc);
929 		return (VS_ERR_SCF);
930 	}
931 	vs_scf_ctx_close(&vsc);
932 
933 	/* Notify the daemon that things have changed */
934 	if ((smf_refresh_instance(VS_INSTANCE_FMRI)) == -1)
935 		return (VS_ERR_SCF);
936 
937 	return (VS_ERR_NONE);
938 }
939 
940 
941 /*
942  * vs_scf_set
943  *
944  * Stores a single value from the appropriate vscan property structure
945  * member into the repository.
946  *
947  * Values are set in the SCF value object, then the value object
948  * is added to the SCF property object.
949  */
950 static int
951 vs_scf_set(const vs_propdef_t *vpd, vs_prop_hd_t *prop_hd,
952     vs_scfctx_t *vsc, int idx)
953 {
954 	int rc;
955 	vs_props_t *vp = &prop_hd->vp_gen;
956 	vs_props_se_t *vep = &prop_hd->vp_se;
957 
958 	if ((rc = vs_validate(prop_hd, vpd->vpd_id)) != VS_ERR_NONE)
959 		return (rc);
960 
961 	rc = VS_ERR_NONE;
962 	switch (vpd->vpd_id) {
963 	case VS_PROPID_MAXSIZE:
964 		if ((scf_value_set_astring(vsc->vscf_val[idx],
965 		    vp->vp_maxsize)) == -1) {
966 			rc = VS_ERR_SCF;
967 		}
968 		break;
969 	case VS_PROPID_MAXSIZE_ACTION:
970 		scf_value_set_boolean(vsc->vscf_val[idx],
971 		    (uint8_t)vp->vp_maxsize_action);
972 		break;
973 	case VS_PROPID_TYPES:
974 		if ((scf_value_set_astring(vsc->vscf_val[idx],
975 		    vp->vp_types)) == -1) {
976 			return (VS_ERR_SCF);
977 		}
978 		break;
979 	case VS_PROPID_SE_ENABLE:
980 		scf_value_set_boolean(vsc->vscf_val[idx],
981 		    (uint8_t)vep->vep_enable);
982 		break;
983 	case VS_PROPID_SE_HOST:
984 		if ((scf_value_set_from_string(vsc->vscf_val[idx],
985 		    vpd->vpd_type, vep->vep_host)) == -1) {
986 			rc = VS_ERR_SCF;
987 		}
988 		break;
989 	case VS_PROPID_SE_PORT:
990 		scf_value_set_integer(vsc->vscf_val[idx], vep->vep_port);
991 		break;
992 	case VS_PROPID_SE_MAXCONN:
993 		scf_value_set_integer(vsc->vscf_val[idx],
994 		    vep->vep_maxconn);
995 		break;
996 	case VS_PROPID_VALUE_AUTH:
997 		if ((scf_value_set_astring(vsc->vscf_val[idx],
998 		    VS_VALUE_AUTH)) == -1) {
999 			return (VS_ERR_SCF);
1000 		}
1001 		break;
1002 	default:
1003 		break;
1004 	}
1005 
1006 	if ((scf_entry_add_value(vsc->vscf_ent[idx],
1007 	    vsc->vscf_val[idx])) == -1) {
1008 		return (VS_ERR_SCF);
1009 	}
1010 
1011 	return (rc);
1012 }
1013 
1014 
1015 /*
1016  * vs_scf_ctx_open
1017  *
1018  * Opens an SCF context; creates the minumum SCF objects
1019  * for use in loading/storing from the SMF repository (meaning
1020  * vscf_property group data).
1021  *
1022  * Other SCF objects in the context may be initialized elsewher
1023  * subsequent to open, but all initialized structures are destroyed
1024  * in vs_scf_ctx_close().
1025  */
1026 static int
1027 vs_scf_ctx_open(vs_scfctx_t *vsc)
1028 {
1029 	(void) memset(vsc, 0, sizeof (vs_scfctx_t));
1030 
1031 	if ((vsc->vscf_handle = scf_handle_create(SCF_VERSION)) == NULL)
1032 		return (VS_ERR_SCF);
1033 
1034 	if (scf_handle_bind(vsc->vscf_handle) == -1)
1035 		return (VS_ERR_SCF);
1036 
1037 	if ((vsc->vscf_inst = scf_instance_create(vsc->vscf_handle)) == NULL)
1038 		return (VS_ERR_SCF);
1039 
1040 	if (scf_handle_decode_fmri(vsc->vscf_handle, VS_INSTANCE_FMRI,
1041 	    NULL, NULL, vsc->vscf_inst, NULL, NULL,
1042 	    SCF_DECODE_FMRI_EXACT) == -1) {
1043 		return (VS_ERR_SCF);
1044 	}
1045 
1046 	if ((vsc->vscf_pgroup = scf_pg_create(vsc->vscf_handle)) == NULL)
1047 		return (VS_ERR_SCF);
1048 
1049 	return (VS_ERR_NONE);
1050 }
1051 
1052 
1053 /*
1054  * vs_scf_ctx_close
1055  *
1056  * Closes an SCF context; destroys all initialized SCF objects.
1057  */
1058 static void
1059 vs_scf_ctx_close(vs_scfctx_t *vsc)
1060 {
1061 	int i;
1062 
1063 	for (i = 0; i < VS_NUM_PROPIDS; i++) {
1064 		if (vsc->vscf_val[i])
1065 			scf_value_destroy(vsc->vscf_val[i]);
1066 		if (vsc->vscf_ent[i])
1067 			scf_entry_destroy(vsc->vscf_ent[i]);
1068 		if (vsc->vscf_prop[i])
1069 			scf_property_destroy(vsc->vscf_prop[i]);
1070 	}
1071 
1072 	if (vsc->vscf_iter)
1073 		scf_iter_destroy(vsc->vscf_iter);
1074 	if (vsc->vscf_tx)
1075 		scf_transaction_destroy(vsc->vscf_tx);
1076 	if (vsc->vscf_pgroup)
1077 		scf_pg_destroy(vsc->vscf_pgroup);
1078 	if (vsc->vscf_inst)
1079 		scf_instance_destroy(vsc->vscf_inst);
1080 	if (vsc->vscf_handle)
1081 		scf_handle_destroy(vsc->vscf_handle);
1082 }
1083 
1084 
1085 /*
1086  * vs_validate
1087  *
1088  * Validate property identified in propid.
1089  *
1090  * Returns: VS_ERR_NONE
1091  *          VS_ERR_INVALID_VALUE
1092  *          VS_ERR_INVALID_PROPERTY
1093  */
1094 static int
1095 vs_validate(const vs_prop_hd_t *prop_hd, uint64_t propid)
1096 {
1097 	uint64_t num;
1098 	const vs_props_t *vp = &prop_hd->vp_gen;
1099 	const vs_props_se_t *vep = &prop_hd->vp_se;
1100 
1101 	switch (propid) {
1102 	case VS_PROPID_MAXSIZE:
1103 		if ((vs_strtonum(vp->vp_maxsize, &num) != 0) || (num == 0))
1104 			return (VS_ERR_INVALID_VALUE);
1105 		break;
1106 	case VS_PROPID_MAXSIZE_ACTION:
1107 		break;
1108 	case VS_PROPID_TYPES:
1109 		if (!vs_is_valid_types(vp->vp_types))
1110 			return (VS_ERR_INVALID_VALUE);
1111 		break;
1112 	case VS_PROPID_SE_ENABLE:
1113 		break;
1114 	case VS_PROPID_SE_PORT:
1115 		if (vep->vep_port == 0)
1116 			return (VS_ERR_INVALID_VALUE);
1117 		break;
1118 	case VS_PROPID_SE_HOST:
1119 		if (!vs_is_valid_host(vep->vep_host))
1120 			return (VS_ERR_INVALID_VALUE);
1121 		break;
1122 	case VS_PROPID_SE_MAXCONN:
1123 		if (vep->vep_maxconn < VS_VAL_SE_MAXCONN_MIN ||
1124 		    vep->vep_maxconn > VS_VAL_SE_MAXCONN_MAX)
1125 			return (VS_ERR_INVALID_VALUE);
1126 		break;
1127 	case VS_PROPID_VALUE_AUTH:
1128 	case VS_PROPID_VLOG:
1129 		break;
1130 	default:
1131 		return (VS_ERR_INVALID_PROPERTY);
1132 	}
1133 
1134 	return (VS_ERR_NONE);
1135 }
1136 
1137 
1138 /*
1139  * vs_props_validate
1140  *
1141  * Validate  properties identified in propids.
1142  *
1143  * Returns: VS_ERR_NONE
1144  *          VS_ERR_INVALID_VALUE
1145  *          VS_ERR_INVALID_PROPERTY
1146  */
1147 int
1148 vs_props_validate(const vs_props_t *props, uint64_t propids)
1149 {
1150 	uint64_t propid;
1151 	vs_prop_hd_t prop_hd;
1152 
1153 	if ((propids & VS_PROPID_GEN_ALL) != propids)
1154 		return (VS_ERR_INVALID_PROPERTY);
1155 
1156 	(void) memset(&prop_hd, 0, sizeof (vs_prop_hd_t));
1157 	prop_hd.vp_gen = *props;
1158 	prop_hd.vp_type = VS_PTYPE_GEN;
1159 	prop_hd.vp_ids = propids;
1160 	prop_hd.vp_all = VS_PROPID_GEN_ALL;
1161 
1162 	for (propid = 1LL; propid <= VS_PROPID_MAX; propid <<= 1) {
1163 		if ((propids & propid) == 0)
1164 			continue;
1165 
1166 		if (vs_validate(&prop_hd, propid) != VS_ERR_NONE)
1167 			return (VS_ERR_INVALID_VALUE);
1168 	}
1169 
1170 	return (VS_ERR_NONE);
1171 }
1172 
1173 
1174 /*
1175  * vs_props_se_validate
1176  *
1177  * Validate properties identified in propids.
1178  *
1179  * Returns: VS_ERR_NONE
1180  *          VS_ERR_INVALID_VALUE
1181  *          VS_ERR_INVALID_PROPERTY
1182  */
1183 int
1184 vs_props_se_validate(const vs_props_se_t *se_props, uint64_t propids)
1185 {
1186 	uint64_t propid;
1187 	vs_prop_hd_t prop_hd;
1188 
1189 	if ((propids & VS_PROPID_SE_ALL) != propids)
1190 		return (VS_ERR_INVALID_PROPERTY);
1191 
1192 	(void) memset(&prop_hd, 0, sizeof (vs_prop_hd_t));
1193 	prop_hd.vp_se = *se_props;
1194 	prop_hd.vp_type = VS_PTYPE_SE;
1195 	prop_hd.vp_ids = propids;
1196 	prop_hd.vp_all = VS_PROPID_SE_ALL;
1197 
1198 	for (propid = 1LL; propid <= VS_PROPID_MAX; propid <<= 1) {
1199 		if ((propids & propid) == 0)
1200 			continue;
1201 
1202 		if (vs_validate(&prop_hd, propid) != VS_ERR_NONE)
1203 			return (VS_ERR_INVALID_VALUE);
1204 	}
1205 
1206 	return (VS_ERR_NONE);
1207 }
1208 
1209 
1210 /*
1211  * vs_is_valid_types
1212  *
1213  * Checks that types property is a valid format:
1214  * - doesn't exceed VS_VAL_TYPES_MAX
1215  * - doesn't contain VS_VAL_TYPES_INVALID_CHARS
1216  * - is correctly formatted - passes the parsing tests
1217  *
1218  * Returns 1 on success, 0 on failure
1219  */
1220 static int
1221 vs_is_valid_types(const char *types)
1222 {
1223 	char buf[VS_VAL_TYPES_LEN];
1224 	uint32_t len = VS_VAL_TYPES_LEN;
1225 
1226 	if (strlen(types) > VS_VAL_TYPES_LEN)
1227 		return (0);
1228 
1229 	if (strpbrk(types, VS_VAL_TYPES_INVALID_CHARS) != NULL)
1230 		return (0);
1231 
1232 	if (vs_parse_types(types, buf, &len) != 0)
1233 		return (0);
1234 
1235 	return (1);
1236 }
1237 
1238 
1239 /*
1240  * vs_is_valid_host
1241  *
1242  * Returns 1 on success, 0 on failure
1243  */
1244 static int
1245 vs_is_valid_host(const char *host)
1246 {
1247 	long naddr;
1248 	const char *p;
1249 
1250 	if (!host || *host == '\0')
1251 		return (0);
1252 
1253 	if ('0' <= host[0] && host[0] <= '9') {
1254 		/* ip address */
1255 		if ((inet_pton(AF_INET, host, &naddr)) == 0)
1256 			return (0);
1257 		if ((naddr & IN_CLASSA_NET) == 0)
1258 			return (0);
1259 		if ((naddr & IN_CLASSC_HOST) == 0)
1260 			return (0);
1261 	} else {
1262 		/* hostname */
1263 		p = host;
1264 		while (*p != '\0') {
1265 			if (!isascii(*p))
1266 				return (0);
1267 
1268 			if (isalnum(*p) ||
1269 			    (*p == '.') || (*p == '-') || (*p == '_')) {
1270 				++p;
1271 			} else {
1272 				return (0);
1273 			}
1274 		}
1275 	}
1276 
1277 	return (1);
1278 }
1279 
1280 
1281 /*
1282  * vs_parse_types
1283  *
1284  * Replace comma separators with '\0'.
1285  *
1286  * Types contains comma separated rules each beginning with +|-
1287  *   - embedded commas are escaped by backslash
1288  *   - backslash is escaped by backslash
1289  *   - a single backslash not followed by comma is illegal
1290  *
1291  * On entry to the function len must contain the length of
1292  * the buffer. On sucecssful exit len will contain the length
1293  * of the parsed data within the buffer.
1294  *
1295  * Returns 0 on success, -1 on failure
1296  */
1297 int
1298 vs_parse_types(const char *types, char *buf, uint32_t *len)
1299 {
1300 	char *p = (char *)types;
1301 	char *b = buf;
1302 
1303 	if (strlen(types) > *len)
1304 		return (-1);
1305 
1306 	if (strchr(VS_TYPES_RULES, *p) == NULL)
1307 		return (-1);
1308 
1309 	(void) memset(buf, 0, *len);
1310 
1311 	while (*p) {
1312 		switch (*p) {
1313 		case VS_TYPES_SEP:
1314 			if (*(p + 1) &&
1315 			    (strchr(VS_TYPES_RULES, *(p + 1))) == NULL)
1316 				return (-1);
1317 			*b = '\0';
1318 			break;
1319 		case VS_TYPES_ESCAPE:
1320 			++p;
1321 			if (*p == VS_TYPES_ESCAPE || *p == VS_TYPES_SEP)
1322 				*b = *p;
1323 			else
1324 				return (-1);
1325 			break;
1326 		default:
1327 			*b = *p;
1328 		}
1329 		++p;
1330 		++b;
1331 	}
1332 
1333 	*len = (b - buf) + 1;
1334 
1335 	return (0);
1336 }
1337 
1338 
1339 /*
1340  * vs_statistics
1341  */
1342 int
1343 vs_statistics(vs_stats_t *stats)
1344 {
1345 	int door_fd, rc = VS_ERR_NONE;
1346 	vs_stats_req_t *req;
1347 	vs_stats_rsp_t *rsp;
1348 	door_arg_t arg;
1349 
1350 	if ((req = calloc(1, sizeof (vs_stats_req_t))) == NULL)
1351 		return (VS_ERR_SYS);
1352 
1353 	if ((rsp = calloc(1, sizeof (vs_stats_rsp_t))) == NULL) {
1354 		free(req);
1355 		return (VS_ERR_SYS);
1356 	}
1357 
1358 	if ((door_fd = open(VS_STATS_DOOR_NAME, O_RDONLY)) < 0) {
1359 		free(req);
1360 		free(rsp);
1361 		return (VS_ERR_DAEMON_COMM);
1362 	}
1363 
1364 	req->vsr_magic = VS_STATS_DOOR_MAGIC;
1365 	req->vsr_id = VS_STATS_GET;
1366 
1367 	arg.data_ptr = (char *)req;
1368 	arg.data_size = sizeof (vs_stats_req_t);
1369 	arg.desc_ptr = NULL;
1370 	arg.desc_num = 0;
1371 	arg.rbuf = (char *)rsp;
1372 	arg.rsize = sizeof (vs_stats_rsp_t);
1373 
1374 	if ((door_call(door_fd, &arg) < 0) ||
1375 	    (rsp->vsr_magic != VS_STATS_DOOR_MAGIC)) {
1376 		rc = VS_ERR_DAEMON_COMM;
1377 	} else {
1378 		*stats = rsp->vsr_stats;
1379 	}
1380 
1381 	(void) close(door_fd);
1382 
1383 	free(req);
1384 	free(rsp);
1385 	return (rc);
1386 }
1387 
1388 
1389 /*
1390  * vs_statistics_reset
1391  */
1392 int
1393 vs_statistics_reset()
1394 {
1395 	int door_fd, rc;
1396 	vs_stats_req_t *req;
1397 	door_arg_t arg;
1398 
1399 	/* ensure that caller has authorization to reset stats */
1400 	if ((rc = vs_checkauth(VS_VALUE_AUTH)) != VS_ERR_NONE)
1401 		return (rc);
1402 
1403 	if ((req = calloc(1, sizeof (vs_stats_req_t))) == NULL)
1404 		return (VS_ERR_SYS);
1405 
1406 	if ((door_fd = open(VS_STATS_DOOR_NAME, O_RDONLY)) < 0) {
1407 		free(req);
1408 		return (VS_ERR_DAEMON_COMM);
1409 	}
1410 
1411 	req->vsr_magic = VS_STATS_DOOR_MAGIC;
1412 	req->vsr_id = VS_STATS_RESET;
1413 
1414 	arg.data_ptr = (char *)req;
1415 	arg.data_size = sizeof (vs_stats_req_t);
1416 	arg.desc_ptr = NULL;
1417 	arg.desc_num = 0;
1418 	arg.rbuf = NULL;
1419 	arg.rsize = 0;
1420 
1421 	if (door_call(door_fd, &arg) < 0)
1422 		rc = VS_ERR_DAEMON_COMM;
1423 	else
1424 		rc = VS_ERR_NONE;
1425 
1426 	(void) close(door_fd);
1427 	free(req);
1428 	return (rc);
1429 }
1430 
1431 
1432 /*
1433  * vs_checkauth
1434  */
1435 static int
1436 vs_checkauth(char *auth)
1437 {
1438 	struct passwd *pw;
1439 	uid_t uid;
1440 
1441 	uid = getuid();
1442 
1443 	if ((pw = getpwuid(uid)) == NULL)
1444 		return (VS_ERR_SYS);
1445 
1446 	if (chkauthattr(auth, pw->pw_name) != 1) {
1447 		return (VS_ERR_AUTH);
1448 	}
1449 
1450 	return (VS_ERR_NONE);
1451 }
1452 
1453 
1454 /*
1455  * vs_props_get_engines
1456  *
1457  * On input, count specifies the maximum number of engine ids to
1458  * return. engids must be an array with count entries.
1459  * On return, count specifies the number of engine ids being
1460  * returned in engids.
1461  *
1462  * Caller is responsible for free'ing the engids allocated herein.
1463  */
1464 static int
1465 vs_props_get_engines(char *engids[], int *count)
1466 {
1467 	int i, prefix_len;
1468 	char pgname[VS_PGNAME_ENGINE_LEN];
1469 	vs_scfctx_t vsc;
1470 
1471 
1472 	if (((vs_scf_ctx_open(&vsc)) != 0) ||
1473 	    ((vsc.vscf_iter = scf_iter_create(vsc.vscf_handle)) == NULL) ||
1474 	    (scf_iter_instance_pgs_typed(vsc.vscf_iter, vsc.vscf_inst,
1475 	    SCF_GROUP_APPLICATION) != 0)) {
1476 		vs_scf_ctx_close(&vsc);
1477 		return (VS_ERR_SCF);
1478 	}
1479 
1480 	for (i = 0; i < *count; i++)
1481 		engids[i] = NULL;
1482 
1483 	i = 0;
1484 	prefix_len = sizeof (VS_PGNAME_ENGINE_PREFIX) - 1;
1485 
1486 	while ((i < VS_SE_MAX) &&
1487 	    (scf_iter_next_pg(vsc.vscf_iter, vsc.vscf_pgroup) == 1)) {
1488 		if (scf_pg_get_name(vsc.vscf_pgroup, pgname,
1489 		    VS_PGNAME_ENGINE_LEN) < 0) {
1490 			vs_scf_ctx_close(&vsc);
1491 			return (VS_ERR_SCF);
1492 		}
1493 
1494 		if (strncmp(pgname, VS_PGNAME_ENGINE_PREFIX, prefix_len) == 0) {
1495 			if ((engids[i] = strdup(pgname + prefix_len)) != NULL) {
1496 				if (++i == *count)
1497 					break;
1498 			}
1499 		}
1500 	}
1501 	vs_scf_ctx_close(&vsc);
1502 
1503 	*count = i;
1504 	return (VS_ERR_NONE);
1505 }
1506 
1507 
1508 /*
1509  * vs_scf_pg_count
1510  */
1511 static int
1512 vs_scf_pg_count(void)
1513 {
1514 	int count = 0;
1515 	vs_scfctx_t vsc;
1516 
1517 	if ((vs_scf_ctx_open(&vsc) != 0) ||
1518 	    ((vsc.vscf_iter = scf_iter_create(vsc.vscf_handle)) == NULL) ||
1519 	    (scf_iter_instance_pgs_typed(vsc.vscf_iter, vsc.vscf_inst,
1520 	    SCF_GROUP_APPLICATION) != 0)) {
1521 		vs_scf_ctx_close(&vsc);
1522 		return (-1);
1523 	}
1524 
1525 	while (scf_iter_next_pg(vsc.vscf_iter, vsc.vscf_pgroup) == 1)
1526 		++count;
1527 
1528 	vs_scf_ctx_close(&vsc);
1529 
1530 	return (count);
1531 }
1532 
1533 
1534 /*
1535  * vs_engid_to_pgname
1536  *
1537  * To convert an engine id (engid) to a property group name (pgname),
1538  * the engine id is prefixed with VS_PGNAME_ENGINE_PREFIX.
1539  */
1540 static void
1541 vs_engid_to_pgname(const char *engid, char pgname[VS_PGNAME_ENGINE_LEN])
1542 {
1543 	(void) snprintf(pgname, VS_PGNAME_ENGINE_LEN, "%s%s",
1544 	    VS_PGNAME_ENGINE_PREFIX, engid);
1545 }
1546 
1547 
1548 /*
1549  *  vs_strtonum
1550  *
1551  *  Converts a size string in the format into an integer.
1552  *
1553  *  A size string is a numeric value followed by an optional unit
1554  *  specifier which is used as a multiplier to calculate a raw
1555  *  number.
1556  *  The size string format is:  N[.N][KMGTP][B]
1557  *
1558  *  The numeric value can contain a decimal portion. Unit specifiers
1559  *  are either a one-character or two-character string; i.e. "K" or
1560  *  "KB" for kilobytes. Unit specifiers must follow the numeric portion
1561  *  immediately, and are not case-sensitive.
1562  *
1563  *  If either "B" is specified, or there is no unit specifier portion
1564  *  in the string, the numeric value is calculated with no multiplier
1565  *  (assumes a basic unit of "bytes").
1566  *
1567  *  Returns:
1568  *	-1:	Failure; errno set to specify the error.
1569  *	 0:	Success.
1570  */
1571 int
1572 vs_strtonum(const char *value, uint64_t *num)
1573 {
1574 	char *end;
1575 	int shift;
1576 	double fval;
1577 
1578 	*num = 0;
1579 
1580 	/* Check to see if this looks like a number.  */
1581 	if ((value[0] < '0' || value[0] > '9') && value[0] != '.') {
1582 		errno = EINVAL;
1583 		return (-1);
1584 	}
1585 
1586 	/* Rely on stroll() to process the numeric portion.  */
1587 	errno = 0;
1588 	*num = strtoll(value, &end, 10);
1589 
1590 	/*
1591 	 * Check for ERANGE, which indicates that the value is too large to
1592 	 * fit in a 64-bit value.
1593 	 */
1594 	if (errno != 0)
1595 		return (-1);
1596 
1597 	/*
1598 	 * If we have a decimal value, then do the computation with floating
1599 	 * point arithmetic.  Otherwise, use standard arithmetic.
1600 	 */
1601 	if (*end == '.') {
1602 		fval = strtod(value, &end);
1603 
1604 		if ((shift = vs_strtoshift(end)) == -1)
1605 			return (-1); /* errno set */
1606 
1607 		fval *= pow(2, shift);
1608 		if (fval > UINT64_MAX) {
1609 			errno = ERANGE;
1610 			return (-1);
1611 		}
1612 
1613 		*num = (uint64_t)fval;
1614 	} else {
1615 		if ((shift = vs_strtoshift(end)) == -1)
1616 			return (-1); /* errno set */
1617 
1618 		/* Check for overflow */
1619 		if (shift >= 64 || (*num << shift) >> shift != *num) {
1620 			errno = ERANGE;
1621 			return (-1);
1622 		}
1623 
1624 		*num <<= shift;
1625 	}
1626 
1627 	return (0);
1628 }
1629 
1630 
1631 /*
1632  *  vs_strtoshift
1633  *
1634  *  Converts a unit specifier string into a number of bits that
1635  *  a numeric value must be shifted.
1636  *
1637  *  Returns:
1638  *	-1:	Failure; errno set to specify the error.
1639  *	>-1:	Success; the shift count.
1640  *
1641  */
1642 static int
1643 vs_strtoshift(const char *buf)
1644 {
1645 	const char *ends = "BKMGTPEZ";
1646 	int i;
1647 
1648 	if (buf[0] == '\0')
1649 		return (0);
1650 	for (i = 0; i < strlen(ends); i++) {
1651 		if (toupper(buf[0]) == ends[i])
1652 			break;
1653 	}
1654 	if (i == strlen(ends)) {
1655 		errno = EINVAL;
1656 		return (-1);
1657 	}
1658 
1659 	/* Allow trailing 'b' characters except in the case of 'BB'. */
1660 	if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' &&
1661 	    toupper(buf[0]) != 'B')) {
1662 		return (10 * i);
1663 	}
1664 
1665 	errno = EINVAL;
1666 	return (-1);
1667 }
1668