xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv.c (revision cdc8986114b0e56584d6cdc3d92dca6f810c954f)
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 2005 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 /*
374  * wrapper around put_prop_value() that errors and exits on malloc failures,
375  * returns -1 on other failures, else returns 0.
376  */
377 static int
378 my_put_prop_value(inetd_prop_t *props, char *pname, void *value)
379 {
380 	if (put_prop_value(props, pname, value) == 0)
381 		return (0);
382 
383 	if (errno == ENOMEM) {
384 		(void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
385 		    progname, strerror(errno));
386 		exit(EXIT_ERROR_SYS);
387 	}
388 	return (-1);
389 }
390 
391 static boolean_t
392 valid_basic_properties(struct inetconfent *iconf, struct fileinfo *finfo)
393 {
394 	size_t prop_size;
395 	inetd_prop_t *prop, *inetd_properties;
396 	boolean_t valid = B_TRUE;
397 	char *proto = iconf->protocol;
398 	char *svc_name = iconf->service;
399 
400 	inetd_properties = get_prop_table(&prop_size);
401 	prop = safe_malloc(prop_size * sizeof (inetd_prop_t));
402 	(void) memcpy(prop, inetd_properties,
403 	    prop_size * sizeof (inetd_prop_t));
404 
405 	(void) put_prop_value(prop, PR_ISRPC_NAME, &iconf->isrpc);
406 	(void) put_prop_value(prop, PR_ISWAIT_NAME, &iconf->wait);
407 	if (iconf->isrpc) {
408 		(void) put_prop_value(prop, PR_RPC_LW_VER_NAME,
409 		    &iconf->rpc_low_version);
410 		(void) put_prop_value(prop, PR_RPC_HI_VER_NAME,
411 		    &iconf->rpc_high_version);
412 		svc_name = iconf->rpc_prog;
413 		proto += 4;	/* skip 'rpc/' */
414 	}
415 
416 	if ((my_put_prop_value(prop, PR_SOCK_TYPE_NAME, iconf->endpoint)
417 	    != 0) ||
418 	    (my_put_prop_value(prop, PR_SVC_NAME_NAME, svc_name) != 0) ||
419 	    (my_put_prop_value(prop, PR_PROTO_NAME, proto) != 0))
420 		valid = B_FALSE;
421 
422 	if (!valid_props(prop, NULL, NULL, NULL, NULL) || !valid) {
423 		valid = B_FALSE;
424 		(void) fprintf(stderr, gettext("%s: Error %s line %d "
425 		    "invalid or inconsistent fields:%s\n"), progname,
426 		    finfo->filename, finfo->lineno,
427 		    invalid_props(prop));
428 	}
429 
430 	free_instance_props(prop);
431 	return (valid);
432 }
433 
434 static boolean_t
435 valid_inetconfent(struct inetconfent *iconf, struct fileinfo *finfo)
436 {
437 	boolean_t valid = B_TRUE;
438 	size_t len;
439 	char *cp, *endp;
440 	struct passwd *pwd;
441 	struct group *grp;
442 	struct stat statb;
443 	char *proto = iconf->protocol;
444 
445 	iconf->isrpc = B_FALSE;
446 	if (strncmp(iconf->protocol, "rpc/", 4) == 0) {
447 		iconf->isrpc = B_TRUE;
448 		iconf->rpc_prog = safe_strdup(iconf->service);
449 
450 		/* set RPC version numbers */
451 		iconf->rpc_low_version = 1;
452 		iconf->rpc_high_version = 1;
453 		if ((cp = strrchr(iconf->rpc_prog, '/')) != NULL) {
454 			*cp = '\0';
455 			if (*++cp != '\0') {
456 				errno = 0;
457 				iconf->rpc_low_version = strtol(cp, &endp, 10);
458 				if (errno != 0)
459 					goto vererr;
460 				cp = endp;
461 				if (*cp == '-') {
462 					if (*++cp == '\0')
463 						goto vererr;
464 					errno = 0;
465 					iconf->rpc_high_version = strtol(cp,
466 					    &endp, 10);
467 					if ((errno != 0) || (*endp != '\0'))
468 						goto vererr;
469 				} else if (*cp == '\0') {
470 					iconf->rpc_high_version =
471 					    iconf->rpc_low_version;
472 				} else {
473 vererr:
474 					(void) fprintf(stderr, gettext(
475 					    "%s: Error %s line %d invalid RPC "
476 					    "version in service: %s\n"),
477 					    progname, finfo->filename,
478 					    finfo->lineno, iconf->service);
479 					valid = B_FALSE;
480 				}
481 			}
482 		}
483 		proto += 4;	/* skip 'rpc/' */
484 	}
485 	/* tcp6only and udp6only are not valid in inetd.conf */
486 	if (is_v6only(proto)) {
487 		(void) fprintf(stderr, gettext("%s: Error %s line %d "
488 		    "invalid protocol: %s\n"), progname,
489 		    finfo->filename, finfo->lineno, proto);
490 		valid = B_FALSE;
491 	}
492 
493 	if (strcmp(iconf->wait_status, "wait") == 0) {
494 		iconf->wait = B_TRUE;
495 	} else if (strcmp(iconf->wait_status, "nowait") == 0) {
496 		iconf->wait = B_FALSE;
497 	} else {
498 		(void) fprintf(stderr,
499 		    gettext("%s: Error %s line %d invalid wait-status: %s\n"),
500 		    progname, finfo->filename, finfo->lineno,
501 		    iconf->wait_status);
502 		valid = B_FALSE;
503 	}
504 
505 	/* look up the username to set the groupname */
506 	if ((pwd = getpwnam(iconf->username)) == NULL) {
507 		(void) fprintf(stderr,
508 		    gettext("%s: Error %s line %d unknown user: %s\n"),
509 		    progname, finfo->filename, finfo->lineno,
510 		    iconf->username);
511 		valid = B_FALSE;
512 	} else {
513 		if ((grp = getgrgid(pwd->pw_gid)) != NULL) {
514 			iconf->groupname = safe_strdup(grp->gr_name);
515 		} else {
516 			/* use the group ID if no groupname */
517 			char s[1];
518 
519 			len = snprintf(s, 1, "%d", pwd->pw_gid) + 1;
520 			iconf->groupname = safe_malloc(len);
521 			(void) snprintf(iconf->groupname, len, "%d",
522 			    pwd->pw_gid);
523 		}
524 	}
525 
526 	/* check for internal services */
527 	if (strcmp(iconf->server_program, "internal") == 0) {
528 		valid = B_FALSE;
529 		if ((strcmp(iconf->service, "echo") == 0) ||
530 		    (strcmp(iconf->service, "discard") == 0) ||
531 		    (strcmp(iconf->service, "time") == 0) ||
532 		    (strcmp(iconf->service, "daytime") == 0) ||
533 		    (strcmp(iconf->service, "chargen") == 0)) {
534 			(void) fprintf(stderr, gettext(
535 			    "%s: Error %s line %d the SUNWcnsr and SUNWcnsu"
536 			    " packages contain the internal services\n"),
537 			    progname, finfo->filename, finfo->lineno);
538 		} else {
539 			(void) fprintf(stderr, gettext("%s: Error %s line %d "
540 			    "unknown internal service: %s\n"), progname,
541 			    finfo->filename, finfo->lineno, iconf->service);
542 		}
543 	} else if ((stat(iconf->server_program, &statb) == -1) &&
544 	    (errno == ENOENT)) {
545 		(void) fprintf(stderr, gettext(
546 		    "%s: Error %s line %d server-program not found: %s\n"),
547 		    progname, finfo->filename, finfo->lineno,
548 		    iconf->server_program);
549 		valid = B_FALSE;
550 	}
551 
552 	return (valid && valid_basic_properties(iconf, finfo));
553 }
554 
555 static void
556 free_inetconfent(struct inetconfent *iconf)
557 {
558 	if (iconf == NULL)
559 		return;
560 
561 	free(iconf->service);
562 	free(iconf->endpoint);
563 	free(iconf->protocol);
564 	free(iconf->wait_status);
565 	free(iconf->username);
566 	free(iconf->server_program);
567 	free(iconf->server_args);
568 	free(iconf->rpc_prog);
569 	free(iconf->groupname);
570 	free(iconf->exec);
571 	free(iconf->arg0);
572 
573 	free(iconf);
574 }
575 
576 static struct inetconfent *
577 line_to_inetconfent(char *line)
578 {
579 	char *cp;
580 	struct inetconfent *iconf;
581 
582 	iconf = safe_malloc(sizeof (struct inetconfent));
583 	(void) memset(iconf, 0, sizeof (struct inetconfent));
584 
585 	if ((cp = strtok(line, " \t\n")) == NULL)
586 		goto fail;
587 	iconf->service = safe_strdup(cp);
588 
589 	if ((cp = strtok(NULL, " \t\n")) == NULL)
590 		goto fail;
591 	iconf->endpoint = safe_strdup(cp);
592 
593 	if ((cp = strtok(NULL, " \t\n")) == NULL)
594 		goto fail;
595 	iconf->protocol = safe_strdup(cp);
596 
597 	if ((cp = strtok(NULL, " \t\n")) == NULL)
598 		goto fail;
599 	iconf->wait_status = safe_strdup(cp);
600 
601 	if ((cp = strtok(NULL, " \t\n")) == NULL)
602 		goto fail;
603 	iconf->username = safe_strdup(cp);
604 
605 	if ((cp = strtok(NULL, " \t\n")) == NULL)
606 		goto fail;
607 	iconf->server_program = safe_strdup(cp);
608 
609 	/* last field is optional */
610 	if ((cp = strtok(NULL, "\n")) != NULL)
611 		iconf->server_args = safe_strdup(cp);
612 
613 	/* Combine args and server name to construct exec and args fields */
614 	if (iconf->server_args == NULL) {
615 		iconf->exec = safe_strdup(iconf->server_program);
616 	} else {
617 		int len;
618 		char *args, *endp;
619 
620 		len = strlen(iconf->server_program) +
621 		    strlen(iconf->server_args) + 1;
622 		iconf->exec = safe_malloc(len);
623 		(void) strlcpy(iconf->exec, iconf->server_program, len);
624 
625 		args = safe_strdup(iconf->server_args);
626 		if ((cp = strtok(args, " \t")) != NULL) {
627 			if ((endp = strrchr(iconf->exec, '/')) == NULL)
628 				endp = iconf->exec;
629 			else
630 				endp++;
631 			/* only set arg0 property value if needed */
632 			if (strcmp(endp, cp) != 0)
633 				iconf->arg0 = safe_strdup(cp);
634 			while ((cp = strtok(NULL, " \t")) != NULL) {
635 				(void) strlcat(iconf->exec, " ", len);
636 				(void) strlcat(iconf->exec, cp, len);
637 			}
638 		}
639 		free(args);
640 	}
641 
642 	return (iconf);
643 fail:
644 	free_inetconfent(iconf);
645 	return (NULL);
646 }
647 
648 static void
649 skipline(FILE *fp)
650 {
651 	int c;
652 
653 	/* skip remainder of a line */
654 	while (((c = getc(fp)) != EOF) && (c != '\n'))
655 		;
656 }
657 
658 static struct inetconfent *
659 fgetinetconfent(struct fileinfo *finfo, boolean_t validate)
660 {
661 	char *cp;
662 	struct inetconfent *iconf;
663 	char line[MAX_SRC_LINELEN];
664 
665 	while (fgets(line, sizeof (line), finfo->fp) != NULL) {
666 		finfo->lineno++;
667 
668 		/* skip empty or commented out lines */
669 		if (*line == '\n')
670 			continue;
671 		if (*line == '#') {
672 			if (line[strlen(line) - 1] != '\n')
673 				skipline(finfo->fp);
674 			continue;
675 		}
676 		/* check for lines which are too long */
677 		if (line[strlen(line) - 1] != '\n') {
678 			(void) fprintf(stderr,
679 			    gettext("%s: Error %s line %d too long, skipped\n"),
680 			    progname, finfo->filename, finfo->lineno);
681 			skipline(finfo->fp);
682 			finfo->failcnt++;
683 			continue;
684 		}
685 		/* remove in line comments and newline character */
686 		if ((cp = strchr(line, '#')) == NULL)
687 			cp = strchr(line, '\n');
688 		if (cp)
689 			*cp = '\0';
690 
691 		if ((iconf = line_to_inetconfent(line)) == NULL) {
692 			(void) fprintf(stderr, gettext(
693 			    "%s: Error %s line %d too few fields, skipped\n"),
694 			    progname, finfo->filename, finfo->lineno);
695 			finfo->failcnt++;
696 			continue;
697 		}
698 
699 		if (!validate || valid_inetconfent(iconf, finfo))
700 			return (iconf);
701 
702 		finfo->failcnt++;
703 		free_inetconfent(iconf);
704 	}
705 	return (NULL);
706 }
707 
708 static char *
709 boolstr(boolean_t val)
710 {
711 	if (val)
712 		return ("true");
713 	return ("false");
714 }
715 
716 static int
717 print_manifest(FILE *f, char *filename, struct inetconfent *iconf)
718 {
719 	if (fprintf(f, xml_header) < 0)
720 		goto print_err;
721 
722 	if (fprintf(f, xml_comment,
723 	    iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0)
724 		goto print_err;
725 
726 	if (fprintf(f, xml_service_bundle, iconf->service) < 0)
727 		goto print_err;
728 	if (fprintf(f, xml_service_name, servicename(iconf)) < 0)
729 		goto print_err;
730 	if (fprintf(f, xml_instance) < 0)
731 		goto print_err;
732 	if (fprintf(f, xml_restarter, INETD_INSTANCE_FMRI) < 0)
733 		goto print_err;
734 	if (iconf->isrpc) {
735 		if (fprintf(f, xml_dependency, "rpcbind", RPCBIND_FMRI) < 0)
736 			goto print_err;
737 	}
738 
739 	if (fprintf(f, xml_exec_method_start, START_METHOD_NAME, PR_EXEC_NAME,
740 	    iconf->exec, PR_USER_NAME, iconf->username, iconf->groupname) < 0)
741 		goto print_err;
742 	if (iconf->arg0 != NULL) {
743 		if (fprintf(f, xml_arg0, PR_ARG0_NAME, iconf->arg0) < 0)
744 			goto print_err;
745 	}
746 	if (fprintf(f, xml_exec_method_end) < 0)
747 		goto print_err;
748 
749 	if (fprintf(f, xml_exec_method_disable, DISABLE_METHOD_NAME,
750 	    PR_EXEC_NAME) < 0)
751 		goto print_err;
752 	if (fprintf(f, xml_exec_method_end) < 0)
753 		goto print_err;
754 
755 	if (iconf->wait) {
756 		if (fprintf(f, xml_exec_method_offline, OFFLINE_METHOD_NAME,
757 		    PR_EXEC_NAME) < 0)
758 			goto print_err;
759 		if (fprintf(f, xml_exec_method_end) < 0)
760 			goto print_err;
761 	}
762 
763 	if (fprintf(f, xml_inetconv_group_start, PG_NAME_INETCONV,
764 	    PR_AUTO_CONVERTED_NAME, boolstr(B_TRUE),
765 	    PR_VERSION_NAME, INETCONV_VERSION,
766 	    PR_SOURCE_LINE_NAME, iconf->service,
767 	    iconf->endpoint, iconf->protocol, iconf->wait_status,
768 	    iconf->username, iconf->server_program,
769 	    iconf->server_args == NULL ? "" : " ",
770 	    iconf->server_args == NULL ? "" : iconf->server_args) < 0)
771 		goto print_err;
772 	if (fprintf(f, xml_property_group_end) < 0)
773 		goto print_err;
774 
775 	if (fprintf(f, xml_property_group_start, PG_NAME_SERVICE_CONFIG,
776 	    PR_SVC_NAME_NAME, iconf->isrpc ? iconf->rpc_prog : iconf->service,
777 	    PR_SOCK_TYPE_NAME, iconf->endpoint,
778 	    PR_PROTO_NAME, iconf->isrpc ? iconf->protocol + 4 :
779 	    iconf->protocol,
780 	    PR_ISWAIT_NAME, boolstr(iconf->wait),
781 	    PR_ISRPC_NAME, boolstr(iconf->isrpc)) < 0)
782 		goto print_err;
783 	if (iconf->isrpc) {
784 		if (fprintf(f, xml_property_group_rpc,
785 		    PR_RPC_LW_VER_NAME, iconf->rpc_low_version,
786 		    PR_RPC_HI_VER_NAME, iconf->rpc_high_version) < 0)
787 			goto print_err;
788 	}
789 	if (fprintf(f, xml_property_group_end) < 0)
790 		goto print_err;
791 
792 	if (fprintf(f, xml_stability) < 0)
793 		goto print_err;
794 	if (fprintf(f, xml_template,
795 	    iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0)
796 		goto print_err;
797 	if (fprintf(f, xml_footer) < 0)
798 		goto print_err;
799 
800 	(void) printf("%s -> %s\n", iconf->service, filename);
801 	return (0);
802 
803 print_err:
804 	(void) fprintf(stderr, gettext("%s: Error writing manifest %s: %s\n"),
805 	    progname, filename, strerror(errno));
806 	return (-1);
807 }
808 
809 static struct fileinfo *
810 open_srcfile(char *filename)
811 {
812 	struct fileinfo *finfo = NULL;
813 	FILE *fp;
814 
815 	if (filename != NULL) {
816 		if ((fp = fopen(filename, "r")) == NULL) {
817 			(void) fprintf(stderr,
818 			    gettext("%s: Error opening %s: %s\n"),
819 			    progname, filename, strerror(errno));
820 		}
821 	} else {
822 		/*
823 		 * If no source file specified, do the same as inetd and first
824 		 * try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
825 		 */
826 		filename = MAIN_CONFIG;
827 		if ((fp = fopen(filename, "r")) == NULL) {
828 			(void) fprintf(stderr,
829 			    gettext("%s: Error opening %s: %s\n"),
830 			    progname, filename, strerror(errno));
831 			filename = ALT_CONFIG;
832 			if ((fp = fopen(filename, "r")) == NULL) {
833 				(void) fprintf(stderr, gettext(
834 				    "%s: Error opening %s: %s\n"), progname,
835 				    filename, strerror(errno));
836 			}
837 		}
838 	}
839 	if (fp != NULL) {
840 		finfo = safe_malloc(sizeof (struct fileinfo));
841 		finfo->fp = fp;
842 		finfo->filename = filename;
843 		finfo->lineno = 0;
844 		finfo->failcnt = 0;
845 		(void) fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
846 	}
847 	return (finfo);
848 }
849 
850 /*
851  * Opens manifest output file.  Returns 0 on success, -1 if the file
852  * exists, -2 on other errors.
853  */
854 static int
855 open_dstfile(
856     char *destdir,
857     boolean_t overwrite,
858     struct inetconfent *iconf,
859     struct fileinfo **finfo)
860 {
861 	int fd;
862 	size_t len;
863 	char *dstfile, *cp, *proto;
864 	FILE *fp;
865 
866 	/* if no destdir specified, use appropriate default */
867 	if (destdir == NULL) {
868 		if (iconf->isrpc)
869 			destdir = MANIFEST_RPC_DIR;
870 		else
871 			destdir = MANIFEST_DIR;
872 	}
873 
874 	len = strlen(destdir) + strlen(iconf->service) +
875 	    strlen(iconf->protocol) + sizeof ("/-visible.xml");
876 	dstfile = safe_malloc(len);
877 
878 	(void) strlcpy(dstfile, destdir, len);
879 	if (dstfile[strlen(dstfile) - 1] != '/')
880 		(void) strlcat(dstfile, "/", len);
881 	cp = dstfile + strlen(dstfile);
882 
883 	(void) strlcat(dstfile, iconf->service, len);
884 	(void) strlcat(dstfile, "-", len);
885 
886 	proto = iconf->protocol;
887 	if (iconf->isrpc && (strcmp(iconf->protocol, "rpc/*") == 0))
888 		proto = "rpc/visible";
889 
890 	(void) strlcat(dstfile, proto, len);
891 	(void) strlcat(dstfile, ".xml", len);
892 
893 	/* convert any '/' chars in service or protocol to '_' chars */
894 	while ((cp = strchr(cp, '/')) != NULL)
895 		*cp = '_';
896 
897 	fd = open(dstfile, O_WRONLY|O_CREAT|(overwrite ? O_TRUNC : O_EXCL),
898 	    0644);
899 	if (fd == -1) {
900 		if (!overwrite && (errno == EEXIST)) {
901 			(void) fprintf(stderr,
902 			    gettext("%s: Notice: Service manifest for "
903 			    "%s already generated as %s, skipped\n"),
904 			    progname, iconf->service, dstfile);
905 			free(dstfile);
906 			return (-1);
907 		} else {
908 			(void) fprintf(stderr,
909 			    gettext("%s: Error opening %s: %s\n"),
910 			    progname, dstfile, strerror(errno));
911 			free(dstfile);
912 			return (-2);
913 		}
914 	}
915 	/* Clear errno to catch the "no stdio streams" case */
916 	errno = 0;
917 	if ((fp = fdopen(fd, "w")) == NULL) {
918 		char *s = strerror(errno);
919 		if (errno == 0)
920 			s = gettext("No stdio streams available");
921 		(void) fprintf(stderr, gettext("%s: Error fdopen failed: %s\n"),
922 		    progname, s);
923 		(void) close(fd);
924 		free(dstfile);
925 		return (-2);
926 	}
927 	*finfo = safe_malloc(sizeof (struct fileinfo));
928 	(*finfo)->fp = fp;
929 	(*finfo)->filename = dstfile;
930 	(*finfo)->lineno = 0;
931 	(*finfo)->failcnt = 0;
932 	return (0);
933 }
934 
935 static int
936 import_manifest(char *filename)
937 {
938 	int status;
939 	pid_t pid, wpid;
940 	char *cp;
941 
942 	if ((cp = strrchr(filename, '/')) == NULL)
943 		cp = filename;
944 	else
945 		cp++;
946 	(void) printf(gettext("Importing %s ..."), cp);
947 
948 	if ((pid = fork()) == -1) {
949 		(void) fprintf(stderr,
950 		    gettext("\n%s: fork failed, %s not imported: %s\n"),
951 		    progname, filename, strerror(errno));
952 		exit(EXIT_ERROR_SYS);
953 	}
954 	if (pid == 0) {
955 		/* child */
956 		(void) fclose(stdin);
957 		(void) setenv("SVCCFG_CHECKHASH", "1", 1);
958 		(void) execl(SVCCFG_PATH, "svccfg", "import", filename, NULL);
959 		(void) fprintf(stderr, gettext("\n%s: exec of %s failed: %s"),
960 		    progname, SVCCFG_PATH, strerror(errno));
961 		_exit(EXIT_ERROR_SYS);
962 	}
963 	/* parent */
964 	if ((wpid = waitpid(pid, &status, 0)) != pid) {
965 		(void) fprintf(stderr, gettext(
966 		    "\n%s: unexpected wait (%d) from import of %s: %s\n"),
967 		    progname, wpid, filename, strerror(errno));
968 		return (-1);
969 	}
970 	if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) {
971 		(void) fprintf(stderr,
972 		    gettext("\n%s: import failure (%d) for %s\n"),
973 		    progname, WEXITSTATUS(status), filename);
974 		return (-1);
975 	}
976 	(void) printf(gettext("Done\n"));
977 	return (0);
978 }
979 
980 static int
981 inetd_config_path(char **path)
982 {
983 	int fd;
984 	char *arg1, *configfile, *configstr;
985 	scf_simple_prop_t *sp;
986 	char cpath[PATH_MAX];
987 
988 	if ((sp = scf_simple_prop_get(NULL, INETD_INSTANCE_FMRI, "start",
989 	    SCF_PROPERTY_EXEC)) == NULL)
990 		return (-1);
991 	if ((configstr = scf_simple_prop_next_astring(sp)) == NULL) {
992 		scf_simple_prop_free(sp);
993 		return (-1);
994 	}
995 	configstr = safe_strdup(configstr);
996 	scf_simple_prop_free(sp);
997 
998 	/*
999 	 * Look for the optional configuration file, the syntax is:
1000 	 * /usr/lib/inet/inetd [config-file] start|stop|refresh|disable|%m
1001 	 */
1002 	if (strtok(configstr, " \t") == NULL) {
1003 		free(configstr);
1004 		return (-1);
1005 	}
1006 	if ((arg1 = strtok(NULL, " \t")) == NULL) {
1007 		free(configstr);
1008 		return (-1);
1009 	}
1010 	if (strtok(NULL, " \t") == NULL) {
1011 		/*
1012 		 * No configuration file specified, do the same as inetd and
1013 		 * first try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
1014 		 */
1015 		configfile = MAIN_CONFIG;
1016 		if ((fd = open(configfile, O_RDONLY)) >= 0)
1017 			(void) close(fd);
1018 		else
1019 			configfile = ALT_CONFIG;
1020 
1021 	} else {
1022 		/* make sure there are no more arguments */
1023 		if (strtok(NULL, " \t") != NULL) {
1024 			free(configstr);
1025 			return (-1);
1026 		}
1027 		configfile = arg1;
1028 	}
1029 
1030 	/* configuration file must be an absolute pathname */
1031 	if (*configfile != '/') {
1032 		free(configstr);
1033 		return (-1);
1034 	}
1035 
1036 	if (realpath(configfile, cpath) == NULL)
1037 		(void) strlcpy(cpath, configfile, sizeof (cpath));
1038 
1039 	free(configstr);
1040 	*path = safe_strdup(cpath);
1041 	return (0);
1042 }
1043 
1044 static int
1045 update_hash(char *srcfile)
1046 {
1047 	scf_error_t rval;
1048 	char *inetd_cpath, *hashstr;
1049 	char cpath[PATH_MAX];
1050 
1051 	/* determine the config file inetd is using */
1052 	if (inetd_config_path(&inetd_cpath) == -1) {
1053 		(void) fprintf(stderr,
1054 		    gettext("%s: Error reading from repository\n"), progname);
1055 		return (-1);
1056 	}
1057 
1058 	/* resolve inetconv input filename */
1059 	if (realpath(srcfile, cpath) == NULL)
1060 		(void) strlcpy(cpath, srcfile, sizeof (cpath));
1061 
1062 	/* if inetconv and inetd are using the same config file, update hash */
1063 	if (strcmp(cpath, inetd_cpath) != 0) {
1064 		free(inetd_cpath);
1065 		return (0);
1066 	}
1067 	free(inetd_cpath);
1068 
1069 	/* generic error message as use of hash is not exposed to the user */
1070 	if (calculate_hash(cpath, &hashstr) != 0) {
1071 		(void) fprintf(stderr,
1072 		    gettext("%s: Error unable to update repository\n"),
1073 		    progname);
1074 		return (-1);
1075 	}
1076 	/* generic error message as use of hash is not exposed to the user */
1077 	if ((rval = store_inetd_hash(hashstr)) != SCF_ERROR_NONE) {
1078 		(void) fprintf(stderr,
1079 		    gettext("%s: Error updating repository: %s\n"),
1080 		    progname, scf_strerror(rval));
1081 		free(hashstr);
1082 		return (-1);
1083 	}
1084 	free(hashstr);
1085 	return (0);
1086 }
1087 
1088 static void
1089 property_error(const char *fmri, const char *prop)
1090 {
1091 	(void) fprintf(stderr,
1092 	    gettext("Error: Instance %1$s is missing property '%2$s'.\n"),
1093 	    fmri, prop);
1094 }
1095 
1096 /*
1097  * modify_sprop takes a handle, an instance, a property group, a property,
1098  * and an astring value, and modifies the instance (or service's) specified
1099  * property in the repository to the submitted value.
1100  *
1101  * returns -1 on error, 1 on successful transaction completion.
1102  */
1103 
1104 static int
1105 modify_sprop(scf_handle_t *h, const scf_instance_t *inst,
1106     const char *pg, const char *prop, const char *value)
1107 {
1108 	scf_transaction_t		*tx = NULL;
1109 	scf_transaction_entry_t		*ent = NULL;
1110 	scf_propertygroup_t		*gpg = NULL;
1111 	scf_property_t			*eprop = NULL;
1112 	scf_value_t			*v = NULL;
1113 	scf_service_t			*svc = NULL;
1114 	int				ret = 0, create = 0;
1115 
1116 	if ((gpg = scf_pg_create(h)) == NULL)
1117 		return (-1);
1118 
1119 	/* Get the property group */
1120 	if (scf_instance_get_pg(inst, pg, gpg) == -1) {
1121 		/* Not a property of the instance, try the service instead */
1122 		if ((svc = scf_service_create(h)) == NULL) {
1123 			ret = -1;
1124 			goto out;
1125 		}
1126 		if ((scf_instance_get_parent(inst, svc) == -1) ||
1127 		    (scf_service_get_pg(svc, pg, gpg) == -1)) {
1128 			ret = -1;
1129 			goto out;
1130 		}
1131 	}
1132 
1133 	if ((eprop = scf_property_create(h)) == NULL) {
1134 		ret = -1;
1135 		goto out;
1136 	}
1137 
1138 	if (scf_pg_get_property(gpg, prop, eprop) == -1) {
1139 		if (scf_error() != SCF_ERROR_NOT_FOUND) {
1140 			ret = -1;
1141 			goto out;
1142 		}
1143 
1144 		create = 1;
1145 	}
1146 
1147 	if ((tx = scf_transaction_create(h)) == NULL ||
1148 	    (ent = scf_entry_create(h)) == NULL) {
1149 		ret = -1;
1150 		goto out;
1151 	}
1152 
1153 	do {
1154 		if (scf_transaction_start(tx, gpg) == -1) {
1155 			ret = -1;
1156 			goto out;
1157 		}
1158 
1159 		/* Modify the property */
1160 		if (create)
1161 			ret = scf_transaction_property_new(tx, ent, prop,
1162 			    SCF_TYPE_ASTRING);
1163 		else
1164 			ret = scf_transaction_property_change_type(tx, ent,
1165 			    prop, SCF_TYPE_ASTRING);
1166 
1167 		if (ret == -1)
1168 			goto out;
1169 
1170 		if ((v = scf_value_create(h)) == NULL) {
1171 			ret = -1;
1172 			goto out;
1173 		}
1174 
1175 		if (scf_value_set_astring(v, value) == -1) {
1176 			ret = -1;
1177 			goto out;
1178 		}
1179 
1180 		if (scf_entry_add_value(ent, v) == -1) {
1181 			ret = -1;
1182 			goto out;
1183 		}
1184 
1185 		ret = scf_transaction_commit(tx);
1186 
1187 		if (ret == 0) {
1188 			/* Property group was stale, retry */
1189 			if (scf_pg_update(gpg) == -1) {
1190 				ret = -1;
1191 				goto out;
1192 			}
1193 			scf_transaction_reset(tx);
1194 		}
1195 
1196 	} while (ret == 0);
1197 out:
1198 	scf_value_destroy(v);
1199 	scf_entry_destroy(ent);
1200 	scf_transaction_destroy(tx);
1201 	scf_property_destroy(eprop);
1202 	scf_service_destroy(svc);
1203 	scf_pg_destroy(gpg);
1204 
1205 	return (ret);
1206 }
1207 
1208 /*
1209  * list_callback is the callback function to be handed to simple_walk_instances
1210  * in main.  It is called once on every instance on a machine.  If that
1211  * instance is controlled by inetd, we test whether it's the same
1212  * service that we're looking at from the inetd.conf file, and enable it if
1213  * they are the same.
1214  */
1215 
1216 /*ARGSUSED*/
1217 static int
1218 list_callback(scf_handle_t *h, scf_instance_t *inst, void *buf)
1219 {
1220 	ssize_t			max_name_length;
1221 	char			*svc_name;
1222 	scf_simple_prop_t	*prop = NULL;
1223 	scf_simple_prop_t	*sockprop = NULL;
1224 	scf_simple_prop_t	*rpcprop = NULL;
1225 	scf_simple_prop_t	*progprop = NULL;
1226 	const char		*name, *endpoint, *restart_str, *prog;
1227 	struct inetconfent	*iconf = (struct inetconfent *)buf;
1228 	uint8_t			*isrpc;
1229 
1230 	max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1231 	if ((svc_name = malloc(max_name_length + 1)) == NULL) {
1232 		(void) fprintf(stderr, gettext("Error: Out of memory.\n"));
1233 		return (SCF_FAILED);
1234 	}
1235 
1236 	/*
1237 	 * Get the FMRI of the instance, and check if its delegated restarter
1238 	 * is inetd.  A missing or empty restarter property implies that
1239 	 * svc.startd is the restarter.
1240 	 */
1241 
1242 	if (scf_instance_to_fmri(inst, svc_name, max_name_length) < 0) {
1243 		(void) fprintf(stderr,
1244 		    gettext("Error: Unable to obtain FMRI for service %1$s."),
1245 		    svc_name);
1246 		free(svc_name);
1247 		return (SCF_FAILED);
1248 	}
1249 
1250 	if ((prop = scf_simple_prop_get(h, svc_name, SCF_PG_GENERAL,
1251 	    SCF_PROPERTY_RESTARTER)) == NULL)
1252 		goto out;
1253 
1254 	if ((restart_str = scf_simple_prop_next_ustring(prop)) == NULL)
1255 		goto out;
1256 
1257 	if (strcmp(restart_str, INETD_INSTANCE_FMRI) != 0)
1258 		goto out;
1259 
1260 	/* Free restarter prop so it can be reused below */
1261 	scf_simple_prop_free(prop);
1262 
1263 	/*
1264 	 * We know that this instance is managed by inetd.
1265 	 * Now get the properties needed to decide if it matches this
1266 	 * line in the old config file.
1267 	 */
1268 
1269 	if (((prop = scf_simple_prop_get(h, svc_name, PG_NAME_SERVICE_CONFIG,
1270 	    PR_SVC_NAME_NAME)) == NULL) ||
1271 	    ((name = scf_simple_prop_next_astring(prop)) == NULL)) {
1272 		property_error(svc_name, PR_SVC_NAME_NAME);
1273 		goto out;
1274 	}
1275 
1276 	if (((sockprop = scf_simple_prop_get(h, svc_name,
1277 	    PG_NAME_SERVICE_CONFIG, PR_SOCK_TYPE_NAME)) == NULL) ||
1278 	    ((endpoint = scf_simple_prop_next_astring(sockprop)) == NULL)) {
1279 		property_error(svc_name, PR_SOCK_TYPE_NAME);
1280 		goto out;
1281 	}
1282 
1283 	if (((rpcprop = scf_simple_prop_get(h, svc_name,
1284 	    PG_NAME_SERVICE_CONFIG, PR_ISRPC_NAME)) == NULL) ||
1285 	    ((isrpc = scf_simple_prop_next_boolean(rpcprop)) == NULL)) {
1286 		property_error(svc_name, PR_ISRPC_NAME);
1287 		goto out;
1288 	}
1289 
1290 	if (((progprop = scf_simple_prop_get(h, svc_name, START_METHOD_NAME,
1291 	    PR_EXEC_NAME)) == NULL) ||
1292 	    ((prog = scf_simple_prop_next_astring(progprop)) == NULL)) {
1293 		property_error(svc_name, PR_EXEC_NAME);
1294 	}
1295 
1296 
1297 	/* If it's RPC, we truncate off the version portion for comparison */
1298 	if (*isrpc) {
1299 		char *cp;
1300 
1301 		cp = strchr(iconf->service, '/');
1302 		if (cp != NULL)
1303 			*cp = '\0';
1304 	}
1305 
1306 	/*
1307 	 * If name of this service and endpoint are equal to values from
1308 	 * iconf fields, and they're either both RPC or both non-RPC,
1309 	 * then we have a match; update the exec and arg0 properties if
1310 	 * necessary, then enable it.
1311 	 * We don't return an error if either operation fails so that we
1312 	 * continue to try all the other services.
1313 	 */
1314 	if (strcmp(name, iconf->service) == 0 &&
1315 	    strcmp(endpoint, iconf->endpoint) == 0 &&
1316 	    *isrpc == (strncmp(iconf->protocol, "rpc/", 4) == 0)) {
1317 		/* Can't update exec on internal services */
1318 		if ((strcmp(iconf->server_program, "internal") != 0) &&
1319 		    (strcmp(iconf->exec, prog) != 0)) {
1320 			/* User had edited the command */
1321 			if (!import) {
1322 				/* Dry run only */
1323 				(void) printf(
1324 				    gettext("Would update %s to %s %s"),
1325 				    svc_name, PR_EXEC_NAME, iconf->exec);
1326 				if (iconf->arg0 != NULL) {
1327 					(void) printf(
1328 					    gettext(" with %s of %s\n"),
1329 					    PR_ARG0_NAME, iconf->arg0);
1330 				} else {
1331 					(void) printf("\n");
1332 				}
1333 			} else {
1334 				/* Update instance's exec property */
1335 				if (modify_sprop(h, inst, START_METHOD_NAME,
1336 				    PR_EXEC_NAME, iconf->exec) != 1)
1337 					(void) fprintf(stderr,
1338 					    gettext("Error: Unable to update "
1339 					    "%s property of %s, %s\n"),
1340 					    PR_EXEC_NAME, svc_name,
1341 					    scf_strerror(scf_error()));
1342 				else
1343 					(void) printf("%s will %s %s\n",
1344 					    svc_name, PR_EXEC_NAME,
1345 					    iconf->exec);
1346 
1347 				/* Update arg0 prop, if needed */
1348 				if (iconf->arg0 != NULL) {
1349 					if (modify_sprop(h, inst,
1350 					    START_METHOD_NAME, PR_ARG0_NAME,
1351 					    iconf->arg0) != 1) {
1352 						(void) fprintf(stderr,
1353 						    gettext("Error: Unable to "
1354 						    "update %s property of "
1355 						    "%s, %s\n"), PR_ARG0_NAME,
1356 						    svc_name,
1357 						    scf_strerror(scf_error()));
1358 					} else {
1359 						(void) printf("%s will have an "
1360 						    "%s of %s\n", svc_name,
1361 						    PR_ARG0_NAME, iconf->arg0);
1362 					}
1363 				}
1364 			}
1365 		}
1366 
1367 		if (!import) {
1368 			/* Dry-run only */
1369 			(void) printf("Would enable %s\n", svc_name);
1370 		} else {
1371 			if (smf_enable_instance(svc_name, 0) != 0)
1372 				(void) fprintf(stderr,
1373 				    gettext("Error: Failed to enable %s\n"),
1374 				    svc_name);
1375 			else
1376 				(void) printf("%s enabled\n", svc_name);
1377 		}
1378 	}
1379 
1380 out:
1381 	free(svc_name);
1382 	scf_simple_prop_free(prop);
1383 	scf_simple_prop_free(sockprop);
1384 	scf_simple_prop_free(rpcprop);
1385 	scf_simple_prop_free(progprop);
1386 	return (SCF_SUCCESS);
1387 }
1388 
1389 static void
1390 usage(void)
1391 {
1392 	(void) fprintf(stderr, gettext(
1393 	    "Usage: %s [-fn] [-i srcfile] [-o destdir]\n"
1394 	    "       %1$s -e [-n] [-i srcfile]\n"
1395 	    "-?          Display this usage message\n"
1396 	    "-e          Enable smf services which are enabled in the input\n"
1397 	    "            file\n"
1398 	    "-f          Force overwrite of existing manifests\n"
1399 	    "-n          Do not import converted manifests,\n"
1400 	    "            or only display services which would be enabled\n"
1401 	    "-i srcfile  Alternate input file\n"
1402 	    "-o destdir  Alternate output directory for manifests\n"),
1403 	    progname);
1404 	exit(EXIT_USAGE);
1405 }
1406 
1407 int
1408 main(int argc, char *argv[])
1409 {
1410 	int c, rval, convert_err, import_err = 0, enable_err = 0;
1411 	boolean_t overwrite = B_FALSE;
1412 	boolean_t enable = B_FALSE;
1413 	char *srcfile = NULL;
1414 	char *destdir = NULL;
1415 	struct fileinfo *srcfinfo, *dstfinfo;
1416 	struct inetconfent *iconf;
1417 
1418 	setbuf(stdout, NULL);
1419 	(void) setlocale(LC_ALL, "");
1420 	(void) textdomain(TEXT_DOMAIN);
1421 
1422 	if ((progname = strrchr(argv[0], '/')) == NULL)
1423 		progname = argv[0];
1424 	else
1425 		progname++;
1426 
1427 	while ((c = getopt(argc, argv, "?efni:o:")) != -1) {
1428 		switch (c) {
1429 		case 'e':
1430 			/* enable services based on existing file config */
1431 			enable = B_TRUE;
1432 			break;
1433 
1434 		case 'f':
1435 			/* overwrite existing manifests */
1436 			overwrite = B_TRUE;
1437 			break;
1438 		case 'n':
1439 			/* don't import manifests, or dry-run enable */
1440 			import = B_FALSE;
1441 			break;
1442 		case 'i':
1443 			/* alternate input file */
1444 			if (srcfile != NULL) {
1445 				(void) fprintf(stderr,
1446 				    gettext("%s: Error only one -%c allowed\n"),
1447 				    progname, optopt);
1448 				usage();
1449 			}
1450 			srcfile = optarg;
1451 			break;
1452 		case 'o':
1453 			/* alternate output directory */
1454 			if (destdir != NULL) {
1455 				(void) fprintf(stderr,
1456 				    gettext("%s: Error only one -%c allowed\n"),
1457 				    progname, optopt);
1458 				usage();
1459 			}
1460 			destdir = optarg;
1461 			break;
1462 		case '?': /*FALLTHROUGH*/
1463 		default:
1464 			usage();
1465 			break;
1466 		}
1467 	}
1468 
1469 	/*
1470 	 * Display usage if extraneous args supplied or enable specified in
1471 	 * combination with overwrite or destdir
1472 	 */
1473 	if ((optind != argc) || (enable && (overwrite || destdir != NULL)))
1474 		usage();
1475 
1476 	if ((srcfinfo = open_srcfile(srcfile)) == NULL)
1477 		return (EXIT_ERROR_CONV);
1478 
1479 	while ((iconf = fgetinetconfent(srcfinfo, !enable)) != NULL) {
1480 		/*
1481 		 * If we're enabling, then just walk all the services for each
1482 		 * line and enable those which match.
1483 		 */
1484 		if (enable) {
1485 			rval = scf_simple_walk_instances(SCF_STATE_ALL, iconf,
1486 			    list_callback);
1487 			free_inetconfent(iconf);
1488 			if (rval == SCF_FAILED) {
1489 				/* Only print msg if framework error */
1490 				if (scf_error() != SCF_ERROR_CALLBACK_FAILED)
1491 					(void) fprintf(stderr, gettext(
1492 					    "Error walking instances: %s.\n"),
1493 					    scf_strerror(scf_error()));
1494 				enable_err++;
1495 				break;
1496 			}
1497 			continue;
1498 		}
1499 
1500 		/* Remainder of loop used for conversion & import */
1501 		if ((rval = open_dstfile(destdir, overwrite, iconf, &dstfinfo))
1502 		    < 0) {
1503 			/*
1504 			 * Only increment error counter if the failure was
1505 			 * other than the file already existing.
1506 			 */
1507 			if (rval == -2)
1508 				srcfinfo->failcnt++;
1509 			free_inetconfent(iconf);
1510 			continue;
1511 		}
1512 		rval = print_manifest(dstfinfo->fp, dstfinfo->filename, iconf);
1513 		(void) fclose(dstfinfo->fp);
1514 		if (rval == 0) {
1515 			if (import &&
1516 			    (import_manifest(dstfinfo->filename) != 0))
1517 				import_err++;
1518 		} else {
1519 			(void) unlink(dstfinfo->filename);
1520 			srcfinfo->failcnt++;
1521 		}
1522 		free(dstfinfo->filename);
1523 		free(dstfinfo);
1524 		free_inetconfent(iconf);
1525 	}
1526 	(void) fclose(srcfinfo->fp);
1527 	convert_err = srcfinfo->failcnt;
1528 
1529 	/* Update hash only if not in enable mode, and only if importing */
1530 	if (!enable && import && (update_hash(srcfinfo->filename) != 0))
1531 		import_err++;
1532 
1533 	free(srcfinfo);
1534 
1535 	if (enable_err != 0)
1536 		return (EXIT_ERROR_ENBL);
1537 	if (import_err != 0)
1538 		return (EXIT_ERROR_IMP);
1539 	if (convert_err != 0)
1540 		return (EXIT_ERROR_CONV);
1541 	return (EXIT_SUCCESS);
1542 }
1543