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(5) service manifests,
28 *            import them into smf(5) 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
77struct 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
97struct fileinfo {
98	FILE *fp;
99	char *filename;
100	int lineno;
101	int failcnt;
102};
103
104static char *progname;
105
106static boolean_t import = B_TRUE;
107
108/* start of manifest XML template strings */
109static 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
114static const char xml_comment[] =
115"<!--\n"
116"    Service manifest for the %s service.\n"
117"\n"
118"    Generated by inetconv(1M) from inetd.conf(4).\n"
119"-->\n\n";
120
121static const char xml_service_bundle[] =
122"<service_bundle type='manifest' name='inetconv:%s'>\n\n";
123
124static const char xml_service_name[] =
125"<service\n"
126"	name='network/%s'\n"
127"	type='service'\n"
128"	version='1'>\n\n";
129
130static 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
139static const char xml_instance[] =
140"	<create_default_instance enabled='true'/>\n\n";
141
142static const char xml_restarter[] =
143"	<restarter>\n"
144"		<service_fmri value='%s' />\n"
145"	</restarter>\n\n";
146
147static 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
165static const char xml_arg0[] =
166"		<propval name='%s' type='astring'\n"
167"		    value='%s' />\n";
168
169static const char xml_exec_method_end[] =
170"	</exec_method>\n\n";
171
172static 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
182static 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
193static 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
206static 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
214static 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
219static const char xml_property_group_end[] =
220"	</property_group>\n\n";
221
222static const char xml_stability[] =
223"	<stability value='External' />\n\n";
224
225static 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
234static const char xml_footer[] =
235"</service>\n"
236"\n"
237"</service_bundle>\n";
238/* end of manifest XML template strings */
239
240static void *
241safe_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
253static char *
254safe_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
266static char *
267propertyname(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
306static char *
307servicename(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
347static boolean_t
348is_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
357static char *
358invalid_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
381static boolean_t
382valid_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
432static boolean_t
433valid_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 {
471vererr:
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
553static void
554free_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
574static struct inetconfent *
575line_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);
641fail:
642	free_inetconfent(iconf);
643	return (NULL);
644}
645
646static void
647skipline(FILE *fp)
648{
649	int c;
650
651	/* skip remainder of a line */
652	while (((c = getc(fp)) != EOF) && (c != '\n'))
653		;
654}
655
656static struct inetconfent *
657fgetinetconfent(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
706static char *
707boolstr(boolean_t val)
708{
709	if (val)
710		return ("true");
711	return ("false");
712}
713
714static int
715print_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
801print_err:
802	(void) fprintf(stderr, gettext("%s: Error writing manifest %s: %s\n"),
803	    progname, filename, strerror(errno));
804	return (-1);
805}
806
807static struct fileinfo *
808open_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 */
852static int
853open_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
933static int
934import_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
978static int
979inetd_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
1042static int
1043update_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
1086static void
1087property_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
1102static int
1103modify_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);
1195out:
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*/
1215static int
1216list_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
1378out:
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
1387static void
1388usage(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
1405int
1406main(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