root/usr/src/cmd/cmd-inet/usr.sbin/inetadm/inetadm.c
/*
 * 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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * inetadm - administer services controlled by inetd and print out inetd
 * service related information.
 */

#include <locale.h>
#include <libintl.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <libuutil.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <inetsvc.h>
#include <errno.h>

#ifndef TEXT_DOMAIN
#define TEXT_DOMAIN     "SUNW_OST_OSCMD"
#endif /* TEXT_DOMAIN */

/* Strings for output to the user, and checking user's input */

#define INETADM_TRUE_STR                "TRUE"
#define INETADM_FALSE_STR               "FALSE"
#define INETADM_ENABLED_STR             "enabled"
#define INETADM_DISABLED_STR            "disabled"
#define INETADM_DEFAULT_STR             "default"

/* String for checking if an instance is under inetd's control. */

#define INETADM_INETD_STR               "network/inetd"

/*
 * Used to hold a list of scf_value_t's whilst performing a transaction
 * to write a proto list back.
 */
typedef struct scf_val_el {
        scf_value_t             *val;
        uu_list_node_t          link;
} scf_val_el_t;

/*
 * Structure used to encapsulate argc and argv so they can be passed using the
 * single data argument supplied by scf_walk_fmri() for the consumption of
 * modify_inst_props_cb().
 */
typedef struct arglist {
        int     argc;
        char    **argv;
} arglist_t;

static scf_handle_t *h;

static void
scfdie()
{
        uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
            scf_strerror(scf_error()));
}

static void
usage(boolean_t detailed)
{

        uu_warn(gettext(
            "Usage:\n"
            "  inetadm\n"
            "  inetadm -?\n"
            "  inetadm -p\n"
            "  inetadm -l {FMRI | pattern}...\n"
            "  inetadm -e {FMRI | pattern}...\n"
            "  inetadm -d {FMRI | pattern}...\n"
            "  inetadm -m {FMRI | pattern}... {name=value}...\n"
            "  inetadm -M {name=value}...\n"));

        if (!detailed)
                exit(UU_EXIT_USAGE);

        (void) fprintf(stdout, gettext(
            "\n"
            "Without any options inetadm lists all inetd managed services.\n"
            "\n"
            "Options:\n"
            "  -?       Print help.\n"
            "  -p       List all default inetd property values.\n"
            "  -l       List all inetd property values for the inet "
            "service(s).\n"
            "  -e       Enable the inet service(s).\n"
            "  -d       Disable the inet service(s).\n"
            "  -m       Modify the inet service(s) inetd property values.\n"
            "  -M       Modify default inetd property values.\n"));
}

/*
 * Add the proto list contained in array 'plist' to entry 'entry', storing
 * aside the scf_value_t's created and added to the entry in a list that the
 * pointer referenced by sv_list is made to point at.
 */
static void
add_proto_list(scf_transaction_entry_t *entry, scf_handle_t *hdl,
    char **plist, uu_list_t **sv_list)
{
        scf_val_el_t            *sv_el;
        int                     i;

        static uu_list_pool_t   *sv_pool = NULL;

        if ((sv_pool == NULL) &&
            ((sv_pool = uu_list_pool_create("sv_pool",
            sizeof (scf_val_el_t), offsetof(scf_val_el_t, link), NULL,
            UU_LIST_POOL_DEBUG)) == NULL))
                uu_die(gettext("Error: %s.\n"), uu_strerror(uu_error()));

        if ((*sv_list = uu_list_create(sv_pool, NULL, 0)) == NULL)
                uu_die(gettext("Error: %s.\n"), uu_strerror(uu_error()));

        for (i = 0; plist[i] != NULL; i++) {
                if ((sv_el = malloc(sizeof (scf_val_el_t))) == NULL)
                        uu_die(gettext("Error:"));

                if (((sv_el->val = scf_value_create(hdl)) == NULL) ||
                    (scf_value_set_astring(sv_el->val, plist[i]) != 0) ||
                    (scf_entry_add_value(entry, sv_el->val) != 0))
                        scfdie();

                uu_list_node_init(sv_el, &sv_el->link, sv_pool);
                (void) uu_list_insert_after(*sv_list, NULL, sv_el);
        }
}

