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