10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*5404Stn143363  * Common Development and Distribution License (the "License").
6*5404Stn143363  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*5404Stn143363  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * inetconv - convert inetd.conf entries into smf(5) service manifests,
300Sstevel@tonic-gate  *            import them into smf(5) repository
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include <sys/types.h>
340Sstevel@tonic-gate #include <sys/param.h>
350Sstevel@tonic-gate #include <sys/stat.h>
360Sstevel@tonic-gate #include <sys/wait.h>
370Sstevel@tonic-gate #include <stdio.h>
380Sstevel@tonic-gate #include <stdlib.h>
390Sstevel@tonic-gate #include <string.h>
400Sstevel@tonic-gate #include <unistd.h>
410Sstevel@tonic-gate #include <fcntl.h>
420Sstevel@tonic-gate #include <pwd.h>
430Sstevel@tonic-gate #include <grp.h>
440Sstevel@tonic-gate #include <errno.h>
450Sstevel@tonic-gate #include <limits.h>
460Sstevel@tonic-gate #include <locale.h>
470Sstevel@tonic-gate #include <libintl.h>
480Sstevel@tonic-gate #include <libscf.h>
490Sstevel@tonic-gate #include <inetsvc.h>
500Sstevel@tonic-gate #include <rpc/nettype.h>
510Sstevel@tonic-gate 
520Sstevel@tonic-gate /* exit codes */
530Sstevel@tonic-gate #define	EXIT_SUCCESS	0	/* succeeded */
540Sstevel@tonic-gate #define	EXIT_USAGE	1	/* bad options */
550Sstevel@tonic-gate #define	EXIT_ERROR_CONV 2	/* error(s) coverting inetd.conf entries */
560Sstevel@tonic-gate #define	EXIT_ERROR_IMP	3	/* error(s) importing manifests */
570Sstevel@tonic-gate #define	EXIT_ERROR_SYS	4	/* system error */
580Sstevel@tonic-gate #define	EXIT_ERROR_ENBL 5	/* error(s) enabling services */
590Sstevel@tonic-gate 
600Sstevel@tonic-gate #ifndef TEXT_DOMAIN
610Sstevel@tonic-gate #define	TEXT_DOMAIN		"SUNW_OST_OSCMD"
620Sstevel@tonic-gate #endif
630Sstevel@tonic-gate 
640Sstevel@tonic-gate #define	MAIN_CONFIG		"/etc/inet/inetd.conf"
650Sstevel@tonic-gate #define	ALT_CONFIG		"/etc/inetd.conf"
660Sstevel@tonic-gate 
670Sstevel@tonic-gate #define	MANIFEST_DIR		"/var/svc/manifest/network"
68290Sdstaff #define	MANIFEST_RPC_DIR	MANIFEST_DIR  "/rpc"
690Sstevel@tonic-gate #define	SVCCFG_PATH		"/usr/sbin/svccfg"
700Sstevel@tonic-gate 
71290Sdstaff #define	RPCBIND_FMRI		"svc:/network/rpc/bind"
72290Sdstaff 
730Sstevel@tonic-gate /* maximum allowed length of an inetd.conf format line */
740Sstevel@tonic-gate #define	MAX_SRC_LINELEN		32768
750Sstevel@tonic-gate 
760Sstevel@tonic-gate /* Version of inetconv, used as a marker in services we generate */
770Sstevel@tonic-gate #define	INETCONV_VERSION	1
780Sstevel@tonic-gate 
790Sstevel@tonic-gate struct inetconfent {
800Sstevel@tonic-gate 	/* fields as read from inetd.conf format line */
810Sstevel@tonic-gate 	char *service;
820Sstevel@tonic-gate 	char *endpoint;
830Sstevel@tonic-gate 	char *protocol;
840Sstevel@tonic-gate 	char *wait_status;
850Sstevel@tonic-gate 	char *username;
860Sstevel@tonic-gate 	char *server_program;
870Sstevel@tonic-gate 	char *server_args;
880Sstevel@tonic-gate 	/* information derived from above fields */
890Sstevel@tonic-gate 	boolean_t wait;
900Sstevel@tonic-gate 	boolean_t isrpc;
910Sstevel@tonic-gate 	int rpc_low_version;
920Sstevel@tonic-gate 	int rpc_high_version;
930Sstevel@tonic-gate 	char *rpc_prog;
940Sstevel@tonic-gate 	char *groupname;
950Sstevel@tonic-gate 	char *exec;
960Sstevel@tonic-gate 	char *arg0;
970Sstevel@tonic-gate };
980Sstevel@tonic-gate 
990Sstevel@tonic-gate struct fileinfo {
1000Sstevel@tonic-gate 	FILE *fp;
1010Sstevel@tonic-gate 	char *filename;
1020Sstevel@tonic-gate 	int lineno;
1030Sstevel@tonic-gate 	int failcnt;
1040Sstevel@tonic-gate };
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate static char *progname;
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate static boolean_t import = B_TRUE;
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate /* start of manifest XML template strings */
1110Sstevel@tonic-gate static const char xml_header[] =
1120Sstevel@tonic-gate "<?xml version='1.0'?>\n"
1130Sstevel@tonic-gate "<!DOCTYPE service_bundle SYSTEM "
1140Sstevel@tonic-gate "'/usr/share/lib/xml/dtd/service_bundle.dtd.1'>\n";
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate static const char xml_comment[] =
1170Sstevel@tonic-gate "<!--\n"
1180Sstevel@tonic-gate "    Service manifest for the %s service.\n"
1190Sstevel@tonic-gate "\n"
1200Sstevel@tonic-gate "    Generated by inetconv(1M) from inetd.conf(4).\n"
1210Sstevel@tonic-gate "-->\n\n";
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate static const char xml_service_bundle[] =
1240Sstevel@tonic-gate "<service_bundle type='manifest' name='inetconv:%s'>\n\n";
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate static const char xml_service_name[] =
1270Sstevel@tonic-gate "<service\n"
1280Sstevel@tonic-gate "	name='network/%s'\n"
1290Sstevel@tonic-gate "	type='service'\n"
1300Sstevel@tonic-gate "	version='1'>\n\n";
1310Sstevel@tonic-gate 
132290Sdstaff static const char xml_dependency[] =
133290Sdstaff "	<dependency\n"
134290Sdstaff "		name='%s'\n"
135290Sdstaff "		grouping='require_all'\n"
136290Sdstaff "		restart_on='restart'\n"
137290Sdstaff "		type='service'>\n"
138290Sdstaff "		<service_fmri value='%s' />\n"
139290Sdstaff "	</dependency>\n\n";
140290Sdstaff 
1410Sstevel@tonic-gate static const char xml_instance[] =
1420Sstevel@tonic-gate "	<create_default_instance enabled='true'/>\n\n";
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate static const char xml_restarter[] =
1450Sstevel@tonic-gate "	<restarter>\n"
1460Sstevel@tonic-gate "		<service_fmri value='%s' />\n"
1470Sstevel@tonic-gate "	</restarter>\n\n";
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate static const char xml_exec_method_start[] =
1500Sstevel@tonic-gate "	<!--\n"
1510Sstevel@tonic-gate "	    Set a timeout of 0 to signify to inetd that we don't want to\n"
1520Sstevel@tonic-gate "	    timeout this service, since the forked process is the one that\n"
1530Sstevel@tonic-gate "	    does the service's work. This is the case for most/all legacy\n"
1540Sstevel@tonic-gate "	    inetd services; for services written to take advantage of SMF\n"
1550Sstevel@tonic-gate "	    capabilities, the start method should fork off a process to\n"
1560Sstevel@tonic-gate "	    handle the request and return a success code.\n"
1570Sstevel@tonic-gate "	-->\n"
1580Sstevel@tonic-gate "	<exec_method\n"
1590Sstevel@tonic-gate "		type='method'\n"
1600Sstevel@tonic-gate "		name='%s'\n"
1610Sstevel@tonic-gate "		%s='%s'\n"
1620Sstevel@tonic-gate "		timeout_seconds='0'>\n"
1630Sstevel@tonic-gate "		<method_context>\n"
1640Sstevel@tonic-gate "			<method_credential %s='%s' group='%s' />\n"
1650Sstevel@tonic-gate "		</method_context>\n";
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate static const char xml_arg0[] =
1680Sstevel@tonic-gate "		<propval name='%s' type='astring'\n"
1690Sstevel@tonic-gate "		    value='%s' />\n";
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate static const char xml_exec_method_end[] =
1720Sstevel@tonic-gate "	</exec_method>\n\n";
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate static const char xml_exec_method_disable[] =
1750Sstevel@tonic-gate "	<!--\n"
1760Sstevel@tonic-gate "	    Use inetd's built-in kill support to disable services.\n"
1770Sstevel@tonic-gate "	-->\n"
1780Sstevel@tonic-gate "	<exec_method\n"
1790Sstevel@tonic-gate "		type='method'\n"
1800Sstevel@tonic-gate "		name='%s'\n"
1810Sstevel@tonic-gate "		%s=':kill'\n"
1820Sstevel@tonic-gate "		timeout_seconds='0'>\n";
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate static const char xml_exec_method_offline[] =
1850Sstevel@tonic-gate "	<!--\n"
1860Sstevel@tonic-gate "	    Use inetd's built-in process kill support to offline wait type\n"
1870Sstevel@tonic-gate "	    services.\n"
1880Sstevel@tonic-gate "	-->\n"
1890Sstevel@tonic-gate "	<exec_method\n"
1900Sstevel@tonic-gate "		type='method'\n"
1910Sstevel@tonic-gate "		name='%s'\n"
1920Sstevel@tonic-gate "		%s=':kill_process'\n"
1930Sstevel@tonic-gate "		timeout_seconds='0'>\n";
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate static const char xml_inetconv_group_start[] =
1960Sstevel@tonic-gate "	<!--\n"
1970Sstevel@tonic-gate "	    This property group is used to record information about\n"
1980Sstevel@tonic-gate "	    how this manifest was created.  It is an implementation\n"
1990Sstevel@tonic-gate "	    detail which should not be modified or deleted.\n"
2000Sstevel@tonic-gate "	-->\n"
2010Sstevel@tonic-gate "	<property_group name='%s' type='framework'>\n"
2020Sstevel@tonic-gate "		<propval name='%s' type='boolean' value='%s' />\n"
2030Sstevel@tonic-gate "		<propval name='%s' type='integer' value='%d' />\n"
2040Sstevel@tonic-gate "		<propval name='%s' type='astring' value=\n"
2050Sstevel@tonic-gate "'%s %s %s %s %s %s%s%s'\n"
2060Sstevel@tonic-gate "		/>\n";
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate static const char xml_property_group_start[] =
2090Sstevel@tonic-gate "	<property_group name='%s' type='framework'>\n"
2100Sstevel@tonic-gate "		<propval name='%s' type='astring' value='%s' />\n"
2110Sstevel@tonic-gate "		<propval name='%s' type='astring' value='%s' />\n"
2120Sstevel@tonic-gate "		<propval name='%s' type='astring' value='%s' />\n"
2130Sstevel@tonic-gate "		<propval name='%s' type='boolean' value='%s' />\n"
2140Sstevel@tonic-gate "		<propval name='%s' type='boolean' value='%s' />\n";
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate static const char xml_property_group_rpc[] =
2170Sstevel@tonic-gate "		<propval name='%s' type='integer' value='%d' />\n"
2180Sstevel@tonic-gate "		<propval name='%s' type='integer' value='%d' />"
2190Sstevel@tonic-gate "\n";
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate static const char xml_property_group_end[] =
2220Sstevel@tonic-gate "	</property_group>\n\n";
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate static const char xml_stability[] =
2250Sstevel@tonic-gate "	<stability value='External' />\n\n";
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate static const char xml_template[] =
2280Sstevel@tonic-gate "	<template>\n"
2290Sstevel@tonic-gate "		<common_name>\n"
2300Sstevel@tonic-gate "			<loctext xml:lang='C'>\n"
2310Sstevel@tonic-gate "%s\n"
2320Sstevel@tonic-gate "			</loctext>\n"
2330Sstevel@tonic-gate "		</common_name>\n"
2340Sstevel@tonic-gate "	</template>\n";
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate static const char xml_footer[] =
2370Sstevel@tonic-gate "</service>\n"
2380Sstevel@tonic-gate "\n"
2390Sstevel@tonic-gate "</service_bundle>\n";
2400Sstevel@tonic-gate /* end of manifest XML template strings */
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate static void *
2430Sstevel@tonic-gate safe_malloc(size_t size)
2440Sstevel@tonic-gate {
2450Sstevel@tonic-gate 	void *cp;
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	if ((cp = malloc(size)) == NULL) {
2480Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
2490Sstevel@tonic-gate 		    progname, strerror(errno));
2500Sstevel@tonic-gate 		exit(EXIT_ERROR_SYS);
2510Sstevel@tonic-gate 	}
2520Sstevel@tonic-gate 	return (cp);
2530Sstevel@tonic-gate }
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate static char *
2560Sstevel@tonic-gate safe_strdup(char *s)
2570Sstevel@tonic-gate {
2580Sstevel@tonic-gate 	char *cp;
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate 	if ((cp = strdup(s)) == NULL) {
2610Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: strdup failed: %s\n"),
2620Sstevel@tonic-gate 		    progname, strerror(errno));
2630Sstevel@tonic-gate 		exit(EXIT_ERROR_SYS);
2640Sstevel@tonic-gate 	}
2650Sstevel@tonic-gate 	return (cp);
2660Sstevel@tonic-gate }
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate static char *
2690Sstevel@tonic-gate propertyname(char *name, char *prefix)
2700Sstevel@tonic-gate {
2710Sstevel@tonic-gate 	static char *buf;
2720Sstevel@tonic-gate 	size_t len;
2730Sstevel@tonic-gate 	int c;
2740Sstevel@tonic-gate 	char *cp;
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate 	/* free any memory allocated by a previous call */
2770Sstevel@tonic-gate 	free(buf);
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	len = strlen(name) + strlen(prefix) + 1;
2800Sstevel@tonic-gate 	buf = safe_malloc(len);
2810Sstevel@tonic-gate 	buf[0] = '\0';
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	/*
2840Sstevel@tonic-gate 	 * Property names must match the regular expression:
2850Sstevel@tonic-gate 	 * ([A-Za-z][_A-Za-z0-9.-]*,)?[A-Za-z][_A-Za-z0-9-]*
2860Sstevel@tonic-gate 	 */
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	/*
2890Sstevel@tonic-gate 	 * Make sure the first character is alphabetic, if not insert prefix.
290*5404Stn143363 	 * Can't use isalpha() here as it's locale dependent but the property
2910Sstevel@tonic-gate 	 * name regular expression isn't.
2920Sstevel@tonic-gate 	 */
2930Sstevel@tonic-gate 	c = name[0];
2940Sstevel@tonic-gate 	if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
2950Sstevel@tonic-gate 		(void) strlcat(buf, prefix, len);
2960Sstevel@tonic-gate 	}
2970Sstevel@tonic-gate 	(void) strlcat(buf, name, len);
2980Sstevel@tonic-gate 
299*5404Stn143363 	/* convert any disallowed characters into '_' */
3000Sstevel@tonic-gate 	for (cp = buf; *cp != '\0'; cp++) {
3010Sstevel@tonic-gate 		if ((*cp < 'A' || *cp > 'Z') && (*cp < 'a' || *cp > 'z') &&
3020Sstevel@tonic-gate 		    (*cp < '0' || *cp > '9') && (*cp != '.') && (*cp != '-'))
3030Sstevel@tonic-gate 			*cp = '_';
3040Sstevel@tonic-gate 	}
3050Sstevel@tonic-gate 	return (buf);
3060Sstevel@tonic-gate }
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate static char *
3090Sstevel@tonic-gate servicename(struct inetconfent *iconf)
3100Sstevel@tonic-gate {
3110Sstevel@tonic-gate 	static char *buf;
3120Sstevel@tonic-gate 	size_t len;
313*5404Stn143363 	char *cp, *proto;
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 	/* free any memory allocated by a previous call */
3160Sstevel@tonic-gate 	free(buf);
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	len = strlen(iconf->service) + strlen(iconf->protocol) +
3190Sstevel@tonic-gate 	    sizeof ("rpc-/visible");
3200Sstevel@tonic-gate 	buf = safe_malloc(len);
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate 	/*
3230Sstevel@tonic-gate 	 * Combine the service and protocol fields to produce a unique
3240Sstevel@tonic-gate 	 * manifest service name. The syntax of a service name is:
3250Sstevel@tonic-gate 	 * prop(/prop)*
3260Sstevel@tonic-gate 	 */
3270Sstevel@tonic-gate 	(void) strlcpy(buf, propertyname(iconf->service,
3280Sstevel@tonic-gate 	    iconf->isrpc ? "rpc-": "s-"), len);
3290Sstevel@tonic-gate 	(void) strlcat(buf, "/", len);
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	proto = iconf->protocol;
3320Sstevel@tonic-gate 	if (iconf->isrpc && (strcmp(iconf->protocol, "rpc/*") == 0))
3330Sstevel@tonic-gate 		proto = "rpc/visible";
334*5404Stn143363 
335*5404Stn143363 	/*
336*5404Stn143363 	 * SMF service names may not contain '.', but IANA services do
337*5404Stn143363 	 * allow its use, and property names can contain '.' as returned
338*5404Stn143363 	 * by propertyname().  So if the resultant SMF service name
339*5404Stn143363 	 * would contain a '.' we fix it here.
340*5404Stn143363 	 */
341*5404Stn143363 	for (cp = buf; *cp != '\0'; cp++) {
342*5404Stn143363 		if (*cp == '.')
343*5404Stn143363 			*cp = '_';
344*5404Stn143363 	}
3450Sstevel@tonic-gate 	(void) strlcat(buf, propertyname(proto, "p-"), len);
3460Sstevel@tonic-gate 	return (buf);
3470Sstevel@tonic-gate }
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate static boolean_t
3500Sstevel@tonic-gate is_v6only(char *protocol)
3510Sstevel@tonic-gate {
3520Sstevel@tonic-gate 	/* returns true if protocol is an IPv6 only protocol */
3530Sstevel@tonic-gate 	if ((strcmp(protocol, SOCKET_PROTO_TCP6_ONLY) == 0) ||
3540Sstevel@tonic-gate 	    (strcmp(protocol, SOCKET_PROTO_UDP6_ONLY) == 0))
3550Sstevel@tonic-gate 		return (B_TRUE);
3560Sstevel@tonic-gate 	return (B_FALSE);
3570Sstevel@tonic-gate }
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate static char *
3600Sstevel@tonic-gate invalid_props(inetd_prop_t *p)
3610Sstevel@tonic-gate {
3620Sstevel@tonic-gate 	static char
3630Sstevel@tonic-gate 	    buf[sizeof (" service-name endpoint-type protocol wait-status")];
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	buf[0] = '\0';
3660Sstevel@tonic-gate 	if ((p[PT_SVC_NAME_INDEX].ip_error == IVE_INVALID) ||
3670Sstevel@tonic-gate 	    (p[PT_SVC_NAME_INDEX].ip_error == IVE_UNSET) ||
3680Sstevel@tonic-gate 	    (p[PT_RPC_LW_VER_INDEX].ip_error == IVE_INVALID) ||
3690Sstevel@tonic-gate 	    (p[PT_RPC_HI_VER_INDEX].ip_error == IVE_INVALID))
3700Sstevel@tonic-gate 		(void) strlcat(buf, " service-name", sizeof (buf));
3710Sstevel@tonic-gate 	if ((p[PT_SOCK_TYPE_INDEX].ip_error == IVE_INVALID) ||
3720Sstevel@tonic-gate 	    (p[PT_SOCK_TYPE_INDEX].ip_error == IVE_UNSET))
3730Sstevel@tonic-gate 		(void) strlcat(buf, " endpoint-type", sizeof (buf));
3740Sstevel@tonic-gate 	if ((p[PT_PROTO_INDEX].ip_error == IVE_INVALID) ||
3750Sstevel@tonic-gate 	    (p[PT_PROTO_INDEX].ip_error == IVE_UNSET) ||
3760Sstevel@tonic-gate 	    (p[PT_ISRPC_INDEX].ip_error == IVE_INVALID))
3770Sstevel@tonic-gate 		(void) strlcat(buf, " protocol", sizeof (buf));
3780Sstevel@tonic-gate 	if (p[PT_ISWAIT_INDEX].ip_error == IVE_INVALID)
3790Sstevel@tonic-gate 		(void) strlcat(buf, " wait-status", sizeof (buf));
3800Sstevel@tonic-gate 	return (buf);
3810Sstevel@tonic-gate }
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate static boolean_t
3840Sstevel@tonic-gate valid_basic_properties(struct inetconfent *iconf, struct fileinfo *finfo)
3850Sstevel@tonic-gate {
3860Sstevel@tonic-gate 	size_t prop_size;
3870Sstevel@tonic-gate 	inetd_prop_t *prop, *inetd_properties;
3880Sstevel@tonic-gate 	boolean_t valid = B_TRUE;
3890Sstevel@tonic-gate 	char *proto = iconf->protocol;
3900Sstevel@tonic-gate 	char *svc_name = iconf->service;
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	inetd_properties = get_prop_table(&prop_size);
3930Sstevel@tonic-gate 	prop = safe_malloc(prop_size * sizeof (inetd_prop_t));
3940Sstevel@tonic-gate 	(void) memcpy(prop, inetd_properties,
3950Sstevel@tonic-gate 	    prop_size * sizeof (inetd_prop_t));
3960Sstevel@tonic-gate 
3971293Smh138676 	put_prop_value_boolean(prop, PR_ISRPC_NAME, iconf->isrpc);
3981293Smh138676 	put_prop_value_boolean(prop, PR_ISWAIT_NAME, iconf->wait);
3990Sstevel@tonic-gate 	if (iconf->isrpc) {
4001293Smh138676 		put_prop_value_int(prop, PR_RPC_LW_VER_NAME,
4011293Smh138676 		    iconf->rpc_low_version);
4021293Smh138676 		put_prop_value_int(prop, PR_RPC_HI_VER_NAME,
4031293Smh138676 		    iconf->rpc_high_version);
4040Sstevel@tonic-gate 		svc_name = iconf->rpc_prog;
4050Sstevel@tonic-gate 		proto += 4;	/* skip 'rpc/' */
4060Sstevel@tonic-gate 	}
4070Sstevel@tonic-gate 
4081293Smh138676 	if (!put_prop_value_string(prop, PR_SOCK_TYPE_NAME, iconf->endpoint) ||
4091293Smh138676 	    !put_prop_value_string(prop, PR_SVC_NAME_NAME, svc_name)) {
4100Sstevel@tonic-gate 		valid = B_FALSE;
4110Sstevel@tonic-gate 
4121293Smh138676 		if (errno == ENOMEM) {
4131293Smh138676 			(void) fprintf(stderr,
4141293Smh138676 			    gettext("%s: failed to allocate memory: %s\n"),
4151293Smh138676 			    progname, strerror(errno));
4161293Smh138676 			exit(EXIT_ERROR_SYS);
4171293Smh138676 		}
4181293Smh138676 	}
4191293Smh138676 
4201293Smh138676 	put_prop_value_string_list(prop, PR_PROTO_NAME, get_protos(proto));
4211293Smh138676 
4220Sstevel@tonic-gate 	if (!valid_props(prop, NULL, NULL, NULL, NULL) || !valid) {
4230Sstevel@tonic-gate 		valid = B_FALSE;
4240Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: Error %s line %d "
4250Sstevel@tonic-gate 		    "invalid or inconsistent fields:%s\n"), progname,
4260Sstevel@tonic-gate 		    finfo->filename, finfo->lineno,
4270Sstevel@tonic-gate 		    invalid_props(prop));
4280Sstevel@tonic-gate 	}
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	free_instance_props(prop);
4310Sstevel@tonic-gate 	return (valid);
4320Sstevel@tonic-gate }
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate static boolean_t
4350Sstevel@tonic-gate valid_inetconfent(struct inetconfent *iconf, struct fileinfo *finfo)
4360Sstevel@tonic-gate {
4370Sstevel@tonic-gate 	boolean_t valid = B_TRUE;
4380Sstevel@tonic-gate 	size_t len;
4390Sstevel@tonic-gate 	char *cp, *endp;
4400Sstevel@tonic-gate 	struct passwd *pwd;
4410Sstevel@tonic-gate 	struct group *grp;
4420Sstevel@tonic-gate 	struct stat statb;
4430Sstevel@tonic-gate 	char *proto = iconf->protocol;
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	iconf->isrpc = B_FALSE;
4460Sstevel@tonic-gate 	if (strncmp(iconf->protocol, "rpc/", 4) == 0) {
4470Sstevel@tonic-gate 		iconf->isrpc = B_TRUE;
4480Sstevel@tonic-gate 		iconf->rpc_prog = safe_strdup(iconf->service);
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 		/* set RPC version numbers */
4510Sstevel@tonic-gate 		iconf->rpc_low_version = 1;
4520Sstevel@tonic-gate 		iconf->rpc_high_version = 1;
4530Sstevel@tonic-gate 		if ((cp = strrchr(iconf->rpc_prog, '/')) != NULL) {
4540Sstevel@tonic-gate 			*cp = '\0';
4550Sstevel@tonic-gate 			if (*++cp != '\0') {
4560Sstevel@tonic-gate 				errno = 0;
4570Sstevel@tonic-gate 				iconf->rpc_low_version = strtol(cp, &endp, 10);
4580Sstevel@tonic-gate 				if (errno != 0)
4590Sstevel@tonic-gate 					goto vererr;
4600Sstevel@tonic-gate 				cp = endp;
4610Sstevel@tonic-gate 				if (*cp == '-') {
4620Sstevel@tonic-gate 					if (*++cp == '\0')
4630Sstevel@tonic-gate 						goto vererr;
4640Sstevel@tonic-gate 					errno = 0;
4650Sstevel@tonic-gate 					iconf->rpc_high_version = strtol(cp,
4660Sstevel@tonic-gate 					    &endp, 10);
4670Sstevel@tonic-gate 					if ((errno != 0) || (*endp != '\0'))
4680Sstevel@tonic-gate 						goto vererr;
4690Sstevel@tonic-gate 				} else if (*cp == '\0') {
4700Sstevel@tonic-gate 					iconf->rpc_high_version =
4710Sstevel@tonic-gate 					    iconf->rpc_low_version;
4720Sstevel@tonic-gate 				} else {
4730Sstevel@tonic-gate vererr:
4740Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
4750Sstevel@tonic-gate 					    "%s: Error %s line %d invalid RPC "
4760Sstevel@tonic-gate 					    "version in service: %s\n"),
4770Sstevel@tonic-gate 					    progname, finfo->filename,
4780Sstevel@tonic-gate 					    finfo->lineno, iconf->service);
4790Sstevel@tonic-gate 					valid = B_FALSE;
4800Sstevel@tonic-gate 				}
4810Sstevel@tonic-gate 			}
4820Sstevel@tonic-gate 		}
4830Sstevel@tonic-gate 		proto += 4;	/* skip 'rpc/' */
4840Sstevel@tonic-gate 	}
4850Sstevel@tonic-gate 	/* tcp6only and udp6only are not valid in inetd.conf */
4860Sstevel@tonic-gate 	if (is_v6only(proto)) {
4870Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: Error %s line %d "
4880Sstevel@tonic-gate 		    "invalid protocol: %s\n"), progname,
4890Sstevel@tonic-gate 		    finfo->filename, finfo->lineno, proto);
4900Sstevel@tonic-gate 		valid = B_FALSE;
4910Sstevel@tonic-gate 	}
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 	if (strcmp(iconf->wait_status, "wait") == 0) {
4940Sstevel@tonic-gate 		iconf->wait = B_TRUE;
4950Sstevel@tonic-gate 	} else if (strcmp(iconf->wait_status, "nowait") == 0) {
4960Sstevel@tonic-gate 		iconf->wait = B_FALSE;
4970Sstevel@tonic-gate 	} else {
4980Sstevel@tonic-gate 		(void) fprintf(stderr,
4990Sstevel@tonic-gate 		    gettext("%s: Error %s line %d invalid wait-status: %s\n"),
5000Sstevel@tonic-gate 		    progname, finfo->filename, finfo->lineno,
5010Sstevel@tonic-gate 		    iconf->wait_status);
5020Sstevel@tonic-gate 		valid = B_FALSE;
5030Sstevel@tonic-gate 	}
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 	/* look up the username to set the groupname */
5060Sstevel@tonic-gate 	if ((pwd = getpwnam(iconf->username)) == NULL) {
5070Sstevel@tonic-gate 		(void) fprintf(stderr,
5080Sstevel@tonic-gate 		    gettext("%s: Error %s line %d unknown user: %s\n"),
5090Sstevel@tonic-gate 		    progname, finfo->filename, finfo->lineno,
5100Sstevel@tonic-gate 		    iconf->username);
5110Sstevel@tonic-gate 		valid = B_FALSE;
5120Sstevel@tonic-gate 	} else {
5130Sstevel@tonic-gate 		if ((grp = getgrgid(pwd->pw_gid)) != NULL) {
5140Sstevel@tonic-gate 			iconf->groupname = safe_strdup(grp->gr_name);
5150Sstevel@tonic-gate 		} else {
5160Sstevel@tonic-gate 			/* use the group ID if no groupname */
5170Sstevel@tonic-gate 			char s[1];
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 			len = snprintf(s, 1, "%d", pwd->pw_gid) + 1;
5200Sstevel@tonic-gate 			iconf->groupname = safe_malloc(len);
5210Sstevel@tonic-gate 			(void) snprintf(iconf->groupname, len, "%d",
5220Sstevel@tonic-gate 			    pwd->pw_gid);
5230Sstevel@tonic-gate 		}
5240Sstevel@tonic-gate 	}
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 	/* check for internal services */
5270Sstevel@tonic-gate 	if (strcmp(iconf->server_program, "internal") == 0) {
5280Sstevel@tonic-gate 		valid = B_FALSE;
5290Sstevel@tonic-gate 		if ((strcmp(iconf->service, "echo") == 0) ||
5300Sstevel@tonic-gate 		    (strcmp(iconf->service, "discard") == 0) ||
5310Sstevel@tonic-gate 		    (strcmp(iconf->service, "time") == 0) ||
5320Sstevel@tonic-gate 		    (strcmp(iconf->service, "daytime") == 0) ||
5330Sstevel@tonic-gate 		    (strcmp(iconf->service, "chargen") == 0)) {
5340Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
5350Sstevel@tonic-gate 			    "%s: Error %s line %d the SUNWcnsr and SUNWcnsu"
5360Sstevel@tonic-gate 			    " packages contain the internal services\n"),
5370Sstevel@tonic-gate 			    progname, finfo->filename, finfo->lineno);
5380Sstevel@tonic-gate 		} else {
5390Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("%s: Error %s line %d "
5400Sstevel@tonic-gate 			    "unknown internal service: %s\n"), progname,
5410Sstevel@tonic-gate 			    finfo->filename, finfo->lineno, iconf->service);
5420Sstevel@tonic-gate 		}
5430Sstevel@tonic-gate 	} else if ((stat(iconf->server_program, &statb) == -1) &&
5440Sstevel@tonic-gate 	    (errno == ENOENT)) {
5450Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
5460Sstevel@tonic-gate 		    "%s: Error %s line %d server-program not found: %s\n"),
5470Sstevel@tonic-gate 		    progname, finfo->filename, finfo->lineno,
5480Sstevel@tonic-gate 		    iconf->server_program);
5490Sstevel@tonic-gate 		valid = B_FALSE;
5500Sstevel@tonic-gate 	}
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 	return (valid && valid_basic_properties(iconf, finfo));
5530Sstevel@tonic-gate }
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate static void
5560Sstevel@tonic-gate free_inetconfent(struct inetconfent *iconf)
5570Sstevel@tonic-gate {
5580Sstevel@tonic-gate 	if (iconf == NULL)
5590Sstevel@tonic-gate 		return;
5600Sstevel@tonic-gate 
5610Sstevel@tonic-gate 	free(iconf->service);
5620Sstevel@tonic-gate 	free(iconf->endpoint);
5630Sstevel@tonic-gate 	free(iconf->protocol);
5640Sstevel@tonic-gate 	free(iconf->wait_status);
5650Sstevel@tonic-gate 	free(iconf->username);
5660Sstevel@tonic-gate 	free(iconf->server_program);
5670Sstevel@tonic-gate 	free(iconf->server_args);
5680Sstevel@tonic-gate 	free(iconf->rpc_prog);
5690Sstevel@tonic-gate 	free(iconf->groupname);
5700Sstevel@tonic-gate 	free(iconf->exec);
5710Sstevel@tonic-gate 	free(iconf->arg0);
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 	free(iconf);
5740Sstevel@tonic-gate }
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate static struct inetconfent *
5770Sstevel@tonic-gate line_to_inetconfent(char *line)
5780Sstevel@tonic-gate {
5790Sstevel@tonic-gate 	char *cp;
5800Sstevel@tonic-gate 	struct inetconfent *iconf;
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	iconf = safe_malloc(sizeof (struct inetconfent));
5830Sstevel@tonic-gate 	(void) memset(iconf, 0, sizeof (struct inetconfent));
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate 	if ((cp = strtok(line, " \t\n")) == NULL)
5860Sstevel@tonic-gate 		goto fail;
5870Sstevel@tonic-gate 	iconf->service = safe_strdup(cp);
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	if ((cp = strtok(NULL, " \t\n")) == NULL)
5900Sstevel@tonic-gate 		goto fail;
5910Sstevel@tonic-gate 	iconf->endpoint = safe_strdup(cp);
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 	if ((cp = strtok(NULL, " \t\n")) == NULL)
5940Sstevel@tonic-gate 		goto fail;
5950Sstevel@tonic-gate 	iconf->protocol = safe_strdup(cp);
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 	if ((cp = strtok(NULL, " \t\n")) == NULL)
5980Sstevel@tonic-gate 		goto fail;
5990Sstevel@tonic-gate 	iconf->wait_status = safe_strdup(cp);
6000Sstevel@tonic-gate 
6010Sstevel@tonic-gate 	if ((cp = strtok(NULL, " \t\n")) == NULL)
6020Sstevel@tonic-gate 		goto fail;
6030Sstevel@tonic-gate 	iconf->username = safe_strdup(cp);
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	if ((cp = strtok(NULL, " \t\n")) == NULL)
6060Sstevel@tonic-gate 		goto fail;
6070Sstevel@tonic-gate 	iconf->server_program = safe_strdup(cp);
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 	/* last field is optional */
6100Sstevel@tonic-gate 	if ((cp = strtok(NULL, "\n")) != NULL)
6110Sstevel@tonic-gate 		iconf->server_args = safe_strdup(cp);
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 	/* Combine args and server name to construct exec and args fields */
6140Sstevel@tonic-gate 	if (iconf->server_args == NULL) {
6150Sstevel@tonic-gate 		iconf->exec = safe_strdup(iconf->server_program);
6160Sstevel@tonic-gate 	} else {
6170Sstevel@tonic-gate 		int len;
6180Sstevel@tonic-gate 		char *args, *endp;
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 		len = strlen(iconf->server_program) +
6210Sstevel@tonic-gate 		    strlen(iconf->server_args) + 1;
6220Sstevel@tonic-gate 		iconf->exec = safe_malloc(len);
6230Sstevel@tonic-gate 		(void) strlcpy(iconf->exec, iconf->server_program, len);
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 		args = safe_strdup(iconf->server_args);
6260Sstevel@tonic-gate 		if ((cp = strtok(args, " \t")) != NULL) {
6270Sstevel@tonic-gate 			if ((endp = strrchr(iconf->exec, '/')) == NULL)
6280Sstevel@tonic-gate 				endp = iconf->exec;
6290Sstevel@tonic-gate 			else
6300Sstevel@tonic-gate 				endp++;
6310Sstevel@tonic-gate 			/* only set arg0 property value if needed */
6320Sstevel@tonic-gate 			if (strcmp(endp, cp) != 0)
6330Sstevel@tonic-gate 				iconf->arg0 = safe_strdup(cp);
6340Sstevel@tonic-gate 			while ((cp = strtok(NULL, " \t")) != NULL) {
6350Sstevel@tonic-gate 				(void) strlcat(iconf->exec, " ", len);
6360Sstevel@tonic-gate 				(void) strlcat(iconf->exec, cp, len);
6370Sstevel@tonic-gate 			}
6380Sstevel@tonic-gate 		}
6390Sstevel@tonic-gate 		free(args);
6400Sstevel@tonic-gate 	}
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 	return (iconf);
6430Sstevel@tonic-gate fail:
6440Sstevel@tonic-gate 	free_inetconfent(iconf);
6450Sstevel@tonic-gate 	return (NULL);
6460Sstevel@tonic-gate }
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate static void
6490Sstevel@tonic-gate skipline(FILE *fp)
6500Sstevel@tonic-gate {
6510Sstevel@tonic-gate 	int c;
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 	/* skip remainder of a line */
6540Sstevel@tonic-gate 	while (((c = getc(fp)) != EOF) && (c != '\n'))
6550Sstevel@tonic-gate 		;
6560Sstevel@tonic-gate }
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate static struct inetconfent *
6590Sstevel@tonic-gate fgetinetconfent(struct fileinfo *finfo, boolean_t validate)
6600Sstevel@tonic-gate {
6610Sstevel@tonic-gate 	char *cp;
6620Sstevel@tonic-gate 	struct inetconfent *iconf;
6630Sstevel@tonic-gate 	char line[MAX_SRC_LINELEN];
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 	while (fgets(line, sizeof (line), finfo->fp) != NULL) {
6660Sstevel@tonic-gate 		finfo->lineno++;
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 		/* skip empty or commented out lines */
6690Sstevel@tonic-gate 		if (*line == '\n')
6700Sstevel@tonic-gate 			continue;
6710Sstevel@tonic-gate 		if (*line == '#') {
6720Sstevel@tonic-gate 			if (line[strlen(line) - 1] != '\n')
6730Sstevel@tonic-gate 				skipline(finfo->fp);
6740Sstevel@tonic-gate 			continue;
6750Sstevel@tonic-gate 		}
6760Sstevel@tonic-gate 		/* check for lines which are too long */
6770Sstevel@tonic-gate 		if (line[strlen(line) - 1] != '\n') {
6780Sstevel@tonic-gate 			(void) fprintf(stderr,
6790Sstevel@tonic-gate 			    gettext("%s: Error %s line %d too long, skipped\n"),
6800Sstevel@tonic-gate 			    progname, finfo->filename, finfo->lineno);
6810Sstevel@tonic-gate 			skipline(finfo->fp);
6820Sstevel@tonic-gate 			finfo->failcnt++;
6830Sstevel@tonic-gate 			continue;
6840Sstevel@tonic-gate 		}
6850Sstevel@tonic-gate 		/* remove in line comments and newline character */
6860Sstevel@tonic-gate 		if ((cp = strchr(line, '#')) == NULL)
6870Sstevel@tonic-gate 			cp = strchr(line, '\n');
6880Sstevel@tonic-gate 		if (cp)
6890Sstevel@tonic-gate 			*cp = '\0';
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate 		if ((iconf = line_to_inetconfent(line)) == NULL) {
6920Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
6930Sstevel@tonic-gate 			    "%s: Error %s line %d too few fields, skipped\n"),
6940Sstevel@tonic-gate 			    progname, finfo->filename, finfo->lineno);
6950Sstevel@tonic-gate 			finfo->failcnt++;
6960Sstevel@tonic-gate 			continue;
6970Sstevel@tonic-gate 		}
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate 		if (!validate || valid_inetconfent(iconf, finfo))
7000Sstevel@tonic-gate 			return (iconf);
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 		finfo->failcnt++;
7030Sstevel@tonic-gate 		free_inetconfent(iconf);
7040Sstevel@tonic-gate 	}
7050Sstevel@tonic-gate 	return (NULL);
7060Sstevel@tonic-gate }
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate static char *
7090Sstevel@tonic-gate boolstr(boolean_t val)
7100Sstevel@tonic-gate {
7110Sstevel@tonic-gate 	if (val)
7120Sstevel@tonic-gate 		return ("true");
7130Sstevel@tonic-gate 	return ("false");
7140Sstevel@tonic-gate }
7150Sstevel@tonic-gate 
7160Sstevel@tonic-gate static int
7170Sstevel@tonic-gate print_manifest(FILE *f, char *filename, struct inetconfent *iconf)
7180Sstevel@tonic-gate {
7190Sstevel@tonic-gate 	if (fprintf(f, xml_header) < 0)
7200Sstevel@tonic-gate 		goto print_err;
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate 	if (fprintf(f, xml_comment,
7230Sstevel@tonic-gate 	    iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0)
7240Sstevel@tonic-gate 		goto print_err;
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate 	if (fprintf(f, xml_service_bundle, iconf->service) < 0)
7270Sstevel@tonic-gate 		goto print_err;
7280Sstevel@tonic-gate 	if (fprintf(f, xml_service_name, servicename(iconf)) < 0)
7290Sstevel@tonic-gate 		goto print_err;
7300Sstevel@tonic-gate 	if (fprintf(f, xml_instance) < 0)
7310Sstevel@tonic-gate 		goto print_err;
7320Sstevel@tonic-gate 	if (fprintf(f, xml_restarter, INETD_INSTANCE_FMRI) < 0)
7330Sstevel@tonic-gate 		goto print_err;
734290Sdstaff 	if (iconf->isrpc) {
735290Sdstaff 		if (fprintf(f, xml_dependency, "rpcbind", RPCBIND_FMRI) < 0)
736290Sdstaff 			goto print_err;
737290Sdstaff 	}
7380Sstevel@tonic-gate 
7390Sstevel@tonic-gate 	if (fprintf(f, xml_exec_method_start, START_METHOD_NAME, PR_EXEC_NAME,
7400Sstevel@tonic-gate 	    iconf->exec, PR_USER_NAME, iconf->username, iconf->groupname) < 0)
7410Sstevel@tonic-gate 		goto print_err;
7420Sstevel@tonic-gate 	if (iconf->arg0 != NULL) {
7430Sstevel@tonic-gate 		if (fprintf(f, xml_arg0, PR_ARG0_NAME, iconf->arg0) < 0)
7440Sstevel@tonic-gate 			goto print_err;
7450Sstevel@tonic-gate 	}
7460Sstevel@tonic-gate 	if (fprintf(f, xml_exec_method_end) < 0)
7470Sstevel@tonic-gate 		goto print_err;
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 	if (fprintf(f, xml_exec_method_disable, DISABLE_METHOD_NAME,
7500Sstevel@tonic-gate 	    PR_EXEC_NAME) < 0)
7510Sstevel@tonic-gate 		goto print_err;
7520Sstevel@tonic-gate 	if (fprintf(f, xml_exec_method_end) < 0)
7530Sstevel@tonic-gate 		goto print_err;
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 	if (iconf->wait) {
7560Sstevel@tonic-gate 		if (fprintf(f, xml_exec_method_offline, OFFLINE_METHOD_NAME,
7570Sstevel@tonic-gate 		    PR_EXEC_NAME) < 0)
7580Sstevel@tonic-gate 			goto print_err;
7590Sstevel@tonic-gate 		if (fprintf(f, xml_exec_method_end) < 0)
7600Sstevel@tonic-gate 			goto print_err;
7610Sstevel@tonic-gate 	}
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate 	if (fprintf(f, xml_inetconv_group_start, PG_NAME_INETCONV,
7640Sstevel@tonic-gate 	    PR_AUTO_CONVERTED_NAME, boolstr(B_TRUE),
7650Sstevel@tonic-gate 	    PR_VERSION_NAME, INETCONV_VERSION,
7660Sstevel@tonic-gate 	    PR_SOURCE_LINE_NAME, iconf->service,
7670Sstevel@tonic-gate 	    iconf->endpoint, iconf->protocol, iconf->wait_status,
7680Sstevel@tonic-gate 	    iconf->username, iconf->server_program,
7690Sstevel@tonic-gate 	    iconf->server_args == NULL ? "" : " ",
7700Sstevel@tonic-gate 	    iconf->server_args == NULL ? "" : iconf->server_args) < 0)
7710Sstevel@tonic-gate 		goto print_err;
7720Sstevel@tonic-gate 	if (fprintf(f, xml_property_group_end) < 0)
7730Sstevel@tonic-gate 		goto print_err;
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate 	if (fprintf(f, xml_property_group_start, PG_NAME_SERVICE_CONFIG,
7760Sstevel@tonic-gate 	    PR_SVC_NAME_NAME, iconf->isrpc ? iconf->rpc_prog : iconf->service,
7770Sstevel@tonic-gate 	    PR_SOCK_TYPE_NAME, iconf->endpoint,
7780Sstevel@tonic-gate 	    PR_PROTO_NAME, iconf->isrpc ? iconf->protocol + 4 :
7790Sstevel@tonic-gate 	    iconf->protocol,
7800Sstevel@tonic-gate 	    PR_ISWAIT_NAME, boolstr(iconf->wait),
7810Sstevel@tonic-gate 	    PR_ISRPC_NAME, boolstr(iconf->isrpc)) < 0)
7820Sstevel@tonic-gate 		goto print_err;
7830Sstevel@tonic-gate 	if (iconf->isrpc) {
7840Sstevel@tonic-gate 		if (fprintf(f, xml_property_group_rpc,
7850Sstevel@tonic-gate 		    PR_RPC_LW_VER_NAME, iconf->rpc_low_version,
7860Sstevel@tonic-gate 		    PR_RPC_HI_VER_NAME, iconf->rpc_high_version) < 0)
7870Sstevel@tonic-gate 			goto print_err;
7880Sstevel@tonic-gate 	}
7890Sstevel@tonic-gate 	if (fprintf(f, xml_property_group_end) < 0)
7900Sstevel@tonic-gate 		goto print_err;
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate 	if (fprintf(f, xml_stability) < 0)
7930Sstevel@tonic-gate 		goto print_err;
7940Sstevel@tonic-gate 	if (fprintf(f, xml_template,
7950Sstevel@tonic-gate 	    iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0)
7960Sstevel@tonic-gate 		goto print_err;
7970Sstevel@tonic-gate 	if (fprintf(f, xml_footer) < 0)
7980Sstevel@tonic-gate 		goto print_err;
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 	(void) printf("%s -> %s\n", iconf->service, filename);
8010Sstevel@tonic-gate 	return (0);
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate print_err:
8040Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("%s: Error writing manifest %s: %s\n"),
8050Sstevel@tonic-gate 	    progname, filename, strerror(errno));
8060Sstevel@tonic-gate 	return (-1);
8070Sstevel@tonic-gate }
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate static struct fileinfo *
8100Sstevel@tonic-gate open_srcfile(char *filename)
8110Sstevel@tonic-gate {
8120Sstevel@tonic-gate 	struct fileinfo *finfo = NULL;
8130Sstevel@tonic-gate 	FILE *fp;
8140Sstevel@tonic-gate 
8150Sstevel@tonic-gate 	if (filename != NULL) {
8160Sstevel@tonic-gate 		if ((fp = fopen(filename, "r")) == NULL) {
8170Sstevel@tonic-gate 			(void) fprintf(stderr,
8180Sstevel@tonic-gate 			    gettext("%s: Error opening %s: %s\n"),
8190Sstevel@tonic-gate 			    progname, filename, strerror(errno));
8200Sstevel@tonic-gate 		}
8210Sstevel@tonic-gate 	} else {
8220Sstevel@tonic-gate 		/*
8230Sstevel@tonic-gate 		 * If no source file specified, do the same as inetd and first
8240Sstevel@tonic-gate 		 * try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
8250Sstevel@tonic-gate 		 */
8260Sstevel@tonic-gate 		filename = MAIN_CONFIG;
8270Sstevel@tonic-gate 		if ((fp = fopen(filename, "r")) == NULL) {
8280Sstevel@tonic-gate 			(void) fprintf(stderr,
8290Sstevel@tonic-gate 			    gettext("%s: Error opening %s: %s\n"),
8300Sstevel@tonic-gate 			    progname, filename, strerror(errno));
8310Sstevel@tonic-gate 			filename = ALT_CONFIG;
8320Sstevel@tonic-gate 			if ((fp = fopen(filename, "r")) == NULL) {
8330Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
8340Sstevel@tonic-gate 				    "%s: Error opening %s: %s\n"), progname,
8350Sstevel@tonic-gate 				    filename, strerror(errno));
8360Sstevel@tonic-gate 			}
8370Sstevel@tonic-gate 		}
8380Sstevel@tonic-gate 	}
8390Sstevel@tonic-gate 	if (fp != NULL) {
8400Sstevel@tonic-gate 		finfo = safe_malloc(sizeof (struct fileinfo));
8410Sstevel@tonic-gate 		finfo->fp = fp;
8420Sstevel@tonic-gate 		finfo->filename = filename;
8430Sstevel@tonic-gate 		finfo->lineno = 0;
8440Sstevel@tonic-gate 		finfo->failcnt = 0;
8450Sstevel@tonic-gate 		(void) fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
8460Sstevel@tonic-gate 	}
8470Sstevel@tonic-gate 	return (finfo);
8480Sstevel@tonic-gate }
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate /*
8510Sstevel@tonic-gate  * Opens manifest output file.  Returns 0 on success, -1 if the file
8520Sstevel@tonic-gate  * exists, -2 on other errors.
8530Sstevel@tonic-gate  */
8540Sstevel@tonic-gate static int
8550Sstevel@tonic-gate open_dstfile(
8560Sstevel@tonic-gate     char *destdir,
8570Sstevel@tonic-gate     boolean_t overwrite,
8580Sstevel@tonic-gate     struct inetconfent *iconf,
8590Sstevel@tonic-gate     struct fileinfo **finfo)
8600Sstevel@tonic-gate {
8610Sstevel@tonic-gate 	int fd;
8620Sstevel@tonic-gate 	size_t len;
8630Sstevel@tonic-gate 	char *dstfile, *cp, *proto;
8640Sstevel@tonic-gate 	FILE *fp;
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate 	/* if no destdir specified, use appropriate default */
8670Sstevel@tonic-gate 	if (destdir == NULL) {
8680Sstevel@tonic-gate 		if (iconf->isrpc)
8690Sstevel@tonic-gate 			destdir = MANIFEST_RPC_DIR;
8700Sstevel@tonic-gate 		else
8710Sstevel@tonic-gate 			destdir = MANIFEST_DIR;
8720Sstevel@tonic-gate 	}
8730Sstevel@tonic-gate 
8740Sstevel@tonic-gate 	len = strlen(destdir) + strlen(iconf->service) +
8750Sstevel@tonic-gate 	    strlen(iconf->protocol) + sizeof ("/-visible.xml");
8760Sstevel@tonic-gate 	dstfile = safe_malloc(len);
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	(void) strlcpy(dstfile, destdir, len);
8790Sstevel@tonic-gate 	if (dstfile[strlen(dstfile) - 1] != '/')
8800Sstevel@tonic-gate 		(void) strlcat(dstfile, "/", len);
8810Sstevel@tonic-gate 	cp = dstfile + strlen(dstfile);
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 	(void) strlcat(dstfile, iconf->service, len);
8840Sstevel@tonic-gate 	(void) strlcat(dstfile, "-", len);
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate 	proto = iconf->protocol;
8870Sstevel@tonic-gate 	if (iconf->isrpc && (strcmp(iconf->protocol, "rpc/*") == 0))
8880Sstevel@tonic-gate 		proto = "rpc/visible";
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate 	(void) strlcat(dstfile, proto, len);
8910Sstevel@tonic-gate 	(void) strlcat(dstfile, ".xml", len);
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 	/* convert any '/' chars in service or protocol to '_' chars */
8940Sstevel@tonic-gate 	while ((cp = strchr(cp, '/')) != NULL)
8950Sstevel@tonic-gate 		*cp = '_';
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate 	fd = open(dstfile, O_WRONLY|O_CREAT|(overwrite ? O_TRUNC : O_EXCL),
8980Sstevel@tonic-gate 	    0644);
8990Sstevel@tonic-gate 	if (fd == -1) {
9000Sstevel@tonic-gate 		if (!overwrite && (errno == EEXIST)) {
9010Sstevel@tonic-gate 			(void) fprintf(stderr,
9020Sstevel@tonic-gate 			    gettext("%s: Notice: Service manifest for "
9030Sstevel@tonic-gate 			    "%s already generated as %s, skipped\n"),
9040Sstevel@tonic-gate 			    progname, iconf->service, dstfile);
9050Sstevel@tonic-gate 			free(dstfile);
9060Sstevel@tonic-gate 			return (-1);
9070Sstevel@tonic-gate 		} else {
9080Sstevel@tonic-gate 			(void) fprintf(stderr,
9090Sstevel@tonic-gate 			    gettext("%s: Error opening %s: %s\n"),
9100Sstevel@tonic-gate 			    progname, dstfile, strerror(errno));
9110Sstevel@tonic-gate 			free(dstfile);
9120Sstevel@tonic-gate 			return (-2);
9130Sstevel@tonic-gate 		}
9140Sstevel@tonic-gate 	}
9150Sstevel@tonic-gate 	/* Clear errno to catch the "no stdio streams" case */
9160Sstevel@tonic-gate 	errno = 0;
9170Sstevel@tonic-gate 	if ((fp = fdopen(fd, "w")) == NULL) {
9180Sstevel@tonic-gate 		char *s = strerror(errno);
9190Sstevel@tonic-gate 		if (errno == 0)
9200Sstevel@tonic-gate 			s = gettext("No stdio streams available");
9210Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: Error fdopen failed: %s\n"),
9220Sstevel@tonic-gate 		    progname, s);
9230Sstevel@tonic-gate 		(void) close(fd);
9240Sstevel@tonic-gate 		free(dstfile);
9250Sstevel@tonic-gate 		return (-2);
9260Sstevel@tonic-gate 	}
9270Sstevel@tonic-gate 	*finfo = safe_malloc(sizeof (struct fileinfo));
9280Sstevel@tonic-gate 	(*finfo)->fp = fp;
9290Sstevel@tonic-gate 	(*finfo)->filename = dstfile;
9300Sstevel@tonic-gate 	(*finfo)->lineno = 0;
9310Sstevel@tonic-gate 	(*finfo)->failcnt = 0;
9320Sstevel@tonic-gate 	return (0);
9330Sstevel@tonic-gate }
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate static int
9360Sstevel@tonic-gate import_manifest(char *filename)
9370Sstevel@tonic-gate {
9380Sstevel@tonic-gate 	int status;
9390Sstevel@tonic-gate 	pid_t pid, wpid;
9400Sstevel@tonic-gate 	char *cp;
9410Sstevel@tonic-gate 
9420Sstevel@tonic-gate 	if ((cp = strrchr(filename, '/')) == NULL)
9430Sstevel@tonic-gate 		cp = filename;
9440Sstevel@tonic-gate 	else
9450Sstevel@tonic-gate 		cp++;
9460Sstevel@tonic-gate 	(void) printf(gettext("Importing %s ..."), cp);
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	if ((pid = fork()) == -1) {
9490Sstevel@tonic-gate 		(void) fprintf(stderr,
9500Sstevel@tonic-gate 		    gettext("\n%s: fork failed, %s not imported: %s\n"),
9510Sstevel@tonic-gate 		    progname, filename, strerror(errno));
9520Sstevel@tonic-gate 		exit(EXIT_ERROR_SYS);
9530Sstevel@tonic-gate 	}
9540Sstevel@tonic-gate 	if (pid == 0) {
9550Sstevel@tonic-gate 		/* child */
9560Sstevel@tonic-gate 		(void) fclose(stdin);
9570Sstevel@tonic-gate 		(void) setenv("SVCCFG_CHECKHASH", "1", 1);
9580Sstevel@tonic-gate 		(void) execl(SVCCFG_PATH, "svccfg", "import", filename, NULL);
9590Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("\n%s: exec of %s failed: %s"),
9600Sstevel@tonic-gate 		    progname, SVCCFG_PATH, strerror(errno));
9610Sstevel@tonic-gate 		_exit(EXIT_ERROR_SYS);
9620Sstevel@tonic-gate 	}
9630Sstevel@tonic-gate 	/* parent */
9640Sstevel@tonic-gate 	if ((wpid = waitpid(pid, &status, 0)) != pid) {
9650Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
9660Sstevel@tonic-gate 		    "\n%s: unexpected wait (%d) from import of %s: %s\n"),
9670Sstevel@tonic-gate 		    progname, wpid, filename, strerror(errno));
9680Sstevel@tonic-gate 		return (-1);
9690Sstevel@tonic-gate 	}
9700Sstevel@tonic-gate 	if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) {
9710Sstevel@tonic-gate 		(void) fprintf(stderr,
9720Sstevel@tonic-gate 		    gettext("\n%s: import failure (%d) for %s\n"),
9730Sstevel@tonic-gate 		    progname, WEXITSTATUS(status), filename);
9740Sstevel@tonic-gate 		return (-1);
9750Sstevel@tonic-gate 	}
9760Sstevel@tonic-gate 	(void) printf(gettext("Done\n"));
9770Sstevel@tonic-gate 	return (0);
9780Sstevel@tonic-gate }
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate static int
9810Sstevel@tonic-gate inetd_config_path(char **path)
9820Sstevel@tonic-gate {
9830Sstevel@tonic-gate 	int fd;
9840Sstevel@tonic-gate 	char *arg1, *configfile, *configstr;
9850Sstevel@tonic-gate 	scf_simple_prop_t *sp;
9860Sstevel@tonic-gate 	char cpath[PATH_MAX];
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 	if ((sp = scf_simple_prop_get(NULL, INETD_INSTANCE_FMRI, "start",
9890Sstevel@tonic-gate 	    SCF_PROPERTY_EXEC)) == NULL)
9900Sstevel@tonic-gate 		return (-1);
9910Sstevel@tonic-gate 	if ((configstr = scf_simple_prop_next_astring(sp)) == NULL) {
9920Sstevel@tonic-gate 		scf_simple_prop_free(sp);
9930Sstevel@tonic-gate 		return (-1);
9940Sstevel@tonic-gate 	}
9950Sstevel@tonic-gate 	configstr = safe_strdup(configstr);
9960Sstevel@tonic-gate 	scf_simple_prop_free(sp);
9970Sstevel@tonic-gate 
9980Sstevel@tonic-gate 	/*
9990Sstevel@tonic-gate 	 * Look for the optional configuration file, the syntax is:
10000Sstevel@tonic-gate 	 * /usr/lib/inet/inetd [config-file] start|stop|refresh|disable|%m
10010Sstevel@tonic-gate 	 */
10020Sstevel@tonic-gate 	if (strtok(configstr, " \t") == NULL) {
10030Sstevel@tonic-gate 		free(configstr);
10040Sstevel@tonic-gate 		return (-1);
10050Sstevel@tonic-gate 	}
10060Sstevel@tonic-gate 	if ((arg1 = strtok(NULL, " \t")) == NULL) {
10070Sstevel@tonic-gate 		free(configstr);
10080Sstevel@tonic-gate 		return (-1);
10090Sstevel@tonic-gate 	}
10100Sstevel@tonic-gate 	if (strtok(NULL, " \t") == NULL) {
10110Sstevel@tonic-gate 		/*
10120Sstevel@tonic-gate 		 * No configuration file specified, do the same as inetd and
10130Sstevel@tonic-gate 		 * first try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
10140Sstevel@tonic-gate 		 */
10150Sstevel@tonic-gate 		configfile = MAIN_CONFIG;
10160Sstevel@tonic-gate 		if ((fd = open(configfile, O_RDONLY)) >= 0)
10170Sstevel@tonic-gate 			(void) close(fd);
10180Sstevel@tonic-gate 		else
10190Sstevel@tonic-gate 			configfile = ALT_CONFIG;
10200Sstevel@tonic-gate 
10210Sstevel@tonic-gate 	} else {
10220Sstevel@tonic-gate 		/* make sure there are no more arguments */
10230Sstevel@tonic-gate 		if (strtok(NULL, " \t") != NULL) {
10240Sstevel@tonic-gate 			free(configstr);
10250Sstevel@tonic-gate 			return (-1);
10260Sstevel@tonic-gate 		}
10270Sstevel@tonic-gate 		configfile = arg1;
10280Sstevel@tonic-gate 	}
10290Sstevel@tonic-gate 
10300Sstevel@tonic-gate 	/* configuration file must be an absolute pathname */
10310Sstevel@tonic-gate 	if (*configfile != '/') {
10320Sstevel@tonic-gate 		free(configstr);
10330Sstevel@tonic-gate 		return (-1);
10340Sstevel@tonic-gate 	}
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	if (realpath(configfile, cpath) == NULL)
10370Sstevel@tonic-gate 		(void) strlcpy(cpath, configfile, sizeof (cpath));
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 	free(configstr);
10400Sstevel@tonic-gate 	*path = safe_strdup(cpath);
10410Sstevel@tonic-gate 	return (0);
10420Sstevel@tonic-gate }
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate static int
10450Sstevel@tonic-gate update_hash(char *srcfile)
10460Sstevel@tonic-gate {
10470Sstevel@tonic-gate 	scf_error_t rval;
10480Sstevel@tonic-gate 	char *inetd_cpath, *hashstr;
10490Sstevel@tonic-gate 	char cpath[PATH_MAX];
10500Sstevel@tonic-gate 
10510Sstevel@tonic-gate 	/* determine the config file inetd is using */
10520Sstevel@tonic-gate 	if (inetd_config_path(&inetd_cpath) == -1) {
10530Sstevel@tonic-gate 		(void) fprintf(stderr,
10540Sstevel@tonic-gate 		    gettext("%s: Error reading from repository\n"), progname);
10550Sstevel@tonic-gate 		return (-1);
10560Sstevel@tonic-gate 	}
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 	/* resolve inetconv input filename */
10590Sstevel@tonic-gate 	if (realpath(srcfile, cpath) == NULL)
10600Sstevel@tonic-gate 		(void) strlcpy(cpath, srcfile, sizeof (cpath));
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 	/* if inetconv and inetd are using the same config file, update hash */
10630Sstevel@tonic-gate 	if (strcmp(cpath, inetd_cpath) != 0) {
10640Sstevel@tonic-gate 		free(inetd_cpath);
10650Sstevel@tonic-gate 		return (0);
10660Sstevel@tonic-gate 	}
10670Sstevel@tonic-gate 	free(inetd_cpath);
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 	/* generic error message as use of hash is not exposed to the user */
10700Sstevel@tonic-gate 	if (calculate_hash(cpath, &hashstr) != 0) {
10710Sstevel@tonic-gate 		(void) fprintf(stderr,
10720Sstevel@tonic-gate 		    gettext("%s: Error unable to update repository\n"),
10730Sstevel@tonic-gate 		    progname);
10740Sstevel@tonic-gate 		return (-1);
10750Sstevel@tonic-gate 	}
10760Sstevel@tonic-gate 	/* generic error message as use of hash is not exposed to the user */
10770Sstevel@tonic-gate 	if ((rval = store_inetd_hash(hashstr)) != SCF_ERROR_NONE) {
10780Sstevel@tonic-gate 		(void) fprintf(stderr,
10790Sstevel@tonic-gate 		    gettext("%s: Error updating repository: %s\n"),
10800Sstevel@tonic-gate 		    progname, scf_strerror(rval));
10810Sstevel@tonic-gate 		free(hashstr);
10820Sstevel@tonic-gate 		return (-1);
10830Sstevel@tonic-gate 	}
10840Sstevel@tonic-gate 	free(hashstr);
10850Sstevel@tonic-gate 	return (0);
10860Sstevel@tonic-gate }
10870Sstevel@tonic-gate 
10880Sstevel@tonic-gate static void
10890Sstevel@tonic-gate property_error(const char *fmri, const char *prop)
10900Sstevel@tonic-gate {
10910Sstevel@tonic-gate 	(void) fprintf(stderr,
10920Sstevel@tonic-gate 	    gettext("Error: Instance %1$s is missing property '%2$s'.\n"),
10930Sstevel@tonic-gate 	    fmri, prop);
10940Sstevel@tonic-gate }
10950Sstevel@tonic-gate 
10960Sstevel@tonic-gate /*
10970Sstevel@tonic-gate  * modify_sprop takes a handle, an instance, a property group, a property,
10980Sstevel@tonic-gate  * and an astring value, and modifies the instance (or service's) specified
10990Sstevel@tonic-gate  * property in the repository to the submitted value.
11000Sstevel@tonic-gate  *
11010Sstevel@tonic-gate  * returns -1 on error, 1 on successful transaction completion.
11020Sstevel@tonic-gate  */
11030Sstevel@tonic-gate 
11040Sstevel@tonic-gate static int
11050Sstevel@tonic-gate modify_sprop(scf_handle_t *h, const scf_instance_t *inst,
11060Sstevel@tonic-gate     const char *pg, const char *prop, const char *value)
11070Sstevel@tonic-gate {
11080Sstevel@tonic-gate 	scf_transaction_t		*tx = NULL;
11090Sstevel@tonic-gate 	scf_transaction_entry_t		*ent = NULL;
11100Sstevel@tonic-gate 	scf_propertygroup_t		*gpg = NULL;
11110Sstevel@tonic-gate 	scf_property_t			*eprop = NULL;
11120Sstevel@tonic-gate 	scf_value_t			*v = NULL;
11130Sstevel@tonic-gate 	scf_service_t			*svc = NULL;
11140Sstevel@tonic-gate 	int				ret = 0, create = 0;
11150Sstevel@tonic-gate 
11160Sstevel@tonic-gate 	if ((gpg = scf_pg_create(h)) == NULL)
11170Sstevel@tonic-gate 		return (-1);
11180Sstevel@tonic-gate 
11190Sstevel@tonic-gate 	/* Get the property group */
11200Sstevel@tonic-gate 	if (scf_instance_get_pg(inst, pg, gpg) == -1) {
11210Sstevel@tonic-gate 		/* Not a property of the instance, try the service instead */
11220Sstevel@tonic-gate 		if ((svc = scf_service_create(h)) == NULL) {
11230Sstevel@tonic-gate 			ret = -1;
11240Sstevel@tonic-gate 			goto out;
11250Sstevel@tonic-gate 		}
11260Sstevel@tonic-gate 		if ((scf_instance_get_parent(inst, svc) == -1) ||
11270Sstevel@tonic-gate 		    (scf_service_get_pg(svc, pg, gpg) == -1)) {
11280Sstevel@tonic-gate 			ret = -1;
11290Sstevel@tonic-gate 			goto out;
11300Sstevel@tonic-gate 		}
11310Sstevel@tonic-gate 	}
11320Sstevel@tonic-gate 
11330Sstevel@tonic-gate 	if ((eprop = scf_property_create(h)) == NULL) {
11340Sstevel@tonic-gate 		ret = -1;
11350Sstevel@tonic-gate 		goto out;
11360Sstevel@tonic-gate 	}
11370Sstevel@tonic-gate 
11380Sstevel@tonic-gate 	if (scf_pg_get_property(gpg, prop, eprop) == -1) {
11390Sstevel@tonic-gate 		if (scf_error() != SCF_ERROR_NOT_FOUND) {
11400Sstevel@tonic-gate 			ret = -1;
11410Sstevel@tonic-gate 			goto out;
11420Sstevel@tonic-gate 		}
11430Sstevel@tonic-gate 
11440Sstevel@tonic-gate 		create = 1;
11450Sstevel@tonic-gate 	}
11460Sstevel@tonic-gate 
11470Sstevel@tonic-gate 	if ((tx = scf_transaction_create(h)) == NULL ||
11480Sstevel@tonic-gate 	    (ent = scf_entry_create(h)) == NULL) {
11490Sstevel@tonic-gate 		ret = -1;
11500Sstevel@tonic-gate 		goto out;
11510Sstevel@tonic-gate 	}
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 	do {
11540Sstevel@tonic-gate 		if (scf_transaction_start(tx, gpg) == -1) {
11550Sstevel@tonic-gate 			ret = -1;
11560Sstevel@tonic-gate 			goto out;
11570Sstevel@tonic-gate 		}
11580Sstevel@tonic-gate 
11590Sstevel@tonic-gate 		/* Modify the property */
11600Sstevel@tonic-gate 		if (create)
11610Sstevel@tonic-gate 			ret = scf_transaction_property_new(tx, ent, prop,
11620Sstevel@tonic-gate 			    SCF_TYPE_ASTRING);
11630Sstevel@tonic-gate 		else
11640Sstevel@tonic-gate 			ret = scf_transaction_property_change_type(tx, ent,
11650Sstevel@tonic-gate 			    prop, SCF_TYPE_ASTRING);
11660Sstevel@tonic-gate 
11670Sstevel@tonic-gate 		if (ret == -1)
11680Sstevel@tonic-gate 			goto out;
11690Sstevel@tonic-gate 
11700Sstevel@tonic-gate 		if ((v = scf_value_create(h)) == NULL) {
11710Sstevel@tonic-gate 			ret = -1;
11720Sstevel@tonic-gate 			goto out;
11730Sstevel@tonic-gate 		}
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate 		if (scf_value_set_astring(v, value) == -1) {
11760Sstevel@tonic-gate 			ret = -1;
11770Sstevel@tonic-gate 			goto out;
11780Sstevel@tonic-gate 		}
11790Sstevel@tonic-gate 
11800Sstevel@tonic-gate 		if (scf_entry_add_value(ent, v) == -1) {
11810Sstevel@tonic-gate 			ret = -1;
11820Sstevel@tonic-gate 			goto out;
11830Sstevel@tonic-gate 		}
11840Sstevel@tonic-gate 
11850Sstevel@tonic-gate 		ret = scf_transaction_commit(tx);
11860Sstevel@tonic-gate 
11870Sstevel@tonic-gate 		if (ret == 0) {
11880Sstevel@tonic-gate 			/* Property group was stale, retry */
11890Sstevel@tonic-gate 			if (scf_pg_update(gpg) == -1) {
11900Sstevel@tonic-gate 				ret = -1;
11910Sstevel@tonic-gate 				goto out;
11920Sstevel@tonic-gate 			}
11930Sstevel@tonic-gate 			scf_transaction_reset(tx);
11940Sstevel@tonic-gate 		}
11950Sstevel@tonic-gate 
11960Sstevel@tonic-gate 	} while (ret == 0);
11970Sstevel@tonic-gate out:
11980Sstevel@tonic-gate 	scf_value_destroy(v);
11990Sstevel@tonic-gate 	scf_entry_destroy(ent);
12000Sstevel@tonic-gate 	scf_transaction_destroy(tx);
12010Sstevel@tonic-gate 	scf_property_destroy(eprop);
12020Sstevel@tonic-gate 	scf_service_destroy(svc);
12030Sstevel@tonic-gate 	scf_pg_destroy(gpg);
12040Sstevel@tonic-gate 
12050Sstevel@tonic-gate 	return (ret);
12060Sstevel@tonic-gate }
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate /*
12090Sstevel@tonic-gate  * list_callback is the callback function to be handed to simple_walk_instances
12100Sstevel@tonic-gate  * in main.  It is called once on every instance on a machine.  If that
12110Sstevel@tonic-gate  * instance is controlled by inetd, we test whether it's the same
12120Sstevel@tonic-gate  * service that we're looking at from the inetd.conf file, and enable it if
12130Sstevel@tonic-gate  * they are the same.
12140Sstevel@tonic-gate  */
12150Sstevel@tonic-gate 
12160Sstevel@tonic-gate /*ARGSUSED*/
12170Sstevel@tonic-gate static int
12180Sstevel@tonic-gate list_callback(scf_handle_t *h, scf_instance_t *inst, void *buf)
12190Sstevel@tonic-gate {
12200Sstevel@tonic-gate 	ssize_t			max_name_length;
12210Sstevel@tonic-gate 	char			*svc_name;
12220Sstevel@tonic-gate 	scf_simple_prop_t	*prop = NULL;
12230Sstevel@tonic-gate 	scf_simple_prop_t	*sockprop = NULL;
12240Sstevel@tonic-gate 	scf_simple_prop_t	*rpcprop = NULL;
12250Sstevel@tonic-gate 	scf_simple_prop_t	*progprop = NULL;
12260Sstevel@tonic-gate 	const char		*name, *endpoint, *restart_str, *prog;
12270Sstevel@tonic-gate 	struct inetconfent	*iconf = (struct inetconfent *)buf;
12280Sstevel@tonic-gate 	uint8_t			*isrpc;
12290Sstevel@tonic-gate 
12300Sstevel@tonic-gate 	max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
12310Sstevel@tonic-gate 	if ((svc_name = malloc(max_name_length + 1)) == NULL) {
12320Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("Error: Out of memory.\n"));
12330Sstevel@tonic-gate 		return (SCF_FAILED);
12340Sstevel@tonic-gate 	}
12350Sstevel@tonic-gate 
12360Sstevel@tonic-gate 	/*
12370Sstevel@tonic-gate 	 * Get the FMRI of the instance, and check if its delegated restarter
12380Sstevel@tonic-gate 	 * is inetd.  A missing or empty restarter property implies that
12390Sstevel@tonic-gate 	 * svc.startd is the restarter.
12400Sstevel@tonic-gate 	 */
12410Sstevel@tonic-gate 
12420Sstevel@tonic-gate 	if (scf_instance_to_fmri(inst, svc_name, max_name_length) < 0) {
12430Sstevel@tonic-gate 		(void) fprintf(stderr,
12440Sstevel@tonic-gate 		    gettext("Error: Unable to obtain FMRI for service %1$s."),
12450Sstevel@tonic-gate 		    svc_name);
12460Sstevel@tonic-gate 		free(svc_name);
12470Sstevel@tonic-gate 		return (SCF_FAILED);
12480Sstevel@tonic-gate 	}
12490Sstevel@tonic-gate 
12500Sstevel@tonic-gate 	if ((prop = scf_simple_prop_get(h, svc_name, SCF_PG_GENERAL,
12510Sstevel@tonic-gate 	    SCF_PROPERTY_RESTARTER)) == NULL)
12520Sstevel@tonic-gate 		goto out;
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate 	if ((restart_str = scf_simple_prop_next_ustring(prop)) == NULL)
12550Sstevel@tonic-gate 		goto out;
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate 	if (strcmp(restart_str, INETD_INSTANCE_FMRI) != 0)
12580Sstevel@tonic-gate 		goto out;
12590Sstevel@tonic-gate 
12600Sstevel@tonic-gate 	/* Free restarter prop so it can be reused below */
12610Sstevel@tonic-gate 	scf_simple_prop_free(prop);
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate 	/*
12640Sstevel@tonic-gate 	 * We know that this instance is managed by inetd.
12650Sstevel@tonic-gate 	 * Now get the properties needed to decide if it matches this
12660Sstevel@tonic-gate 	 * line in the old config file.
12670Sstevel@tonic-gate 	 */
12680Sstevel@tonic-gate 
12690Sstevel@tonic-gate 	if (((prop = scf_simple_prop_get(h, svc_name, PG_NAME_SERVICE_CONFIG,
12700Sstevel@tonic-gate 	    PR_SVC_NAME_NAME)) == NULL) ||
12710Sstevel@tonic-gate 	    ((name = scf_simple_prop_next_astring(prop)) == NULL)) {
12720Sstevel@tonic-gate 		property_error(svc_name, PR_SVC_NAME_NAME);
12730Sstevel@tonic-gate 		goto out;
12740Sstevel@tonic-gate 	}
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate 	if (((sockprop = scf_simple_prop_get(h, svc_name,
12770Sstevel@tonic-gate 	    PG_NAME_SERVICE_CONFIG, PR_SOCK_TYPE_NAME)) == NULL) ||
12780Sstevel@tonic-gate 	    ((endpoint = scf_simple_prop_next_astring(sockprop)) == NULL)) {
12790Sstevel@tonic-gate 		property_error(svc_name, PR_SOCK_TYPE_NAME);
12800Sstevel@tonic-gate 		goto out;
12810Sstevel@tonic-gate 	}
12820Sstevel@tonic-gate 
12830Sstevel@tonic-gate 	if (((rpcprop = scf_simple_prop_get(h, svc_name,
12840Sstevel@tonic-gate 	    PG_NAME_SERVICE_CONFIG, PR_ISRPC_NAME)) == NULL) ||
12850Sstevel@tonic-gate 	    ((isrpc = scf_simple_prop_next_boolean(rpcprop)) == NULL)) {
12860Sstevel@tonic-gate 		property_error(svc_name, PR_ISRPC_NAME);
12870Sstevel@tonic-gate 		goto out;
12880Sstevel@tonic-gate 	}
12890Sstevel@tonic-gate 
12900Sstevel@tonic-gate 	if (((progprop = scf_simple_prop_get(h, svc_name, START_METHOD_NAME,
12910Sstevel@tonic-gate 	    PR_EXEC_NAME)) == NULL) ||
12920Sstevel@tonic-gate 	    ((prog = scf_simple_prop_next_astring(progprop)) == NULL)) {
12930Sstevel@tonic-gate 		property_error(svc_name, PR_EXEC_NAME);
12940Sstevel@tonic-gate 	}
12950Sstevel@tonic-gate 
12960Sstevel@tonic-gate 
12970Sstevel@tonic-gate 	/* If it's RPC, we truncate off the version portion for comparison */
12980Sstevel@tonic-gate 	if (*isrpc) {
12990Sstevel@tonic-gate 		char *cp;
13000Sstevel@tonic-gate 
13010Sstevel@tonic-gate 		cp = strchr(iconf->service, '/');
13020Sstevel@tonic-gate 		if (cp != NULL)
13030Sstevel@tonic-gate 			*cp = '\0';
13040Sstevel@tonic-gate 	}
13050Sstevel@tonic-gate 
13060Sstevel@tonic-gate 	/*
13070Sstevel@tonic-gate 	 * If name of this service and endpoint are equal to values from
13080Sstevel@tonic-gate 	 * iconf fields, and they're either both RPC or both non-RPC,
13090Sstevel@tonic-gate 	 * then we have a match; update the exec and arg0 properties if
13100Sstevel@tonic-gate 	 * necessary, then enable it.
13110Sstevel@tonic-gate 	 * We don't return an error if either operation fails so that we
13120Sstevel@tonic-gate 	 * continue to try all the other services.
13130Sstevel@tonic-gate 	 */
13140Sstevel@tonic-gate 	if (strcmp(name, iconf->service) == 0 &&
13150Sstevel@tonic-gate 	    strcmp(endpoint, iconf->endpoint) == 0 &&
13160Sstevel@tonic-gate 	    *isrpc == (strncmp(iconf->protocol, "rpc/", 4) == 0)) {
13170Sstevel@tonic-gate 		/* Can't update exec on internal services */
13180Sstevel@tonic-gate 		if ((strcmp(iconf->server_program, "internal") != 0) &&
13190Sstevel@tonic-gate 		    (strcmp(iconf->exec, prog) != 0)) {
13200Sstevel@tonic-gate 			/* User had edited the command */
13210Sstevel@tonic-gate 			if (!import) {
13220Sstevel@tonic-gate 				/* Dry run only */
13230Sstevel@tonic-gate 				(void) printf(
13240Sstevel@tonic-gate 				    gettext("Would update %s to %s %s"),
13250Sstevel@tonic-gate 				    svc_name, PR_EXEC_NAME, iconf->exec);
13260Sstevel@tonic-gate 				if (iconf->arg0 != NULL) {
13270Sstevel@tonic-gate 					(void) printf(
13280Sstevel@tonic-gate 					    gettext(" with %s of %s\n"),
13290Sstevel@tonic-gate 					    PR_ARG0_NAME, iconf->arg0);
13300Sstevel@tonic-gate 				} else {
13310Sstevel@tonic-gate 					(void) printf("\n");
13320Sstevel@tonic-gate 				}
13330Sstevel@tonic-gate 			} else {
13340Sstevel@tonic-gate 				/* Update instance's exec property */
13350Sstevel@tonic-gate 				if (modify_sprop(h, inst, START_METHOD_NAME,
13360Sstevel@tonic-gate 				    PR_EXEC_NAME, iconf->exec) != 1)
13370Sstevel@tonic-gate 					(void) fprintf(stderr,
13380Sstevel@tonic-gate 					    gettext("Error: Unable to update "
13390Sstevel@tonic-gate 					    "%s property of %s, %s\n"),
13400Sstevel@tonic-gate 					    PR_EXEC_NAME, svc_name,
13410Sstevel@tonic-gate 					    scf_strerror(scf_error()));
13420Sstevel@tonic-gate 				else
13430Sstevel@tonic-gate 					(void) printf("%s will %s %s\n",
13440Sstevel@tonic-gate 					    svc_name, PR_EXEC_NAME,
13450Sstevel@tonic-gate 					    iconf->exec);
13460Sstevel@tonic-gate 
13470Sstevel@tonic-gate 				/* Update arg0 prop, if needed */
13480Sstevel@tonic-gate 				if (iconf->arg0 != NULL) {
13490Sstevel@tonic-gate 					if (modify_sprop(h, inst,
13500Sstevel@tonic-gate 					    START_METHOD_NAME, PR_ARG0_NAME,
13510Sstevel@tonic-gate 					    iconf->arg0) != 1) {
13520Sstevel@tonic-gate 						(void) fprintf(stderr,
13530Sstevel@tonic-gate 						    gettext("Error: Unable to "
13540Sstevel@tonic-gate 						    "update %s property of "
13550Sstevel@tonic-gate 						    "%s, %s\n"), PR_ARG0_NAME,
13560Sstevel@tonic-gate 						    svc_name,
13570Sstevel@tonic-gate 						    scf_strerror(scf_error()));
13580Sstevel@tonic-gate 					} else {
13590Sstevel@tonic-gate 						(void) printf("%s will have an "
13600Sstevel@tonic-gate 						    "%s of %s\n", svc_name,
13610Sstevel@tonic-gate 						    PR_ARG0_NAME, iconf->arg0);
13620Sstevel@tonic-gate 					}
13630Sstevel@tonic-gate 				}
13640Sstevel@tonic-gate 			}
13650Sstevel@tonic-gate 		}
13660Sstevel@tonic-gate 
13670Sstevel@tonic-gate 		if (!import) {
13680Sstevel@tonic-gate 			/* Dry-run only */
13690Sstevel@tonic-gate 			(void) printf("Would enable %s\n", svc_name);
13700Sstevel@tonic-gate 		} else {
13710Sstevel@tonic-gate 			if (smf_enable_instance(svc_name, 0) != 0)
13720Sstevel@tonic-gate 				(void) fprintf(stderr,
13730Sstevel@tonic-gate 				    gettext("Error: Failed to enable %s\n"),
13740Sstevel@tonic-gate 				    svc_name);
13750Sstevel@tonic-gate 			else
13760Sstevel@tonic-gate 				(void) printf("%s enabled\n", svc_name);
13770Sstevel@tonic-gate 		}
13780Sstevel@tonic-gate 	}
13790Sstevel@tonic-gate 
13800Sstevel@tonic-gate out:
13810Sstevel@tonic-gate 	free(svc_name);
13820Sstevel@tonic-gate 	scf_simple_prop_free(prop);
13830Sstevel@tonic-gate 	scf_simple_prop_free(sockprop);
13840Sstevel@tonic-gate 	scf_simple_prop_free(rpcprop);
13850Sstevel@tonic-gate 	scf_simple_prop_free(progprop);
13860Sstevel@tonic-gate 	return (SCF_SUCCESS);
13870Sstevel@tonic-gate }
13880Sstevel@tonic-gate 
13890Sstevel@tonic-gate static void
13900Sstevel@tonic-gate usage(void)
13910Sstevel@tonic-gate {
13920Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
13930Sstevel@tonic-gate 	    "Usage: %s [-fn] [-i srcfile] [-o destdir]\n"
13940Sstevel@tonic-gate 	    "       %1$s -e [-n] [-i srcfile]\n"
13950Sstevel@tonic-gate 	    "-?          Display this usage message\n"
13960Sstevel@tonic-gate 	    "-e          Enable smf services which are enabled in the input\n"
13970Sstevel@tonic-gate 	    "            file\n"
13980Sstevel@tonic-gate 	    "-f          Force overwrite of existing manifests\n"
13990Sstevel@tonic-gate 	    "-n          Do not import converted manifests,\n"
14000Sstevel@tonic-gate 	    "            or only display services which would be enabled\n"
14010Sstevel@tonic-gate 	    "-i srcfile  Alternate input file\n"
14020Sstevel@tonic-gate 	    "-o destdir  Alternate output directory for manifests\n"),
14030Sstevel@tonic-gate 	    progname);
14040Sstevel@tonic-gate 	exit(EXIT_USAGE);
14050Sstevel@tonic-gate }
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate int
14080Sstevel@tonic-gate main(int argc, char *argv[])
14090Sstevel@tonic-gate {
14100Sstevel@tonic-gate 	int c, rval, convert_err, import_err = 0, enable_err = 0;
14110Sstevel@tonic-gate 	boolean_t overwrite = B_FALSE;
14120Sstevel@tonic-gate 	boolean_t enable = B_FALSE;
14130Sstevel@tonic-gate 	char *srcfile = NULL;
14140Sstevel@tonic-gate 	char *destdir = NULL;
14150Sstevel@tonic-gate 	struct fileinfo *srcfinfo, *dstfinfo;
14160Sstevel@tonic-gate 	struct inetconfent *iconf;
14170Sstevel@tonic-gate 
14180Sstevel@tonic-gate 	setbuf(stdout, NULL);
14190Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
14200Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
14210Sstevel@tonic-gate 
14220Sstevel@tonic-gate 	if ((progname = strrchr(argv[0], '/')) == NULL)
14230Sstevel@tonic-gate 		progname = argv[0];
14240Sstevel@tonic-gate 	else
14250Sstevel@tonic-gate 		progname++;
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "?efni:o:")) != -1) {
14280Sstevel@tonic-gate 		switch (c) {
14290Sstevel@tonic-gate 		case 'e':
14300Sstevel@tonic-gate 			/* enable services based on existing file config */
14310Sstevel@tonic-gate 			enable = B_TRUE;
14320Sstevel@tonic-gate 			break;
14330Sstevel@tonic-gate 
14340Sstevel@tonic-gate 		case 'f':
14350Sstevel@tonic-gate 			/* overwrite existing manifests */
14360Sstevel@tonic-gate 			overwrite = B_TRUE;
14370Sstevel@tonic-gate 			break;
14380Sstevel@tonic-gate 		case 'n':
14390Sstevel@tonic-gate 			/* don't import manifests, or dry-run enable */
14400Sstevel@tonic-gate 			import = B_FALSE;
14410Sstevel@tonic-gate 			break;
14420Sstevel@tonic-gate 		case 'i':
14430Sstevel@tonic-gate 			/* alternate input file */
14440Sstevel@tonic-gate 			if (srcfile != NULL) {
14450Sstevel@tonic-gate 				(void) fprintf(stderr,
14460Sstevel@tonic-gate 				    gettext("%s: Error only one -%c allowed\n"),
14470Sstevel@tonic-gate 				    progname, optopt);
14480Sstevel@tonic-gate 				usage();
14490Sstevel@tonic-gate 			}
14500Sstevel@tonic-gate 			srcfile = optarg;
14510Sstevel@tonic-gate 			break;
14520Sstevel@tonic-gate 		case 'o':
14530Sstevel@tonic-gate 			/* alternate output directory */
14540Sstevel@tonic-gate 			if (destdir != NULL) {
14550Sstevel@tonic-gate 				(void) fprintf(stderr,
14560Sstevel@tonic-gate 				    gettext("%s: Error only one -%c allowed\n"),
14570Sstevel@tonic-gate 				    progname, optopt);
14580Sstevel@tonic-gate 				usage();
14590Sstevel@tonic-gate 			}
14600Sstevel@tonic-gate 			destdir = optarg;
14610Sstevel@tonic-gate 			break;
14620Sstevel@tonic-gate 		case '?': /*FALLTHROUGH*/
14630Sstevel@tonic-gate 		default:
14640Sstevel@tonic-gate 			usage();
14650Sstevel@tonic-gate 			break;
14660Sstevel@tonic-gate 		}
14670Sstevel@tonic-gate 	}
14680Sstevel@tonic-gate 
14690Sstevel@tonic-gate 	/*
14700Sstevel@tonic-gate 	 * Display usage if extraneous args supplied or enable specified in
14710Sstevel@tonic-gate 	 * combination with overwrite or destdir
14720Sstevel@tonic-gate 	 */
14730Sstevel@tonic-gate 	if ((optind != argc) || (enable && (overwrite || destdir != NULL)))
14740Sstevel@tonic-gate 		usage();
14750Sstevel@tonic-gate 
14760Sstevel@tonic-gate 	if ((srcfinfo = open_srcfile(srcfile)) == NULL)
14770Sstevel@tonic-gate 		return (EXIT_ERROR_CONV);
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate 	while ((iconf = fgetinetconfent(srcfinfo, !enable)) != NULL) {
14800Sstevel@tonic-gate 		/*
14810Sstevel@tonic-gate 		 * If we're enabling, then just walk all the services for each
14820Sstevel@tonic-gate 		 * line and enable those which match.
14830Sstevel@tonic-gate 		 */
14840Sstevel@tonic-gate 		if (enable) {
14850Sstevel@tonic-gate 			rval = scf_simple_walk_instances(SCF_STATE_ALL, iconf,
14860Sstevel@tonic-gate 			    list_callback);
14870Sstevel@tonic-gate 			free_inetconfent(iconf);
14880Sstevel@tonic-gate 			if (rval == SCF_FAILED) {
14890Sstevel@tonic-gate 				/* Only print msg if framework error */
14900Sstevel@tonic-gate 				if (scf_error() != SCF_ERROR_CALLBACK_FAILED)
14910Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
14920Sstevel@tonic-gate 					    "Error walking instances: %s.\n"),
14930Sstevel@tonic-gate 					    scf_strerror(scf_error()));
14940Sstevel@tonic-gate 				enable_err++;
14950Sstevel@tonic-gate 				break;
14960Sstevel@tonic-gate 			}
14970Sstevel@tonic-gate 			continue;
14980Sstevel@tonic-gate 		}
14990Sstevel@tonic-gate 
15000Sstevel@tonic-gate 		/* Remainder of loop used for conversion & import */
15010Sstevel@tonic-gate 		if ((rval = open_dstfile(destdir, overwrite, iconf, &dstfinfo))
15020Sstevel@tonic-gate 		    < 0) {
15030Sstevel@tonic-gate 			/*
15040Sstevel@tonic-gate 			 * Only increment error counter if the failure was
15050Sstevel@tonic-gate 			 * other than the file already existing.
15060Sstevel@tonic-gate 			 */
15070Sstevel@tonic-gate 			if (rval == -2)
15080Sstevel@tonic-gate 				srcfinfo->failcnt++;
15090Sstevel@tonic-gate 			free_inetconfent(iconf);
15100Sstevel@tonic-gate 			continue;
15110Sstevel@tonic-gate 		}
15120Sstevel@tonic-gate 		rval = print_manifest(dstfinfo->fp, dstfinfo->filename, iconf);
15130Sstevel@tonic-gate 		(void) fclose(dstfinfo->fp);
15140Sstevel@tonic-gate 		if (rval == 0) {
15150Sstevel@tonic-gate 			if (import &&
15160Sstevel@tonic-gate 			    (import_manifest(dstfinfo->filename) != 0))
15170Sstevel@tonic-gate 				import_err++;
15180Sstevel@tonic-gate 		} else {
15190Sstevel@tonic-gate 			(void) unlink(dstfinfo->filename);
15200Sstevel@tonic-gate 			srcfinfo->failcnt++;
15210Sstevel@tonic-gate 		}
15220Sstevel@tonic-gate 		free(dstfinfo->filename);
15230Sstevel@tonic-gate 		free(dstfinfo);
15240Sstevel@tonic-gate 		free_inetconfent(iconf);
15250Sstevel@tonic-gate 	}
15260Sstevel@tonic-gate 	(void) fclose(srcfinfo->fp);
15270Sstevel@tonic-gate 	convert_err = srcfinfo->failcnt;
15280Sstevel@tonic-gate 
15290Sstevel@tonic-gate 	/* Update hash only if not in enable mode, and only if importing */
15300Sstevel@tonic-gate 	if (!enable && import && (update_hash(srcfinfo->filename) != 0))
15310Sstevel@tonic-gate 		import_err++;
15320Sstevel@tonic-gate 
15330Sstevel@tonic-gate 	free(srcfinfo);
15340Sstevel@tonic-gate 
15350Sstevel@tonic-gate 	if (enable_err != 0)
15360Sstevel@tonic-gate 		return (EXIT_ERROR_ENBL);
15370Sstevel@tonic-gate 	if (import_err != 0)
15380Sstevel@tonic-gate 		return (EXIT_ERROR_IMP);
15390Sstevel@tonic-gate 	if (convert_err != 0)
15400Sstevel@tonic-gate 		return (EXIT_ERROR_CONV);
15410Sstevel@tonic-gate 	return (EXIT_SUCCESS);
15420Sstevel@tonic-gate }
1543