xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/inetadm/inetadm.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
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 2004 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 	size_t			numprops;
391 
392 	(void) get_prop_table(&numprops);
393 
394 	for (i = 0; i < numprops; i++) {
395 		switch (mod[i].ip_error) {
396 		case IVE_UNSET:
397 			break;
398 		case IVE_INVALID:
399 			delete_prop(inst, mod[i].ip_pg, mod[i].ip_name);
400 			break;
401 		case IVE_VALID:
402 			switch (mod[i].ip_type) {
403 			case SCF_TYPE_ASTRING:
404 				modify_prop(inst,
405 				    defaults ? PG_NAME_SERVICE_DEFAULTS :
406 				    mod[i].ip_pg, mod[i].ip_name,
407 				    SCF_TYPE_ASTRING,
408 				    (i == PT_PROTO_INDEX) ?
409 				    (void *)mod[i].ip_value.iv_proto_list :
410 				    (void *)mod[i].ip_value.iv_astring);
411 				break;
412 			case SCF_TYPE_INTEGER:
413 				modify_prop(inst,
414 				    defaults ? PG_NAME_SERVICE_DEFAULTS :
415 				    mod[i].ip_pg, mod[i].ip_name,
416 				    SCF_TYPE_INTEGER, &mod[i].ip_value.iv_int);
417 				break;
418 			case SCF_TYPE_BOOLEAN:
419 				new_bool = (mod[i].ip_value.iv_boolean) ? 1 : 0;
420 
421 				modify_prop(inst,
422 				    defaults ? PG_NAME_SERVICE_DEFAULTS :
423 				    mod[i].ip_pg, mod[i].ip_name,
424 				    SCF_TYPE_BOOLEAN, &new_bool);
425 				break;
426 			}
427 		}
428 	}
429 }
430 
431 /*
432  * list_callback is the callback function to be handed to simple_walk_instances
433  * in list_services.  It is called once on every instance on a machine.  If
434  * that instance is controlled by inetd, it prints enabled/disabled, state,
435  * and instance FMRI.
436  */
437 
438 /*ARGSUSED*/
439 static int
440 list_callback(scf_handle_t *hin, scf_instance_t *inst, void *buf)
441 {
442 	ssize_t			max_name_length;
443 	char			*inst_name;
444 	scf_simple_prop_t	*prop = NULL, *prop2 = NULL;
445 	const uint8_t		*enabled;
446 	const char		*state, *restart_str;
447 
448 	max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
449 	if ((inst_name = malloc(max_name_length + 1)) == NULL)
450 		uu_die(gettext("Error: Out of memory.\n"));
451 
452 	/*
453 	 * Get the FMRI of the instance, and check if its delegated restarter
454 	 * is inetd.
455 	 */
456 
457 	if (scf_instance_to_fmri(inst, inst_name, max_name_length + 1) < 0)
458 		return (SCF_FAILED);
459 
460 	if ((prop = scf_simple_prop_get(hin, inst_name, SCF_PG_GENERAL,
461 	    SCF_PROPERTY_RESTARTER)) == NULL)
462 		goto out;
463 
464 	if ((restart_str = scf_simple_prop_next_ustring(prop)) == NULL)
465 		goto out;
466 
467 	if (strstr(restart_str, INETADM_INETD_STR) == NULL)
468 		goto out;
469 
470 	/* Free restarter prop so it can be reused below */
471 	scf_simple_prop_free(prop);
472 
473 	/*
474 	 * We know that this instance is managed by inetd.
475 	 * Now get the enabled and state properties.
476 	 */
477 
478 	if (((prop = scf_simple_prop_get(hin, inst_name, SCF_PG_GENERAL,
479 	    SCF_PROPERTY_ENABLED)) == NULL) ||
480 	    ((enabled = scf_simple_prop_next_boolean(prop)) == NULL)) {
481 		(void) uu_warn(gettext("Error: Instance %s is missing enabled "
482 		    "property.\n"), inst_name);
483 		goto out;
484 	}
485 
486 	if (((prop2 = scf_simple_prop_get(hin, inst_name, SCF_PG_RESTARTER,
487 	    SCF_PROPERTY_STATE)) == NULL) ||
488 	    ((state = scf_simple_prop_next_astring(prop2)) == NULL)) {
489 		(void) uu_warn(gettext("Error: Instance %s is missing state "
490 		    "property.\n"), inst_name);
491 		goto out;
492 	}
493 
494 	/* Print enabled/disabled, state, and FMRI for the instance. */
495 
496 	if (*enabled)
497 		(void) printf("%-10s%-15s%s\n", INETADM_ENABLED_STR, state,
498 		    inst_name);
499 	else
500 		(void) printf("%-10s%-15s%s\n", INETADM_DISABLED_STR, state,
501 		    inst_name);
502 
503 out:
504 	free(inst_name);
505 	scf_simple_prop_free(prop);
506 	scf_simple_prop_free(prop2);
507 	return (SCF_SUCCESS);
508 }
509 
510 /*
511  * list_services calls list_callback on every instance on the machine.
512  */
513 
514 static void
515 list_services()
516 {
517 	(void) printf("%-10s%-15s%s\n", "ENABLED", "STATE", "FMRI");
518 
519 	if (scf_simple_walk_instances(SCF_STATE_ALL, NULL, list_callback) ==
520 	    SCF_FAILED)
521 		scfdie();
522 }
523 
524 static void
525 print_prop_val(int index, inetd_prop_t *prop)
526 {
527 	switch (prop->ip_type) {
528 	case SCF_TYPE_ASTRING:
529 		if (index == PT_PROTO_INDEX) {
530 			int	j = 0;
531 			char	**cpp = prop->ip_value.iv_proto_list;
532 
533 			/*
534 			 * Print proto string array as comma separated list.
535 			 */
536 
537 			(void) printf("\"%s", cpp[j]);
538 			while (cpp[++j] != NULL)
539 				(void) printf(",%s", cpp[j]);
540 			(void) printf("\"\n");
541 		} else {
542 			(void) printf("\"%s\"\n",
543 			    prop->ip_value.iv_astring);
544 		}
545 		break;
546 	case SCF_TYPE_INTEGER:
547 		(void) printf("%lld\n", prop->ip_value.iv_int);
548 		break;
549 	case SCF_TYPE_BOOLEAN:
550 		if (prop->ip_value.iv_boolean)
551 			(void) printf("%s\n", INETADM_TRUE_STR);
552 		else
553 			(void) printf("%s\n", INETADM_FALSE_STR);
554 		break;
555 	}
556 }
557 
558 /*
559  * list_props_cb is a callback function for scf_walk_fmri that lists all
560  * relevant inetd properties for an instance managed by inetd.
561  */
562 
563 /* ARGSUSED0 */
564 static int
565 list_props_cb(void *data, scf_walkinfo_t *wip)
566 {
567 	int			i;
568 	const char		*instname = wip->fmri;
569 	scf_simple_prop_t	*prop;
570 	inetd_prop_t		*proplist;
571 	const char		*restart_str;
572 	boolean_t		is_rpc = B_FALSE;
573 	size_t			numprops;
574 	scf_handle_t		*h;
575 	scf_error_t		err;
576 
577 	if (((h = scf_handle_create(SCF_VERSION)) == NULL) ||
578 	    (scf_handle_bind(h) == -1))
579 		scfdie();
580 
581 	/*
582 	 * Get the property that holds the name of this instance's
583 	 * restarter, and make sure that it is inetd.
584 	 */
585 	if ((prop = scf_simple_prop_get(h, instname, SCF_PG_GENERAL,
586 	    SCF_PROPERTY_RESTARTER)) == NULL) {
587 		if (scf_error() == SCF_ERROR_NOT_FOUND)
588 			uu_die(gettext("Error: Specified service instance "
589 			    "\"%s\" has no restarter property.  inetd is not "
590 			    "the delegated restarter of this instance.\n"),
591 			    instname);
592 		if (scf_error() == SCF_ERROR_INVALID_ARGUMENT)
593 			uu_die(gettext("Error: \"%s\" is not a valid service "
594 			    "instance.\n"), instname);
595 
596 		scfdie();
597 	}
598 
599 	if (((restart_str = scf_simple_prop_next_ustring(prop)) == NULL) ||
600 	    (strstr(restart_str, INETADM_INETD_STR) == NULL)) {
601 		uu_die(gettext("Error: inetd is not the delegated restarter of "
602 		    "specified service instance \"%s\".\n"), instname);
603 	}
604 
605 	scf_simple_prop_free(prop);
606 
607 	/*
608 	 * This instance is controlled by inetd, so now we display all
609 	 * of its properties.  First the mandatory properties, and then
610 	 * the properties that have default values, substituting the
611 	 * default values inherited from inetd as necessary (this is done
612 	 * for us by read_instance_props()).
613 	 */
614 
615 	if ((proplist = read_instance_props(h, instname, &numprops, &err)) ==
616 	    NULL) {
617 		uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
618 		    scf_strerror(err));
619 	}
620 	scf_handle_destroy(h);
621 
622 	(void) printf("%-9s%s\n", "SCOPE", "NAME=VALUE");
623 
624 	for (i = 0; i < numprops; i++) {
625 		/* Skip rpc version properties if it's not an RPC service */
626 		if ((strcmp(PR_RPC_LW_VER_NAME, proplist[i].ip_name) == 0) ||
627 		    (strcmp(PR_RPC_HI_VER_NAME, proplist[i].ip_name) == 0))
628 			if (!is_rpc)
629 				continue;
630 
631 		/* If it's not an unset property, print it out. */
632 		if (proplist[i].ip_error != IVE_UNSET) {
633 			if (strcmp(PR_ISRPC_NAME, proplist[i].ip_name) == 0)
634 				is_rpc = proplist[i].ip_value.iv_boolean;
635 
636 			(void) printf("%-9s%s=",
637 			    proplist[i].from_inetd ? INETADM_DEFAULT_STR : "",
638 			    proplist[i].ip_name);
639 			print_prop_val(i, &proplist[i]);
640 			continue;
641 		}
642 
643 		/* arg0 is non-default, but also doesn't have to be set. */
644 
645 		if (i == PT_ARG0_INDEX)
646 			continue;
647 
648 		/* all other properties should have values. */
649 		if (proplist[i].ip_default) {
650 			(void) uu_warn(gettext("Error: Property %s is missing "
651 			    "and has no defined default value.\n"),
652 			    proplist[i].ip_name);
653 		} else {
654 			(void) uu_warn(gettext("Error: Required property %s is "
655 			    "missing.\n"), proplist[i].ip_name);
656 		}
657 	}
658 
659 	free_instance_props(proplist);
660 	return (0);
661 }
662 
663 /*
664  * set_svc_enable_cb is a callback function for scf_walk_fmri that sets the
665  * enabled property in the repository for an instance based on the value given
666  * by 'data'.
667  */
668 
669 static int
670 set_svc_enable_cb(void *data, scf_walkinfo_t *wip)
671 {
672 	uint8_t			desired = *(uint8_t *)data;
673 	const char		*instname = wip->fmri;
674 
675 	if (desired) {
676 		if (smf_enable_instance(instname, 0) == 0)
677 			return (0);
678 	} else {
679 		if (smf_disable_instance(instname, 0) == 0)
680 			return (0);
681 	}
682 
683 	switch (scf_error()) {
684 	case SCF_ERROR_INVALID_ARGUMENT:
685 		uu_die(gettext("Error: \"%s\" is not a valid service "
686 		    "instance.\n"), instname);
687 		break;
688 	case SCF_ERROR_NOT_FOUND:
689 		uu_die(gettext("Error: Service instance \"%s\" not found.\n"),
690 		    instname);
691 		break;
692 	default:
693 		scfdie();
694 	}
695 
696 	return (0);
697 }
698 
699 /*
700  * list_defaults lists all the default property values being provided by
701  * inetd.
702  */
703 
704 static void
705 list_defaults()
706 {
707 	scf_handle_t		*h;
708 	scf_error_t		err;
709 	int			i;
710 	inetd_prop_t		*proptable;
711 	size_t			numprops;
712 
713 	if (((h = scf_handle_create(SCF_VERSION)) == NULL) ||
714 	    (scf_handle_bind(h) == -1))
715 		scfdie();
716 
717 	if ((proptable = read_default_props(h, &numprops, &err)) == NULL) {
718 		uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
719 		    scf_strerror(err));
720 	}
721 
722 	(void) printf("NAME=VALUE\n");
723 
724 	for (i = 0; i < numprops; i++) {
725 		if (!proptable[i].ip_default)
726 			continue;
727 
728 		if (proptable[i].ip_error == IVE_UNSET) {
729 			(void) uu_warn(gettext("Error: Default property %s "
730 			    "missing.\n"), proptable[i].ip_name);
731 			continue;
732 		}
733 
734 		(void) printf("%s=", proptable[i].ip_name);
735 		print_prop_val(i, &proptable[i]);
736 	}
737 
738 	free_instance_props(proptable);
739 }
740 
741 /*
742  * modify_inst_props_cb is a callback function for scf_walk_fmri that modifies
743  * the properties that are given as name=value pairs on the command line
744  * to the requested value.
745  */
746 
747 static int
748 modify_inst_props_cb(void *data, scf_walkinfo_t *wip)
749 {
750 	int			i, j;
751 	char			*value;
752 	const char		*fmri = wip->fmri;
753 	scf_instance_t		*inst = wip->inst;
754 	inetd_prop_t		*mod, *prop_table;
755 	size_t			numprops;
756 	ssize_t			max_val;
757 	int64_t			new_int;
758 	int			argc = ((arglist_t *)data)->argc;
759 	char			**argv = ((arglist_t *)data)->argv;
760 
761 	if ((max_val = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) < 0)
762 		scfdie();
763 
764 	prop_table = get_prop_table(&numprops);
765 
766 	if ((mod = malloc(numprops * sizeof (inetd_prop_t))) == NULL)
767 		uu_die(gettext("Error: Out of memory.\n"));
768 
769 	(void) memcpy(mod, prop_table, numprops * sizeof (inetd_prop_t));
770 
771 	/*
772 	 * For each property to be changed, look up the property name in the
773 	 * property table.  Change each property in the mod array, and then
774 	 * write the entire thing back.
775 	 */
776 	for (i = 0; i < argc; i++) {
777 		/* Separate argument into name and value pair */
778 		if ((value = strchr(argv[i], '=')) == NULL)
779 			uu_die(gettext("Error: Malformed name=value pair "
780 			    "\"%s\"\n"), argv[i]);
781 
782 		*value = '\0';
783 		value++;
784 
785 		/* Find property name in array of properties */
786 		for (j = 0; j < numprops; j++) {
787 			if (strcmp(mod[j].ip_name, argv[i]) == 0)
788 				break;
789 		}
790 
791 		if (j >= numprops)
792 			uu_die(gettext("Error: \"%s\" is not a valid "
793 			    "property.\n"), argv[i]);
794 
795 		if (*value == '\0') {
796 			if ((mod[j].ip_default) || (j == PT_ARG0_INDEX)) {
797 				/* mark property for deletion */
798 				mod[j].ip_error = IVE_INVALID;
799 
800 				/* return the '=' taken out above */
801 				*(--value) = '=';
802 
803 				continue;
804 			} else {
805 				uu_die(gettext("\"%s\" is a mandatory "
806 				    "property and can not be deleted.\n"),
807 				    argv[i]);
808 			}
809 		}
810 
811 		switch (mod[j].ip_type) {
812 		case SCF_TYPE_INTEGER:
813 			if (uu_strtoint(value, &new_int, sizeof (new_int), NULL,
814 			    NULL, NULL) == -1)
815 				uu_die(gettext("Error: \"%s\" is not a valid "
816 				    "integer value.\n"), value);
817 
818 			mod[j].ip_value.iv_int = new_int;
819 			break;
820 		case SCF_TYPE_ASTRING:
821 			if (j == PT_PROTO_INDEX) {
822 				if ((mod[j].ip_value.iv_proto_list =
823 				    get_protos(value)) == NULL) {
824 					if (errno == ENOMEM) {
825 						uu_die(gettext(
826 						    "Error: Out of memory.\n"));
827 					} else if (errno == E2BIG) {
828 						uu_die(gettext(
829 						    "Error: String value in "
830 						    "%s property longer than "
831 						    "%l characters.\n"),
832 						    PR_PROTO_NAME, max_val);
833 					} else {
834 						uu_die(gettext(
835 						    "Error: No values "
836 						    "specified for %s "
837 						    "property.\n"),
838 						    PR_PROTO_NAME);
839 					}
840 				}
841 			} else if (strlen(value) >= max_val) {
842 				uu_die(gettext("Error: String value is longer "
843 				    "than %l characters.\n"), max_val);
844 			} else if ((mod[j].ip_value.iv_astring = strdup(value))
845 			    == NULL) {
846 				uu_die(gettext("Error: Out of memory.\n"));
847 			}
848 			break;
849 		case SCF_TYPE_BOOLEAN:
850 			if (strcasecmp(value, INETADM_TRUE_STR) == 0)
851 				mod[j].ip_value.iv_boolean = B_TRUE;
852 			else if (strcasecmp(value, INETADM_FALSE_STR) == 0)
853 				mod[j].ip_value.iv_boolean = B_FALSE;
854 			else
855 				uu_die(gettext("Error: \"%s\" is not a valid "
856 				    "boolean value. (TRUE or FALSE)\n"), value);
857 		}
858 		/* mark property for modification */
859 		mod[j].ip_error = IVE_VALID;
860 
861 		/* return the '=' taken out above */
862 		*(--value) = '=';
863 	}
864 
865 	commit_props(inst, mod, B_FALSE);
866 	free(mod);
867 	if (smf_refresh_instance(fmri) != 0)
868 		uu_die(gettext("Error: Unable to refresh instance %s.\n"),
869 		    fmri);
870 
871 	return (0);
872 }
873 
874 /*
875  * modify_defaults takes n name=value pairs for inetd default property values,
876  * parses them, and then modifies the values in the repository.
877  */
878 
879 static void
880 modify_defaults(int argc, char *argv[])
881 {
882 	int			i, j;
883 	char			*value;
884 	scf_instance_t		*inst;
885 	inetd_prop_t		*mod, *prop_table;
886 	size_t			numprops;
887 	ssize_t			max_val;
888 	int64_t			new_int;
889 
890 	if ((inst = scf_instance_create(h)) == NULL)
891 		scfdie();
892 
893 	if (scf_handle_decode_fmri(h, INETD_INSTANCE_FMRI, NULL, NULL,
894 	    inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
895 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
896 			uu_die(gettext("inetd instance missing in repository."
897 			    "\n"));
898 		} else {
899 			scfdie();
900 		}
901 	}
902 
903 	if ((max_val = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) < 0)
904 		scfdie();
905 
906 	prop_table = get_prop_table(&numprops);
907 
908 	if ((mod = malloc(numprops * sizeof (inetd_prop_t))) == NULL)
909 		uu_die(gettext("Error: Out of memory.\n"));
910 
911 	(void) memcpy(mod, prop_table, numprops * sizeof (inetd_prop_t));
912 
913 	for (i = 0; i < argc; i++) {
914 		/* Separate argument into name and value pair */
915 		if ((value = strchr(argv[i], '=')) == NULL)
916 			uu_die(gettext("Error: Malformed name=value pair \"%s"
917 			    "\"\n"), argv[i]);
918 
919 		*value = '\0';
920 		value++;
921 
922 		/* Find property name in array of defaults */
923 		for (j = 0; j < numprops; j++) {
924 			if (!mod[j].ip_default)
925 				continue;
926 			if (strcmp(mod[j].ip_name, argv[i]) == 0)
927 				break;
928 		}
929 
930 		if (j >= numprops)
931 			uu_die(gettext("Error: \"%s\" is not a default inetd "
932 			    "property.\n"), argv[i]);
933 
934 		if (*value == '\0')
935 			uu_die(gettext("Cannot accept NULL values for default "
936 			    "properties.\n"));
937 
938 		switch (mod[j].ip_type) {
939 		case SCF_TYPE_INTEGER:
940 			if (uu_strtoint(value, &new_int, sizeof (new_int), NULL,
941 			    NULL, NULL) == -1)
942 				uu_die(gettext("Error: \"%s\" is not a valid "
943 				    "integer value.\n"), value);
944 
945 			mod[j].ip_value.iv_int = new_int;
946 			break;
947 		case SCF_TYPE_ASTRING:
948 			if (strlen(value) >= max_val)
949 				uu_die(gettext("Error: String value is longer "
950 				    "than %l characters.\n"), max_val);
951 			if ((mod[j].ip_value.iv_astring = strdup(value))
952 			    == NULL)
953 				uu_die(gettext("Error: Out of memory.\n"));
954 			break;
955 		case SCF_TYPE_BOOLEAN:
956 			if (strcasecmp(value, INETADM_TRUE_STR) == 0)
957 				mod[j].ip_value.iv_boolean = B_TRUE;
958 			else if (strcasecmp(value, INETADM_FALSE_STR) == 0)
959 				mod[j].ip_value.iv_boolean = B_FALSE;
960 			else
961 				uu_die(gettext("Error: \"%s\" is not a valid "
962 				    "boolean value. (TRUE or FALSE)\n"), value);
963 		}
964 		/* mark property for modification */
965 		mod[j].ip_error = IVE_VALID;
966 	}
967 
968 	commit_props(inst, mod, B_TRUE);
969 	free(mod);
970 	scf_instance_destroy(inst);
971 	if (refresh_inetd() != 0)
972 		uu_warn(gettext("Warning: Unable to refresh inetd.\n"));
973 }
974 
975 int
976 main(int argc, char *argv[])
977 {
978 	int		opt;
979 	uint_t		lflag, eflag, dflag, pflag, mflag, Mflag;
980 	uint8_t		enable;
981 	scf_error_t	serr;
982 	int		exit_status = 0;
983 
984 	(void) setlocale(LC_ALL, "");
985 	(void) textdomain(TEXT_DOMAIN);
986 
987 	if ((h = scf_handle_create(SCF_VERSION)) == NULL)
988 		scfdie();
989 
990 	if (scf_handle_bind(h) == -1)
991 		uu_die(gettext("Error: Couldn't bind to svc.configd.\n"));
992 
993 	if (argc == 1) {
994 		list_services();
995 		goto out;
996 	}
997 
998 	lflag = eflag = dflag = pflag = mflag = Mflag = 0;
999 	while ((opt = getopt(argc, argv, "ledpMm?")) != -1) {
1000 		switch (opt) {
1001 		case 'l':
1002 			lflag = 1;
1003 			break;
1004 		case 'e':
1005 			eflag = 1;
1006 			break;
1007 		case 'd':
1008 			dflag = 1;
1009 			break;
1010 		case 'p':
1011 			pflag = 1;
1012 			break;
1013 		case 'M':
1014 			Mflag = 1;
1015 			break;
1016 		case 'm':
1017 			mflag = 1;
1018 			break;
1019 		case '?':
1020 			if (optopt == '?') {
1021 				usage(B_TRUE);
1022 				goto out;
1023 			} else {
1024 				usage(B_FALSE);
1025 			}
1026 		default:
1027 			usage(B_FALSE);
1028 		}
1029 	}
1030 
1031 	/*
1032 	 * All options are mutually exclusive, and we must have an option
1033 	 * if we reached here.
1034 	 */
1035 	if (lflag + eflag + dflag + pflag + mflag + Mflag != 1)
1036 		usage(B_FALSE);
1037 
1038 	argv += optind;
1039 	argc -= optind;
1040 	if ((pflag == 0) && (argc == 0))
1041 		usage(B_FALSE);
1042 
1043 	serr = 0;
1044 	if (lflag) {
1045 		serr = scf_walk_fmri(h, argc, argv, 0, list_props_cb, NULL,
1046 		    &exit_status, uu_warn);
1047 	} else if (dflag) {
1048 		enable = 0;
1049 		serr = scf_walk_fmri(h, argc, argv, 0, set_svc_enable_cb,
1050 		    &enable, &exit_status, uu_warn);
1051 	} else if (eflag) {
1052 		enable = 1;
1053 		serr = scf_walk_fmri(h, argc, argv, 0, set_svc_enable_cb,
1054 		    &enable, &exit_status, uu_warn);
1055 	} else if (mflag) {
1056 		arglist_t	args;
1057 		char		**cpp = argv;
1058 		uint_t		fmri_args = 0;
1059 
1060 		/* count number of fmri arguments */
1061 		while ((fmri_args < argc) && (strchr(*cpp, '=') == NULL)) {
1062 			fmri_args++;
1063 			cpp++;
1064 		}
1065 
1066 		/* if no x=y args or no fmri, show usage */
1067 		if ((fmri_args == argc) || (fmri_args == 0))
1068 			usage(B_FALSE);
1069 
1070 		/* setup args for modify_inst_props_cb */
1071 		args.argc = argc - fmri_args;
1072 		args.argv = argv + fmri_args;
1073 
1074 		serr = scf_walk_fmri(h, fmri_args, argv, 0,
1075 		    modify_inst_props_cb, &args, &exit_status, uu_warn);
1076 	} else if (Mflag) {
1077 		modify_defaults(argc, argv);
1078 	} else if (pflag) {
1079 		/* ensure there's no trailing garbage */
1080 		if (argc != 0)
1081 			usage(B_FALSE);
1082 		list_defaults();
1083 	}
1084 	if (serr != 0) {
1085 		uu_warn(gettext("failed to iterate over instances: %s"),
1086 		    scf_strerror(serr));
1087 		exit(UU_EXIT_FATAL);
1088 	}
1089 
1090 out:
1091 	(void) scf_handle_unbind(h);
1092 	scf_handle_destroy(h);
1093 
1094 	return (exit_status);
1095 }
1096