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