/*
 * A counterpart to add_proto_list(), this function removes and frees the
 * scf_value_t's it added to entry 'entry'.
 */
static void
remove_proto_list(scf_transaction_entry_t *entry, uu_list_t *sv_list)
{
        scf_val_el_t    *sv_el;
        void            *cookie = NULL;

        scf_entry_reset(entry);

        while ((sv_el = uu_list_teardown(sv_list, &cookie)) != NULL) {
                scf_value_destroy(sv_el->val);
                free(sv_el);
        }

        uu_list_destroy(sv_list);
}

/*
 * modify_prop takes an instance, property group, property name, type, and
 * value, and modifies the specified property in the repository to the
 * submitted value.
 */

static void
modify_prop(const scf_instance_t *inst, const char *pg, const char *prop,
    scf_type_t type, void *value)
{
        scf_transaction_t               *tx;
        scf_transaction_entry_t         *ent;
        scf_propertygroup_t             *gpg;
        scf_property_t                  *eprop;
        scf_value_t                     *v;
        int                             ret, create = 0;
        char                            *fmri;
        ssize_t                         max_fmri_len;

        if ((gpg = scf_pg_create(h)) == NULL ||
            (eprop = scf_property_create(h)) == NULL ||
            (v = scf_value_create(h)) == NULL)
                scfdie();

        /* Get the property group or create it if it is missing. */
        if (scf_instance_get_pg(inst, pg, gpg) == -1) {
                if (scf_error() != SCF_ERROR_NOT_FOUND)
                        scfdie();

                max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
                if ((fmri = malloc(max_fmri_len + 1)) == NULL)
                        uu_die(gettext("Error: Out of memory.\n"));

                if (scf_instance_to_fmri(inst, fmri, max_fmri_len + 1) < 0)
                        scfdie();

                syslog(LOG_NOTICE, "inetadm: Property group \"%s\" missing "
                    "from \"%s\", attempting to add it.\n", pg, fmri);
                free(fmri);

                if (scf_instance_add_pg(inst, pg, SCF_GROUP_FRAMEWORK, 0,
                    gpg) == -1) {
                        switch (scf_error()) {
                        case SCF_ERROR_EXISTS:
                                break;
                        case SCF_ERROR_PERMISSION_DENIED:
                                uu_die(gettext("Error: Permission denied.\n"));
                        default:
                                scfdie();
                        }
                }
        }

        if (scf_pg_get_property(gpg, prop, eprop) == -1) {
                if (scf_error() != SCF_ERROR_NOT_FOUND)
                        scfdie();

                create = 1;
        }

        if ((tx = scf_transaction_create(h)) == NULL ||
            (ent = scf_entry_create(h)) == NULL)
                scfdie();

        do {
                uu_list_t       *sv_list;

                if (scf_transaction_start(tx, gpg) == -1) {
                        if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
                                scfdie();

                        uu_die(gettext("Error: Permission denied.\n"));
                }

                /* Modify the property or create it if it is missing */
                if (create)
                        ret = scf_transaction_property_new(tx, ent, prop, type);
                else
                        ret = scf_transaction_property_change_type(tx, ent,
                            prop, type);
                if (ret == -1)
                        scfdie();

                switch (type) {
                case SCF_TYPE_BOOLEAN:
                        scf_value_set_boolean(v, *(uint8_t *)value);
                        break;
                case SCF_TYPE_INTEGER:
                        scf_value_set_integer(v, *(int64_t *)value);
                        break;
                case SCF_TYPE_ASTRING:
                        if (strcmp(prop, PR_PROTO_NAME) == 0) {
                                add_proto_list(ent, h, (char **)value,
                                    &sv_list);
                        } else if (scf_value_set_astring(v, value) == -1) {
                                scfdie();
                        }
                        break;
                default:
                        uu_die(gettext("Error: Internal inetadm error"));
                }

                if ((strcmp(prop, PR_PROTO_NAME) != 0) &&
                    (scf_entry_add_value(ent, v) == -1))
                        scfdie();

                ret = scf_transaction_commit(tx);
                if (ret == -1) {
                        if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
                                scfdie();

                        uu_die(gettext("Error: Permission denied.\n"));
                }

                scf_transaction_reset(tx);

                if (ret == 0) {
                        if (scf_pg_update(gpg) == -1)
                                scfdie();
                }

                if (strcmp(prop, PR_PROTO_NAME) == 0)
                        remove_proto_list(ent, sv_list);

        } while (ret == 0);

        scf_value_destroy(v);
        scf_entry_destroy(ent);
        scf_transaction_destroy(tx);
        scf_property_destroy(eprop);
        scf_pg_destroy(gpg);
}

