summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv.c')
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv.c1527
1 files changed, 1527 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv.c b/usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv.c
new file mode 100644
index 0000000000..fcc10d0a44
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv.c
@@ -0,0 +1,1527 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * inetconv - convert inetd.conf entries into smf(5) service manifests,
+ * import them into smf(5) repository
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <libintl.h>
+#include <libscf.h>
+#include <inetsvc.h>
+#include <rpc/nettype.h>
+
+/* exit codes */
+#define EXIT_SUCCESS 0 /* succeeded */
+#define EXIT_USAGE 1 /* bad options */
+#define EXIT_ERROR_CONV 2 /* error(s) coverting inetd.conf entries */
+#define EXIT_ERROR_IMP 3 /* error(s) importing manifests */
+#define EXIT_ERROR_SYS 4 /* system error */
+#define EXIT_ERROR_ENBL 5 /* error(s) enabling services */
+
+#ifndef TEXT_DOMAIN
+#define TEXT_DOMAIN "SUNW_OST_OSCMD"
+#endif
+
+#define MAIN_CONFIG "/etc/inet/inetd.conf"
+#define ALT_CONFIG "/etc/inetd.conf"
+
+#define MANIFEST_DIR "/var/svc/manifest/network"
+#define MANIFEST_RPC_DIR MANIFEST_DIR ## "/rpc"
+#define SVCCFG_PATH "/usr/sbin/svccfg"
+
+/* maximum allowed length of an inetd.conf format line */
+#define MAX_SRC_LINELEN 32768
+
+/* Version of inetconv, used as a marker in services we generate */
+#define INETCONV_VERSION 1
+
+struct inetconfent {
+ /* fields as read from inetd.conf format line */
+ char *service;
+ char *endpoint;
+ char *protocol;
+ char *wait_status;
+ char *username;
+ char *server_program;
+ char *server_args;
+ /* information derived from above fields */
+ boolean_t wait;
+ boolean_t isrpc;
+ int rpc_low_version;
+ int rpc_high_version;
+ char *rpc_prog;
+ char *groupname;
+ char *exec;
+ char *arg0;
+};
+
+struct fileinfo {
+ FILE *fp;
+ char *filename;
+ int lineno;
+ int failcnt;
+};
+
+static char *progname;
+
+static boolean_t import = B_TRUE;
+
+/* start of manifest XML template strings */
+static const char xml_header[] =
+"<?xml version='1.0'?>\n"
+"<!DOCTYPE service_bundle SYSTEM "
+"'/usr/share/lib/xml/dtd/service_bundle.dtd.1'>\n";
+
+static const char xml_comment[] =
+"<!--\n"
+" Service manifest for the %s service.\n"
+"\n"
+" Generated by inetconv(1M) from inetd.conf(4).\n"
+"-->\n\n";
+
+static const char xml_service_bundle[] =
+"<service_bundle type='manifest' name='inetconv:%s'>\n\n";
+
+static const char xml_service_name[] =
+"<service\n"
+" name='network/%s'\n"
+" type='service'\n"
+" version='1'>\n\n";
+
+static const char xml_instance[] =
+" <create_default_instance enabled='true'/>\n\n";
+
+static const char xml_restarter[] =
+" <restarter>\n"
+" <service_fmri value='%s' />\n"
+" </restarter>\n\n";
+
+static const char xml_exec_method_start[] =
+" <!--\n"
+" Set a timeout of 0 to signify to inetd that we don't want to\n"
+" timeout this service, since the forked process is the one that\n"
+" does the service's work. This is the case for most/all legacy\n"
+" inetd services; for services written to take advantage of SMF\n"
+" capabilities, the start method should fork off a process to\n"
+" handle the request and return a success code.\n"
+" -->\n"
+" <exec_method\n"
+" type='method'\n"
+" name='%s'\n"
+" %s='%s'\n"
+" timeout_seconds='0'>\n"
+" <method_context>\n"
+" <method_credential %s='%s' group='%s' />\n"
+" </method_context>\n";
+
+static const char xml_arg0[] =
+" <propval name='%s' type='astring'\n"
+" value='%s' />\n";
+
+static const char xml_exec_method_end[] =
+" </exec_method>\n\n";
+
+static const char xml_exec_method_disable[] =
+" <!--\n"
+" Use inetd's built-in kill support to disable services.\n"
+" -->\n"
+" <exec_method\n"
+" type='method'\n"
+" name='%s'\n"
+" %s=':kill'\n"
+" timeout_seconds='0'>\n";
+
+static const char xml_exec_method_offline[] =
+" <!--\n"
+" Use inetd's built-in process kill support to offline wait type\n"
+" services.\n"
+" -->\n"
+" <exec_method\n"
+" type='method'\n"
+" name='%s'\n"
+" %s=':kill_process'\n"
+" timeout_seconds='0'>\n";
+
+static const char xml_inetconv_group_start[] =
+" <!--\n"
+" This property group is used to record information about\n"
+" how this manifest was created. It is an implementation\n"
+" detail which should not be modified or deleted.\n"
+" -->\n"
+" <property_group name='%s' type='framework'>\n"
+" <propval name='%s' type='boolean' value='%s' />\n"
+" <propval name='%s' type='integer' value='%d' />\n"
+" <propval name='%s' type='astring' value=\n"
+"'%s %s %s %s %s %s%s%s'\n"
+" />\n";
+
+static const char xml_property_group_start[] =
+" <property_group name='%s' type='framework'>\n"
+" <propval name='%s' type='astring' value='%s' />\n"
+" <propval name='%s' type='astring' value='%s' />\n"
+" <propval name='%s' type='astring' value='%s' />\n"
+" <propval name='%s' type='boolean' value='%s' />\n"
+" <propval name='%s' type='boolean' value='%s' />\n";
+
+static const char xml_property_group_rpc[] =
+" <propval name='%s' type='integer' value='%d' />\n"
+" <propval name='%s' type='integer' value='%d' />"
+"\n";
+
+static const char xml_property_group_end[] =
+" </property_group>\n\n";
+
+static const char xml_stability[] =
+" <stability value='External' />\n\n";
+
+static const char xml_template[] =
+" <template>\n"
+" <common_name>\n"
+" <loctext xml:lang='C'>\n"
+"%s\n"
+" </loctext>\n"
+" </common_name>\n"
+" </template>\n";
+
+static const char xml_footer[] =
+"</service>\n"
+"\n"
+"</service_bundle>\n";
+/* end of manifest XML template strings */
+
+static void *
+safe_malloc(size_t size)
+{
+ void *cp;
+
+ if ((cp = malloc(size)) == NULL) {
+ (void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
+ progname, strerror(errno));
+ exit(EXIT_ERROR_SYS);
+ }
+ return (cp);
+}
+
+static char *
+safe_strdup(char *s)
+{
+ char *cp;
+
+ if ((cp = strdup(s)) == NULL) {
+ (void) fprintf(stderr, gettext("%s: strdup failed: %s\n"),
+ progname, strerror(errno));
+ exit(EXIT_ERROR_SYS);
+ }
+ return (cp);
+}
+
+static char *
+propertyname(char *name, char *prefix)
+{
+ static char *buf;
+ size_t len;
+ int c;
+ char *cp;
+
+ /* free any memory allocated by a previous call */
+ free(buf);
+
+ len = strlen(name) + strlen(prefix) + 1;
+ buf = safe_malloc(len);
+ buf[0] = '\0';
+
+ /*
+ * Property names must match the regular expression:
+ * ([A-Za-z][_A-Za-z0-9.-]*,)?[A-Za-z][_A-Za-z0-9-]*
+ */
+
+ /*
+ * Make sure the first character is alphabetic, if not insert prefix.
+ * Can't use isalpha() here as its locale dependent but the property
+ * name regular expression isn't.
+ */
+ c = name[0];
+ if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
+ (void) strlcat(buf, prefix, len);
+ }
+ (void) strlcat(buf, name, len);
+
+ /* convert any dissallowed characters into '_' */
+ for (cp = buf; *cp != '\0'; cp++) {
+ if ((*cp < 'A' || *cp > 'Z') && (*cp < 'a' || *cp > 'z') &&
+ (*cp < '0' || *cp > '9') && (*cp != '.') && (*cp != '-'))
+ *cp = '_';
+ }
+ return (buf);
+}
+
+static char *
+servicename(struct inetconfent *iconf)
+{
+ static char *buf;
+ size_t len;
+ char *proto;
+
+ /* free any memory allocated by a previous call */
+ free(buf);
+
+ len = strlen(iconf->service) + strlen(iconf->protocol) +
+ sizeof ("rpc-/visible");
+ buf = safe_malloc(len);
+
+ /*
+ * Combine the service and protocol fields to produce a unique
+ * manifest service name. The syntax of a service name is:
+ * prop(/prop)*
+ */
+ (void) strlcpy(buf, propertyname(iconf->service,
+ iconf->isrpc ? "rpc-": "s-"), len);
+ (void) strlcat(buf, "/", len);
+
+ proto = iconf->protocol;
+ if (iconf->isrpc && (strcmp(iconf->protocol, "rpc/*") == 0))
+ proto = "rpc/visible";
+ (void) strlcat(buf, propertyname(proto, "p-"), len);
+ return (buf);
+}
+
+static boolean_t
+is_v6only(char *protocol)
+{
+ /* returns true if protocol is an IPv6 only protocol */
+ if ((strcmp(protocol, SOCKET_PROTO_TCP6_ONLY) == 0) ||
+ (strcmp(protocol, SOCKET_PROTO_UDP6_ONLY) == 0))
+ return (B_TRUE);
+ return (B_FALSE);
+}
+
+static char *
+invalid_props(inetd_prop_t *p)
+{
+ static char
+ buf[sizeof (" service-name endpoint-type protocol wait-status")];
+
+ buf[0] = '\0';
+ if ((p[PT_SVC_NAME_INDEX].ip_error == IVE_INVALID) ||
+ (p[PT_SVC_NAME_INDEX].ip_error == IVE_UNSET) ||
+ (p[PT_RPC_LW_VER_INDEX].ip_error == IVE_INVALID) ||
+ (p[PT_RPC_HI_VER_INDEX].ip_error == IVE_INVALID))
+ (void) strlcat(buf, " service-name", sizeof (buf));
+ if ((p[PT_SOCK_TYPE_INDEX].ip_error == IVE_INVALID) ||
+ (p[PT_SOCK_TYPE_INDEX].ip_error == IVE_UNSET))
+ (void) strlcat(buf, " endpoint-type", sizeof (buf));
+ if ((p[PT_PROTO_INDEX].ip_error == IVE_INVALID) ||
+ (p[PT_PROTO_INDEX].ip_error == IVE_UNSET) ||
+ (p[PT_ISRPC_INDEX].ip_error == IVE_INVALID))
+ (void) strlcat(buf, " protocol", sizeof (buf));
+ if (p[PT_ISWAIT_INDEX].ip_error == IVE_INVALID)
+ (void) strlcat(buf, " wait-status", sizeof (buf));
+ return (buf);
+}
+
+/*
+ * wrapper around put_prop_value() that errors and exits on malloc failures,
+ * returns -1 on other failures, else returns 0.
+ */
+static int
+my_put_prop_value(inetd_prop_t *props, char *pname, void *value)
+{
+ if (put_prop_value(props, pname, value) == 0)
+ return (0);
+
+ if (errno == ENOMEM) {
+ (void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
+ progname, strerror(errno));
+ exit(EXIT_ERROR_SYS);
+ }
+ return (-1);
+}
+
+static boolean_t
+valid_basic_properties(struct inetconfent *iconf, struct fileinfo *finfo)
+{
+ size_t prop_size;
+ inetd_prop_t *prop, *inetd_properties;
+ boolean_t valid = B_TRUE;
+ char *proto = iconf->protocol;
+ char *svc_name = iconf->service;
+
+ inetd_properties = get_prop_table(&prop_size);
+ prop = safe_malloc(prop_size * sizeof (inetd_prop_t));
+ (void) memcpy(prop, inetd_properties,
+ prop_size * sizeof (inetd_prop_t));
+
+ (void) put_prop_value(prop, PR_ISRPC_NAME, &iconf->isrpc);
+ (void) put_prop_value(prop, PR_ISWAIT_NAME, &iconf->wait);
+ if (iconf->isrpc) {
+ (void) put_prop_value(prop, PR_RPC_LW_VER_NAME,
+ &iconf->rpc_low_version);
+ (void) put_prop_value(prop, PR_RPC_HI_VER_NAME,
+ &iconf->rpc_high_version);
+ svc_name = iconf->rpc_prog;
+ proto += 4; /* skip 'rpc/' */
+ }
+
+ if ((my_put_prop_value(prop, PR_SOCK_TYPE_NAME, iconf->endpoint)
+ != 0) ||
+ (my_put_prop_value(prop, PR_SVC_NAME_NAME, svc_name) != 0) ||
+ (my_put_prop_value(prop, PR_PROTO_NAME, proto) != 0))
+ valid = B_FALSE;
+
+ if (!valid_props(prop, NULL, NULL, NULL, NULL) || !valid) {
+ valid = B_FALSE;
+ (void) fprintf(stderr, gettext("%s: Error %s line %d "
+ "invalid or inconsistent fields:%s\n"), progname,
+ finfo->filename, finfo->lineno,
+ invalid_props(prop));
+ }
+
+ free_instance_props(prop);
+ return (valid);
+}
+
+static boolean_t
+valid_inetconfent(struct inetconfent *iconf, struct fileinfo *finfo)
+{
+ boolean_t valid = B_TRUE;
+ size_t len;
+ char *cp, *endp;
+ struct passwd *pwd;
+ struct group *grp;
+ struct stat statb;
+ char *proto = iconf->protocol;
+
+ iconf->isrpc = B_FALSE;
+ if (strncmp(iconf->protocol, "rpc/", 4) == 0) {
+ iconf->isrpc = B_TRUE;
+ iconf->rpc_prog = safe_strdup(iconf->service);
+
+ /* set RPC version numbers */
+ iconf->rpc_low_version = 1;
+ iconf->rpc_high_version = 1;
+ if ((cp = strrchr(iconf->rpc_prog, '/')) != NULL) {
+ *cp = '\0';
+ if (*++cp != '\0') {
+ errno = 0;
+ iconf->rpc_low_version = strtol(cp, &endp, 10);
+ if (errno != 0)
+ goto vererr;
+ cp = endp;
+ if (*cp == '-') {
+ if (*++cp == '\0')
+ goto vererr;
+ errno = 0;
+ iconf->rpc_high_version = strtol(cp,
+ &endp, 10);
+ if ((errno != 0) || (*endp != '\0'))
+ goto vererr;
+ } else if (*cp == '\0') {
+ iconf->rpc_high_version =
+ iconf->rpc_low_version;
+ } else {
+vererr:
+ (void) fprintf(stderr, gettext(
+ "%s: Error %s line %d invalid RPC "
+ "version in service: %s\n"),
+ progname, finfo->filename,
+ finfo->lineno, iconf->service);
+ valid = B_FALSE;
+ }
+ }
+ }
+ proto += 4; /* skip 'rpc/' */
+ }
+ /* tcp6only and udp6only are not valid in inetd.conf */
+ if (is_v6only(proto)) {
+ (void) fprintf(stderr, gettext("%s: Error %s line %d "
+ "invalid protocol: %s\n"), progname,
+ finfo->filename, finfo->lineno, proto);
+ valid = B_FALSE;
+ }
+
+ if (strcmp(iconf->wait_status, "wait") == 0) {
+ iconf->wait = B_TRUE;
+ } else if (strcmp(iconf->wait_status, "nowait") == 0) {
+ iconf->wait = B_FALSE;
+ } else {
+ (void) fprintf(stderr,
+ gettext("%s: Error %s line %d invalid wait-status: %s\n"),
+ progname, finfo->filename, finfo->lineno,
+ iconf->wait_status);
+ valid = B_FALSE;
+ }
+
+ /* look up the username to set the groupname */
+ if ((pwd = getpwnam(iconf->username)) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: Error %s line %d unknown user: %s\n"),
+ progname, finfo->filename, finfo->lineno,
+ iconf->username);
+ valid = B_FALSE;
+ } else {
+ if ((grp = getgrgid(pwd->pw_gid)) != NULL) {
+ iconf->groupname = safe_strdup(grp->gr_name);
+ } else {
+ /* use the group ID if no groupname */
+ char s[1];
+
+ len = snprintf(s, 1, "%d", pwd->pw_gid) + 1;
+ iconf->groupname = safe_malloc(len);
+ (void) snprintf(iconf->groupname, len, "%d",
+ pwd->pw_gid);
+ }
+ }
+
+ /* check for internal services */
+ if (strcmp(iconf->server_program, "internal") == 0) {
+ valid = B_FALSE;
+ if ((strcmp(iconf->service, "echo") == 0) ||
+ (strcmp(iconf->service, "discard") == 0) ||
+ (strcmp(iconf->service, "time") == 0) ||
+ (strcmp(iconf->service, "daytime") == 0) ||
+ (strcmp(iconf->service, "chargen") == 0)) {
+ (void) fprintf(stderr, gettext(
+ "%s: Error %s line %d the SUNWcnsr and SUNWcnsu"
+ " packages contain the internal services\n"),
+ progname, finfo->filename, finfo->lineno);
+ } else {
+ (void) fprintf(stderr, gettext("%s: Error %s line %d "
+ "unknown internal service: %s\n"), progname,
+ finfo->filename, finfo->lineno, iconf->service);
+ }
+ } else if ((stat(iconf->server_program, &statb) == -1) &&
+ (errno == ENOENT)) {
+ (void) fprintf(stderr, gettext(
+ "%s: Error %s line %d server-program not found: %s\n"),
+ progname, finfo->filename, finfo->lineno,
+ iconf->server_program);
+ valid = B_FALSE;
+ }
+
+ return (valid && valid_basic_properties(iconf, finfo));
+}
+
+static void
+free_inetconfent(struct inetconfent *iconf)
+{
+ if (iconf == NULL)
+ return;
+
+ free(iconf->service);
+ free(iconf->endpoint);
+ free(iconf->protocol);
+ free(iconf->wait_status);
+ free(iconf->username);
+ free(iconf->server_program);
+ free(iconf->server_args);
+ free(iconf->rpc_prog);
+ free(iconf->groupname);
+ free(iconf->exec);
+ free(iconf->arg0);
+
+ free(iconf);
+}
+
+static struct inetconfent *
+line_to_inetconfent(char *line)
+{
+ char *cp;
+ struct inetconfent *iconf;
+
+ iconf = safe_malloc(sizeof (struct inetconfent));
+ (void) memset(iconf, 0, sizeof (struct inetconfent));
+
+ if ((cp = strtok(line, " \t\n")) == NULL)
+ goto fail;
+ iconf->service = safe_strdup(cp);
+
+ if ((cp = strtok(NULL, " \t\n")) == NULL)
+ goto fail;
+ iconf->endpoint = safe_strdup(cp);
+
+ if ((cp = strtok(NULL, " \t\n")) == NULL)
+ goto fail;
+ iconf->protocol = safe_strdup(cp);
+
+ if ((cp = strtok(NULL, " \t\n")) == NULL)
+ goto fail;
+ iconf->wait_status = safe_strdup(cp);
+
+ if ((cp = strtok(NULL, " \t\n")) == NULL)
+ goto fail;
+ iconf->username = safe_strdup(cp);
+
+ if ((cp = strtok(NULL, " \t\n")) == NULL)
+ goto fail;
+ iconf->server_program = safe_strdup(cp);
+
+ /* last field is optional */
+ if ((cp = strtok(NULL, "\n")) != NULL)
+ iconf->server_args = safe_strdup(cp);
+
+ /* Combine args and server name to construct exec and args fields */
+ if (iconf->server_args == NULL) {
+ iconf->exec = safe_strdup(iconf->server_program);
+ } else {
+ int len;
+ char *args, *endp;
+
+ len = strlen(iconf->server_program) +
+ strlen(iconf->server_args) + 1;
+ iconf->exec = safe_malloc(len);
+ (void) strlcpy(iconf->exec, iconf->server_program, len);
+
+ args = safe_strdup(iconf->server_args);
+ if ((cp = strtok(args, " \t")) != NULL) {
+ if ((endp = strrchr(iconf->exec, '/')) == NULL)
+ endp = iconf->exec;
+ else
+ endp++;
+ /* only set arg0 property value if needed */
+ if (strcmp(endp, cp) != 0)
+ iconf->arg0 = safe_strdup(cp);
+ while ((cp = strtok(NULL, " \t")) != NULL) {
+ (void) strlcat(iconf->exec, " ", len);
+ (void) strlcat(iconf->exec, cp, len);
+ }
+ }
+ free(args);
+ }
+
+ return (iconf);
+fail:
+ free_inetconfent(iconf);
+ return (NULL);
+}
+
+static void
+skipline(FILE *fp)
+{
+ int c;
+
+ /* skip remainder of a line */
+ while (((c = getc(fp)) != EOF) && (c != '\n'))
+ ;
+}
+
+static struct inetconfent *
+fgetinetconfent(struct fileinfo *finfo, boolean_t validate)
+{
+ char *cp;
+ struct inetconfent *iconf;
+ char line[MAX_SRC_LINELEN];
+
+ while (fgets(line, sizeof (line), finfo->fp) != NULL) {
+ finfo->lineno++;
+
+ /* skip empty or commented out lines */
+ if (*line == '\n')
+ continue;
+ if (*line == '#') {
+ if (line[strlen(line) - 1] != '\n')
+ skipline(finfo->fp);
+ continue;
+ }
+ /* check for lines which are too long */
+ if (line[strlen(line) - 1] != '\n') {
+ (void) fprintf(stderr,
+ gettext("%s: Error %s line %d too long, skipped\n"),
+ progname, finfo->filename, finfo->lineno);
+ skipline(finfo->fp);
+ finfo->failcnt++;
+ continue;
+ }
+ /* remove in line comments and newline character */
+ if ((cp = strchr(line, '#')) == NULL)
+ cp = strchr(line, '\n');
+ if (cp)
+ *cp = '\0';
+
+ if ((iconf = line_to_inetconfent(line)) == NULL) {
+ (void) fprintf(stderr, gettext(
+ "%s: Error %s line %d too few fields, skipped\n"),
+ progname, finfo->filename, finfo->lineno);
+ finfo->failcnt++;
+ continue;
+ }
+
+ if (!validate || valid_inetconfent(iconf, finfo))
+ return (iconf);
+
+ finfo->failcnt++;
+ free_inetconfent(iconf);
+ }
+ return (NULL);
+}
+
+static char *
+boolstr(boolean_t val)
+{
+ if (val)
+ return ("true");
+ return ("false");
+}
+
+static int
+print_manifest(FILE *f, char *filename, struct inetconfent *iconf)
+{
+ if (fprintf(f, xml_header) < 0)
+ goto print_err;
+
+ if (fprintf(f, xml_comment,
+ iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0)
+ goto print_err;
+
+ if (fprintf(f, xml_service_bundle, iconf->service) < 0)
+ goto print_err;
+ if (fprintf(f, xml_service_name, servicename(iconf)) < 0)
+ goto print_err;
+ if (fprintf(f, xml_instance) < 0)
+ goto print_err;
+ if (fprintf(f, xml_restarter, INETD_INSTANCE_FMRI) < 0)
+ goto print_err;
+
+ if (fprintf(f, xml_exec_method_start, START_METHOD_NAME, PR_EXEC_NAME,
+ iconf->exec, PR_USER_NAME, iconf->username, iconf->groupname) < 0)
+ goto print_err;
+ if (iconf->arg0 != NULL) {
+ if (fprintf(f, xml_arg0, PR_ARG0_NAME, iconf->arg0) < 0)
+ goto print_err;
+ }
+ if (fprintf(f, xml_exec_method_end) < 0)
+ goto print_err;
+
+ if (fprintf(f, xml_exec_method_disable, DISABLE_METHOD_NAME,
+ PR_EXEC_NAME) < 0)
+ goto print_err;
+ if (fprintf(f, xml_exec_method_end) < 0)
+ goto print_err;
+
+ if (iconf->wait) {
+ if (fprintf(f, xml_exec_method_offline, OFFLINE_METHOD_NAME,
+ PR_EXEC_NAME) < 0)
+ goto print_err;
+ if (fprintf(f, xml_exec_method_end) < 0)
+ goto print_err;
+ }
+
+ if (fprintf(f, xml_inetconv_group_start, PG_NAME_INETCONV,
+ PR_AUTO_CONVERTED_NAME, boolstr(B_TRUE),
+ PR_VERSION_NAME, INETCONV_VERSION,
+ PR_SOURCE_LINE_NAME, iconf->service,
+ iconf->endpoint, iconf->protocol, iconf->wait_status,
+ iconf->username, iconf->server_program,
+ iconf->server_args == NULL ? "" : " ",
+ iconf->server_args == NULL ? "" : iconf->server_args) < 0)
+ goto print_err;
+ if (fprintf(f, xml_property_group_end) < 0)
+ goto print_err;
+
+ if (fprintf(f, xml_property_group_start, PG_NAME_SERVICE_CONFIG,
+ PR_SVC_NAME_NAME, iconf->isrpc ? iconf->rpc_prog : iconf->service,
+ PR_SOCK_TYPE_NAME, iconf->endpoint,
+ PR_PROTO_NAME, iconf->isrpc ? iconf->protocol + 4 :
+ iconf->protocol,
+ PR_ISWAIT_NAME, boolstr(iconf->wait),
+ PR_ISRPC_NAME, boolstr(iconf->isrpc)) < 0)
+ goto print_err;
+ if (iconf->isrpc) {
+ if (fprintf(f, xml_property_group_rpc,
+ PR_RPC_LW_VER_NAME, iconf->rpc_low_version,
+ PR_RPC_HI_VER_NAME, iconf->rpc_high_version) < 0)
+ goto print_err;
+ }
+ if (fprintf(f, xml_property_group_end) < 0)
+ goto print_err;
+
+ if (fprintf(f, xml_stability) < 0)
+ goto print_err;
+ if (fprintf(f, xml_template,
+ iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0)
+ goto print_err;
+ if (fprintf(f, xml_footer) < 0)
+ goto print_err;
+
+ (void) printf("%s -> %s\n", iconf->service, filename);
+ return (0);
+
+print_err:
+ (void) fprintf(stderr, gettext("%s: Error writing manifest %s: %s\n"),
+ progname, filename, strerror(errno));
+ return (-1);
+}
+
+static struct fileinfo *
+open_srcfile(char *filename)
+{
+ struct fileinfo *finfo = NULL;
+ FILE *fp;
+
+ if (filename != NULL) {
+ if ((fp = fopen(filename, "r")) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: Error opening %s: %s\n"),
+ progname, filename, strerror(errno));
+ }
+ } else {
+ /*
+ * If no source file specified, do the same as inetd and first
+ * try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
+ */
+ filename = MAIN_CONFIG;
+ if ((fp = fopen(filename, "r")) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: Error opening %s: %s\n"),
+ progname, filename, strerror(errno));
+ filename = ALT_CONFIG;
+ if ((fp = fopen(filename, "r")) == NULL) {
+ (void) fprintf(stderr, gettext(
+ "%s: Error opening %s: %s\n"), progname,
+ filename, strerror(errno));
+ }
+ }
+ }
+ if (fp != NULL) {
+ finfo = safe_malloc(sizeof (struct fileinfo));
+ finfo->fp = fp;
+ finfo->filename = filename;
+ finfo->lineno = 0;
+ finfo->failcnt = 0;
+ (void) fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+ }
+ return (finfo);
+}
+
+/*
+ * Opens manifest output file. Returns 0 on success, -1 if the file
+ * exists, -2 on other errors.
+ */
+static int
+open_dstfile(
+ char *destdir,
+ boolean_t overwrite,
+ struct inetconfent *iconf,
+ struct fileinfo **finfo)
+{
+ int fd;
+ size_t len;
+ char *dstfile, *cp, *proto;
+ FILE *fp;
+
+ /* if no destdir specified, use appropriate default */
+ if (destdir == NULL) {
+ if (iconf->isrpc)
+ destdir = MANIFEST_RPC_DIR;
+ else
+ destdir = MANIFEST_DIR;
+ }
+
+ len = strlen(destdir) + strlen(iconf->service) +
+ strlen(iconf->protocol) + sizeof ("/-visible.xml");
+ dstfile = safe_malloc(len);
+
+ (void) strlcpy(dstfile, destdir, len);
+ if (dstfile[strlen(dstfile) - 1] != '/')
+ (void) strlcat(dstfile, "/", len);
+ cp = dstfile + strlen(dstfile);
+
+ (void) strlcat(dstfile, iconf->service, len);
+ (void) strlcat(dstfile, "-", len);
+
+ proto = iconf->protocol;
+ if (iconf->isrpc && (strcmp(iconf->protocol, "rpc/*") == 0))
+ proto = "rpc/visible";
+
+ (void) strlcat(dstfile, proto, len);
+ (void) strlcat(dstfile, ".xml", len);
+
+ /* convert any '/' chars in service or protocol to '_' chars */
+ while ((cp = strchr(cp, '/')) != NULL)
+ *cp = '_';
+
+ fd = open(dstfile, O_WRONLY|O_CREAT|(overwrite ? O_TRUNC : O_EXCL),
+ 0644);
+ if (fd == -1) {
+ if (!overwrite && (errno == EEXIST)) {
+ (void) fprintf(stderr,
+ gettext("%s: Notice: Service manifest for "
+ "%s already generated as %s, skipped\n"),
+ progname, iconf->service, dstfile);
+ free(dstfile);
+ return (-1);
+ } else {
+ (void) fprintf(stderr,
+ gettext("%s: Error opening %s: %s\n"),
+ progname, dstfile, strerror(errno));
+ free(dstfile);
+ return (-2);
+ }
+ }
+ /* Clear errno to catch the "no stdio streams" case */
+ errno = 0;
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ char *s = strerror(errno);
+ if (errno == 0)
+ s = gettext("No stdio streams available");
+ (void) fprintf(stderr, gettext("%s: Error fdopen failed: %s\n"),
+ progname, s);
+ (void) close(fd);
+ free(dstfile);
+ return (-2);
+ }
+ *finfo = safe_malloc(sizeof (struct fileinfo));
+ (*finfo)->fp = fp;
+ (*finfo)->filename = dstfile;
+ (*finfo)->lineno = 0;
+ (*finfo)->failcnt = 0;
+ return (0);
+}
+
+static int
+import_manifest(char *filename)
+{
+ int status;
+ pid_t pid, wpid;
+ char *cp;
+
+ if ((cp = strrchr(filename, '/')) == NULL)
+ cp = filename;
+ else
+ cp++;
+ (void) printf(gettext("Importing %s ..."), cp);
+
+ if ((pid = fork()) == -1) {
+ (void) fprintf(stderr,
+ gettext("\n%s: fork failed, %s not imported: %s\n"),
+ progname, filename, strerror(errno));
+ exit(EXIT_ERROR_SYS);
+ }
+ if (pid == 0) {
+ /* child */
+ (void) fclose(stdin);
+ (void) setenv("SVCCFG_CHECKHASH", "1", 1);
+ (void) execl(SVCCFG_PATH, "svccfg", "import", filename, NULL);
+ (void) fprintf(stderr, gettext("\n%s: exec of %s failed: %s"),
+ progname, SVCCFG_PATH, strerror(errno));
+ _exit(EXIT_ERROR_SYS);
+ }
+ /* parent */
+ if ((wpid = waitpid(pid, &status, 0)) != pid) {
+ (void) fprintf(stderr, gettext(
+ "\n%s: unexpected wait (%d) from import of %s: %s\n"),
+ progname, wpid, filename, strerror(errno));
+ return (-1);
+ }
+ if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) {
+ (void) fprintf(stderr,
+ gettext("\n%s: import failure (%d) for %s\n"),
+ progname, WEXITSTATUS(status), filename);
+ return (-1);
+ }
+ (void) printf(gettext("Done\n"));
+ return (0);
+}
+
+static int
+inetd_config_path(char **path)
+{
+ int fd;
+ char *arg1, *configfile, *configstr;
+ scf_simple_prop_t *sp;
+ char cpath[PATH_MAX];
+
+ if ((sp = scf_simple_prop_get(NULL, INETD_INSTANCE_FMRI, "start",
+ SCF_PROPERTY_EXEC)) == NULL)
+ return (-1);
+ if ((configstr = scf_simple_prop_next_astring(sp)) == NULL) {
+ scf_simple_prop_free(sp);
+ return (-1);
+ }
+ configstr = safe_strdup(configstr);
+ scf_simple_prop_free(sp);
+
+ /*
+ * Look for the optional configuration file, the syntax is:
+ * /usr/lib/inet/inetd [config-file] start|stop|refresh|disable|%m
+ */
+ if (strtok(configstr, " \t") == NULL) {
+ free(configstr);
+ return (-1);
+ }
+ if ((arg1 = strtok(NULL, " \t")) == NULL) {
+ free(configstr);
+ return (-1);
+ }
+ if (strtok(NULL, " \t") == NULL) {
+ /*
+ * No configuration file specified, do the same as inetd and
+ * first try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
+ */
+ configfile = MAIN_CONFIG;
+ if ((fd = open(configfile, O_RDONLY)) >= 0)
+ (void) close(fd);
+ else
+ configfile = ALT_CONFIG;
+
+ } else {
+ /* make sure there are no more arguments */
+ if (strtok(NULL, " \t") != NULL) {
+ free(configstr);
+ return (-1);
+ }
+ configfile = arg1;
+ }
+
+ /* configuration file must be an absolute pathname */
+ if (*configfile != '/') {
+ free(configstr);
+ return (-1);
+ }
+
+ if (realpath(configfile, cpath) == NULL)
+ (void) strlcpy(cpath, configfile, sizeof (cpath));
+
+ free(configstr);
+ *path = safe_strdup(cpath);
+ return (0);
+}
+
+static int
+update_hash(char *srcfile)
+{
+ scf_error_t rval;
+ char *inetd_cpath, *hashstr;
+ char cpath[PATH_MAX];
+
+ /* determine the config file inetd is using */
+ if (inetd_config_path(&inetd_cpath) == -1) {
+ (void) fprintf(stderr,
+ gettext("%s: Error reading from repository\n"), progname);
+ return (-1);
+ }
+
+ /* resolve inetconv input filename */
+ if (realpath(srcfile, cpath) == NULL)
+ (void) strlcpy(cpath, srcfile, sizeof (cpath));
+
+ /* if inetconv and inetd are using the same config file, update hash */
+ if (strcmp(cpath, inetd_cpath) != 0) {
+ free(inetd_cpath);
+ return (0);
+ }
+ free(inetd_cpath);
+
+ /* generic error message as use of hash is not exposed to the user */
+ if (calculate_hash(cpath, &hashstr) != 0) {
+ (void) fprintf(stderr,
+ gettext("%s: Error unable to update repository\n"),
+ progname);
+ return (-1);
+ }
+ /* generic error message as use of hash is not exposed to the user */
+ if ((rval = store_inetd_hash(hashstr)) != SCF_ERROR_NONE) {
+ (void) fprintf(stderr,
+ gettext("%s: Error updating repository: %s\n"),
+ progname, scf_strerror(rval));
+ free(hashstr);
+ return (-1);
+ }
+ free(hashstr);
+ return (0);
+}
+
+static void
+property_error(const char *fmri, const char *prop)
+{
+ (void) fprintf(stderr,
+ gettext("Error: Instance %1$s is missing property '%2$s'.\n"),
+ fmri, prop);
+}
+
+/*
+ * modify_sprop takes a handle, an instance, a property group, a property,
+ * and an astring value, and modifies the instance (or service's) specified
+ * property in the repository to the submitted value.
+ *
+ * returns -1 on error, 1 on successful transaction completion.
+ */
+
+static int
+modify_sprop(scf_handle_t *h, const scf_instance_t *inst,
+ const char *pg, const char *prop, const char *value)
+{
+ scf_transaction_t *tx = NULL;
+ scf_transaction_entry_t *ent = NULL;
+ scf_propertygroup_t *gpg = NULL;
+ scf_property_t *eprop = NULL;
+ scf_value_t *v = NULL;
+ scf_service_t *svc = NULL;
+ int ret = 0, create = 0;
+
+ if ((gpg = scf_pg_create(h)) == NULL)
+ return (-1);
+
+ /* Get the property group */
+ if (scf_instance_get_pg(inst, pg, gpg) == -1) {
+ /* Not a property of the instance, try the service instead */
+ if ((svc = scf_service_create(h)) == NULL) {
+ ret = -1;
+ goto out;
+ }
+ if ((scf_instance_get_parent(inst, svc) == -1) ||
+ (scf_service_get_pg(svc, pg, gpg) == -1)) {
+ ret = -1;
+ goto out;
+ }
+ }
+
+ if ((eprop = scf_property_create(h)) == NULL) {
+ ret = -1;
+ goto out;
+ }
+
+ if (scf_pg_get_property(gpg, prop, eprop) == -1) {
+ if (scf_error() != SCF_ERROR_NOT_FOUND) {
+ ret = -1;
+ goto out;
+ }
+
+ create = 1;
+ }
+
+ if ((tx = scf_transaction_create(h)) == NULL ||
+ (ent = scf_entry_create(h)) == NULL) {
+ ret = -1;
+ goto out;
+ }
+
+ do {
+ if (scf_transaction_start(tx, gpg) == -1) {
+ ret = -1;
+ goto out;
+ }
+
+ /* Modify the property */
+ if (create)
+ ret = scf_transaction_property_new(tx, ent, prop,
+ SCF_TYPE_ASTRING);
+ else
+ ret = scf_transaction_property_change_type(tx, ent,
+ prop, SCF_TYPE_ASTRING);
+
+ if (ret == -1)
+ goto out;
+
+ if ((v = scf_value_create(h)) == NULL) {
+ ret = -1;
+ goto out;
+ }
+
+ if (scf_value_set_astring(v, value) == -1) {
+ ret = -1;
+ goto out;
+ }
+
+ if (scf_entry_add_value(ent, v) == -1) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = scf_transaction_commit(tx);
+
+ if (ret == 0) {
+ /* Property group was stale, retry */
+ if (scf_pg_update(gpg) == -1) {
+ ret = -1;
+ goto out;
+ }
+ scf_transaction_reset(tx);
+ }
+
+ } while (ret == 0);
+out:
+ scf_value_destroy(v);
+ scf_entry_destroy(ent);
+ scf_transaction_destroy(tx);
+ scf_property_destroy(eprop);
+ scf_service_destroy(svc);
+ scf_pg_destroy(gpg);
+
+ return (ret);
+}
+
+/*
+ * list_callback is the callback function to be handed to simple_walk_instances
+ * in main. It is called once on every instance on a machine. If that
+ * instance is controlled by inetd, we test whether it's the same
+ * service that we're looking at from the inetd.conf file, and enable it if
+ * they are the same.
+ */
+
+/*ARGSUSED*/
+static int
+list_callback(scf_handle_t *h, scf_instance_t *inst, void *buf)
+{
+ ssize_t max_name_length;
+ char *svc_name;
+ scf_simple_prop_t *prop = NULL;
+ scf_simple_prop_t *sockprop = NULL;
+ scf_simple_prop_t *rpcprop = NULL;
+ scf_simple_prop_t *progprop = NULL;
+ const char *name, *endpoint, *restart_str, *prog;
+ struct inetconfent *iconf = (struct inetconfent *)buf;
+ uint8_t *isrpc;
+
+ max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
+ if ((svc_name = malloc(max_name_length + 1)) == NULL) {
+ (void) fprintf(stderr, gettext("Error: Out of memory.\n"));
+ return (SCF_FAILED);
+ }
+
+ /*
+ * Get the FMRI of the instance, and check if its delegated restarter
+ * is inetd. A missing or empty restarter property implies that
+ * svc.startd is the restarter.
+ */
+
+ if (scf_instance_to_fmri(inst, svc_name, max_name_length) < 0) {
+ (void) fprintf(stderr,
+ gettext("Error: Unable to obtain FMRI for service %1$s."),
+ svc_name);
+ free(svc_name);
+ return (SCF_FAILED);
+ }
+
+ if ((prop = scf_simple_prop_get(h, svc_name, SCF_PG_GENERAL,
+ SCF_PROPERTY_RESTARTER)) == NULL)
+ goto out;
+
+ if ((restart_str = scf_simple_prop_next_ustring(prop)) == NULL)
+ goto out;
+
+ if (strcmp(restart_str, INETD_INSTANCE_FMRI) != 0)
+ goto out;
+
+ /* Free restarter prop so it can be reused below */
+ scf_simple_prop_free(prop);
+
+ /*
+ * We know that this instance is managed by inetd.
+ * Now get the properties needed to decide if it matches this
+ * line in the old config file.
+ */
+
+ if (((prop = scf_simple_prop_get(h, svc_name, PG_NAME_SERVICE_CONFIG,
+ PR_SVC_NAME_NAME)) == NULL) ||
+ ((name = scf_simple_prop_next_astring(prop)) == NULL)) {
+ property_error(svc_name, PR_SVC_NAME_NAME);
+ goto out;
+ }
+
+ if (((sockprop = scf_simple_prop_get(h, svc_name,
+ PG_NAME_SERVICE_CONFIG, PR_SOCK_TYPE_NAME)) == NULL) ||
+ ((endpoint = scf_simple_prop_next_astring(sockprop)) == NULL)) {
+ property_error(svc_name, PR_SOCK_TYPE_NAME);
+ goto out;
+ }
+
+ if (((rpcprop = scf_simple_prop_get(h, svc_name,
+ PG_NAME_SERVICE_CONFIG, PR_ISRPC_NAME)) == NULL) ||
+ ((isrpc = scf_simple_prop_next_boolean(rpcprop)) == NULL)) {
+ property_error(svc_name, PR_ISRPC_NAME);
+ goto out;
+ }
+
+ if (((progprop = scf_simple_prop_get(h, svc_name, START_METHOD_NAME,
+ PR_EXEC_NAME)) == NULL) ||
+ ((prog = scf_simple_prop_next_astring(progprop)) == NULL)) {
+ property_error(svc_name, PR_EXEC_NAME);
+ }
+
+
+ /* If it's RPC, we truncate off the version portion for comparison */
+ if (*isrpc) {
+ char *cp;
+
+ cp = strchr(iconf->service, '/');
+ if (cp != NULL)
+ *cp = '\0';
+ }
+
+ /*
+ * If name of this service and endpoint are equal to values from
+ * iconf fields, and they're either both RPC or both non-RPC,
+ * then we have a match; update the exec and arg0 properties if
+ * necessary, then enable it.
+ * We don't return an error if either operation fails so that we
+ * continue to try all the other services.
+ */
+ if (strcmp(name, iconf->service) == 0 &&
+ strcmp(endpoint, iconf->endpoint) == 0 &&
+ *isrpc == (strncmp(iconf->protocol, "rpc/", 4) == 0)) {
+ /* Can't update exec on internal services */
+ if ((strcmp(iconf->server_program, "internal") != 0) &&
+ (strcmp(iconf->exec, prog) != 0)) {
+ /* User had edited the command */
+ if (!import) {
+ /* Dry run only */
+ (void) printf(
+ gettext("Would update %s to %s %s"),
+ svc_name, PR_EXEC_NAME, iconf->exec);
+ if (iconf->arg0 != NULL) {
+ (void) printf(
+ gettext(" with %s of %s\n"),
+ PR_ARG0_NAME, iconf->arg0);
+ } else {
+ (void) printf("\n");
+ }
+ } else {
+ /* Update instance's exec property */
+ if (modify_sprop(h, inst, START_METHOD_NAME,
+ PR_EXEC_NAME, iconf->exec) != 1)
+ (void) fprintf(stderr,
+ gettext("Error: Unable to update "
+ "%s property of %s, %s\n"),
+ PR_EXEC_NAME, svc_name,
+ scf_strerror(scf_error()));
+ else
+ (void) printf("%s will %s %s\n",
+ svc_name, PR_EXEC_NAME,
+ iconf->exec);
+
+ /* Update arg0 prop, if needed */
+ if (iconf->arg0 != NULL) {
+ if (modify_sprop(h, inst,
+ START_METHOD_NAME, PR_ARG0_NAME,
+ iconf->arg0) != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: Unable to "
+ "update %s property of "
+ "%s, %s\n"), PR_ARG0_NAME,
+ svc_name,
+ scf_strerror(scf_error()));
+ } else {
+ (void) printf("%s will have an "
+ "%s of %s\n", svc_name,
+ PR_ARG0_NAME, iconf->arg0);
+ }
+ }
+ }
+ }
+
+ if (!import) {
+ /* Dry-run only */
+ (void) printf("Would enable %s\n", svc_name);
+ } else {
+ if (smf_enable_instance(svc_name, 0) != 0)
+ (void) fprintf(stderr,
+ gettext("Error: Failed to enable %s\n"),
+ svc_name);
+ else
+ (void) printf("%s enabled\n", svc_name);
+ }
+ }
+
+out:
+ free(svc_name);
+ scf_simple_prop_free(prop);
+ scf_simple_prop_free(sockprop);
+ scf_simple_prop_free(rpcprop);
+ scf_simple_prop_free(progprop);
+ return (SCF_SUCCESS);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext(
+ "Usage: %s [-fn] [-i srcfile] [-o destdir]\n"
+ " %1$s -e [-n] [-i srcfile]\n"
+ "-? Display this usage message\n"
+ "-e Enable smf services which are enabled in the input\n"
+ " file\n"
+ "-f Force overwrite of existing manifests\n"
+ "-n Do not import converted manifests,\n"
+ " or only display services which would be enabled\n"
+ "-i srcfile Alternate input file\n"
+ "-o destdir Alternate output directory for manifests\n"),
+ progname);
+ exit(EXIT_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, rval, convert_err, import_err = 0, enable_err = 0;
+ boolean_t overwrite = B_FALSE;
+ boolean_t enable = B_FALSE;
+ char *srcfile = NULL;
+ char *destdir = NULL;
+ struct fileinfo *srcfinfo, *dstfinfo;
+ struct inetconfent *iconf;
+
+ setbuf(stdout, NULL);
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ if ((progname = strrchr(argv[0], '/')) == NULL)
+ progname = argv[0];
+ else
+ progname++;
+
+ while ((c = getopt(argc, argv, "?efni:o:")) != -1) {
+ switch (c) {
+ case 'e':
+ /* enable services based on existing file config */
+ enable = B_TRUE;
+ break;
+
+ case 'f':
+ /* overwrite existing manifests */
+ overwrite = B_TRUE;
+ break;
+ case 'n':
+ /* don't import manifests, or dry-run enable */
+ import = B_FALSE;
+ break;
+ case 'i':
+ /* alternate input file */
+ if (srcfile != NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: Error only one -%c allowed\n"),
+ progname, optopt);
+ usage();
+ }
+ srcfile = optarg;
+ break;
+ case 'o':
+ /* alternate output directory */
+ if (destdir != NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: Error only one -%c allowed\n"),
+ progname, optopt);
+ usage();
+ }
+ destdir = optarg;
+ break;
+ case '?': /*FALLTHROUGH*/
+ default:
+ usage();
+ break;
+ }
+ }
+
+ /*
+ * Display usage if extraneous args supplied or enable specified in
+ * combination with overwrite or destdir
+ */
+ if ((optind != argc) || (enable && (overwrite || destdir != NULL)))
+ usage();
+
+ if ((srcfinfo = open_srcfile(srcfile)) == NULL)
+ return (EXIT_ERROR_CONV);
+
+ while ((iconf = fgetinetconfent(srcfinfo, !enable)) != NULL) {
+ /*
+ * If we're enabling, then just walk all the services for each
+ * line and enable those which match.
+ */
+ if (enable) {
+ rval = scf_simple_walk_instances(SCF_STATE_ALL, iconf,
+ list_callback);
+ free_inetconfent(iconf);
+ if (rval == SCF_FAILED) {
+ /* Only print msg if framework error */
+ if (scf_error() != SCF_ERROR_CALLBACK_FAILED)
+ (void) fprintf(stderr, gettext(
+ "Error walking instances: %s.\n"),
+ scf_strerror(scf_error()));
+ enable_err++;
+ break;
+ }
+ continue;
+ }
+
+ /* Remainder of loop used for conversion & import */
+ if ((rval = open_dstfile(destdir, overwrite, iconf, &dstfinfo))
+ < 0) {
+ /*
+ * Only increment error counter if the failure was
+ * other than the file already existing.
+ */
+ if (rval == -2)
+ srcfinfo->failcnt++;
+ free_inetconfent(iconf);
+ continue;
+ }
+ rval = print_manifest(dstfinfo->fp, dstfinfo->filename, iconf);
+ (void) fclose(dstfinfo->fp);
+ if (rval == 0) {
+ if (import &&
+ (import_manifest(dstfinfo->filename) != 0))
+ import_err++;
+ } else {
+ (void) unlink(dstfinfo->filename);
+ srcfinfo->failcnt++;
+ }
+ free(dstfinfo->filename);
+ free(dstfinfo);
+ free_inetconfent(iconf);
+ }
+ (void) fclose(srcfinfo->fp);
+ convert_err = srcfinfo->failcnt;
+
+ /* Update hash only if not in enable mode, and only if importing */
+ if (!enable && import && (update_hash(srcfinfo->filename) != 0))
+ import_err++;
+
+ free(srcfinfo);
+
+ if (enable_err != 0)
+ return (EXIT_ERROR_ENBL);
+ if (import_err != 0)
+ return (EXIT_ERROR_IMP);
+ if (convert_err != 0)
+ return (EXIT_ERROR_CONV);
+ return (EXIT_SUCCESS);
+}