xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv.c (revision ba1637f8b78b432c41b36839c92aecf1f5f9fafb)
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  * inetconv - convert inetd.conf entries into smf(5) service manifests,
31  *            import them into smf(5) repository
32  */
33 
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 #include <sys/wait.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <fcntl.h>
43 #include <pwd.h>
44 #include <grp.h>
45 #include <errno.h>
46 #include <limits.h>
47 #include <locale.h>
48 #include <libintl.h>
49 #include <libscf.h>
50 #include <inetsvc.h>
51 #include <rpc/nettype.h>
52 
53 /* exit codes */
54 #define	EXIT_SUCCESS	0	/* succeeded */
55 #define	EXIT_USAGE	1	/* bad options */
56 #define	EXIT_ERROR_CONV 2	/* error(s) coverting inetd.conf entries */
57 #define	EXIT_ERROR_IMP	3	/* error(s) importing manifests */
58 #define	EXIT_ERROR_SYS	4	/* system error */
59 #define	EXIT_ERROR_ENBL 5	/* error(s) enabling services */
60 
61 #ifndef TEXT_DOMAIN
62 #define	TEXT_DOMAIN		"SUNW_OST_OSCMD"
63 #endif
64 
65 #define	MAIN_CONFIG		"/etc/inet/inetd.conf"
66 #define	ALT_CONFIG		"/etc/inetd.conf"
67 
68 #define	MANIFEST_DIR		"/var/svc/manifest/network"
69 #define	MANIFEST_RPC_DIR	MANIFEST_DIR  "/rpc"
70 #define	SVCCFG_PATH		"/usr/sbin/svccfg"
71 
72 #define	RPCBIND_FMRI		"svc:/network/rpc/bind"
73 
74 /* maximum allowed length of an inetd.conf format line */
75 #define	MAX_SRC_LINELEN		32768
76 
77 /* Version of inetconv, used as a marker in services we generate */
78 #define	INETCONV_VERSION	1
79 
80 struct inetconfent {
81 	/* fields as read from inetd.conf format line */
82 	char *service;
83 	char *endpoint;
84 	char *protocol;
85 	char *wait_status;
86 	char *username;
87 	char *server_program;
88 	char *server_args;
89 	/* information derived from above fields */
90 	boolean_t wait;
91 	boolean_t isrpc;
92 	int rpc_low_version;
93 	int rpc_high_version;
94 	char *rpc_prog;
95 	char *groupname;
96 	char *exec;
97 	char *arg0;
98 };
99 
100 struct fileinfo {
101 	FILE *fp;
102 	char *filename;
103 	int lineno;
104 	int failcnt;
105 };
106 
107 static char *progname;
108 
109 static boolean_t import = B_TRUE;
110 
111 /* start of manifest XML template strings */
112 static const char xml_header[] =
113 "<?xml version='1.0'?>\n"
114 "<!DOCTYPE service_bundle SYSTEM "
115 "'/usr/share/lib/xml/dtd/service_bundle.dtd.1'>\n";
116 
117 static const char xml_comment[] =
118 "<!--\n"
119 "    Service manifest for the %s service.\n"
120 "\n"
121 "    Generated by inetconv(1M) from inetd.conf(4).\n"
122 "-->\n\n";
123 
124 static const char xml_service_bundle[] =
125 "<service_bundle type='manifest' name='inetconv:%s'>\n\n";
126 
127 static const char xml_service_name[] =
128 "<service\n"
129 "	name='network/%s'\n"
130 "	type='service'\n"
131 "	version='1'>\n\n";
132 
133 static const char xml_dependency[] =
134 "	<dependency\n"
135 "		name='%s'\n"
136 "		grouping='require_all'\n"
137 "		restart_on='restart'\n"
138 "		type='service'>\n"
139 "		<service_fmri value='%s' />\n"
140 "	</dependency>\n\n";
141 
142 static const char xml_instance[] =
143 "	<create_default_instance enabled='true'/>\n\n";
144 
145 static const char xml_restarter[] =
146 "	<restarter>\n"
147 "		<service_fmri value='%s' />\n"
148 "	</restarter>\n\n";
149 
150 static const char xml_exec_method_start[] =
151 "	<!--\n"
152 "	    Set a timeout of 0 to signify to inetd that we don't want to\n"
153 "	    timeout this service, since the forked process is the one that\n"
154 "	    does the service's work. This is the case for most/all legacy\n"
155 "	    inetd services; for services written to take advantage of SMF\n"
156 "	    capabilities, the start method should fork off a process to\n"
157 "	    handle the request and return a success code.\n"
158 "	-->\n"
159 "	<exec_method\n"
160 "		type='method'\n"
161 "		name='%s'\n"
162 "		%s='%s'\n"
163 "		timeout_seconds='0'>\n"
164 "		<method_context>\n"
165 "			<method_credential %s='%s' group='%s' />\n"
166 "		</method_context>\n";
167 
168 static const char xml_arg0[] =
169 "		<propval name='%s' type='astring'\n"
170 "		    value='%s' />\n";
171 
172 static const char xml_exec_method_end[] =
173 "	</exec_method>\n\n";
174 
175 static const char xml_exec_method_disable[] =
176 "	<!--\n"
177 "	    Use inetd's built-in kill support to disable services.\n"
178 "	-->\n"
179 "	<exec_method\n"
180 "		type='method'\n"
181 "		name='%s'\n"
182 "		%s=':kill'\n"
183 "		timeout_seconds='0'>\n";
184 
185 static const char xml_exec_method_offline[] =
186 "	<!--\n"
187 "	    Use inetd's built-in process kill support to offline wait type\n"
188 "	    services.\n"
189 "	-->\n"
190 "	<exec_method\n"
191 "		type='method'\n"
192 "		name='%s'\n"
193 "		%s=':kill_process'\n"
194 "		timeout_seconds='0'>\n";
195 
196 static const char xml_inetconv_group_start[] =
197 "	<!--\n"
198 "	    This property group is used to record information about\n"
199 "	    how this manifest was created.  It is an implementation\n"
200 "	    detail which should not be modified or deleted.\n"
201 "	-->\n"
202 "	<property_group name='%s' type='framework'>\n"
203 "		<propval name='%s' type='boolean' value='%s' />\n"
204 "		<propval name='%s' type='integer' value='%d' />\n"
205 "		<propval name='%s' type='astring' value=\n"
206 "'%s %s %s %s %s %s%s%s'\n"
207 "		/>\n";
208 
209 static const char xml_property_group_start[] =
210 "	<property_group name='%s' type='framework'>\n"
211 "		<propval name='%s' type='astring' value='%s' />\n"
212 "		<propval name='%s' type='astring' value='%s' />\n"
213 "		<propval name='%s' type='astring' value='%s' />\n"
214 "		<propval name='%s' type='boolean' value='%s' />\n"
215 "		<propval name='%s' type='boolean' value='%s' />\n";
216 
217 static const char xml_property_group_rpc[] =
218 "		<propval name='%s' type='integer' value='%d' />\n"
219 "		<propval name='%s' type='integer' value='%d' />"
220 "\n";
221 
222 static const char xml_property_group_end[] =
223 "	</property_group>\n\n";
224 
225 static const char xml_stability[] =
226 "	<stability value='External' />\n\n";
227 
228 static const char xml_template[] =
229 "	<template>\n"
230 "		<common_name>\n"
231 "			<loctext xml:lang='C'>\n"
232 "%s\n"
233 "			</loctext>\n"
234 "		</common_name>\n"
235 "	</template>\n";
236 
237 static const char xml_footer[] =
238 "</service>\n"
239 "\n"
240 "</service_bundle>\n";
241 /* end of manifest XML template strings */
242 
243 static void *
244 safe_malloc(size_t size)
245 {
246 	void *cp;
247 
248 	if ((cp = malloc(size)) == NULL) {
249 		(void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
250 		    progname, strerror(errno));
251 		exit(EXIT_ERROR_SYS);
252 	}
253 	return (cp);
254 }
255 
256 static char *
257 safe_strdup(char *s)
258 {
259 	char *cp;
260 
261 	if ((cp = strdup(s)) == NULL) {
262 		(void) fprintf(stderr, gettext("%s: strdup failed: %s\n"),
263 		    progname, strerror(errno));
264 		exit(EXIT_ERROR_SYS);
265 	}
266 	return (cp);
267 }
268 
269 static char *
270 propertyname(char *name, char *prefix)
271 {
272 	static char *buf;
273 	size_t len;
274 	int c;
275 	char *cp;
276 
277 	/* free any memory allocated by a previous call */
278 	free(buf);
279 
280 	len = strlen(name) + strlen(prefix) + 1;
281 	buf = safe_malloc(len);
282 	buf[0] = '\0';
283 
284 	/*
285 	 * Property names must match the regular expression:
286 	 * ([A-Za-z][_A-Za-z0-9.-]*,)?[A-Za-z][_A-Za-z0-9-]*
287 	 */
288 
289 	/*
290 	 * Make sure the first character is alphabetic, if not insert prefix.
291 	 * Can't use isalpha() here as its locale dependent but the property
292 	 * name regular expression isn't.
293 	 */
294 	c = name[0];
295 	if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
296 		(void) strlcat(buf, prefix, len);
297 	}
298 	(void) strlcat(buf, name, len);
299 
300 	/* convert any dissallowed characters into '_' */
301 	for (cp = buf; *cp != '\0'; cp++) {
302 		if ((*cp < 'A' || *cp > 'Z') && (*cp < 'a' || *cp > 'z') &&
303 		    (*cp < '0' || *cp > '9') && (*cp != '.') && (*cp != '-'))
304 			*cp = '_';
305 	}
306 	return (buf);
307 }
308 
309 static char *
310 servicename(struct inetconfent *iconf)
311 {
312 	static char *buf;
313 	size_t len;
314 	char *proto;
315 
316 	/* free any memory allocated by a previous call */
317 	free(buf);
318 
319 	len = strlen(iconf->service) + strlen(iconf->protocol) +
320 	    sizeof ("rpc-/visible");
321 	buf = safe_malloc(len);
322 
323 	/*
324 	 * Combine the service and protocol fields to produce a unique
325 	 * manifest service name. The syntax of a service name is:
326 	 * prop(/prop)*
327 	 */
328 	(void) strlcpy(buf, propertyname(iconf->service,
329 	    iconf->isrpc ? "rpc-": "s-"), len);
330 	(void) strlcat(buf, "/", len);
331 
332 	proto = iconf->protocol;
333 	if (iconf->isrpc && (strcmp(iconf->protocol, "rpc/*") == 0))
334 		proto = "rpc/visible";
335 	(void) strlcat(buf, propertyname(proto, "p-"), len);
336 	return (buf);
337 }
338 
339 static boolean_t
340 is_v6only(char *protocol)
341 {
342 	/* returns true if protocol is an IPv6 only protocol */
343 	if ((strcmp(protocol, SOCKET_PROTO_TCP6_ONLY) == 0) ||
344 	    (strcmp(protocol, SOCKET_PROTO_UDP6_ONLY) == 0))
345 		return (B_TRUE);
346 	return (B_FALSE);
347 }
348 
349 static char *
350 invalid_props(inetd_prop_t *p)
351 {
352 	static char
353 	    buf[sizeof (" service-name endpoint-type protocol wait-status")];
354 
355 	buf[0] = '\0';
356 	if ((p[PT_SVC_NAME_INDEX].ip_error == IVE_INVALID) ||
357 	    (p[PT_SVC_NAME_INDEX].ip_error == IVE_UNSET) ||
358 	    (p[PT_RPC_LW_VER_INDEX].ip_error == IVE_INVALID) ||
359 	    (p[PT_RPC_HI_VER_INDEX].ip_error == IVE_INVALID))
360 		(void) strlcat(buf, " service-name", sizeof (buf));
361 	if ((p[PT_SOCK_TYPE_INDEX].ip_error == IVE_INVALID) ||
362 	    (p[PT_SOCK_TYPE_INDEX].ip_error == IVE_UNSET))
363 		(void) strlcat(buf, " endpoint-type", sizeof (buf));
364 	if ((p[PT_PROTO_INDEX].ip_error == IVE_INVALID) ||
365 	    (p[PT_PROTO_INDEX].ip_error == IVE_UNSET) ||
366 	    (p[PT_ISRPC_INDEX].ip_error == IVE_INVALID))
367 		(void) strlcat(buf, " protocol", sizeof (buf));
368 	if (p[PT_ISWAIT_INDEX].ip_error == IVE_INVALID)
369 		(void) strlcat(buf, " wait-status", sizeof (buf));
370 	return (buf);
371 }
372 
373 static boolean_t
374 valid_basic_properties(struct inetconfent *iconf, struct fileinfo *finfo)
375 {
376 	size_t prop_size;
377 	inetd_prop_t *prop, *inetd_properties;
378 	boolean_t valid = B_TRUE;
379 	char *proto = iconf->protocol;
380 	char *svc_name = iconf->service;
381 
382 	inetd_properties = get_prop_table(&prop_size);
383 	prop = safe_malloc(prop_size * sizeof (inetd_prop_t));
384 	(void) memcpy(prop, inetd_properties,
385 	    prop_size * sizeof (inetd_prop_t));
386 
387 	put_prop_value_boolean(prop, PR_ISRPC_NAME, iconf->isrpc);
388 	put_prop_value_boolean(prop, PR_ISWAIT_NAME, iconf->wait);
389 	if (iconf->isrpc) {
390 		put_prop_value_int(prop, PR_RPC_LW_VER_NAME,
391 		    iconf->rpc_low_version);
392 		put_prop_value_int(prop, PR_RPC_HI_VER_NAME,
393 		    iconf->rpc_high_version);
394 		svc_name = iconf->rpc_prog;
395 		proto += 4;	/* skip 'rpc/' */
396 	}
397 
398 	if (!put_prop_value_string(prop, PR_SOCK_TYPE_NAME, iconf->endpoint) ||
399 	    !put_prop_value_string(prop, PR_SVC_NAME_NAME, svc_name)) {
400 		valid = B_FALSE;
401 
402 		if (errno == ENOMEM) {
403 			(void) fprintf(stderr,
404 			    gettext("%s: failed to allocate memory: %s\n"),
405 			    progname, strerror(errno));
406 			exit(EXIT_ERROR_SYS);
407 		}
408 	}
409 
410 	put_prop_value_string_list(prop, PR_PROTO_NAME, get_protos(proto));
411 
412 	if (!valid_props(prop, NULL, NULL, NULL, NULL) || !valid) {
413 		valid = B_FALSE;
414 		(void) fprintf(stderr, gettext("%s: Error %s line %d "
415 		    "invalid or inconsistent fields:%s\n"), progname,
416 		    finfo->filename, finfo->lineno,
417 		    invalid_props(prop));
418 	}
419 
420 	free_instance_props(prop);
421 	return (valid);
422 }
423 
424 static boolean_t
425 valid_inetconfent(struct inetconfent *iconf, struct fileinfo *finfo)
426 {
427 	boolean_t valid = B_TRUE;
428 	size_t len;
429 	char *cp, *endp;
430 	struct passwd *pwd;
431 	struct group *grp;
432 	struct stat statb;
433 	char *proto = iconf->protocol;
434 
435 	iconf->isrpc = B_FALSE;
436 	if (strncmp(iconf->protocol, "rpc/", 4) == 0) {
437 		iconf->isrpc = B_TRUE;
438 		iconf->rpc_prog = safe_strdup(iconf->service);
439 
440 		/* set RPC version numbers */
441 		iconf->rpc_low_version = 1;
442 		iconf->rpc_high_version = 1;
443 		if ((cp = strrchr(iconf->rpc_prog, '/')) != NULL) {
444 			*cp = '\0';
445 			if (*++cp != '\0') {
446 				errno = 0;
447 				iconf->rpc_low_version = strtol(cp, &endp, 10);
448 				if (errno != 0)
449 					goto vererr;
450 				cp = endp;
451 				if (*cp == '-') {
452 					if (*++cp == '\0')
453 						goto vererr;
454 					errno = 0;
455 					iconf->rpc_high_version = strtol(cp,
456 					    &endp, 10);
457 					if ((errno != 0) || (*endp != '\0'))
458 						goto vererr;
459 				} else if (*cp == '\0') {
460 					iconf->rpc_high_version =
461 					    iconf->rpc_low_version;
462 				} else {
463 vererr:
464 					(void) fprintf(stderr, gettext(
465 					    "%s: Error %s line %d invalid RPC "
466 					    "version in service: %s\n"),
467 					    progname, finfo->filename,
468 					    finfo->lineno, iconf->service);
469 					valid = B_FALSE;
470 				}
471 			}
472 		}
473 		proto += 4;	/* skip 'rpc/' */
474 	}
475 	/* tcp6only and udp6only are not valid in inetd.conf */
476 	if (is_v6only(proto)) {
477 		(void) fprintf(stderr, gettext("%s: Error %s line %d "
478 		    "invalid protocol: %s\n"), progname,
479 		    finfo->filename, finfo->lineno, proto);
480 		valid = B_FALSE;
481 	}
482 
483 	if (strcmp(iconf->wait_status, "wait") == 0) {
484 		iconf->wait = B_TRUE;
485 	} else if (strcmp(iconf->wait_status, "nowait") == 0) {
486 		iconf->wait = B_FALSE;
487 	} else {
488 		(void) fprintf(stderr,
489 		    gettext("%s: Error %s line %d invalid wait-status: %s\n"),
490 		    progname, finfo->filename, finfo->lineno,
491 		    iconf->wait_status);
492 		valid = B_FALSE;
493 	}
494 
495 	/* look up the username to set the groupname */
496 	if ((pwd = getpwnam(iconf->username)) == NULL) {
497 		(void) fprintf(stderr,
498 		    gettext("%s: Error %s line %d unknown user: %s\n"),
499 		    progname, finfo->filename, finfo->lineno,
500 		    iconf->username);
501 		valid = B_FALSE;
502 	} else {
503 		if ((grp = getgrgid(pwd->pw_gid)) != NULL) {
504 			iconf->groupname = safe_strdup(grp->gr_name);
505 		} else {
506 			/* use the group ID if no groupname */
507 			char s[1];
508 
509 			len = snprintf(s, 1, "%d", pwd->pw_gid) + 1;
510 			iconf->groupname = safe_malloc(len);
511 			(void) snprintf(iconf->groupname, len, "%d",
512 			    pwd->pw_gid);
513 		}
514 	}
515 
516 	/* check for internal services */
517 	if (strcmp(iconf->server_program, "internal") == 0) {
518 		valid = B_FALSE;
519 		if ((strcmp(iconf->service, "echo") == 0) ||
520 		    (strcmp(iconf->service, "discard") == 0) ||
521 		    (strcmp(iconf->service, "time") == 0) ||
522 		    (strcmp(iconf->service, "daytime") == 0) ||
523 		    (strcmp(iconf->service, "chargen") == 0)) {
524 			(void) fprintf(stderr, gettext(
525 			    "%s: Error %s line %d the SUNWcnsr and SUNWcnsu"
526 			    " packages contain the internal services\n"),
527 			    progname, finfo->filename, finfo->lineno);
528 		} else {
529 			(void) fprintf(stderr, gettext("%s: Error %s line %d "
530 			    "unknown internal service: %s\n"), progname,
531 			    finfo->filename, finfo->lineno, iconf->service);
532 		}
533 	} else if ((stat(iconf->server_program, &statb) == -1) &&
534 	    (errno == ENOENT)) {
535 		(void) fprintf(stderr, gettext(
536 		    "%s: Error %s line %d server-program not found: %s\n"),
537 		    progname, finfo->filename, finfo->lineno,
538 		    iconf->server_program);
539 		valid = B_FALSE;
540 	}
541 
542 	return (valid && valid_basic_properties(iconf, finfo));
543 }
544 
545 static void
546 free_inetconfent(struct inetconfent *iconf)
547 {
548 	if (iconf == NULL)
549 		return;
550 
551 	free(iconf->service);
552 	free(iconf->endpoint);
553 	free(iconf->protocol);
554 	free(iconf->wait_status);
555 	free(iconf->username);
556 	free(iconf->server_program);
557 	free(iconf->server_args);
558 	free(iconf->rpc_prog);
559 	free(iconf->groupname);
560 	free(iconf->exec);
561 	free(iconf->arg0);
562 
563 	free(iconf);
564 }
565 
566 static struct inetconfent *
567 line_to_inetconfent(char *line)
568 {
569 	char *cp;
570 	struct inetconfent *iconf;
571 
572 	iconf = safe_malloc(sizeof (struct inetconfent));
573 	(void) memset(iconf, 0, sizeof (struct inetconfent));
574 
575 	if ((cp = strtok(line, " \t\n")) == NULL)
576 		goto fail;
577 	iconf->service = safe_strdup(cp);
578 
579 	if ((cp = strtok(NULL, " \t\n")) == NULL)
580 		goto fail;
581 	iconf->endpoint = safe_strdup(cp);
582 
583 	if ((cp = strtok(NULL, " \t\n")) == NULL)
584 		goto fail;
585 	iconf->protocol = safe_strdup(cp);
586 
587 	if ((cp = strtok(NULL, " \t\n")) == NULL)
588 		goto fail;
589 	iconf->wait_status = safe_strdup(cp);
590 
591 	if ((cp = strtok(NULL, " \t\n")) == NULL)
592 		goto fail;
593 	iconf->username = safe_strdup(cp);
594 
595 	if ((cp = strtok(NULL, " \t\n")) == NULL)
596 		goto fail;
597 	iconf->server_program = safe_strdup(cp);
598 
599 	/* last field is optional */
600 	if ((cp = strtok(NULL, "\n")) != NULL)
601 		iconf->server_args = safe_strdup(cp);
602 
603 	/* Combine args and server name to construct exec and args fields */
604 	if (iconf->server_args == NULL) {
605 		iconf->exec = safe_strdup(iconf->server_program);
606 	} else {
607 		int len;
608 		char *args, *endp;
609 
610 		len = strlen(iconf->server_program) +
611 		    strlen(iconf->server_args) + 1;
612 		iconf->exec = safe_malloc(len);
613 		(void) strlcpy(iconf->exec, iconf->server_program, len);
614 
615 		args = safe_strdup(iconf->server_args);
616 		if ((cp = strtok(args, " \t")) != NULL) {
617 			if ((endp = strrchr(iconf->exec, '/')) == NULL)
618 				endp = iconf->exec;
619 			else
620 				endp++;
621 			/* only set arg0 property value if needed */
622 			if (strcmp(endp, cp) != 0)
623 				iconf->arg0 = safe_strdup(cp);
624 			while ((cp = strtok(NULL, " \t")) != NULL) {
625 				(void) strlcat(iconf->exec, " ", len);
626 				(void) strlcat(iconf->exec, cp, len);
627 			}
628 		}
629 		free(args);
630 	}
631 
632 	return (iconf);
633 fail:
634 	free_inetconfent(iconf);
635 	return (NULL);
636 }
637 
638 static void
639 skipline(FILE *fp)
640 {
641 	int c;
642 
643 	/* skip remainder of a line */
644 	while (((c = getc(fp)) != EOF) && (c != '\n'))
645 		;
646 }
647 
648 static struct inetconfent *
649 fgetinetconfent(struct fileinfo *finfo, boolean_t validate)
650 {
651 	char *cp;
652 	struct inetconfent *iconf;
653 	char line[MAX_SRC_LINELEN];
654 
655 	while (fgets(line, sizeof (line), finfo->fp) != NULL) {
656 		finfo->lineno++;
657 
658 		/* skip empty or commented out lines */
659 		if (*line == '\n')
660 			continue;
661 		if (*line == '#') {
662 			if (line[strlen(line) - 1] != '\n')
663 				skipline(finfo->fp);
664 			continue;
665 		}
666 		/* check for lines which are too long */
667 		if (line[strlen(line) - 1] != '\n') {
668 			(void) fprintf(stderr,
669 			    gettext("%s: Error %s line %d too long, skipped\n"),
670 			    progname, finfo->filename, finfo->lineno);
671 			skipline(finfo->fp);
672 			finfo->failcnt++;
673 			continue;
674 		}
675 		/* remove in line comments and newline character */
676 		if ((cp = strchr(line, '#')) == NULL)
677 			cp = strchr(line, '\n');
678 		if (cp)
679 			*cp = '\0';
680 
681 		if ((iconf = line_to_inetconfent(line)) == NULL) {
682 			(void) fprintf(stderr, gettext(
683 			    "%s: Error %s line %d too few fields, skipped\n"),
684 			    progname, finfo->filename, finfo->lineno);
685 			finfo->failcnt++;
686 			continue;
687 		}
688 
689 		if (!validate || valid_inetconfent(iconf, finfo))
690 			return (iconf);
691 
692 		finfo->failcnt++;
693 		free_inetconfent(iconf);
694 	}
695 	return (NULL);
696 }
697 
698 static char *
699 boolstr(boolean_t val)
700 {
701 	if (val)
702 		return ("true");
703 	return ("false");
704 }
705 
706 static int
707 print_manifest(FILE *f, char *filename, struct inetconfent *iconf)
708 {
709 	if (fprintf(f, xml_header) < 0)
710 		goto print_err;
711 
712 	if (fprintf(f, xml_comment,
713 	    iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0)
714 		goto print_err;
715 
716 	if (fprintf(f, xml_service_bundle, iconf->service) < 0)
717 		goto print_err;
718 	if (fprintf(f, xml_service_name, servicename(iconf)) < 0)
719 		goto print_err;
720 	if (fprintf(f, xml_instance) < 0)
721 		goto print_err;
722 	if (fprintf(f, xml_restarter, INETD_INSTANCE_FMRI) < 0)
723 		goto print_err;
724 	if (iconf->isrpc) {
725 		if (fprintf(f, xml_dependency, "rpcbind", RPCBIND_FMRI) < 0)
726 			goto print_err;
727 	}
728 
729 	if (fprintf(f, xml_exec_method_start, START_METHOD_NAME, PR_EXEC_NAME,
730 	    iconf->exec, PR_USER_NAME, iconf->username, iconf->groupname) < 0)
731 		goto print_err;
732 	if (iconf->arg0 != NULL) {
733 		if (fprintf(f, xml_arg0, PR_ARG0_NAME, iconf->arg0) < 0)
734 			goto print_err;
735 	}
736 	if (fprintf(f, xml_exec_method_end) < 0)
737 		goto print_err;
738 
739 	if (fprintf(f, xml_exec_method_disable, DISABLE_METHOD_NAME,
740 	    PR_EXEC_NAME) < 0)
741 		goto print_err;
742 	if (fprintf(f, xml_exec_method_end) < 0)
743 		goto print_err;
744 
745 	if (iconf->wait) {
746 		if (fprintf(f, xml_exec_method_offline, OFFLINE_METHOD_NAME,
747 		    PR_EXEC_NAME) < 0)
748 			goto print_err;
749 		if (fprintf(f, xml_exec_method_end) < 0)
750 			goto print_err;
751 	}
752 
753 	if (fprintf(f, xml_inetconv_group_start, PG_NAME_INETCONV,
754 	    PR_AUTO_CONVERTED_NAME, boolstr(B_TRUE),
755 	    PR_VERSION_NAME, INETCONV_VERSION,
756 	    PR_SOURCE_LINE_NAME, iconf->service,
757 	    iconf->endpoint, iconf->protocol, iconf->wait_status,
758 	    iconf->username, iconf->server_program,
759 	    iconf->server_args == NULL ? "" : " ",
760 	    iconf->server_args == NULL ? "" : iconf->server_args) < 0)
761 		goto print_err;
762 	if (fprintf(f, xml_property_group_end) < 0)
763 		goto print_err;
764 
765 	if (fprintf(f, xml_property_group_start, PG_NAME_SERVICE_CONFIG,
766 	    PR_SVC_NAME_NAME, iconf->isrpc ? iconf->rpc_prog : iconf->service,
767 	    PR_SOCK_TYPE_NAME, iconf->endpoint,
768 	    PR_PROTO_NAME, iconf->isrpc ? iconf->protocol + 4 :
769 	    iconf->protocol,
770 	    PR_ISWAIT_NAME, boolstr(iconf->wait),
771 	    PR_ISRPC_NAME, boolstr(iconf->isrpc)) < 0)
772 		goto print_err;
773 	if (iconf->isrpc) {
774 		if (fprintf(f, xml_property_group_rpc,
775 		    PR_RPC_LW_VER_NAME, iconf->rpc_low_version,
776 		    PR_RPC_HI_VER_NAME, iconf->rpc_high_version) < 0)
777 			goto print_err;
778 	}
779 	if (fprintf(f, xml_property_group_end) < 0)
780 		goto print_err;
781 
782 	if (fprintf(f, xml_stability) < 0)
783 		goto print_err;
784 	if (fprintf(f, xml_template,
785 	    iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0)
786 		goto print_err;
787 	if (fprintf(f, xml_footer) < 0)
788 		goto print_err;
789 
790 	(void) printf("%s -> %s\n", iconf->service, filename);
791 	return (0);
792 
793 print_err:
794 	(void) fprintf(stderr, gettext("%s: Error writing manifest %s: %s\n"),
795 	    progname, filename, strerror(errno));
796 	return (-1);
797 }
798 
799 static struct fileinfo *
800 open_srcfile(char *filename)
801 {
802 	struct fileinfo *finfo = NULL;
803 	FILE *fp;
804 
805 	if (filename != NULL) {
806 		if ((fp = fopen(filename, "r")) == NULL) {
807 			(void) fprintf(stderr,
808 			    gettext("%s: Error opening %s: %s\n"),
809 			    progname, filename, strerror(errno));
810 		}
811 	} else {
812 		/*
813 		 * If no source file specified, do the same as inetd and first
814 		 * try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
815 		 */
816 		filename = MAIN_CONFIG;
817 		if ((fp = fopen(filename, "r")) == NULL) {
818 			(void) fprintf(stderr,
819 			    gettext("%s: Error opening %s: %s\n"),
820 			    progname, filename, strerror(errno));
821 			filename = ALT_CONFIG;
822 			if ((fp = fopen(filename, "r")) == NULL) {
823 				(void) fprintf(stderr, gettext(
824 				    "%s: Error opening %s: %s\n"), progname,
825 				    filename, strerror(errno));
826 			}
827 		}
828 	}
829 	if (fp != NULL) {
830 		finfo = safe_malloc(sizeof (struct fileinfo));
831 		finfo->fp = fp;
832 		finfo->filename = filename;
833 		finfo->lineno = 0;
834 		finfo->failcnt = 0;
835 		(void) fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
836 	}
837 	return (finfo);
838 }
839 
840 /*
841  * Opens manifest output file.  Returns 0 on success, -1 if the file
842  * exists, -2 on other errors.
843  */
844 static int
845 open_dstfile(
846     char *destdir,
847     boolean_t overwrite,
848     struct inetconfent *iconf,
849     struct fileinfo **finfo)
850 {
851 	int fd;
852 	size_t len;
853 	char *dstfile, *cp, *proto;
854 	FILE *fp;
855 
856 	/* if no destdir specified, use appropriate default */
857 	if (destdir == NULL) {
858 		if (iconf->isrpc)
859 			destdir = MANIFEST_RPC_DIR;
860 		else
861 			destdir = MANIFEST_DIR;
862 	}
863 
864 	len = strlen(destdir) + strlen(iconf->service) +
865 	    strlen(iconf->protocol) + sizeof ("/-visible.xml");
866 	dstfile = safe_malloc(len);
867 
868 	(void) strlcpy(dstfile, destdir, len);
869 	if (dstfile[strlen(dstfile) - 1] != '/')
870 		(void) strlcat(dstfile, "/", len);
871 	cp = dstfile + strlen(dstfile);
872 
873 	(void) strlcat(dstfile, iconf->service, len);
874 	(void) strlcat(dstfile, "-", len);
875 
876 	proto = iconf->protocol;
877 	if (iconf->isrpc && (strcmp(iconf->protocol, "rpc/*") == 0))
878 		proto = "rpc/visible";
879 
880 	(void) strlcat(dstfile, proto, len);
881 	(void) strlcat(dstfile, ".xml", len);
882 
883 	/* convert any '/' chars in service or protocol to '_' chars */
884 	while ((cp = strchr(cp, '/')) != NULL)
885 		*cp = '_';
886 
887 	fd = open(dstfile, O_WRONLY|O_CREAT|(overwrite ? O_TRUNC : O_EXCL),
888 	    0644);
889 	if (fd == -1) {
890 		if (!overwrite && (errno == EEXIST)) {
891 			(void) fprintf(stderr,
892 			    gettext("%s: Notice: Service manifest for "
893 			    "%s already generated as %s, skipped\n"),
894 			    progname, iconf->service, dstfile);
895 			free(dstfile);
896 			return (-1);
897 		} else {
898 			(void) fprintf(stderr,
899 			    gettext("%s: Error opening %s: %s\n"),
900 			    progname, dstfile, strerror(errno));
901 			free(dstfile);
902 			return (-2);
903 		}
904 	}
905 	/* Clear errno to catch the "no stdio streams" case */
906 	errno = 0;
907 	if ((fp = fdopen(fd, "w")) == NULL) {
908 		char *s = strerror(errno);
909 		if (errno == 0)
910 			s = gettext("No stdio streams available");
911 		(void) fprintf(stderr, gettext("%s: Error fdopen failed: %s\n"),
912 		    progname, s);
913 		(void) close(fd);
914 		free(dstfile);
915 		return (-2);
916 	}
917 	*finfo = safe_malloc(sizeof (struct fileinfo));
918 	(*finfo)->fp = fp;
919 	(*finfo)->filename = dstfile;
920 	(*finfo)->lineno = 0;
921 	(*finfo)->failcnt = 0;
922 	return (0);
923 }
924 
925 static int
926 import_manifest(char *filename)
927 {
928 	int status;
929 	pid_t pid, wpid;
930 	char *cp;
931 
932 	if ((cp = strrchr(filename, '/')) == NULL)
933 		cp = filename;
934 	else
935 		cp++;
936 	(void) printf(gettext("Importing %s ..."), cp);
937 
938 	if ((pid = fork()) == -1) {
939 		(void) fprintf(stderr,
940 		    gettext("\n%s: fork failed, %s not imported: %s\n"),
941 		    progname, filename, strerror(errno));
942 		exit(EXIT_ERROR_SYS);
943 	}
944 	if (pid == 0) {
945 		/* child */
946 		(void) fclose(stdin);
947 		(void) setenv("SVCCFG_CHECKHASH", "1", 1);
948 		(void) execl(SVCCFG_PATH, "svccfg", "import", filename, NULL);
949 		(void) fprintf(stderr, gettext("\n%s: exec of %s failed: %s"),
950 		    progname, SVCCFG_PATH, strerror(errno));
951 		_exit(EXIT_ERROR_SYS);
952 	}
953 	/* parent */
954 	if ((wpid = waitpid(pid, &status, 0)) != pid) {
955 		(void) fprintf(stderr, gettext(
956 		    "\n%s: unexpected wait (%d) from import of %s: %s\n"),
957 		    progname, wpid, filename, strerror(errno));
958 		return (-1);
959 	}
960 	if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) {
961 		(void) fprintf(stderr,
962 		    gettext("\n%s: import failure (%d) for %s\n"),
963 		    progname, WEXITSTATUS(status), filename);
964 		return (-1);
965 	}
966 	(void) printf(gettext("Done\n"));
967 	return (0);
968 }
969 
970 static int
971 inetd_config_path(char **path)
972 {
973 	int fd;
974 	char *arg1, *configfile, *configstr;
975 	scf_simple_prop_t *sp;
976 	char cpath[PATH_MAX];
977 
978 	if ((sp = scf_simple_prop_get(NULL, INETD_INSTANCE_FMRI, "start",
979 	    SCF_PROPERTY_EXEC)) == NULL)
980 		return (-1);
981 	if ((configstr = scf_simple_prop_next_astring(sp)) == NULL) {
982 		scf_simple_prop_free(sp);
983 		return (-1);
984 	}
985 	configstr = safe_strdup(configstr);
986 	scf_simple_prop_free(sp);
987 
988 	/*
989 	 * Look for the optional configuration file, the syntax is:
990 	 * /usr/lib/inet/inetd [config-file] start|stop|refresh|disable|%m
991 	 */
992 	if (strtok(configstr, " \t") == NULL) {
993 		free(configstr);
994 		return (-1);
995 	}
996 	if ((arg1 = strtok(NULL, " \t")) == NULL) {
997 		free(configstr);
998 		return (-1);
999 	}
1000 	if (strtok(NULL, " \t") == NULL) {
1001 		/*
1002 		 * No configuration file specified, do the same as inetd and
1003 		 * first try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
1004 		 */
1005 		configfile = MAIN_CONFIG;
1006 		if ((fd = open(configfile, O_RDONLY)) >= 0)
1007 			(void) close(fd);
1008 		else
1009 			configfile = ALT_CONFIG;
1010 
1011 	} else {
1012 		/* make sure there are no more arguments */
1013 		if (strtok(NULL, " \t") != NULL) {
1014 			free(configstr);
1015 			return (-1);
1016 		}
1017 		configfile = arg1;
1018 	}
1019 
1020 	/* configuration file must be an absolute pathname */
1021 	if (*configfile != '/') {
1022 		free(configstr);
1023 		return (-1);
1024 	}
1025 
1026 	if (realpath(configfile, cpath) == NULL)
1027 		(void) strlcpy(cpath, configfile, sizeof (cpath));
1028 
1029 	free(configstr);
1030 	*path = safe_strdup(cpath);
1031 	return (0);
1032 }
1033 
1034 static int
1035 update_hash(char *srcfile)
1036 {
1037 	scf_error_t rval;
1038 	char *inetd_cpath, *hashstr;
1039 	char cpath[PATH_MAX];
1040 
1041 	/* determine the config file inetd is using */
1042 	if (inetd_config_path(&inetd_cpath) == -1) {
1043 		(void) fprintf(stderr,
1044 		    gettext("%s: Error reading from repository\n"), progname);
1045 		return (-1);
1046 	}
1047 
1048 	/* resolve inetconv input filename */
1049 	if (realpath(srcfile, cpath) == NULL)
1050 		(void) strlcpy(cpath, srcfile, sizeof (cpath));
1051 
1052 	/* if inetconv and inetd are using the same config file, update hash */
1053 	if (strcmp(cpath, inetd_cpath) != 0) {
1054 		free(inetd_cpath);
1055 		return (0);
1056 	}
1057 	free(inetd_cpath);
1058 
1059 	/* generic error message as use of hash is not exposed to the user */
1060 	if (calculate_hash(cpath, &hashstr) != 0) {
1061 		(void) fprintf(stderr,
1062 		    gettext("%s: Error unable to update repository\n"),
1063 		    progname);
1064 		return (-1);
1065 	}
1066 	/* generic error message as use of hash is not exposed to the user */
1067 	if ((rval = store_inetd_hash(hashstr)) != SCF_ERROR_NONE) {
1068 		(void) fprintf(stderr,
1069 		    gettext("%s: Error updating repository: %s\n"),
1070 		    progname, scf_strerror(rval));
1071 		free(hashstr);
1072 		return (-1);
1073 	}
1074 	free(hashstr);
1075 	return (0);
1076 }
1077 
1078 static void
1079 property_error(const char *fmri, const char *prop)
1080 {
1081 	(void) fprintf(stderr,
1082 	    gettext("Error: Instance %1$s is missing property '%2$s'.\n"),
1083 	    fmri, prop);
1084 }
1085 
1086 /*
1087  * modify_sprop takes a handle, an instance, a property group, a property,
1088  * and an astring value, and modifies the instance (or service's) specified
1089  * property in the repository to the submitted value.
1090  *
1091  * returns -1 on error, 1 on successful transaction completion.
1092  */
1093 
1094 static int
1095 modify_sprop(scf_handle_t *h, const scf_instance_t *inst,
1096     const char *pg, const char *prop, const char *value)
1097 {
1098 	scf_transaction_t		*tx = NULL;
1099 	scf_transaction_entry_t		*ent = NULL;
1100 	scf_propertygroup_t		*gpg = NULL;
1101 	scf_property_t			*eprop = NULL;
1102 	scf_value_t			*v = NULL;
1103 	scf_service_t			*svc = NULL;
1104 	int				ret = 0, create = 0;
1105 
1106 	if ((gpg = scf_pg_create(h)) == NULL)
1107 		return (-1);
1108 
1109 	/* Get the property group */
1110 	if (scf_instance_get_pg(inst, pg, gpg) == -1) {
1111 		/* Not a property of the instance, try the service instead */
1112 		if ((svc = scf_service_create(h)) == NULL) {
1113 			ret = -1;
1114 			goto out;
1115 		}
1116 		if ((scf_instance_get_parent(inst, svc) == -1) ||
1117 		    (scf_service_get_pg(svc, pg, gpg) == -1)) {
1118 			ret = -1;
1119 			goto out;
1120 		}
1121 	}
1122 
1123 	if ((eprop = scf_property_create(h)) == NULL) {
1124 		ret = -1;
1125 		goto out;
1126 	}
1127 
1128 	if (scf_pg_get_property(gpg, prop, eprop) == -1) {
1129 		if (scf_error() != SCF_ERROR_NOT_FOUND) {
1130 			ret = -1;
1131 			goto out;
1132 		}
1133 
1134 		create = 1;
1135 	}
1136 
1137 	if ((tx = scf_transaction_create(h)) == NULL ||
1138 	    (ent = scf_entry_create(h)) == NULL) {
1139 		ret = -1;
1140 		goto out;
1141 	}
1142 
1143 	do {
1144 		if (scf_transaction_start(tx, gpg) == -1) {
1145 			ret = -1;
1146 			goto out;
1147 		}
1148 
1149 		/* Modify the property */
1150 		if (create)
1151 			ret = scf_transaction_property_new(tx, ent, prop,
1152 			    SCF_TYPE_ASTRING);
1153 		else
1154 			ret = scf_transaction_property_change_type(tx, ent,
1155 			    prop, SCF_TYPE_ASTRING);
1156 
1157 		if (ret == -1)
1158 			goto out;
1159 
1160 		if ((v = scf_value_create(h)) == NULL) {
1161 			ret = -1;
1162 			goto out;
1163 		}
1164 
1165 		if (scf_value_set_astring(v, value) == -1) {
1166 			ret = -1;
1167 			goto out;
1168 		}
1169 
1170 		if (scf_entry_add_value(ent, v) == -1) {
1171 			ret = -1;
1172 			goto out;
1173 		}
1174 
1175 		ret = scf_transaction_commit(tx);
1176 
1177 		if (ret == 0) {
1178 			/* Property group was stale, retry */
1179 			if (scf_pg_update(gpg) == -1) {
1180 				ret = -1;
1181 				goto out;
1182 			}
1183 			scf_transaction_reset(tx);
1184 		}
1185 
1186 	} while (ret == 0);
1187 out:
1188 	scf_value_destroy(v);
1189 	scf_entry_destroy(ent);
1190 	scf_transaction_destroy(tx);
1191 	scf_property_destroy(eprop);
1192 	scf_service_destroy(svc);
1193 	scf_pg_destroy(gpg);
1194 
1195 	return (ret);
1196 }
1197 
1198 /*
1199  * list_callback is the callback function to be handed to simple_walk_instances
1200  * in main.  It is called once on every instance on a machine.  If that
1201  * instance is controlled by inetd, we test whether it's the same
1202  * service that we're looking at from the inetd.conf file, and enable it if
1203  * they are the same.
1204  */
1205 
1206 /*ARGSUSED*/
1207 static int
1208 list_callback(scf_handle_t *h, scf_instance_t *inst, void *buf)
1209 {
1210 	ssize_t			max_name_length;
1211 	char			*svc_name;
1212 	scf_simple_prop_t	*prop = NULL;
1213 	scf_simple_prop_t	*sockprop = NULL;
1214 	scf_simple_prop_t	*rpcprop = NULL;
1215 	scf_simple_prop_t	*progprop = NULL;
1216 	const char		*name, *endpoint, *restart_str, *prog;
1217 	struct inetconfent	*iconf = (struct inetconfent *)buf;
1218 	uint8_t			*isrpc;
1219 
1220 	max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1221 	if ((svc_name = malloc(max_name_length + 1)) == NULL) {
1222 		(void) fprintf(stderr, gettext("Error: Out of memory.\n"));
1223 		return (SCF_FAILED);
1224 	}
1225 
1226 	/*
1227 	 * Get the FMRI of the instance, and check if its delegated restarter
1228 	 * is inetd.  A missing or empty restarter property implies that
1229 	 * svc.startd is the restarter.
1230 	 */
1231 
1232 	if (scf_instance_to_fmri(inst, svc_name, max_name_length) < 0) {
1233 		(void) fprintf(stderr,
1234 		    gettext("Error: Unable to obtain FMRI for service %1$s."),
1235 		    svc_name);
1236 		free(svc_name);
1237 		return (SCF_FAILED);
1238 	}
1239 
1240 	if ((prop = scf_simple_prop_get(h, svc_name, SCF_PG_GENERAL,
1241 	    SCF_PROPERTY_RESTARTER)) == NULL)
1242 		goto out;
1243 
1244 	if ((restart_str = scf_simple_prop_next_ustring(prop)) == NULL)
1245 		goto out;
1246 
1247 	if (strcmp(restart_str, INETD_INSTANCE_FMRI) != 0)
1248 		goto out;
1249 
1250 	/* Free restarter prop so it can be reused below */
1251 	scf_simple_prop_free(prop);
1252 
1253 	/*
1254 	 * We know that this instance is managed by inetd.
1255 	 * Now get the properties needed to decide if it matches this
1256 	 * line in the old config file.
1257 	 */
1258 
1259 	if (((prop = scf_simple_prop_get(h, svc_name, PG_NAME_SERVICE_CONFIG,
1260 	    PR_SVC_NAME_NAME)) == NULL) ||
1261 	    ((name = scf_simple_prop_next_astring(prop)) == NULL)) {
1262 		property_error(svc_name, PR_SVC_NAME_NAME);
1263 		goto out;
1264 	}
1265 
1266 	if (((sockprop = scf_simple_prop_get(h, svc_name,
1267 	    PG_NAME_SERVICE_CONFIG, PR_SOCK_TYPE_NAME)) == NULL) ||
1268 	    ((endpoint = scf_simple_prop_next_astring(sockprop)) == NULL)) {
1269 		property_error(svc_name, PR_SOCK_TYPE_NAME);
1270 		goto out;
1271 	}
1272 
1273 	if (((rpcprop = scf_simple_prop_get(h, svc_name,
1274 	    PG_NAME_SERVICE_CONFIG, PR_ISRPC_NAME)) == NULL) ||
1275 	    ((isrpc = scf_simple_prop_next_boolean(rpcprop)) == NULL)) {
1276 		property_error(svc_name, PR_ISRPC_NAME);
1277 		goto out;
1278 	}
1279 
1280 	if (((progprop = scf_simple_prop_get(h, svc_name, START_METHOD_NAME,
1281 	    PR_EXEC_NAME)) == NULL) ||
1282 	    ((prog = scf_simple_prop_next_astring(progprop)) == NULL)) {
1283 		property_error(svc_name, PR_EXEC_NAME);
1284 	}
1285 
1286 
1287 	/* If it's RPC, we truncate off the version portion for comparison */
1288 	if (*isrpc) {
1289 		char *cp;
1290 
1291 		cp = strchr(iconf->service, '/');
1292 		if (cp != NULL)
1293 			*cp = '\0';
1294 	}
1295 
1296 	/*
1297 	 * If name of this service and endpoint are equal to values from
1298 	 * iconf fields, and they're either both RPC or both non-RPC,
1299 	 * then we have a match; update the exec and arg0 properties if
1300 	 * necessary, then enable it.
1301 	 * We don't return an error if either operation fails so that we
1302 	 * continue to try all the other services.
1303 	 */
1304 	if (strcmp(name, iconf->service) == 0 &&
1305 	    strcmp(endpoint, iconf->endpoint) == 0 &&
1306 	    *isrpc == (strncmp(iconf->protocol, "rpc/", 4) == 0)) {
1307 		/* Can't update exec on internal services */
1308 		if ((strcmp(iconf->server_program, "internal") != 0) &&
1309 		    (strcmp(iconf->exec, prog) != 0)) {
1310 			/* User had edited the command */
1311 			if (!import) {
1312 				/* Dry run only */
1313 				(void) printf(
1314 				    gettext("Would update %s to %s %s"),
1315 				    svc_name, PR_EXEC_NAME, iconf->exec);
1316 				if (iconf->arg0 != NULL) {
1317 					(void) printf(
1318 					    gettext(" with %s of %s\n"),
1319 					    PR_ARG0_NAME, iconf->arg0);
1320 				} else {
1321 					(void) printf("\n");
1322 				}
1323 			} else {
1324 				/* Update instance's exec property */
1325 				if (modify_sprop(h, inst, START_METHOD_NAME,
1326 				    PR_EXEC_NAME, iconf->exec) != 1)
1327 					(void) fprintf(stderr,
1328 					    gettext("Error: Unable to update "
1329 					    "%s property of %s, %s\n"),
1330 					    PR_EXEC_NAME, svc_name,
1331 					    scf_strerror(scf_error()));
1332 				else
1333 					(void) printf("%s will %s %s\n",
1334 					    svc_name, PR_EXEC_NAME,
1335 					    iconf->exec);
1336 
1337 				/* Update arg0 prop, if needed */
1338 				if (iconf->arg0 != NULL) {
1339 					if (modify_sprop(h, inst,
1340 					    START_METHOD_NAME, PR_ARG0_NAME,
1341 					    iconf->arg0) != 1) {
1342 						(void) fprintf(stderr,
1343 						    gettext("Error: Unable to "
1344 						    "update %s property of "
1345 						    "%s, %s\n"), PR_ARG0_NAME,
1346 						    svc_name,
1347 						    scf_strerror(scf_error()));
1348 					} else {
1349 						(void) printf("%s will have an "
1350 						    "%s of %s\n", svc_name,
1351 						    PR_ARG0_NAME, iconf->arg0);
1352 					}
1353 				}
1354 			}
1355 		}
1356 
1357 		if (!import) {
1358 			/* Dry-run only */
1359 			(void) printf("Would enable %s\n", svc_name);
1360 		} else {
1361 			if (smf_enable_instance(svc_name, 0) != 0)
1362 				(void) fprintf(stderr,
1363 				    gettext("Error: Failed to enable %s\n"),
1364 				    svc_name);
1365 			else
1366 				(void) printf("%s enabled\n", svc_name);
1367 		}
1368 	}
1369 
1370 out:
1371 	free(svc_name);
1372 	scf_simple_prop_free(prop);
1373 	scf_simple_prop_free(sockprop);
1374 	scf_simple_prop_free(rpcprop);
1375 	scf_simple_prop_free(progprop);
1376 	return (SCF_SUCCESS);
1377 }
1378 
1379 static void
1380 usage(void)
1381 {
1382 	(void) fprintf(stderr, gettext(
1383 	    "Usage: %s [-fn] [-i srcfile] [-o destdir]\n"
1384 	    "       %1$s -e [-n] [-i srcfile]\n"
1385 	    "-?          Display this usage message\n"
1386 	    "-e          Enable smf services which are enabled in the input\n"
1387 	    "            file\n"
1388 	    "-f          Force overwrite of existing manifests\n"
1389 	    "-n          Do not import converted manifests,\n"
1390 	    "            or only display services which would be enabled\n"
1391 	    "-i srcfile  Alternate input file\n"
1392 	    "-o destdir  Alternate output directory for manifests\n"),
1393 	    progname);
1394 	exit(EXIT_USAGE);
1395 }
1396 
1397 int
1398 main(int argc, char *argv[])
1399 {
1400 	int c, rval, convert_err, import_err = 0, enable_err = 0;
1401 	boolean_t overwrite = B_FALSE;
1402 	boolean_t enable = B_FALSE;
1403 	char *srcfile = NULL;
1404 	char *destdir = NULL;
1405 	struct fileinfo *srcfinfo, *dstfinfo;
1406 	struct inetconfent *iconf;
1407 
1408 	setbuf(stdout, NULL);
1409 	(void) setlocale(LC_ALL, "");
1410 	(void) textdomain(TEXT_DOMAIN);
1411 
1412 	if ((progname = strrchr(argv[0], '/')) == NULL)
1413 		progname = argv[0];
1414 	else
1415 		progname++;
1416 
1417 	while ((c = getopt(argc, argv, "?efni:o:")) != -1) {
1418 		switch (c) {
1419 		case 'e':
1420 			/* enable services based on existing file config */
1421 			enable = B_TRUE;
1422 			break;
1423 
1424 		case 'f':
1425 			/* overwrite existing manifests */
1426 			overwrite = B_TRUE;
1427 			break;
1428 		case 'n':
1429 			/* don't import manifests, or dry-run enable */
1430 			import = B_FALSE;
1431 			break;
1432 		case 'i':
1433 			/* alternate input file */
1434 			if (srcfile != NULL) {
1435 				(void) fprintf(stderr,
1436 				    gettext("%s: Error only one -%c allowed\n"),
1437 				    progname, optopt);
1438 				usage();
1439 			}
1440 			srcfile = optarg;
1441 			break;
1442 		case 'o':
1443 			/* alternate output directory */
1444 			if (destdir != NULL) {
1445 				(void) fprintf(stderr,
1446 				    gettext("%s: Error only one -%c allowed\n"),
1447 				    progname, optopt);
1448 				usage();
1449 			}
1450 			destdir = optarg;
1451 			break;
1452 		case '?': /*FALLTHROUGH*/
1453 		default:
1454 			usage();
1455 			break;
1456 		}
1457 	}
1458 
1459 	/*
1460 	 * Display usage if extraneous args supplied or enable specified in
1461 	 * combination with overwrite or destdir
1462 	 */
1463 	if ((optind != argc) || (enable && (overwrite || destdir != NULL)))
1464 		usage();
1465 
1466 	if ((srcfinfo = open_srcfile(srcfile)) == NULL)
1467 		return (EXIT_ERROR_CONV);
1468 
1469 	while ((iconf = fgetinetconfent(srcfinfo, !enable)) != NULL) {
1470 		/*
1471 		 * If we're enabling, then just walk all the services for each
1472 		 * line and enable those which match.
1473 		 */
1474 		if (enable) {
1475 			rval = scf_simple_walk_instances(SCF_STATE_ALL, iconf,
1476 			    list_callback);
1477 			free_inetconfent(iconf);
1478 			if (rval == SCF_FAILED) {
1479 				/* Only print msg if framework error */
1480 				if (scf_error() != SCF_ERROR_CALLBACK_FAILED)
1481 					(void) fprintf(stderr, gettext(
1482 					    "Error walking instances: %s.\n"),
1483 					    scf_strerror(scf_error()));
1484 				enable_err++;
1485 				break;
1486 			}
1487 			continue;
1488 		}
1489 
1490 		/* Remainder of loop used for conversion & import */
1491 		if ((rval = open_dstfile(destdir, overwrite, iconf, &dstfinfo))
1492 		    < 0) {
1493 			/*
1494 			 * Only increment error counter if the failure was
1495 			 * other than the file already existing.
1496 			 */
1497 			if (rval == -2)
1498 				srcfinfo->failcnt++;
1499 			free_inetconfent(iconf);
1500 			continue;
1501 		}
1502 		rval = print_manifest(dstfinfo->fp, dstfinfo->filename, iconf);
1503 		(void) fclose(dstfinfo->fp);
1504 		if (rval == 0) {
1505 			if (import &&
1506 			    (import_manifest(dstfinfo->filename) != 0))
1507 				import_err++;
1508 		} else {
1509 			(void) unlink(dstfinfo->filename);
1510 			srcfinfo->failcnt++;
1511 		}
1512 		free(dstfinfo->filename);
1513 		free(dstfinfo);
1514 		free_inetconfent(iconf);
1515 	}
1516 	(void) fclose(srcfinfo->fp);
1517 	convert_err = srcfinfo->failcnt;
1518 
1519 	/* Update hash only if not in enable mode, and only if importing */
1520 	if (!enable && import && (update_hash(srcfinfo->filename) != 0))
1521 		import_err++;
1522 
1523 	free(srcfinfo);
1524 
1525 	if (enable_err != 0)
1526 		return (EXIT_ERROR_ENBL);
1527 	if (import_err != 0)
1528 		return (EXIT_ERROR_IMP);
1529 	if (convert_err != 0)
1530 		return (EXIT_ERROR_CONV);
1531 	return (EXIT_SUCCESS);
1532 }
1533