/*
 * delete_prop takes an instance, property group name and property, and
 * deletes the specified property from the repository.
 */

static void
delete_prop(const scf_instance_t *inst, const char *pg, const char *prop)
{
        scf_transaction_t               *tx;
        scf_transaction_entry_t         *ent;
        scf_propertygroup_t             *gpg;
        scf_property_t                  *eprop;
        int                             ret;

        if ((gpg = scf_pg_create(h)) == NULL ||
            (eprop = scf_property_create(h)) == NULL ||
            (tx = scf_transaction_create(h)) == NULL ||
            (ent = scf_entry_create(h)) == NULL)
                scfdie();

        if (scf_instance_get_pg(inst, pg, gpg) != SCF_SUCCESS) {
                if (scf_error() != SCF_ERROR_NOT_FOUND)
                        scfdie();

                uu_die(gettext("Error: \"%s\" property group missing.\n"), pg);
        }

        do {
                if (scf_transaction_start(tx, gpg) != SCF_SUCCESS) {
                        if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
                                scfdie();

                        uu_die(gettext("Error: Permission denied.\n"));
                }

                if (scf_transaction_property_delete(tx, ent,
                    prop) != SCF_SUCCESS) {
                        if (scf_error() != SCF_ERROR_NOT_FOUND)
                                scfdie();

                        uu_die(
                            gettext("Error: \"%s\" property does not exist.\n"),
                            prop);
                }

                ret = scf_transaction_commit(tx);
                if (ret < 0) {
                        if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
                                scfdie();

                        uu_die(gettext("Error: Permission denied.\n"));
                }
                if (ret == 0) {
                        scf_transaction_reset(tx);
                        if (scf_pg_update(gpg) == -1)
                                scfdie();
                }
        } while (ret == 0);

        (void) scf_entry_destroy(ent);
        scf_transaction_destroy(tx);
        scf_property_destroy(eprop);
        scf_pg_destroy(gpg);
}

/*
 * commit_props evaluates an entire property list that has been created
 * based on command line options, and either deletes or modifies properties
 * as requested.
 */

static void
commit_props(const scf_instance_t *inst, inetd_prop_t *mod, boolean_t defaults)
{
        int                     i;
        uint8_t                 new_bool;

        for (i = 0; mod[i].ip_name != NULL; i++) {
                switch (mod[i].ip_error) {
                case IVE_UNSET:
                        break;
                case IVE_INVALID:
                        delete_prop(inst, mod[i].ip_pg, mod[i].ip_name);
                        break;
                case IVE_VALID:
                        switch (mod[i].ip_type) {
                        case INET_TYPE_STRING:
                                modify_prop(inst,
                                    defaults ? PG_NAME_SERVICE_DEFAULTS :
                                    mod[i].ip_pg, mod[i].ip_name,
                                    SCF_TYPE_ASTRING,
                                    mod[i].ip_value.iv_string);
                                break;
                        case INET_TYPE_STRING_LIST:
                                modify_prop(inst,
                                    defaults ? PG_NAME_SERVICE_DEFAULTS :
                                    mod[i].ip_pg, mod[i].ip_name,
                                    SCF_TYPE_ASTRING,
                                    mod[i].ip_value.iv_string_list);
                                break;
                        case INET_TYPE_INTEGER:
                                modify_prop(inst,
                                    defaults ? PG_NAME_SERVICE_DEFAULTS :
                                    mod[i].ip_pg, mod[i].ip_name,
                                    SCF_TYPE_INTEGER, &mod[i].ip_value.iv_int);
                                break;
                        case INET_TYPE_BOOLEAN:
                                new_bool = (mod[i].ip_value.iv_boolean) ? 1 : 0;

                                modify_prop(inst,
                                    defaults ? PG_NAME_SERVICE_DEFAULTS :
                                    mod[i].ip_pg, mod[i].ip_name,
                                    SCF_TYPE_BOOLEAN, &new_bool);
                                break;
                        }
                }
        }
}

