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