xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/inetadm/inetadm.c (revision d6bb6a8465e557cb946ef49d56ed3202f6218652)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * inetadm - administer services controlled by inetd and print out inetd
31  * service related information.
32  */
33 
34 #include <locale.h>
35 #include <libintl.h>
36 #include <libscf.h>
37 #include <libscf_priv.h>
38 #include <libuutil.h>
39 #include <stddef.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <syslog.h>
44 #include <inetsvc.h>
45 #include <errno.h>
46 
47 #ifndef TEXT_DOMAIN
48 #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
49 #endif /* TEXT_DOMAIN */
50 
51 /* Strings for output to the user, and checking user's input */
52 
53 #define	INETADM_TRUE_STR		"TRUE"
54 #define	INETADM_FALSE_STR		"FALSE"
55 #define	INETADM_ENABLED_STR		"enabled"
56 #define	INETADM_DISABLED_STR		"disabled"
57 #define	INETADM_DEFAULT_STR		"default"
58 
59 /* String for checking if an instance is under inetd's control. */
60 
61 #define	INETADM_INETD_STR		"network/inetd"
62 
63 /*
64  * Used to hold a list of scf_value_t's whilst performing a transaction
65  * to write a proto list back.
66  */
67 typedef struct scf_val_el {
68 	scf_value_t		*val;
69 	uu_list_node_t		link;
70 } scf_val_el_t;
71 
72 /*
73  * Structure used to encapsulate argc and argv so they can be passed using the
74  * single data argument supplied by scf_walk_fmri() for the consumption of
75  * modify_inst_props_cb().
76  */
77 typedef struct arglist {
78 	int	argc;
79 	char	**argv;
80 } arglist_t;
81 
82 static scf_handle_t *h;
83 
84 static void
85 scfdie()
86 {
87 	uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
88 	    scf_strerror(scf_error()));
89 }
90 
91 static void
92 usage(boolean_t detailed)
93 {
94 
95 	uu_warn(gettext(
96 	    "Usage:\n"
97 	    "  inetadm\n"
98 	    "  inetadm -?\n"
99 	    "  inetadm -p\n"
100 	    "  inetadm -l {FMRI | pattern}...\n"
101 	    "  inetadm -e {FMRI | pattern}...\n"
102 	    "  inetadm -d {FMRI | pattern}...\n"
103 	    "  inetadm -m {FMRI | pattern}... {name=value}...\n"
104 	    "  inetadm -M {name=value}...\n"));
105 
106 	if (!detailed)
107 		exit(UU_EXIT_USAGE);
108 
109 	(void) fprintf(stdout, gettext(
110 	    "\n"
111 	    "Without any options inetadm lists all inetd managed services.\n"
112 	    "\n"
113 	    "Options:\n"
114 	    "  -?	Print help.\n"
115 	    "  -p	List all default inetd property values.\n"
116 	    "  -l 	List all inetd property values for the inet "
117 	    "service(s).\n"
118 	    "  -e	Enable the inet service(s).\n"
119 	    "  -d	Disable the inet service(s).\n"
120 	    "  -m	Modify the inet service(s) inetd property values.\n"
121 	    "  -M	Modify default inetd property values.\n"));
122 }
123 
124 /*
125  * Add the proto list contained in array 'plist' to entry 'entry', storing
126  * aside the scf_value_t's created and added to the entry in a list that the
127  * pointer referenced by sv_list is made to point at.
128  */
129 static void
130 add_proto_list(scf_transaction_entry_t *entry, scf_handle_t *hdl,
131     char **plist, uu_list_t **sv_list)
132 {
133 	scf_val_el_t		*sv_el;
134 	int			i;
135 
136 	static uu_list_pool_t	*sv_pool = NULL;
137 
138 	if ((sv_pool == NULL) &&
139 	    ((sv_pool = uu_list_pool_create("sv_pool",
140 	    sizeof (scf_val_el_t), offsetof(scf_val_el_t, link), NULL,
141 	    UU_LIST_POOL_DEBUG)) == NULL))
142 		uu_die(gettext("Error: %s.\n"), uu_strerror(uu_error()));
143 
144 	if ((*sv_list = uu_list_create(sv_pool, NULL, 0)) == NULL)
145 		uu_die(gettext("Error: %s.\n"), uu_strerror(uu_error()));
146 
147 	for (i = 0; plist[i] != NULL; i++) {
148 		if ((sv_el = malloc(sizeof (scf_val_el_t))) == NULL)
149 			uu_die(gettext("Error:"));
150 
151 		if (((sv_el->val = scf_value_create(hdl)) == NULL) ||
152 		    (scf_value_set_astring(sv_el->val, plist[i]) != 0) ||
153 		    (scf_entry_add_value(entry, sv_el->val) != 0))
154 			scfdie();
155 
156 		uu_list_node_init(sv_el, &sv_el->link, sv_pool);
157 		(void) uu_list_insert_after(*sv_list, NULL, sv_el);
158 	}
159 }
160 
161 /*
162  * A counterpart to add_proto_list(), this function removes and frees the
163  * scf_value_t's it added to entry 'entry'.
164  */
165 static void
166 remove_proto_list(scf_transaction_entry_t *entry, uu_list_t *sv_list)
167 {
168 	scf_val_el_t	*sv_el;
169 	void		*cookie = NULL;
170 
171 	scf_entry_reset(entry);
172 
173 	while ((sv_el = uu_list_teardown(sv_list, &cookie)) != NULL) {
174 		scf_value_destroy(sv_el->val);
175 		free(sv_el);
176 	}
177 
178 	uu_list_destroy(sv_list);
179 }
180 
181 /*
182  * modify_prop takes an instance, property group, property name, type, and
183  * value, and modifies the specified property in the repository to the
184  * submitted value.
185  */
186 
187 static void
188 modify_prop(const scf_instance_t *inst, const char *pg, const char *prop,
189     scf_type_t type, void *value)
190 {
191 	scf_transaction_t		*tx;
192 	scf_transaction_entry_t		*ent;
193 	scf_propertygroup_t		*gpg;
194 	scf_property_t			*eprop;
195 	scf_value_t			*v;
196 	int				ret, create = 0;
197 	char				*fmri;
198 	ssize_t				max_fmri_len;
199 
200 	if ((gpg = scf_pg_create(h)) == NULL ||
201 	    (eprop = scf_property_create(h)) == NULL ||
202 	    (v = scf_value_create(h)) == NULL)
203 		scfdie();
204 
205 	/* Get the property group or create it if it is missing. */
206 	if (scf_instance_get_pg(inst, pg, gpg) == -1) {
207 		if (scf_error() != SCF_ERROR_NOT_FOUND)
208 			scfdie();
209 
210 		max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
211 		if ((fmri = malloc(max_fmri_len + 1)) == NULL)
212 			uu_die(gettext("Error: Out of memory.\n"));
213 
214 		if (scf_instance_to_fmri(inst, fmri, max_fmri_len + 1) < 0)
215 			scfdie();
216 
217 		syslog(LOG_NOTICE, "inetadm: Property group \"%s\" missing "
218 		    "from \"%s\", attempting to add it.\n", pg, fmri);
219 		free(fmri);
220 
221 		if (scf_instance_add_pg(inst, pg, SCF_GROUP_FRAMEWORK, 0,
222 		    gpg) == -1) {
223 			switch (scf_error()) {
224 			case SCF_ERROR_EXISTS:
225 				break;
226 			case SCF_ERROR_PERMISSION_DENIED:
227 				uu_die(gettext("Error: Permission denied.\n"));
228 			default:
229 				scfdie();
230 			}
231 		}
232 	}
233 
234 	if (scf_pg_get_property(gpg, prop, eprop) == -1) {
235 		if (scf_error() != SCF_ERROR_NOT_FOUND)
236 			scfdie();
237 
238 		create = 1;
239 	}
240 
241 	if ((tx = scf_transaction_create(h)) == NULL ||
242 	    (ent = scf_entry_create(h)) == NULL)
243 		scfdie();
244 
245 	do {
246 		uu_list_t	*sv_list;
247 
248 		if (scf_transaction_start(tx, gpg) == -1) {
249 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
250 				scfdie();
251 
252 			uu_die(gettext("Error: Permission denied.\n"));
253 		}
254 
255 		/* Modify the property or create it if it is missing */
256 		if (create)
257 			ret = scf_transaction_property_new(tx, ent, prop, type);
258 		else
259 			ret = scf_transaction_property_change_type(tx, ent,
260 			    prop, type);
261 		if (ret == -1)
262 			scfdie();
263 
264 		switch (type) {
265 		case SCF_TYPE_BOOLEAN:
266 			scf_value_set_boolean(v, *(uint8_t *)value);
267 			break;
268 		case SCF_TYPE_INTEGER:
269 			scf_value_set_integer(v, *(int64_t *)value);
270 			break;
271 		case SCF_TYPE_ASTRING:
272 			if (strcmp(prop, PR_PROTO_NAME) == 0) {
273 				add_proto_list(ent, h, (char **)value,
274 				    &sv_list);
275 			} else if (scf_value_set_astring(v, value) == -1) {
276 				scfdie();
277 			}
278 			break;
279 		default:
280 			uu_die(gettext("Error: Internal inetadm error"));
281 		}
282 
283 		if ((strcmp(prop, PR_PROTO_NAME) != 0) &&
284 		    (scf_entry_add_value(ent, v) == -1))
285 			scfdie();
286 
287 		ret = scf_transaction_commit(tx);
288 		if (ret == -1) {
289 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
290 				scfdie();
291 
292 			uu_die(gettext("Error: Permission denied.\n"));
293 		}
294 
295 		scf_transaction_reset(tx);
296 
297 		if (ret == 0) {
298 			if (scf_pg_update(gpg) == -1)
299 				scfdie();
300 		}
301 
302 		if (strcmp(prop, PR_PROTO_NAME) == 0)
303 			remove_proto_list(ent, sv_list);
304 
305 	} while (ret == 0);
306 
307 	scf_value_destroy(v);
308 	scf_entry_destroy(ent);
309 	scf_transaction_destroy(tx);
310 	scf_property_destroy(eprop);
311 	scf_pg_destroy(gpg);
312 }
313 
314 /*
315  * delete_prop takes an instance, property group name and property, and
316  * deletes the specified property from the repository.
317  */
318 
319 static void
320 delete_prop(const scf_instance_t *inst, const char *pg, const char *prop)
321 {
322 	scf_transaction_t		*tx;
323 	scf_transaction_entry_t		*ent;
324 	scf_propertygroup_t		*gpg;
325 	scf_property_t			*eprop;
326 	int				ret;
327 
328 	if ((gpg = scf_pg_create(h)) == NULL ||
329 	    (eprop = scf_property_create(h)) == NULL ||
330 	    (tx = scf_transaction_create(h)) == NULL ||
331 	    (ent = scf_entry_create(h)) == NULL)
332 		scfdie();
333 
334 	if (scf_instance_get_pg(inst, pg, gpg) != SCF_SUCCESS) {
335 		if (scf_error() != SCF_ERROR_NOT_FOUND)
336 			scfdie();
337 
338 		uu_die(gettext("Error: \"%s\" property group missing.\n"), pg);
339 	}
340 
341 	do {
342 		if (scf_transaction_start(tx, gpg) != SCF_SUCCESS) {
343 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
344 				scfdie();
345 
346 			uu_die(gettext("Error: Permission denied.\n"));
347 		}
348 
349 		if (scf_transaction_property_delete(tx, ent,
350 		    prop) != SCF_SUCCESS) {
351 			if (scf_error() != SCF_ERROR_NOT_FOUND)
352 				scfdie();
353 
354 			uu_die(
355 			    gettext("Error: \"%s\" property does not exist.\n"),
356 			    prop);
357 		}
358 
359 		ret = scf_transaction_commit(tx);
360 		if (ret < 0) {
361 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
362 				scfdie();
363 
364 			uu_die(gettext("Error: Permission denied.\n"));
365 		}
366 		if (ret == 0) {
367 			scf_transaction_reset(tx);
368 			if (scf_pg_update(gpg) == -1)
369 				scfdie();
370 		}
371 	} while (ret == 0);
372 
373 	(void) scf_entry_destroy(ent);
374 	scf_transaction_destroy(tx);
375 	scf_property_destroy(eprop);
376 	scf_pg_destroy(gpg);
377 }
378 
379 /*
380  * commit_props evaluates an entire property list that has been created
381  * based on command line options, and either deletes or modifies properties
382  * as requested.
383  */
384 
385 static void
386 commit_props(const scf_instance_t *inst, inetd_prop_t *mod, boolean_t defaults)
387 {
388 	int			i;
389 	uint8_t			new_bool;
390 
391 	for (i = 0; mod[i].ip_name != NULL; i++) {
392 		switch (mod[i].ip_error) {
393 		case IVE_UNSET:
394 			break;
395 		case IVE_INVALID:
396 			delete_prop(inst, mod[i].ip_pg, mod[i].ip_name);
397 			break;
398 		case IVE_VALID:
399 			switch (mod[i].ip_type) {
400 			case INET_TYPE_STRING:
401 				modify_prop(inst,
402 				    defaults ? PG_NAME_SERVICE_DEFAULTS :
403 				    mod[i].ip_pg, mod[i].ip_name,
404 				    SCF_TYPE_ASTRING,
405 				    mod[i].ip_value.iv_string);
406 				break;
407 			case INET_TYPE_STRING_LIST:
408 				modify_prop(inst,
409 				    defaults ? PG_NAME_SERVICE_DEFAULTS :
410 				    mod[i].ip_pg, mod[i].ip_name,
411 				    SCF_TYPE_ASTRING,
412 				    mod[i].ip_value.iv_string_list);
413 				break;
414 			case INET_TYPE_INTEGER:
415 				modify_prop(inst,
416 				    defaults ? PG_NAME_SERVICE_DEFAULTS :
417 				    mod[i].ip_pg, mod[i].ip_name,
418 				    SCF_TYPE_INTEGER, &mod[i].ip_value.iv_int);
419 				break;
420 			case INET_TYPE_BOOLEAN:
421 				new_bool = (mod[i].ip_value.iv_boolean) ? 1 : 0;
422 
423 				modify_prop(inst,
424 				    defaults ? PG_NAME_SERVICE_DEFAULTS :
425 				    mod[i].ip_pg, mod[i].ip_name,
426 				    SCF_TYPE_BOOLEAN, &new_bool);
427 				break;
428 			}
429 		}
430 	}
431 }
432 
433 /*
434  * list_callback is the callback function to be handed to simple_walk_instances
435  * in list_services.  It is called once on every instance on a machine.  If
436  * that instance is controlled by inetd, it prints enabled/disabled, state,
437  * and instance FMRI.
438  */
439 
440 /*ARGSUSED*/
441 static int
442 list_callback(scf_handle_t *hin, scf_instance_t *inst, void *buf)
443 {
444 	ssize_t			max_name_length;
445 	char			*inst_name;
446 	scf_simple_prop_t	*prop = NULL, *prop2 = NULL;
447 	const uint8_t		*enabled;
448 	const char		*state, *restart_str;
449 
450 	max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
451 	if ((inst_name = malloc(max_name_length + 1)) == NULL)
452 		uu_die(gettext("Error: Out of memory.\n"));
453 
454 	/*
455 	 * Get the FMRI of the instance, and check if its delegated restarter
456 	 * is inetd.
457 	 */
458 
459 	if (scf_instance_to_fmri(inst, inst_name, max_name_length + 1) < 0)
460 		return (SCF_FAILED);
461 
462 	if ((prop = scf_simple_prop_get(hin, inst_name, SCF_PG_GENERAL,
463 	    SCF_PROPERTY_RESTARTER)) == NULL)
464 		goto out;
465 
466 	if ((restart_str = scf_simple_prop_next_ustring(prop)) == NULL)
467 		goto out;
468 
469 	if (strstr(restart_str, INETADM_INETD_STR) == NULL)
470 		goto out;
471 
472 	/* Free restarter prop so it can be reused below */
473 	scf_simple_prop_free(prop);
474 
475 	/*
476 	 * We know that this instance is managed by inetd.
477 	 * Now get the enabled and state properties.
478 	 */
479 
480 	if (((prop = scf_simple_prop_get(hin, inst_name, SCF_PG_GENERAL,
481 	    SCF_PROPERTY_ENABLED)) == NULL) ||
482 	    ((enabled = scf_simple_prop_next_boolean(prop)) == NULL)) {
483 		(void) uu_warn(gettext("Error: Instance %s is missing enabled "
484 		    "property.\n"), inst_name);
485 		goto out;
486 	}
487 
488 	if (((prop2 = scf_simple_prop_get(hin, inst_name, SCF_PG_RESTARTER,
489 	    SCF_PROPERTY_STATE)) == NULL) ||
490 	    ((state = scf_simple_prop_next_astring(prop2)) == NULL)) {
491 		(void) uu_warn(gettext("Error: Instance %s is missing state "
492 		    "property.\n"), inst_name);
493 		goto out;
494 	}
495 
496 	/* Print enabled/disabled, state, and FMRI for the instance. */
497 
498 	if (*enabled)
499 		(void) printf("%-10s%-15s%s\n", INETADM_ENABLED_STR, state,
500 		    inst_name);
501 	else
502 		(void) printf("%-10s%-15s%s\n", INETADM_DISABLED_STR, state,
503 		    inst_name);
504 
505 out:
506 	free(inst_name);
507 	scf_simple_prop_free(prop);
508 	scf_simple_prop_free(prop2);
509 	return (SCF_SUCCESS);
510 }
511 
512 /*
513  * list_services calls list_callback on every instance on the machine.
514  */
515 
516 static void
517 list_services()
518 {
519 	(void) printf("%-10s%-15s%s\n", "ENABLED", "STATE", "FMRI");
520 
521 	if (scf_simple_walk_instances(SCF_STATE_ALL, NULL, list_callback) ==
522 	    SCF_FAILED)
523 		scfdie();
524 }
525 
526 static void
527 print_prop_val(inetd_prop_t *prop)
528 {
529 	switch (prop->ip_type) {
530 	case INET_TYPE_STRING:
531 		(void) printf("\"%s\"\n", prop->ip_value.iv_string);
532 		break;
533 	case INET_TYPE_STRING_LIST:
534 		{
535 			int	j = 0;
536 			char	**cpp = prop->ip_value.iv_string_list;
537 
538 			/*
539 			 * Print string list as comma separated list.
540 			 */
541 
542 			(void) printf("\"%s", cpp[j]);
543 			while (cpp[++j] != NULL)
544 				(void) printf(",%s", cpp[j]);
545 			(void) printf("\"\n");
546 		}
547 		break;
548 	case INET_TYPE_INTEGER:
549 		(void) printf("%lld\n", prop->ip_value.iv_int);
550 		break;
551 	case INET_TYPE_BOOLEAN:
552 		if (prop->ip_value.iv_boolean)
553 			(void) printf("%s\n", INETADM_TRUE_STR);
554 		else
555 			(void) printf("%s\n", INETADM_FALSE_STR);
556 		break;
557 	}
558 }
559 
560 /*
561  * list_props_cb is a callback function for scf_walk_fmri that lists all
562  * relevant inetd properties for an instance managed by inetd.
563  */
564 
565 /* ARGSUSED0 */
566 static int
567 list_props_cb(void *data, scf_walkinfo_t *wip)
568 {
569 	int			i;
570 	const char		*instname = wip->fmri;
571 	scf_simple_prop_t	*prop;
572 	inetd_prop_t		*proplist;
573 	const char		*restart_str;
574 	boolean_t		is_rpc = B_FALSE;
575 	size_t			numprops;
576 	scf_handle_t		*h;
577 	scf_error_t		err;
578 
579 	if (((h = scf_handle_create(SCF_VERSION)) == NULL) ||
580 	    (scf_handle_bind(h) == -1))
581 		scfdie();
582 
583 	/*
584 	 * Get the property that holds the name of this instance's
585 	 * restarter, and make sure that it is inetd.
586 	 */
587 	if ((prop = scf_simple_prop_get(h, instname, SCF_PG_GENERAL,
588 	    SCF_PROPERTY_RESTARTER)) == NULL) {
589 		if (scf_error() == SCF_ERROR_NOT_FOUND)
590 			uu_die(gettext("Error: Specified service instance "
591 			    "\"%s\" has no restarter property.  inetd is not "
592 			    "the delegated restarter of this instance.\n"),
593 			    instname);
594 		if (scf_error() == SCF_ERROR_INVALID_ARGUMENT)
595 			uu_die(gettext("Error: \"%s\" is not a valid service "
596 			    "instance.\n"), instname);
597 
598 		scfdie();
599 	}
600 
601 	if (((restart_str = scf_simple_prop_next_ustring(prop)) == NULL) ||
602 	    (strstr(restart_str, INETADM_INETD_STR) == NULL)) {
603 		uu_die(gettext("Error: inetd is not the delegated restarter of "
604 		    "specified service instance \"%s\".\n"), instname);
605 	}
606 
607 	scf_simple_prop_free(prop);
608 
609 	/*
610 	 * This instance is controlled by inetd, so now we display all
611 	 * of its properties.  First the mandatory properties, and then
612 	 * the properties that have default values, substituting the
613 	 * default values inherited from inetd as necessary (this is done
614 	 * for us by read_instance_props()).
615 	 */
616 
617 	if ((proplist = read_instance_props(h, instname, &numprops, &err)) ==
618 	    NULL) {
619 		uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
620 		    scf_strerror(err));
621 	}
622 	scf_handle_destroy(h);
623 
624 	(void) printf("%-9s%s\n", "SCOPE", "NAME=VALUE");
625 
626 	for (i = 0; i < numprops; i++) {
627 		/* Skip rpc version properties if it's not an RPC service */
628 		if ((strcmp(PR_RPC_LW_VER_NAME, proplist[i].ip_name) == 0) ||
629 		    (strcmp(PR_RPC_HI_VER_NAME, proplist[i].ip_name) == 0))
630 			if (!is_rpc)
631 				continue;
632 
633 		/* If it's not an unset property, print it out. */
634 		if (proplist[i].ip_error != IVE_UNSET) {
635 			if (strcmp(PR_ISRPC_NAME, proplist[i].ip_name) == 0)
636 				is_rpc = proplist[i].ip_value.iv_boolean;
637 
638 			(void) printf("%-9s%s=",
639 			    proplist[i].from_inetd ? INETADM_DEFAULT_STR : "",
640 			    proplist[i].ip_name);
641 			print_prop_val(&proplist[i]);
642 			continue;
643 		}
644 
645 		/* arg0 is non-default, but also doesn't have to be set. */
646 
647 		if (i == PT_ARG0_INDEX)
648 			continue;
649 
650 		/* all other properties should have values. */
651 		if (proplist[i].ip_default) {
652 			(void) uu_warn(gettext("Error: Property %s is missing "
653 			    "and has no defined default value.\n"),
654 			    proplist[i].ip_name);
655 		} else {
656 			(void) uu_warn(gettext("Error: Required property %s is "
657 			    "missing.\n"), proplist[i].ip_name);
658 		}
659 	}
660 
661 	free_instance_props(proplist);
662 	return (0);
663 }
664 
665 /*
666  * set_svc_enable_cb is a callback function for scf_walk_fmri that sets the
667  * enabled property in the repository for an instance based on the value given
668  * by 'data'.
669  */
670 
671 static int
672 set_svc_enable_cb(void *data, scf_walkinfo_t *wip)
673 {
674 	uint8_t			desired = *(uint8_t *)data;
675 	const char		*instname = wip->fmri;
676 
677 	if (desired) {
678 		if (smf_enable_instance(instname, 0) == 0)
679 			return (0);
680 	} else {
681 		if (smf_disable_instance(instname, 0) == 0)
682 			return (0);
683 	}
684 
685 	switch (scf_error()) {
686 	case SCF_ERROR_INVALID_ARGUMENT:
687 		uu_die(gettext("Error: \"%s\" is not a valid service "
688 		    "instance.\n"), instname);
689 		break;
690 	case SCF_ERROR_NOT_FOUND:
691 		uu_die(gettext("Error: Service instance \"%s\" not found.\n"),
692 		    instname);
693 		break;
694 	default:
695 		scfdie();
696 	}
697 
698 	return (0);
699 }
700 
701 /*
702  * list_defaults lists all the default property values being provided by
703  * inetd.
704  */
705 
706 static void
707 list_defaults()
708 {
709 	scf_handle_t		*h;
710 	scf_error_t		err;
711 	int			i;
712 	inetd_prop_t		*proptable;
713 	size_t			numprops;
714 
715 	if (((h = scf_handle_create(SCF_VERSION)) == NULL) ||
716 	    (scf_handle_bind(h) == -1))
717 		scfdie();
718 
719 	if ((proptable = read_default_props(h, &numprops, &err)) == NULL) {
720 		uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
721 		    scf_strerror(err));
722 	}
723 
724 	(void) printf("NAME=VALUE\n");
725 
726 	for (i = 0; i < numprops; i++) {
727 		if (!proptable[i].ip_default)
728 			continue;
729 
730 		if (proptable[i].ip_error == IVE_UNSET) {
731 			(void) uu_warn(gettext("Error: Default property %s "
732 			    "missing.\n"), proptable[i].ip_name);
733 			continue;
734 		}
735 
736 		(void) printf("%s=", proptable[i].ip_name);
737 		print_prop_val(&proptable[i]);
738 	}
739 
740 	free_instance_props(proptable);
741 }
742 
743 /*
744  * modify_inst_props_cb is a callback function for scf_walk_fmri that modifies
745  * the properties that are given as name=value pairs on the command line
746  * to the requested value.
747  */
748 
749 static int
750 modify_inst_props_cb(void *data, scf_walkinfo_t *wip)
751 {
752 	int			i, j;
753 	char			*value;
754 	const char		*fmri = wip->fmri;
755 	scf_instance_t		*inst = wip->inst;
756 	inetd_prop_t		*mod, *prop_table;
757 	size_t			numprops;
758 	ssize_t			max_val;
759 	int64_t			new_int;
760 	int			argc = ((arglist_t *)data)->argc;
761 	char			**argv = ((arglist_t *)data)->argv;
762 
763 	if ((max_val = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) < 0)
764 		scfdie();
765 
766 	prop_table = get_prop_table(&numprops);
767 
768 	if ((mod = malloc(numprops * sizeof (inetd_prop_t))) == NULL)
769 		uu_die(gettext("Error: Out of memory.\n"));
770 
771 	(void) memcpy(mod, prop_table, numprops * sizeof (inetd_prop_t));
772 
773 	/*
774 	 * For each property to be changed, look up the property name in the
775 	 * property table.  Change each property in the mod array, and then
776 	 * write the entire thing back.
777 	 */
778 	for (i = 0; i < argc; i++) {
779 		/* Separate argument into name and value pair */
780 		if ((value = strchr(argv[i], '=')) == NULL)
781 			uu_die(gettext("Error: Malformed name=value pair "
782 			    "\"%s\"\n"), argv[i]);
783 
784 		*value = '\0';
785 		value++;
786 
787 		/* Find property name in array of properties */
788 		for (j = 0; mod[j].ip_name != NULL; j++) {
789 			if (strcmp(mod[j].ip_name, argv[i]) == 0)
790 				break;
791 		}
792 
793 		if (mod[j].ip_name == NULL)
794 			uu_die(gettext("Error: \"%s\" is not a valid "
795 			    "property.\n"), argv[i]);
796 
797 		if (*value == '\0') {
798 			if ((mod[j].ip_default) || (j == PT_ARG0_INDEX)) {
799 				/* mark property for deletion */
800 				mod[j].ip_error = IVE_INVALID;
801 
802 				/* return the '=' taken out above */
803 				*(--value) = '=';
804 
805 				continue;
806 			} else {
807 				uu_die(gettext("\"%s\" is a mandatory "
808 				    "property and can not be deleted.\n"),
809 				    argv[i]);
810 			}
811 		}
812 
813 		switch (mod[j].ip_type) {
814 		case INET_TYPE_INTEGER:
815 			if (uu_strtoint(value, &new_int, sizeof (new_int), NULL,
816 			    NULL, NULL) == -1)
817 				uu_die(gettext("Error: \"%s\" is not a valid "
818 				    "integer value.\n"), value);
819 
820 			mod[j].ip_value.iv_int = new_int;
821 			break;
822 		case INET_TYPE_STRING:
823 			if (strlen(value) >= max_val) {
824 				uu_die(gettext("Error: String value is longer "
825 				    "than %l characters.\n"), max_val);
826 			} else if ((mod[j].ip_value.iv_string = strdup(value))
827 			    == NULL) {
828 				uu_die(gettext("Error: Out of memory.\n"));
829 			}
830 			break;
831 		case INET_TYPE_STRING_LIST:
832 			if ((mod[j].ip_value.iv_string_list =
833 			    get_protos(value)) == NULL) {
834 				if (errno == ENOMEM) {
835 					uu_die(gettext(
836 					    "Error: Out of memory.\n"));
837 				} else if (errno == E2BIG) {
838 					uu_die(gettext(
839 					    "Error: String value in "
840 					    "%s property longer than "
841 					    "%l characters.\n"),
842 					    PR_PROTO_NAME, max_val);
843 				} else {
844 					uu_die(gettext(
845 					    "Error: No values "
846 					    "specified for %s "
847 					    "property.\n"),
848 					    PR_PROTO_NAME);
849 				}
850 			}
851 			break;
852 		case INET_TYPE_BOOLEAN:
853 			if (strcasecmp(value, INETADM_TRUE_STR) == 0)
854 				mod[j].ip_value.iv_boolean = B_TRUE;
855 			else if (strcasecmp(value, INETADM_FALSE_STR) == 0)
856 				mod[j].ip_value.iv_boolean = B_FALSE;
857 			else
858 				uu_die(gettext("Error: \"%s\" is not a valid "
859 				    "boolean value. (TRUE or FALSE)\n"), value);
860 		}
861 		/* mark property for modification */
862 		mod[j].ip_error = IVE_VALID;
863 
864 		/* return the '=' taken out above */
865 		*(--value) = '=';
866 	}
867 
868 	commit_props(inst, mod, B_FALSE);
869 	free(mod);
870 	if (smf_refresh_instance(fmri) != 0)
871 		uu_die(gettext("Error: Unable to refresh instance %s.\n"),
872 		    fmri);
873 
874 	return (0);
875 }
876 
877 /*
878  * modify_defaults takes n name=value pairs for inetd default property values,
879  * parses them, and then modifies the values in the repository.
880  */
881 
882 static void
883 modify_defaults(int argc, char *argv[])
884 {
885 	int			i, j;
886 	char			*value;
887 	scf_instance_t		*inst;
888 	inetd_prop_t		*mod, *prop_table;
889 	size_t			numprops;
890 	ssize_t			max_val;
891 	int64_t			new_int;
892 
893 	if ((inst = scf_instance_create(h)) == NULL)
894 		scfdie();
895 
896 	if (scf_handle_decode_fmri(h, INETD_INSTANCE_FMRI, NULL, NULL,
897 	    inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
898 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
899 			uu_die(gettext("inetd instance missing in repository."
900 			    "\n"));
901 		} else {
902 			scfdie();
903 		}
904 	}
905 
906 	if ((max_val = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) < 0)
907 		scfdie();
908 
909 	prop_table = get_prop_table(&numprops);
910 
911 	if ((mod = malloc(numprops * sizeof (inetd_prop_t))) == NULL)
912 		uu_die(gettext("Error: Out of memory.\n"));
913 
914 	(void) memcpy(mod, prop_table, numprops * sizeof (inetd_prop_t));
915 
916 	for (i = 0; i < argc; i++) {
917 		/* Separate argument into name and value pair */
918 		if ((value = strchr(argv[i], '=')) == NULL)
919 			uu_die(gettext("Error: Malformed name=value pair \"%s"
920 			    "\"\n"), argv[i]);
921 
922 		*value = '\0';
923 		value++;
924 
925 		/* Find property name in array of defaults */
926 		for (j = 0; mod[j].ip_name != NULL; j++) {
927 			if (!mod[j].ip_default)
928 				continue;
929 			if (strcmp(mod[j].ip_name, argv[i]) == 0)
930 				break;
931 		}
932 
933 		if (mod[j].ip_name == NULL)
934 			uu_die(gettext("Error: \"%s\" is not a default inetd "
935 			    "property.\n"), argv[i]);
936 
937 		if (*value == '\0')
938 			uu_die(gettext("Cannot accept NULL values for default "
939 			    "properties.\n"));
940 
941 		switch (mod[j].ip_type) {
942 		case INET_TYPE_INTEGER:
943 			if (uu_strtoint(value, &new_int, sizeof (new_int), NULL,
944 			    NULL, NULL) == -1)
945 				uu_die(gettext("Error: \"%s\" is not a valid "
946 				    "integer value.\n"), value);
947 
948 			mod[j].ip_value.iv_int = new_int;
949 			break;
950 		case INET_TYPE_STRING:
951 			if (strlen(value) >= max_val)
952 				uu_die(gettext("Error: String value is longer "
953 				    "than %l characters.\n"), max_val);
954 			if ((mod[j].ip_value.iv_string = strdup(value))
955 			    == NULL)
956 				uu_die(gettext("Error: Out of memory.\n"));
957 			break;
958 		case INET_TYPE_BOOLEAN:
959 			if (strcasecmp(value, INETADM_TRUE_STR) == 0)
960 				mod[j].ip_value.iv_boolean = B_TRUE;
961 			else if (strcasecmp(value, INETADM_FALSE_STR) == 0)
962 				mod[j].ip_value.iv_boolean = B_FALSE;
963 			else
964 				uu_die(gettext("Error: \"%s\" is not a valid "
965 				    "boolean value. (TRUE or FALSE)\n"), value);
966 		}
967 		/* mark property for modification */
968 		mod[j].ip_error = IVE_VALID;
969 	}
970 
971 	commit_props(inst, mod, B_TRUE);
972 	free(mod);
973 	scf_instance_destroy(inst);
974 	if (refresh_inetd() != 0)
975 		uu_warn(gettext("Warning: Unable to refresh inetd.\n"));
976 }
977 
978 int
979 main(int argc, char *argv[])
980 {
981 	int		opt;
982 	uint_t		lflag, eflag, dflag, pflag, mflag, Mflag;
983 	uint8_t		enable;
984 	scf_error_t	serr;
985 	int		exit_status = 0;
986 
987 	(void) setlocale(LC_ALL, "");
988 	(void) textdomain(TEXT_DOMAIN);
989 
990 	if ((h = scf_handle_create(SCF_VERSION)) == NULL)
991 		scfdie();
992 
993 	if (scf_handle_bind(h) == -1)
994 		uu_die(gettext("Error: Couldn't bind to svc.configd.\n"));
995 
996 	if (argc == 1) {
997 		list_services();
998 		goto out;
999 	}
1000 
1001 	lflag = eflag = dflag = pflag = mflag = Mflag = 0;
1002 	while ((opt = getopt(argc, argv, "ledpMm?")) != -1) {
1003 		switch (opt) {
1004 		case 'l':
1005 			lflag = 1;
1006 			break;
1007 		case 'e':
1008 			eflag = 1;
1009 			break;
1010 		case 'd':
1011 			dflag = 1;
1012 			break;
1013 		case 'p':
1014 			pflag = 1;
1015 			break;
1016 		case 'M':
1017 			Mflag = 1;
1018 			break;
1019 		case 'm':
1020 			mflag = 1;
1021 			break;
1022 		case '?':
1023 			if (optopt == '?') {
1024 				usage(B_TRUE);
1025 				goto out;
1026 			} else {
1027 				usage(B_FALSE);
1028 			}
1029 		default:
1030 			usage(B_FALSE);
1031 		}
1032 	}
1033 
1034 	/*
1035 	 * All options are mutually exclusive, and we must have an option
1036 	 * if we reached here.
1037 	 */
1038 	if (lflag + eflag + dflag + pflag + mflag + Mflag != 1)
1039 		usage(B_FALSE);
1040 
1041 	argv += optind;
1042 	argc -= optind;
1043 	if ((pflag == 0) && (argc == 0))
1044 		usage(B_FALSE);
1045 
1046 	serr = 0;
1047 	if (lflag) {
1048 		serr = scf_walk_fmri(h, argc, argv, 0, list_props_cb, NULL,
1049 		    &exit_status, uu_warn);
1050 	} else if (dflag) {
1051 		enable = 0;
1052 		serr = scf_walk_fmri(h, argc, argv, 0, set_svc_enable_cb,
1053 		    &enable, &exit_status, uu_warn);
1054 	} else if (eflag) {
1055 		enable = 1;
1056 		serr = scf_walk_fmri(h, argc, argv, 0, set_svc_enable_cb,
1057 		    &enable, &exit_status, uu_warn);
1058 	} else if (mflag) {
1059 		arglist_t	args;
1060 		char		**cpp = argv;
1061 		uint_t		fmri_args = 0;
1062 
1063 		/* count number of fmri arguments */
1064 		while ((fmri_args < argc) && (strchr(*cpp, '=') == NULL)) {
1065 			fmri_args++;
1066 			cpp++;
1067 		}
1068 
1069 		/* if no x=y args or no fmri, show usage */
1070 		if ((fmri_args == argc) || (fmri_args == 0))
1071 			usage(B_FALSE);
1072 
1073 		/* setup args for modify_inst_props_cb */
1074 		args.argc = argc - fmri_args;
1075 		args.argv = argv + fmri_args;
1076 
1077 		serr = scf_walk_fmri(h, fmri_args, argv, 0,
1078 		    modify_inst_props_cb, &args, &exit_status, uu_warn);
1079 	} else if (Mflag) {
1080 		modify_defaults(argc, argv);
1081 	} else if (pflag) {
1082 		/* ensure there's no trailing garbage */
1083 		if (argc != 0)
1084 			usage(B_FALSE);
1085 		list_defaults();
1086 	}
1087 	if (serr != 0) {
1088 		uu_warn(gettext("failed to iterate over instances: %s"),
1089 		    scf_strerror(serr));
1090 		exit(UU_EXIT_FATAL);
1091 	}
1092 
1093 out:
1094 	(void) scf_handle_unbind(h);
1095 	scf_handle_destroy(h);
1096 
1097 	return (exit_status);
1098 }
1099