/*
 * list_callback is the callback function to be handed to simple_walk_instances
 * in list_services.  It is called once on every instance on a machine.  If
 * that instance is controlled by inetd, it prints enabled/disabled, state,
 * and instance FMRI.
 */

/*ARGSUSED*/
static int
list_callback(scf_handle_t *hin, scf_instance_t *inst, void *buf)
{
        ssize_t                 max_name_length;
        char                    *inst_name;
        scf_simple_prop_t       *prop = NULL, *prop2 = NULL;
        const uint8_t           *enabled;
        const char              *state, *restart_str;

        max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
        if ((inst_name = malloc(max_name_length + 1)) == NULL)
                uu_die(gettext("Error: Out of memory.\n"));

        /*
         * Get the FMRI of the instance, and check if its delegated restarter
         * is inetd.
         */

        if (scf_instance_to_fmri(inst, inst_name, max_name_length + 1) < 0)
                return (SCF_FAILED);

        if ((prop = scf_simple_prop_get(hin, inst_name, SCF_PG_GENERAL,
            SCF_PROPERTY_RESTARTER)) == NULL)
                goto out;

        if ((restart_str = scf_simple_prop_next_ustring(prop)) == NULL)
                goto out;

        if (strstr(restart_str, INETADM_INETD_STR) == NULL)
                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 enabled and state properties.
         */

        if (((prop = scf_simple_prop_get(hin, inst_name, SCF_PG_GENERAL,
            SCF_PROPERTY_ENABLED)) == NULL) ||
            ((enabled = scf_simple_prop_next_boolean(prop)) == NULL)) {
                (void) uu_warn(gettext("Error: Instance %s is missing enabled "
                    "property.\n"), inst_name);
                goto out;
        }

        if (((prop2 = scf_simple_prop_get(hin, inst_name, SCF_PG_RESTARTER,
            SCF_PROPERTY_STATE)) == NULL) ||
            ((state = scf_simple_prop_next_astring(prop2)) == NULL)) {
                (void) uu_warn(gettext("Error: Instance %s is missing state "
                    "property.\n"), inst_name);
                goto out;
        }

        /* Print enabled/disabled, state, and FMRI for the instance. */

        if (*enabled)
                (void) printf("%-10s%-15s%s\n", INETADM_ENABLED_STR, state,
                    inst_name);
        else
                (void) printf("%-10s%-15s%s\n", INETADM_DISABLED_STR, state,
                    inst_name);

out:
        free(inst_name);
        scf_simple_prop_free(prop);
        scf_simple_prop_free(prop2);
        return (SCF_SUCCESS);
}

/*
 * list_services calls list_callback on every instance on the machine.
 */

static void
list_services()
{
        (void) printf("%-10s%-15s%s\n", "ENABLED", "STATE", "FMRI");

        if (scf_simple_walk_instances(SCF_STATE_ALL, NULL, list_callback) ==
            SCF_FAILED)
                scfdie();
}

static void
print_prop_val(inetd_prop_t *prop)
{
        switch (prop->ip_type) {
        case INET_TYPE_STRING:
                (void) printf("\"%s\"\n", prop->ip_value.iv_string);
                break;
        case INET_TYPE_STRING_LIST:
                {
                        int     j = 0;
                        char    **cpp = prop->ip_value.iv_string_list;

                        /*
                         * Print string list as comma separated list.
                         */

                        (void) printf("\"%s", cpp[j]);
                        while (cpp[++j] != NULL)
                                (void) printf(",%s", cpp[j]);
                        (void) printf("\"\n");
                }
                break;
        case INET_TYPE_INTEGER:
                (void) printf("%lld\n", prop->ip_value.iv_int);
                break;
        case INET_TYPE_BOOLEAN:
                if (prop->ip_value.iv_boolean)
                        (void) printf("%s\n", INETADM_TRUE_STR);
                else
                        (void) printf("%s\n", INETADM_FALSE_STR);
                break;
        }
}

/*
 * list_props_cb is a callback function for scf_walk_fmri that lists all
 * relevant inetd properties for an instance managed by inetd.
 */

