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