#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>
#define EXIT_SUCCESS 0
#define EXIT_USAGE 1
#define EXIT_ERROR_CONV 2
#define EXIT_ERROR_IMP 3
#define EXIT_ERROR_SYS 4
#define EXIT_ERROR_ENBL 5
#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 "/lib/svc/manifest/network"
#define MANIFEST_RPC_DIR MANIFEST_DIR "/rpc"
#define SVCCFG_PATH "/usr/sbin/svccfg"
#define RPCBIND_FMRI "svc:/network/rpc/bind"
#define MAX_SRC_LINELEN 32768
#define INETCONV_VERSION 1
struct inetconfent {
char *service;
char *endpoint;
char *protocol;
char *wait_status;
char *username;
char *server_program;
char *server_args;
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;
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(8) from inetd.conf(5).\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_dependency[] =
" <dependency\n"
" name='%s'\n"
" grouping='require_all'\n"
" restart_on='restart'\n"
" type='service'>\n"
" <service_fmri value='%s' />\n"
" </dependency>\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";
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(buf);
len = strlen(name) + strlen(prefix) + 1;
buf = safe_malloc(len);
buf[0] = '\0';
c = name[0];
if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
(void) strlcat(buf, prefix, len);
}
(void) strlcat(buf, name, len);
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 *cp, *proto;
free(buf);
len = strlen(iconf->service) + strlen(iconf->protocol) +
sizeof ("rpc-/visible");
buf = safe_malloc(len);
(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";
for (cp = buf; *cp != '\0'; cp++) {
if (*cp == '.')
*cp = '_';
}
(void) strlcat(buf, propertyname(proto, "p-"), len);
return (buf);
}
static boolean_t
is_v6only(char *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);
}
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));
put_prop_value_boolean(prop, PR_ISRPC_NAME, iconf->isrpc);
put_prop_value_boolean(prop, PR_ISWAIT_NAME, iconf->wait);
if (iconf->isrpc) {
put_prop_value_int(prop, PR_RPC_LW_VER_NAME,
iconf->rpc_low_version);
put_prop_value_int(prop, PR_RPC_HI_VER_NAME,
iconf->rpc_high_version);
svc_name = iconf->rpc_prog;
proto += 4;
}
if (!put_prop_value_string(prop, PR_SOCK_TYPE_NAME, iconf->endpoint) ||
!put_prop_value_string(prop, PR_SVC_NAME_NAME, svc_name)) {
valid = B_FALSE;
if (errno == ENOMEM) {
(void) fprintf(stderr,
gettext("%s: failed to allocate memory: %s\n"),
progname, strerror(errno));
exit(EXIT_ERROR_SYS);
}
}
put_prop_value_string_list(prop, PR_PROTO_NAME, get_protos(proto));
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);
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;
}
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;
}
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 {
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);
}
}
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);
if ((cp = strtok(NULL, "\n")) != NULL)
iconf->server_args = safe_strdup(cp);
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++;
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;
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++;
if (*line == '\n')
continue;
if (*line == '#') {
if (line[strlen(line) - 1] != '\n')
skipline(finfo->fp);
continue;
}
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;
}
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 (iconf->isrpc) {
if (fprintf(f, xml_dependency, "rpcbind", RPCBIND_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 {
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);
}
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 (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);
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);
}
}
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) {
(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);
}
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);
if (strtok(configstr, " \t") == NULL) {
free(configstr);
return (-1);
}
if ((arg1 = strtok(NULL, " \t")) == NULL) {
free(configstr);
return (-1);
}
if (strtok(NULL, " \t") == NULL) {
configfile = MAIN_CONFIG;
if ((fd = open(configfile, O_RDONLY)) >= 0)
(void) close(fd);
else
configfile = ALT_CONFIG;
} else {
if (strtok(NULL, " \t") != NULL) {
free(configstr);
return (-1);
}
configfile = arg1;
}
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];
if (inetd_config_path(&inetd_cpath) == -1) {
(void) fprintf(stderr,
gettext("%s: Error reading from repository\n"), progname);
return (-1);
}
if (realpath(srcfile, cpath) == NULL)
(void) strlcpy(cpath, srcfile, sizeof (cpath));
if (strcmp(cpath, inetd_cpath) != 0) {
free(inetd_cpath);
return (0);
}
free(inetd_cpath);
if (calculate_hash(cpath, &hashstr) != 0) {
(void) fprintf(stderr,
gettext("%s: Error unable to update repository\n"),
progname);
return (-1);
}
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);
}
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);
if (scf_instance_get_pg(inst, pg, gpg) == -1) {
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;
}
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) {
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);
}
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);
}
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;
scf_simple_prop_free(prop);
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 (*isrpc) {
char *cp;
cp = strchr(iconf->service, '/');
if (cp != NULL)
*cp = '\0';
}
if (strcmp(name, iconf->service) == 0 &&
strcmp(endpoint, iconf->endpoint) == 0 &&
*isrpc == (strncmp(iconf->protocol, "rpc/", 4) == 0)) {
if ((strcmp(iconf->server_program, "internal") != 0) &&
(strcmp(iconf->exec, prog) != 0)) {
if (!import) {
(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 {
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);
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) {
(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 = B_TRUE;
break;
case 'f':
overwrite = B_TRUE;
break;
case 'n':
import = B_FALSE;
break;
case 'i':
if (srcfile != NULL) {
(void) fprintf(stderr,
gettext("%s: Error only one -%c allowed\n"),
progname, optopt);
usage();
}
srcfile = optarg;
break;
case 'o':
if (destdir != NULL) {
(void) fprintf(stderr,
gettext("%s: Error only one -%c allowed\n"),
progname, optopt);
usage();
}
destdir = optarg;
break;
case '?':
default:
usage();
break;
}
}
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 (enable) {
rval = scf_simple_walk_instances(SCF_STATE_ALL, iconf,
list_callback);
free_inetconfent(iconf);
if (rval == SCF_FAILED) {
if (scf_error() != SCF_ERROR_CALLBACK_FAILED)
(void) fprintf(stderr, gettext(
"Error walking instances: %s.\n"),
scf_strerror(scf_error()));
enable_err++;
break;
}
continue;
}
if ((rval = open_dstfile(destdir, overwrite, iconf, &dstfinfo))
< 0) {
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;
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);
}