/* ARGSUSED0 */
static int
list_props_cb(void *data, scf_walkinfo_t *wip)
{
        int                     i;
        const char              *instname = wip->fmri;
        scf_simple_prop_t       *prop;
        inetd_prop_t            *proplist;
        const char              *restart_str;
        boolean_t               is_rpc = B_FALSE;
        size_t                  numprops;
        scf_handle_t            *h;
        scf_error_t             err;

        if (((h = scf_handle_create(SCF_VERSION)) == NULL) ||
            (scf_handle_bind(h) == -1))
                scfdie();

        /*
         * Get the property that holds the name of this instance's
         * restarter, and make sure that it is inetd.
         */
        if ((prop = scf_simple_prop_get(h, instname, SCF_PG_GENERAL,
            SCF_PROPERTY_RESTARTER)) == NULL) {
                if (scf_error() == SCF_ERROR_NOT_FOUND)
                        uu_die(gettext("Error: Specified service instance "
                            "\"%s\" has no restarter property.  inetd is not "
                            "the delegated restarter of this instance.\n"),
                            instname);
                if (scf_error() == SCF_ERROR_INVALID_ARGUMENT)
                        uu_die(gettext("Error: \"%s\" is not a valid service "
                            "instance.\n"), instname);

                scfdie();
        }

        if (((restart_str = scf_simple_prop_next_ustring(prop)) == NULL) ||
            (strstr(restart_str, INETADM_INETD_STR) == NULL)) {
                uu_die(gettext("Error: inetd is not the delegated restarter of "
                    "specified service instance \"%s\".\n"), instname);
        }

        scf_simple_prop_free(prop);

        /*
         * This instance is controlled by inetd, so now we display all
         * of its properties.  First the mandatory properties, and then
         * the properties that have default values, substituting the
         * default values inherited from inetd as necessary (this is done
         * for us by read_instance_props()).
         */

        if ((proplist = read_instance_props(h, instname, &numprops, &err)) ==
            NULL) {
                uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
                    scf_strerror(err));
        }
        scf_handle_destroy(h);

        (void) printf("%-9s%s\n", "SCOPE", "NAME=VALUE");

        for (i = 0; i < numprops; i++) {
                /* Skip rpc version properties if it's not an RPC service */
                if ((strcmp(PR_RPC_LW_VER_NAME, proplist[i].ip_name) == 0) ||
                    (strcmp(PR_RPC_HI_VER_NAME, proplist[i].ip_name) == 0))
                        if (!is_rpc)
                                continue;

                /* If it's not an unset property, print it out. */
                if (proplist[i].ip_error != IVE_UNSET) {
                        if (strcmp(PR_ISRPC_NAME, proplist[i].ip_name) == 0)
                                is_rpc = proplist[i].ip_value.iv_boolean;

                        (void) printf("%-9s%s=",
                            proplist[i].from_inetd ? INETADM_DEFAULT_STR : "",
                            proplist[i].ip_name);
                        print_prop_val(&proplist[i]);
                        continue;
                }

                /* arg0 is non-default, but also doesn't have to be set. */

                if (i == PT_ARG0_INDEX)
                        continue;

                /* all other properties should have values. */
                if (proplist[i].ip_default) {
                        (void) uu_warn(gettext("Error: Property %s is missing "
                            "and has no defined default value.\n"),
                            proplist[i].ip_name);
                } else {
                        (void) uu_warn(gettext("Error: Required property %s is "
                            "missing.\n"), proplist[i].ip_name);
                }
        }

        free_instance_props(proplist);
        return (0);
}

/*
 * set_svc_enable_cb is a callback function for scf_walk_fmri that sets the
 * enabled property in the repository for an instance based on the value given
 * by 'data'.
 */

static int
set_svc_enable_cb(void *data, scf_walkinfo_t *wip)
{
        uint8_t                 desired = *(uint8_t *)data;
        const char              *instname = wip->fmri;

        if (desired) {
                if (smf_enable_instance(instname, 0) == 0)
                        return (0);
        } else {
                if (smf_disable_instance(instname, 0) == 0)
                        return (0);
        }

        switch (scf_error()) {
        case SCF_ERROR_INVALID_ARGUMENT:
                uu_die(gettext("Error: \"%s\" is not a valid service "
                    "instance.\n"), instname);
                break;
        case SCF_ERROR_NOT_FOUND:
                uu_die(gettext("Error: Service instance \"%s\" not found.\n"),
                    instname);
                break;
        default:
                scfdie();
        }

        return (0);
}

