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