xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/inetadm/inetadm.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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 /*
28  * inetadm - administer services controlled by inetd and print out inetd
29  * service related information.
30  */
31 
32 #include <locale.h>
33 #include <libintl.h>
34 #include <libscf.h>
35 #include <libscf_priv.h>
36 #include <libuutil.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <syslog.h>
42 #include <inetsvc.h>
43 #include <errno.h>
44 
45 #ifndef TEXT_DOMAIN
46 #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
47 #endif /* TEXT_DOMAIN */
48 
49 /* Strings for output to the user, and checking user's input */
50 
51 #define	INETADM_TRUE_STR		"TRUE"
52 #define	INETADM_FALSE_STR		"FALSE"
53 #define	INETADM_ENABLED_STR		"enabled"
54 #define	INETADM_DISABLED_STR		"disabled"
55 #define	INETADM_DEFAULT_STR		"default"
56 
57 /* String for checking if an instance is under inetd's control. */
58 
59 #define	INETADM_INETD_STR		"network/inetd"
60 
61 /*
62  * Used to hold a list of scf_value_t's whilst performing a transaction
63  * to write a proto list back.
64  */
65 typedef struct scf_val_el {
66 	scf_value_t		*val;
67 	uu_list_node_t		link;
68 } scf_val_el_t;
69 
70 /*
71  * Structure used to encapsulate argc and argv so they can be passed using the
72  * single data argument supplied by scf_walk_fmri() for the consumption of
73  * modify_inst_props_cb().
74  */
75 typedef struct arglist {
76 	int	argc;
77 	char	**argv;
78 } arglist_t;
79 
80 static scf_handle_t *h;
81 
82 static void
83 scfdie()
84 {
85 	uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
86 	    scf_strerror(scf_error()));
87 }
88 
89 static void
90 usage(boolean_t detailed)
91 {
92 
93 	uu_warn(gettext(
94 	    "Usage:\n"
95 	    "  inetadm\n"
96 	    "  inetadm -?\n"
97 	    "  inetadm -p\n"
98 	    "  inetadm -l {FMRI | pattern}...\n"
99 	    "  inetadm -e {FMRI | pattern}...\n"
100 	    "  inetadm -d {FMRI | pattern}...\n"
101 	    "  inetadm -m {FMRI | pattern}... {name=value}...\n"
102 	    "  inetadm -M {name=value}...\n"));
103 
104 	if (!detailed)
105 		exit(UU_EXIT_USAGE);
106 
107 	(void) fprintf(stdout, gettext(
108 	    "\n"
109 	    "Without any options inetadm lists all inetd managed services.\n"
110 	    "\n"
111 	    "Options:\n"
112 	    "  -?	Print help.\n"
113 	    "  -p	List all default inetd property values.\n"
114 	    "  -l 	List all inetd property values for the inet "
115 	    "service(s).\n"
116 	    "  -e	Enable the inet service(s).\n"
117 	    "  -d	Disable the inet service(s).\n"
118 	    "  -m	Modify the inet service(s) inetd property values.\n"
119 	    "  -M	Modify default inetd property values.\n"));
120 }
121 
122 /*
123  * Add the proto list contained in array 'plist' to entry 'entry', storing
124  * aside the scf_value_t's created and added to the entry in a list that the
125  * pointer referenced by sv_list is made to point at.
126  */
127 static void
128 add_proto_list(scf_transaction_entry_t *entry, scf_handle_t *hdl,
129     char **plist, uu_list_t **sv_list)
130 {
131 	scf_val_el_t		*sv_el;
132 	int			i;
133 
134 	static uu_list_pool_t	*sv_pool = NULL;
135 
136 	if ((sv_pool == NULL) &&
137 	    ((sv_pool = uu_list_pool_create("sv_pool",
138 	    sizeof (scf_val_el_t), offsetof(scf_val_el_t, link), NULL,
139 	    UU_LIST_POOL_DEBUG)) == NULL))
140 		uu_die(gettext("Error: %s.\n"), uu_strerror(uu_error()));
141 
142 	if ((*sv_list = uu_list_create(sv_pool, NULL, 0)) == NULL)
143 		uu_die(gettext("Error: %s.\n"), uu_strerror(uu_error()));
144 
145 	for (i = 0; plist[i] != NULL; i++) {
146 		if ((sv_el = malloc(sizeof (scf_val_el_t))) == NULL)
147 			uu_die(gettext("Error:"));
148 
149 		if (((sv_el->val = scf_value_create(hdl)) == NULL) ||
150 		    (scf_value_set_astring(sv_el->val, plist[i]) != 0) ||
151 		    (scf_entry_add_value(entry, sv_el->val) != 0))
152 			scfdie();
153 
154 		uu_list_node_init(sv_el, &sv_el->link, sv_pool);
155 		(void) uu_list_insert_after(*sv_list, NULL, sv_el);
156 	}
157 }
158 
159 /*
160  * A counterpart to add_proto_list(), this function removes and frees the
161  * scf_value_t's it added to entry 'entry'.
162  */
163 static void
164 remove_proto_list(scf_transaction_entry_t *entry, uu_list_t *sv_list)
165 {
166 	scf_val_el_t	*sv_el;
167 	void		*cookie = NULL;
168 
169 	scf_entry_reset(entry);
170 
171 	while ((sv_el = uu_list_teardown(sv_list, &cookie)) != NULL) {
172 		scf_value_destroy(sv_el->val);
173 		free(sv_el);
174 	}
175 
176 	uu_list_destroy(sv_list);
177 }
178 
179 /*
180  * modify_prop takes an instance, property group, property name, type, and
181  * value, and modifies the specified property in the repository to the
182  * submitted value.
183  */
184 
185 static void
186 modify_prop(const scf_instance_t *inst, const char *pg, const char *prop,
187     scf_type_t type, void *value)
188 {
189 	scf_transaction_t		*tx;
190 	scf_transaction_entry_t		*ent;
191 	scf_propertygroup_t		*gpg;
192 	scf_property_t			*eprop;
193 	scf_value_t			*v;
194 	int				ret, create = 0;
195 	char				*fmri;
196 	ssize_t				max_fmri_len;
197 
198 	if ((gpg = scf_pg_create(h)) == NULL ||
199 	    (eprop = scf_property_create(h)) == NULL ||
200 	    (v = scf_value_create(h)) == NULL)
201 		scfdie();
202 
203 	/* Get the property group or create it if it is missing. */
204 	if (scf_instance_get_pg(inst, pg, gpg) == -1) {
205 		if (scf_error() != SCF_ERROR_NOT_FOUND)
206 			scfdie();
207 
208 		max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
209 		if ((fmri = malloc(max_fmri_len + 1)) == NULL)
210 			uu_die(gettext("Error: Out of memory.\n"));
211 
212 		if (scf_instance_to_fmri(inst, fmri, max_fmri_len + 1) < 0)
213 			scfdie();
214 
215 		syslog(LOG_NOTICE, "inetadm: Property group \"%s\" missing "
216 		    "from \"%s\", attempting to add it.\n", pg, fmri);
217 		free(fmri);
218 
219 		if (scf_instance_add_pg(inst, pg, SCF_GROUP_FRAMEWORK, 0,
220 		    gpg) == -1) {
221 			switch (scf_error()) {
222 			case SCF_ERROR_EXISTS:
223 				break;
224 			case SCF_ERROR_PERMISSION_DENIED:
225 				uu_die(gettext("Error: Permission denied.\n"));
226 			default:
227 				scfdie();
228 			}
229 		}
230 	}
231 
232 	if (scf_pg_get_property(gpg, prop, eprop) == -1) {
233 		if (scf_error() != SCF_ERROR_NOT_FOUND)
234 			scfdie();
235 
236 		create = 1;
237 	}
238 
239 	if ((tx = scf_transaction_create(h)) == NULL ||
240 	    (ent = scf_entry_create(h)) == NULL)
241 		scfdie();
242 
243 	do {
244 		uu_list_t	*sv_list;
245 
246 		if (scf_transaction_start(tx, gpg) == -1) {
247 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
248 				scfdie();
249 
250 			uu_die(gettext("Error: Permission denied.\n"));
251 		}
252 
253 		/* Modify the property or create it if it is missing */
254 		if (create)
255 			ret = scf_transaction_property_new(tx, ent, prop, type);
256 		else
257 			ret = scf_transaction_property_change_type(tx, ent,
258 			    prop, type);
259 		if (ret == -1)
260 			scfdie();
261 
262 		switch (type) {
263 		case SCF_TYPE_BOOLEAN:
264 			scf_value_set_boolean(v, *(uint8_t *)value);
265 			break;
266 		case SCF_TYPE_INTEGER:
267 			scf_value_set_integer(v, *(int64_t *)value);
268 			break;
269 		case SCF_TYPE_ASTRING:
270 			if (strcmp(prop, PR_PROTO_NAME) == 0) {
271 				add_proto_list(ent, h, (char **)value,
272 				    &sv_list);
273 			} else if (scf_value_set_astring(v, value) == -1) {
274 				scfdie();
275 			}
276 			break;
277 		default:
278 			uu_die(gettext("Error: Internal inetadm error"));
279 		}
280 
281 		if ((strcmp(prop, PR_PROTO_NAME) != 0) &&
282 		    (scf_entry_add_value(ent, v) == -1))
283 			scfdie();
284 
285 		ret = scf_transaction_commit(tx);
286 		if (ret == -1) {
287 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
288 				scfdie();
289 
290 			uu_die(gettext("Error: Permission denied.\n"));
291 		}
292 
293 		scf_transaction_reset(tx);
294 
295 		if (ret == 0) {
296 			if (scf_pg_update(gpg) == -1)
297 				scfdie();
298 		}
299 
300 		if (strcmp(prop, PR_PROTO_NAME) == 0)
301 			remove_proto_list(ent, sv_list);
302 
303 	} while (ret == 0);
304 
305 	scf_value_destroy(v);
306 	scf_entry_destroy(ent);
307 	scf_transaction_destroy(tx);
308 	scf_property_destroy(eprop);
309 	scf_pg_destroy(gpg);
310 }
311 
312 /*
313  * delete_prop takes an instance, property group name and property, and
314  * deletes the specified property from the repository.
315  */
316 
317 static void
318 delete_prop(const scf_instance_t *inst, const char *pg, const char *prop)
319 {
320 	scf_transaction_t		*tx;
321 	scf_transaction_entry_t		*ent;
322 	scf_propertygroup_t		*gpg;
323 	scf_property_t			*eprop;
324 	int				ret;
325 
326 	if ((gpg = scf_pg_create(h)) == NULL ||
327 	    (eprop = scf_property_create(h)) == NULL ||
328 	    (tx = scf_transaction_create(h)) == NULL ||
329 	    (ent = scf_entry_create(h)) == NULL)
330 		scfdie();
331 
332 	if (scf_instance_get_pg(inst, pg, gpg) != SCF_SUCCESS) {
333 		if (scf_error() != SCF_ERROR_NOT_FOUND)
334 			scfdie();
335 
336 		uu_die(gettext("Error: \"%s\" property group missing.\n"), pg);
337 	}
338 
339 	do {
340 		if (scf_transaction_start(tx, gpg) != SCF_SUCCESS) {
341 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
342 				scfdie();
343 
344 			uu_die(gettext("Error: Permission denied.\n"));
345 		}
346 
347 		if (scf_transaction_property_delete(tx, ent,
348 		    prop) != SCF_SUCCESS) {
349 			if (scf_error() != SCF_ERROR_NOT_FOUND)
350 				scfdie();
351 
352 			uu_die(
353 			    gettext("Error: \"%s\" property does not exist.\n"),
354 			    prop);
355 		}
356 
357 		ret = scf_transaction_commit(tx);
358 		if (ret < 0) {
359 			if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
360 				scfdie();
361 
362 			uu_die(gettext("Error: Permission denied.\n"));
363 		}
364 		if (ret == 0) {
365 			scf_transaction_reset(tx);
366 			if (scf_pg_update(gpg) == -1)
367 				scfdie();
368 		}
369 	} while (ret == 0);
370 
371 	(void) scf_entry_destroy(ent);
372 	scf_transaction_destroy(tx);
373 	scf_property_destroy(eprop);
374 	scf_pg_destroy(gpg);
375 }
376 
377 /*
378  * commit_props evaluates an entire property list that has been created
379  * based on command line options, and either deletes or modifies properties
380  * as requested.
381  */
382 
383 static void
384 commit_props(const scf_instance_t *inst, inetd_prop_t *mod, boolean_t defaults)
385 {
386 	int			i;
387 	uint8_t			new_bool;
388 
389 	for (i = 0; mod[i].ip_name != NULL; i++) {
390 		switch (mod[i].ip_error) {
391 		case IVE_UNSET:
392 			break;
393 		case IVE_INVALID:
394 			delete_prop(inst, mod[i].ip_pg, mod[i].ip_name);
395 			break;
396 		case IVE_VALID:
397 			switch (mod[i].ip_type) {
398 			case INET_TYPE_STRING:
399 				modify_prop(inst,
400 				    defaults ? PG_NAME_SERVICE_DEFAULTS :
401 				    mod[i].ip_pg, mod[i].ip_name,
402 				    SCF_TYPE_ASTRING,
403 				    mod[i].ip_value.iv_string);
404 				break;
405 			case INET_TYPE_STRING_LIST:
406 				modify_prop(inst,
407 				    defaults ? PG_NAME_SERVICE_DEFAULTS :
408 				    mod[i].ip_pg, mod[i].ip_name,
409 				    SCF_TYPE_ASTRING,
410 				    mod[i].ip_value.iv_string_list);
411 				break;
412 			case INET_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 INET_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(inetd_prop_t *prop)
526 {
527 	switch (prop->ip_type) {
528 	case INET_TYPE_STRING:
529 		(void) printf("\"%s\"\n", prop->ip_value.iv_string);
530 		break;
531 	case INET_TYPE_STRING_LIST:
532 		{
533 			int	j = 0;
534 			char	**cpp = prop->ip_value.iv_string_list;
535 
536 			/*
537 			 * Print string list as comma separated list.
538 			 */
539 
540 			(void) printf("\"%s", cpp[j]);
541 			while (cpp[++j] != NULL)
542 				(void) printf(",%s", cpp[j]);
543 			(void) printf("\"\n");
544 		}
545 		break;
546 	case INET_TYPE_INTEGER:
547 		(void) printf("%lld\n", prop->ip_value.iv_int);
548 		break;
549 	case INET_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(&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(&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; mod[j].ip_name != NULL; j++) {
787 			if (strcmp(mod[j].ip_name, argv[i]) == 0)
788 				break;
789 		}
790 
791 		if (mod[j].ip_name == NULL)
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 INET_TYPE_INTEGER:
813 			if (uu_strtoint(value, &new_int, sizeof (new_int), 0,
814 			    0, 0) == -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 INET_TYPE_STRING:
821 			if (strlen(value) >= max_val) {
822 				uu_die(gettext("Error: String value is longer "
823 				    "than %l characters.\n"), max_val);
824 			} else if ((mod[j].ip_value.iv_string = strdup(value))
825 			    == NULL) {
826 				uu_die(gettext("Error: Out of memory.\n"));
827 			}
828 			break;
829 		case INET_TYPE_STRING_LIST:
830 			if ((mod[j].ip_value.iv_string_list =
831 			    get_protos(value)) == NULL) {
832 				if (errno == ENOMEM) {
833 					uu_die(gettext(
834 					    "Error: Out of memory.\n"));
835 				} else if (errno == E2BIG) {
836 					uu_die(gettext(
837 					    "Error: String value in "
838 					    "%s property longer than "
839 					    "%l characters.\n"),
840 					    PR_PROTO_NAME, max_val);
841 				} else {
842 					uu_die(gettext(
843 					    "Error: No values "
844 					    "specified for %s "
845 					    "property.\n"),
846 					    PR_PROTO_NAME);
847 				}
848 			}
849 			break;
850 		case INET_TYPE_BOOLEAN:
851 			if (strcasecmp(value, INETADM_TRUE_STR) == 0)
852 				mod[j].ip_value.iv_boolean = B_TRUE;
853 			else if (strcasecmp(value, INETADM_FALSE_STR) == 0)
854 				mod[j].ip_value.iv_boolean = B_FALSE;
855 			else
856 				uu_die(gettext("Error: \"%s\" is not a valid "
857 				    "boolean value. (TRUE or FALSE)\n"), value);
858 		}
859 		/* mark property for modification */
860 		mod[j].ip_error = IVE_VALID;
861 
862 		/* return the '=' taken out above */
863 		*(--value) = '=';
864 	}
865 
866 	commit_props(inst, mod, B_FALSE);
867 	free(mod);
868 	if (smf_refresh_instance(fmri) != 0)
869 		uu_die(gettext("Error: Unable to refresh instance %s.\n"),
870 		    fmri);
871 
872 	return (0);
873 }
874 
875 /*
876  * modify_defaults takes n name=value pairs for inetd default property values,
877  * parses them, and then modifies the values in the repository.
878  */
879 
880 static void
881 modify_defaults(int argc, char *argv[])
882 {
883 	int			i, j;
884 	char			*value;
885 	scf_instance_t		*inst;
886 	inetd_prop_t		*mod, *prop_table;
887 	size_t			numprops;
888 	ssize_t			max_val;
889 	int64_t			new_int;
890 
891 	if ((inst = scf_instance_create(h)) == NULL)
892 		scfdie();
893 
894 	if (scf_handle_decode_fmri(h, INETD_INSTANCE_FMRI, NULL, NULL,
895 	    inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
896 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
897 			uu_die(gettext("inetd instance missing in repository."
898 			    "\n"));
899 		} else {
900 			scfdie();
901 		}
902 	}
903 
904 	if ((max_val = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) < 0)
905 		scfdie();
906 
907 	prop_table = get_prop_table(&numprops);
908 
909 	if ((mod = malloc(numprops * sizeof (inetd_prop_t))) == NULL)
910 		uu_die(gettext("Error: Out of memory.\n"));
911 
912 	(void) memcpy(mod, prop_table, numprops * sizeof (inetd_prop_t));
913 
914 	for (i = 0; i < argc; i++) {
915 		/* Separate argument into name and value pair */
916 		if ((value = strchr(argv[i], '=')) == NULL)
917 			uu_die(gettext("Error: Malformed name=value pair \"%s"
918 			    "\"\n"), argv[i]);
919 
920 		*value = '\0';
921 		value++;
922 
923 		/* Find property name in array of defaults */
924 		for (j = 0; mod[j].ip_name != NULL; j++) {
925 			if (!mod[j].ip_default)
926 				continue;
927 			if (strcmp(mod[j].ip_name, argv[i]) == 0)
928 				break;
929 		}
930 
931 		if (mod[j].ip_name == NULL)
932 			uu_die(gettext("Error: \"%s\" is not a default inetd "
933 			    "property.\n"), argv[i]);
934 
935 		if (*value == '\0')
936 			uu_die(gettext("Cannot accept NULL values for default "
937 			    "properties.\n"));
938 
939 		switch (mod[j].ip_type) {
940 		case INET_TYPE_INTEGER:
941 			if (uu_strtoint(value, &new_int, sizeof (new_int), 0,
942 			    0, 0) == -1)
943 				uu_die(gettext("Error: \"%s\" is not a valid "
944 				    "integer value.\n"), value);
945 
946 			mod[j].ip_value.iv_int = new_int;
947 			break;
948 		case INET_TYPE_STRING:
949 			if (strlen(value) >= max_val)
950 				uu_die(gettext("Error: String value is longer "
951 				    "than %l characters.\n"), max_val);
952 			if ((mod[j].ip_value.iv_string = strdup(value))
953 			    == NULL)
954 				uu_die(gettext("Error: Out of memory.\n"));
955 			break;
956 		case INET_TYPE_BOOLEAN:
957 			if (strcasecmp(value, INETADM_TRUE_STR) == 0)
958 				mod[j].ip_value.iv_boolean = B_TRUE;
959 			else if (strcasecmp(value, INETADM_FALSE_STR) == 0)
960 				mod[j].ip_value.iv_boolean = B_FALSE;
961 			else
962 				uu_die(gettext("Error: \"%s\" is not a valid "
963 				    "boolean value. (TRUE or FALSE)\n"), value);
964 		}
965 		/* mark property for modification */
966 		mod[j].ip_error = IVE_VALID;
967 	}
968 
969 	commit_props(inst, mod, B_TRUE);
970 	free(mod);
971 	scf_instance_destroy(inst);
972 	if (refresh_inetd() != 0)
973 		uu_warn(gettext("Warning: Unable to refresh inetd.\n"));
974 }
975 
976 int
977 main(int argc, char *argv[])
978 {
979 	int		opt;
980 	uint_t		lflag, eflag, dflag, pflag, mflag, Mflag;
981 	uint8_t		enable;
982 	scf_error_t	serr;
983 	int		exit_status = 0;
984 
985 	(void) setlocale(LC_ALL, "");
986 	(void) textdomain(TEXT_DOMAIN);
987 
988 	if ((h = scf_handle_create(SCF_VERSION)) == NULL)
989 		scfdie();
990 
991 	if (scf_handle_bind(h) == -1)
992 		uu_die(gettext("Error: Couldn't bind to svc.configd.\n"));
993 
994 	if (argc == 1) {
995 		list_services();
996 		goto out;
997 	}
998 
999 	lflag = eflag = dflag = pflag = mflag = Mflag = 0;
1000 	while ((opt = getopt(argc, argv, "ledpMm?")) != -1) {
1001 		switch (opt) {
1002 		case 'l':
1003 			lflag = 1;
1004 			break;
1005 		case 'e':
1006 			eflag = 1;
1007 			break;
1008 		case 'd':
1009 			dflag = 1;
1010 			break;
1011 		case 'p':
1012 			pflag = 1;
1013 			break;
1014 		case 'M':
1015 			Mflag = 1;
1016 			break;
1017 		case 'm':
1018 			mflag = 1;
1019 			break;
1020 		case '?':
1021 			if (optopt == '?') {
1022 				usage(B_TRUE);
1023 				goto out;
1024 			} else {
1025 				usage(B_FALSE);
1026 			}
1027 			break;
1028 		default:
1029 			usage(B_FALSE);
1030 		}
1031 	}
1032 
1033 	/*
1034 	 * All options are mutually exclusive, and we must have an option
1035 	 * if we reached here.
1036 	 */
1037 	if (lflag + eflag + dflag + pflag + mflag + Mflag != 1)
1038 		usage(B_FALSE);
1039 
1040 	argv += optind;
1041 	argc -= optind;
1042 	if ((pflag == 0) && (argc == 0))
1043 		usage(B_FALSE);
1044 
1045 	serr = 0;
1046 	if (lflag) {
1047 		serr = scf_walk_fmri(h, argc, argv, 0, list_props_cb, NULL,
1048 		    &exit_status, uu_warn);
1049 	} else if (dflag) {
1050 		enable = 0;
1051 		serr = scf_walk_fmri(h, argc, argv, 0, set_svc_enable_cb,
1052 		    &enable, &exit_status, uu_warn);
1053 	} else if (eflag) {
1054 		enable = 1;
1055 		serr = scf_walk_fmri(h, argc, argv, 0, set_svc_enable_cb,
1056 		    &enable, &exit_status, uu_warn);
1057 	} else if (mflag) {
1058 		arglist_t	args;
1059 		char		**cpp = argv;
1060 		uint_t		fmri_args = 0;
1061 
1062 		/* count number of fmri arguments */
1063 		while ((fmri_args < argc) && (strchr(*cpp, '=') == NULL)) {
1064 			fmri_args++;
1065 			cpp++;
1066 		}
1067 
1068 		/* if no x=y args or no fmri, show usage */
1069 		if ((fmri_args == argc) || (fmri_args == 0))
1070 			usage(B_FALSE);
1071 
1072 		/* setup args for modify_inst_props_cb */
1073 		args.argc = argc - fmri_args;
1074 		args.argv = argv + fmri_args;
1075 
1076 		serr = scf_walk_fmri(h, fmri_args, argv, 0,
1077 		    modify_inst_props_cb, &args, &exit_status, uu_warn);
1078 	} else if (Mflag) {
1079 		modify_defaults(argc, argv);
1080 	} else if (pflag) {
1081 		/* ensure there's no trailing garbage */
1082 		if (argc != 0)
1083 			usage(B_FALSE);
1084 		list_defaults();
1085 	}
1086 	if (serr != 0) {
1087 		uu_warn(gettext("failed to iterate over instances: %s"),
1088 		    scf_strerror(serr));
1089 		exit(UU_EXIT_FATAL);
1090 	}
1091 
1092 out:
1093 	(void) scf_handle_unbind(h);
1094 	scf_handle_destroy(h);
1095 
1096 	return (exit_status);
1097 }
1098