/*
 * list_defaults lists all the default property values being provided by
 * inetd.
 */

static void
list_defaults()
{
        scf_handle_t            *h;
        scf_error_t             err;
        int                     i;
        inetd_prop_t            *proptable;
        size_t                  numprops;

        if (((h = scf_handle_create(SCF_VERSION)) == NULL) ||
            (scf_handle_bind(h) == -1))
                scfdie();

        if ((proptable = read_default_props(h, &numprops, &err)) == NULL) {
                uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
                    scf_strerror(err));
        }

        (void) printf("NAME=VALUE\n");

        for (i = 0; i < numprops; i++) {
                if (!proptable[i].ip_default)
                        continue;

                if (proptable[i].ip_error == IVE_UNSET) {
                        (void) uu_warn(gettext("Error: Default property %s "
                            "missing.\n"), proptable[i].ip_name);
                        continue;
                }

                (void) printf("%s=", proptable[i].ip_name);
                print_prop_val(&proptable[i]);
        }

        free_instance_props(proptable);
}

/*
 * modify_inst_props_cb is a callback function for scf_walk_fmri that modifies
 * the properties that are given as name=value pairs on the command line
 * to the requested value.
 */

static int
modify_inst_props_cb(void *data, scf_walkinfo_t *wip)
{
        int                     i, j;
        char                    *value;
        const char              *fmri = wip->fmri;
        scf_instance_t          *inst = wip->inst;
        inetd_prop_t            *mod, *prop_table;
        size_t                  numprops;
        ssize_t                 max_val;
        int64_t                 new_int;
        int                     argc = ((arglist_t *)data)->argc;
        char                    **argv = ((arglist_t *)data)->argv;

        if ((max_val = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) < 0)
                scfdie();

        prop_table = get_prop_table(&numprops);

        if ((mod = malloc(numprops * sizeof (inetd_prop_t))) == NULL)
                uu_die(gettext("Error: Out of memory.\n"));

        (void) memcpy(mod, prop_table, numprops * sizeof (inetd_prop_t));

        /*
         * For each property to be changed, look up the property name in the
         * property table.  Change each property in the mod array, and then
         * write the entire thing back.
         */
        for (i = 0; i < argc; i++) {
                /* Separate argument into name and value pair */
                if ((value = strchr(argv[i], '=')) == NULL)
                        uu_die(gettext("Error: Malformed name=value pair "
                            "\"%s\"\n"), argv[i]);

                *value = '\0';
                value++;

                /* Find property name in array of properties */
                for (j = 0; mod[j].ip_name != NULL; j++) {
                        if (strcmp(mod[j].ip_name, argv[i]) == 0)
                                break;
                }

                if (mod[j].ip_name == NULL)
                        uu_die(gettext("Error: \"%s\" is not a valid "
                            "property.\n"), argv[i]);

                if (*value == '\0') {
                        if ((mod[j].ip_default) || (j == PT_ARG0_INDEX)) {
                                /* mark property for deletion */
                                mod[j].ip_error = IVE_INVALID;

                                /* return the '=' taken out above */
                                *(--value) = '=';

                                continue;
                        } else {
                                uu_die(gettext("\"%s\" is a mandatory "
                                    "property and can not be deleted.\n"),
                                    argv[i]);
                        }
                }

                switch (mod[j].ip_type) {
                case INET_TYPE_INTEGER:
                        if (uu_strtoint(value, &new_int, sizeof (new_int), 0,
                            0, 0) == -1)
                                uu_die(gettext("Error: \"%s\" is not a valid "
                                    "integer value.\n"), value);

                        mod[j].ip_value.iv_int = new_int;
                        break;
                case INET_TYPE_STRING:
                        if (strlen(value) >= max_val) {
                                uu_die(gettext("Error: String value is longer "
                                    "than %l characters.\n"), max_val);
                        } else if ((mod[j].ip_value.iv_string = strdup(value))
                            == NULL) {
                                uu_die(gettext("Error: Out of memory.\n"));
                        }
                        break;
                case INET_TYPE_STRING_LIST:
                        if ((mod[j].ip_value.iv_string_list =
                            get_protos(value)) == NULL) {
                                if (errno == ENOMEM) {
                                        uu_die(gettext(
                                            "Error: Out of memory.\n"));
                                } else if (errno == E2BIG) {
                                        uu_die(gettext(
                                            "Error: String value in "
                                            "%s property longer than "
                                            "%l characters.\n"),
                                            PR_PROTO_NAME, max_val);
                                } else {
                                        uu_die(gettext(
                                            "Error: No values "
                                            "specified for %s "
                                            "property.\n"),
                                            PR_PROTO_NAME);
                                }
                        }
                        break;
                case INET_TYPE_BOOLEAN:
                        if (strcasecmp(value, INETADM_TRUE_STR) == 0)
                                mod[j].ip_value.iv_boolean = B_TRUE;
                        else if (strcasecmp(value, INETADM_FALSE_STR) == 0)
                                mod[j].ip_value.iv_boolean = B_FALSE;
                        else
                                uu_die(gettext("Error: \"%s\" is not a valid "
                                    "boolean value. (TRUE or FALSE)\n"), value);
                }
                /* mark property for modification */
                mod[j].ip_error = IVE_VALID;

                /* return the '=' taken out above */
                *(--value) = '=';
        }

        commit_props(inst, mod, B_FALSE);
        free(mod);
        if (smf_refresh_instance(fmri) != 0)
                uu_die(gettext("Error: Unable to refresh instance %s.\n"),
                    fmri);

        return (0);
}

/*
 * modify_defaults takes n name=value pairs for inetd default property values,
 * parses them, and then modifies the values in the repository.
 */

static void
modify_defaults(int argc, char *argv[])
{
        int                     i, j;
        char                    *value;
        scf_instance_t          *inst;
        inetd_prop_t            *mod, *prop_table;
        size_t                  numprops;
        ssize_t                 max_val;
        int64_t                 new_int;

        if ((inst = scf_instance_create(h)) == NULL)
                scfdie();

        if (scf_handle_decode_fmri(h, INETD_INSTANCE_FMRI, NULL, NULL,
            inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
                if (scf_error() == SCF_ERROR_NOT_FOUND) {
                        uu_die(gettext("inetd instance missing in repository."
                            "\n"));
                } else {
                        scfdie();
                }
        }

        if ((max_val = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) < 0)
                scfdie();

        prop_table = get_prop_table(&numprops);

        if ((mod = malloc(numprops * sizeof (inetd_prop_t))) == NULL)
                uu_die(gettext("Error: Out of memory.\n"));

        (void) memcpy(mod, prop_table, numprops * sizeof (inetd_prop_t));

        for (i = 0; i < argc; i++) {
                /* Separate argument into name and value pair */
                if ((value = strchr(argv[i], '=')) == NULL)
                        uu_die(gettext("Error: Malformed name=value pair \"%s"
                            "\"\n"), argv[i]);

                *value = '\0';
                value++;

                /* Find property name in array of defaults */
                for (j = 0; mod[j].ip_name != NULL; j++) {
                        if (!mod[j].ip_default)
                                continue;
                        if (strcmp(mod[j].ip_name, argv[i]) == 0)
                                break;
                }

                if (mod[j].ip_name == NULL)
                        uu_die(gettext("Error: \"%s\" is not a default inetd "
                            "property.\n"), argv[i]);

                if (*value == '\0')
                        uu_die(gettext("Cannot accept NULL values for default "
                            "properties.\n"));

                switch (mod[j].ip_type) {
                case INET_TYPE_INTEGER:
                        if (uu_strtoint(value, &new_int, sizeof (new_int), 0,
                            0, 0) == -1)
                                uu_die(gettext("Error: \"%s\" is not a valid "
                                    "integer value.\n"), value);

                        mod[j].ip_value.iv_int = new_int;
                        break;
                case INET_TYPE_STRING:
                        if (strlen(value) >= max_val)
                                uu_die(gettext("Error: String value is longer "
                                    "than %l characters.\n"), max_val);
                        if ((mod[j].ip_value.iv_string = strdup(value))
                            == NULL)
                                uu_die(gettext("Error: Out of memory.\n"));
                        break;
                case INET_TYPE_BOOLEAN:
                        if (strcasecmp(value, INETADM_TRUE_STR) == 0)
                                mod[j].ip_value.iv_boolean = B_TRUE;
                        else if (strcasecmp(value, INETADM_FALSE_STR) == 0)
                                mod[j].ip_value.iv_boolean = B_FALSE;
                        else
                                uu_die(gettext("Error: \"%s\" is not a valid "
                                    "boolean value. (TRUE or FALSE)\n"), value);
                }
                /* mark property for modification */
                mod[j].ip_error = IVE_VALID;
        }

        commit_props(inst, mod, B_TRUE);
        free(mod);
        scf_instance_destroy(inst);
        if (refresh_inetd() != 0)
                uu_warn(gettext("Warning: Unable to refresh inetd.\n"));
}

int
main(int argc, char *argv[])
{
        int             opt;
        uint_t          lflag, eflag, dflag, pflag, mflag, Mflag;
        uint8_t         enable;
        scf_error_t     serr;
        int             exit_status = 0;

        (void) setlocale(LC_ALL, "");
        (void) textdomain(TEXT_DOMAIN);

        if ((h = scf_handle_create(SCF_VERSION)) == NULL)
                scfdie();

        if (scf_handle_bind(h) == -1)
                uu_die(gettext("Error: Couldn't bind to svc.configd.\n"));

        if (argc == 1) {
                list_services();
                goto out;
        }

        lflag = eflag = dflag = pflag = mflag = Mflag = 0;
        while ((opt = getopt(argc, argv, "ledpMm?")) != -1) {
                switch (opt) {
                case 'l':
                        lflag = 1;
                        break;
                case 'e':
                        eflag = 1;
                        break;
                case 'd':
                        dflag = 1;
                        break;
                case 'p':
                        pflag = 1;
                        break;
                case 'M':
                        Mflag = 1;
                        break;
                case 'm':
                        mflag = 1;
                        break;
                case '?':
                        if (optopt == '?') {
                                usage(B_TRUE);
                                goto out;
                        } else {
                                usage(B_FALSE);
                        }
                        break;
                default:
                        usage(B_FALSE);
                }
        }

        /*
         * All options are mutually exclusive, and we must have an option
         * if we reached here.
         */
        if (lflag + eflag + dflag + pflag + mflag + Mflag != 1)
                usage(B_FALSE);

        argv += optind;
        argc -= optind;
        if ((pflag == 0) && (argc == 0))
                usage(B_FALSE);

        serr = 0;
        if (lflag) {
                serr = scf_walk_fmri(h, argc, argv, 0, list_props_cb, NULL,
                    &exit_status, uu_warn);
        } else if (dflag) {
                enable = 0;
                serr = scf_walk_fmri(h, argc, argv, 0, set_svc_enable_cb,
                    &enable, &exit_status, uu_warn);
        } else if (eflag) {
                enable = 1;
                serr = scf_walk_fmri(h, argc, argv, 0, set_svc_enable_cb,
                    &enable, &exit_status, uu_warn);
        } else if (mflag) {
                arglist_t       args;
                char            **cpp = argv;
                uint_t          fmri_args = 0;

                /* count number of fmri arguments */
                while ((fmri_args < argc) && (strchr(*cpp, '=') == NULL)) {
                        fmri_args++;
                        cpp++;
                }

                /* if no x=y args or no fmri, show usage */
                if ((fmri_args == argc) || (fmri_args == 0))
                        usage(B_FALSE);

                /* setup args for modify_inst_props_cb */
                args.argc = argc - fmri_args;
                args.argv = argv + fmri_args;

                serr = scf_walk_fmri(h, fmri_args, argv, 0,
                    modify_inst_props_cb, &args, &exit_status, uu_warn);
        } else if (Mflag) {
                modify_defaults(argc, argv);
        } else if (pflag) {
                /* ensure there's no trailing garbage */
                if (argc != 0)
                        usage(B_FALSE);
                list_defaults();
        }
        if (serr != 0) {
                uu_warn(gettext("failed to iterate over instances: %s"),
                    scf_strerror(serr));
                exit(UU_EXIT_FATAL);
        }

out:
        (void) scf_handle_unbind(h);
        scf_handle_destroy(h);

        return (exit_status);
}