root/usr/src/lib/libdtrace/common/dt_options.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
 * Copyright (c) 2012 by Delphix. All rights reserved.
 */

#include <sys/resource.h>
#include <sys/mman.h>
#include <sys/types.h>

#include <strings.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <alloca.h>
#include <errno.h>
#include <fcntl.h>

#include <dt_impl.h>
#include <dt_string.h>

static int
dt_opt_agg(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        dt_aggregate_t *agp = &dtp->dt_aggregate;

        if (arg != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        agp->dtat_flags |= option;
        return (0);
}

/*ARGSUSED*/
static int
dt_opt_amin(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        char str[DTRACE_ATTR2STR_MAX];
        dtrace_attribute_t attr;

        if (arg == NULL || dtrace_str2attr(arg, &attr) == -1)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        dt_dprintf("set compiler attribute minimum to %s\n",
            dtrace_attr2str(attr, str, sizeof (str)));

        if (dtp->dt_pcb != NULL) {
                dtp->dt_pcb->pcb_cflags |= DTRACE_C_EATTR;
                dtp->dt_pcb->pcb_amin = attr;
        } else {
                dtp->dt_cflags |= DTRACE_C_EATTR;
                dtp->dt_amin = attr;
        }

        return (0);
}

static void
dt_coredump(void)
{
        const char msg[] = "libdtrace DEBUG: [ forcing coredump ]\n";

        struct sigaction act;
        struct rlimit lim;

        (void) write(STDERR_FILENO, msg, sizeof (msg) - 1);

        act.sa_handler = SIG_DFL;
        act.sa_flags = 0;

        (void) sigemptyset(&act.sa_mask);
        (void) sigaction(SIGABRT, &act, NULL);

        lim.rlim_cur = RLIM_INFINITY;
        lim.rlim_max = RLIM_INFINITY;

        (void) setrlimit(RLIMIT_CORE, &lim);
        abort();
}

/*ARGSUSED*/
static int
dt_opt_core(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        static int enabled = 0;

        if (arg != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (enabled++ || atexit(dt_coredump) == 0)
                return (0);

        return (dt_set_errno(dtp, errno));
}

/*ARGSUSED*/
static int
dt_opt_cpp_hdrs(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        if (arg != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (dtp->dt_pcb != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTCTX));

        if (dt_cpp_add_arg(dtp, "-H") == NULL)
                return (dt_set_errno(dtp, EDT_NOMEM));

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_cpp_path(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        char *cpp;

        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (dtp->dt_pcb != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTCTX));

        if ((cpp = strdup(arg)) == NULL)
                return (dt_set_errno(dtp, EDT_NOMEM));

        dtp->dt_cpp_argv[0] = (char *)strbasename(cpp);
        free(dtp->dt_cpp_path);
        dtp->dt_cpp_path = cpp;

        return (0);
}

static int
dt_opt_cpp_opts(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        char *buf;
        size_t len;
        const char *opt = (const char *)option;

        if (opt == NULL || arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (dtp->dt_pcb != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTCTX));

        len = strlen(opt) + strlen(arg) + 1;
        buf = alloca(len);

        (void) strcpy(buf, opt);
        (void) strcat(buf, arg);

        if (dt_cpp_add_arg(dtp, buf) == NULL)
                return (dt_set_errno(dtp, EDT_NOMEM));

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_ctypes(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        int fd;

        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if ((fd = open64(arg, O_CREAT | O_WRONLY, 0666)) == -1)
                return (dt_set_errno(dtp, errno));

        (void) close(dtp->dt_cdefs_fd);
        dtp->dt_cdefs_fd = fd;
        return (0);
}

/*ARGSUSED*/
static int
dt_opt_droptags(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        dtp->dt_droptags = 1;
        return (0);
}

/*ARGSUSED*/
static int
dt_opt_dtypes(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        int fd;

        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if ((fd = open64(arg, O_CREAT | O_WRONLY, 0666)) == -1)
                return (dt_set_errno(dtp, errno));

        (void) close(dtp->dt_ddefs_fd);
        dtp->dt_ddefs_fd = fd;
        return (0);
}

/*ARGSUSED*/
static int
dt_opt_debug(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        if (arg != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        _dtrace_debug = 1;
        return (0);
}

/*ARGSUSED*/
static int
dt_opt_iregs(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        int n;

        if (arg == NULL || (n = atoi(arg)) <= 0)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        dtp->dt_conf.dtc_difintregs = n;
        return (0);
}

/*ARGSUSED*/
static int
dt_opt_lazyload(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        dtp->dt_lazyload = 1;

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_ld_path(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        char *ld;

        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (dtp->dt_pcb != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTCTX));

        if ((ld = strdup(arg)) == NULL)
                return (dt_set_errno(dtp, EDT_NOMEM));

        free(dtp->dt_ld_path);
        dtp->dt_ld_path = ld;

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_libdir(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        dt_dirpath_t *dp;

        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if ((dp = malloc(sizeof (dt_dirpath_t))) == NULL ||
            (dp->dir_path = strdup(arg)) == NULL) {
                free(dp);
                return (dt_set_errno(dtp, EDT_NOMEM));
        }

        dt_list_append(&dtp->dt_lib_path, dp);
        return (0);
}

/*ARGSUSED*/
static int
dt_opt_linkmode(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (strcmp(arg, "kernel") == 0)
                dtp->dt_linkmode = DT_LINK_KERNEL;
        else if (strcmp(arg, "primary") == 0)
                dtp->dt_linkmode = DT_LINK_PRIMARY;
        else if (strcmp(arg, "dynamic") == 0)
                dtp->dt_linkmode = DT_LINK_DYNAMIC;
        else if (strcmp(arg, "static") == 0)
                dtp->dt_linkmode = DT_LINK_STATIC;
        else
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_linktype(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (strcasecmp(arg, "elf") == 0)
                dtp->dt_linktype = DT_LTYP_ELF;
        else if (strcasecmp(arg, "dof") == 0)
                dtp->dt_linktype = DT_LTYP_DOF;
        else
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_encoding(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (strcmp(arg, "ascii") == 0)
                dtp->dt_encoding = DT_ENCODING_ASCII;
        else if (strcmp(arg, "utf8") == 0)
                dtp->dt_encoding = DT_ENCODING_UTF8;
        else
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_evaltime(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (strcmp(arg, "exec") == 0)
                dtp->dt_prcmode = DT_PROC_STOP_CREATE;
        else if (strcmp(arg, "preinit") == 0)
                dtp->dt_prcmode = DT_PROC_STOP_PREINIT;
        else if (strcmp(arg, "postinit") == 0)
                dtp->dt_prcmode = DT_PROC_STOP_POSTINIT;
        else if (strcmp(arg, "main") == 0)
                dtp->dt_prcmode = DT_PROC_STOP_MAIN;
        else
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_pgmax(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        int n;

        if (arg == NULL || (n = atoi(arg)) < 0)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        dtp->dt_procs->dph_lrulim = n;
        return (0);
}

static int
dt_opt_setenv(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        char **p;
        char *var;
        int i;

        /*
         * We can't effectively set environment variables from #pragma lines
         * since the processes have already been spawned.
         */
        if (dtp->dt_pcb != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTCTX));

        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (!option && strchr(arg, '=') != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        for (i = 1, p = dtp->dt_proc_env; *p != NULL; i++, p++)
                continue;

        for (p = dtp->dt_proc_env; *p != NULL; p++) {
                var = strchr(*p, '=');
                if (var == NULL)
                        var = *p + strlen(*p);
                if (strncmp(*p, arg, var - *p) == 0) {
                        dt_free(dtp, *p);
                        *p = dtp->dt_proc_env[i - 1];
                        dtp->dt_proc_env[i - 1] = NULL;
                        i--;
                }
        }

        if (option) {
                if ((var = strdup(arg)) == NULL)
                        return (dt_set_errno(dtp, EDT_NOMEM));

                if ((p = dt_alloc(dtp, sizeof (char *) * (i + 1))) == NULL) {
                        dt_free(dtp, var);
                        return (dt_set_errno(dtp, EDT_NOMEM));
                }

                bcopy(dtp->dt_proc_env, p, sizeof (char *) * i);
                dt_free(dtp, dtp->dt_proc_env);
                dtp->dt_proc_env = p;

                dtp->dt_proc_env[i - 1] = var;
                dtp->dt_proc_env[i] = NULL;
        }

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_stdc(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (dtp->dt_pcb != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTCTX));

        if (strcmp(arg, "a") == 0)
                dtp->dt_stdcmode = DT_STDC_XA;
        else if (strcmp(arg, "c") == 0)
                dtp->dt_stdcmode = DT_STDC_XC;
        else if (strcmp(arg, "s") == 0)
                dtp->dt_stdcmode = DT_STDC_XS;
        else if (strcmp(arg, "t") == 0)
                dtp->dt_stdcmode = DT_STDC_XT;
        else
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_syslibdir(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        dt_dirpath_t *dp = dt_list_next(&dtp->dt_lib_path);
        char *path;

        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if ((path = strdup(arg)) == NULL)
                return (dt_set_errno(dtp, EDT_NOMEM));

        free(dp->dir_path);
        dp->dir_path = path;

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_tree(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        int m;

        if (arg == NULL || (m = atoi(arg)) <= 0)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        dtp->dt_treedump = m;
        return (0);
}

/*ARGSUSED*/
static int
dt_opt_tregs(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        int n;

        if (arg == NULL || (n = atoi(arg)) <= 0)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        dtp->dt_conf.dtc_diftupregs = n;
        return (0);
}

/*ARGSUSED*/
static int
dt_opt_xlate(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (strcmp(arg, "dynamic") == 0)
                dtp->dt_xlatemode = DT_XL_DYNAMIC;
        else if (strcmp(arg, "static") == 0)
                dtp->dt_xlatemode = DT_XL_STATIC;
        else
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_cflags(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        if (arg != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (dtp->dt_pcb != NULL)
                dtp->dt_pcb->pcb_cflags |= option;
        else
                dtp->dt_cflags |= option;

        return (0);
}

static int
dt_opt_dflags(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        if (arg != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        dtp->dt_dflags |= option;
        return (0);
}

static int
dt_opt_invcflags(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        if (arg != NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (dtp->dt_pcb != NULL)
                dtp->dt_pcb->pcb_cflags &= ~option;
        else
                dtp->dt_cflags &= ~option;

        return (0);
}

/*ARGSUSED*/
static int
dt_opt_version(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        dt_version_t v;

        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        if (dt_version_str2num(arg, &v) == -1)
                return (dt_set_errno(dtp, EDT_VERSINVAL));

        if (!dt_version_defined(v))
                return (dt_set_errno(dtp, EDT_VERSUNDEF));

        return (dt_reduce(dtp, v));
}

static int
dt_opt_runtime(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        char *end;
        dtrace_optval_t val = 0;
        int i;

        const struct {
                char *positive;
                char *negative;
        } couples[] = {
                { "yes",        "no" },
                { "enable",     "disable" },
                { "enabled",    "disabled" },
                { "true",       "false" },
                { "on",         "off" },
                { "set",        "unset" },
                { NULL }
        };

        if (arg != NULL) {
                if (arg[0] == '\0') {
                        val = DTRACEOPT_UNSET;
                        goto out;
                }

                for (i = 0; couples[i].positive != NULL; i++) {
                        if (strcasecmp(couples[i].positive, arg) == 0) {
                                val = 1;
                                goto out;
                        }

                        if (strcasecmp(couples[i].negative, arg) == 0) {
                                val = DTRACEOPT_UNSET;
                                goto out;
                        }
                }

                errno = 0;
                val = strtoull(arg, &end, 0);

                if (*end != '\0' || errno != 0 || val < 0)
                        return (dt_set_errno(dtp, EDT_BADOPTVAL));
        }

out:
        dtp->dt_options[option] = val;
        return (0);
}

static int
dt_optval_parse(const char *arg, dtrace_optval_t *rval)
{
        dtrace_optval_t mul = 1;
        size_t len;
        char *end;

        len = strlen(arg);
        errno = 0;

        switch (arg[len - 1]) {
        case 't':
        case 'T':
                mul *= 1024;
                /*FALLTHRU*/
        case 'g':
        case 'G':
                mul *= 1024;
                /*FALLTHRU*/
        case 'm':
        case 'M':
                mul *= 1024;
                /*FALLTHRU*/
        case 'k':
        case 'K':
                mul *= 1024;
                /*FALLTHRU*/
        default:
                break;
        }

        errno = 0;
        *rval = strtoull(arg, &end, 0) * mul;

        if ((mul > 1 && end != &arg[len - 1]) || (mul == 1 && *end != '\0') ||
            *rval < 0 || errno != 0)
                return (-1);

        return (0);
}

static int
dt_opt_size(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        dtrace_optval_t val = 0;

        if (arg != NULL && dt_optval_parse(arg, &val) != 0)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        dtp->dt_options[option] = val;
        return (0);
}

static int
dt_opt_rate(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        char *end;
        int i;
        dtrace_optval_t mul = 1, val = 0;

        const struct {
                char *name;
                hrtime_t mul;
        } suffix[] = {
                { "ns",         NANOSEC / NANOSEC },
                { "nsec",       NANOSEC / NANOSEC },
                { "us",         NANOSEC / MICROSEC },
                { "usec",       NANOSEC / MICROSEC },
                { "ms",         NANOSEC / MILLISEC },
                { "msec",       NANOSEC / MILLISEC },
                { "s",          NANOSEC / SEC },
                { "sec",        NANOSEC / SEC },
                { "m",          NANOSEC * (hrtime_t)60 },
                { "min",        NANOSEC * (hrtime_t)60 },
                { "h",          NANOSEC * (hrtime_t)60 * (hrtime_t)60 },
                { "hour",       NANOSEC * (hrtime_t)60 * (hrtime_t)60 },
                { "d",          NANOSEC * (hrtime_t)(24 * 60 * 60) },
                { "day",        NANOSEC * (hrtime_t)(24 * 60 * 60) },
                { "hz",         0 },
                { NULL }
        };

        if (arg != NULL) {
                errno = 0;
                val = strtoull(arg, &end, 0);

                for (i = 0; suffix[i].name != NULL; i++) {
                        if (strcasecmp(suffix[i].name, end) == 0) {
                                mul = suffix[i].mul;
                                break;
                        }
                }

                if (suffix[i].name == NULL && *end != '\0' || val < 0)
                        return (dt_set_errno(dtp, EDT_BADOPTVAL));

                if (mul == 0) {
                        /*
                         * The rate has been specified in frequency-per-second.
                         */
                        if (val != 0)
                                val = NANOSEC / val;
                } else {
                        val *= mul;
                }
        }

        dtp->dt_options[option] = val;
        return (0);
}

/*
 * When setting the strsize option, set the option in the dt_options array
 * using dt_opt_size() as usual, and then update the definition of the CTF
 * type for the D intrinsic "string" to be an array of the corresponding size.
 * If any errors occur, reset dt_options[option] to its previous value.
 */
static int
dt_opt_strsize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        dtrace_optval_t val = dtp->dt_options[option];
        ctf_file_t *fp = DT_STR_CTFP(dtp);
        ctf_id_t type = ctf_type_resolve(fp, DT_STR_TYPE(dtp));
        ctf_arinfo_t r;

        if (dt_opt_size(dtp, arg, option) != 0)
                return (-1); /* dt_errno is set for us */

        if (dtp->dt_options[option] > UINT_MAX) {
                dtp->dt_options[option] = val;
                return (dt_set_errno(dtp, EOVERFLOW));
        }

        if (ctf_array_info(fp, type, &r) == CTF_ERR) {
                dtp->dt_options[option] = val;
                dtp->dt_ctferr = ctf_errno(fp);
                return (dt_set_errno(dtp, EDT_CTF));
        }

        r.ctr_nelems = (uint_t)dtp->dt_options[option];

        if (ctf_set_array(fp, type, &r) == CTF_ERR ||
            ctf_update(fp) == CTF_ERR) {
                dtp->dt_options[option] = val;
                dtp->dt_ctferr = ctf_errno(fp);
                return (dt_set_errno(dtp, EDT_CTF));
        }

        return (0);
}

static const struct {
        const char *dtbp_name;
        int dtbp_policy;
} _dtrace_bufpolicies[] = {
        { "ring", DTRACEOPT_BUFPOLICY_RING },
        { "fill", DTRACEOPT_BUFPOLICY_FILL },
        { "switch", DTRACEOPT_BUFPOLICY_SWITCH },
        { NULL, 0 }
};

/*ARGSUSED*/
static int
dt_opt_bufpolicy(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        dtrace_optval_t policy = DTRACEOPT_UNSET;
        int i;

        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        for (i = 0; _dtrace_bufpolicies[i].dtbp_name != NULL; i++) {
                if (strcmp(_dtrace_bufpolicies[i].dtbp_name, arg) == 0) {
                        policy = _dtrace_bufpolicies[i].dtbp_policy;
                        break;
                }
        }

        if (policy == DTRACEOPT_UNSET)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        dtp->dt_options[DTRACEOPT_BUFPOLICY] = policy;

        return (0);
}

static const struct {
        const char *dtbr_name;
        int dtbr_policy;
} _dtrace_bufresize[] = {
        { "auto", DTRACEOPT_BUFRESIZE_AUTO },
        { "manual", DTRACEOPT_BUFRESIZE_MANUAL },
        { NULL, 0 }
};

/*ARGSUSED*/
static int
dt_opt_bufresize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
        dtrace_optval_t policy = DTRACEOPT_UNSET;
        int i;

        if (arg == NULL)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        for (i = 0; _dtrace_bufresize[i].dtbr_name != NULL; i++) {
                if (strcmp(_dtrace_bufresize[i].dtbr_name, arg) == 0) {
                        policy = _dtrace_bufresize[i].dtbr_policy;
                        break;
                }
        }

        if (policy == DTRACEOPT_UNSET)
                return (dt_set_errno(dtp, EDT_BADOPTVAL));

        dtp->dt_options[DTRACEOPT_BUFRESIZE] = policy;

        return (0);
}

int
dt_options_load(dtrace_hdl_t *dtp)
{
        dof_hdr_t hdr, *dof;
        dof_sec_t *sec;
        size_t offs;
        int i;

        /*
         * To load the option values, we need to ask the kernel to provide its
         * DOF, which we'll sift through to look for OPTDESC sections.
         */
        bzero(&hdr, sizeof (dof_hdr_t));
        hdr.dofh_loadsz = sizeof (dof_hdr_t);

        if (dt_ioctl(dtp, DTRACEIOC_DOFGET, &hdr) == -1)
                return (dt_set_errno(dtp, errno));

        if (hdr.dofh_loadsz < sizeof (dof_hdr_t))
                return (dt_set_errno(dtp, EINVAL));

        dof = calloc(1, hdr.dofh_loadsz);
        if (dof == NULL)
                return (dt_set_errno(dtp, errno));

        dof->dofh_loadsz = hdr.dofh_loadsz;

        for (i = 0; i < DTRACEOPT_MAX; i++)
                dtp->dt_options[i] = DTRACEOPT_UNSET;

        if (dt_ioctl(dtp, DTRACEIOC_DOFGET, dof) == -1) {
                free(dof);
                return (dt_set_errno(dtp, errno));
        }

        for (i = 0; i < dof->dofh_secnum; i++) {
                sec = (dof_sec_t *)(uintptr_t)((uintptr_t)dof +
                    dof->dofh_secoff + i * dof->dofh_secsize);

                if (sec->dofs_type != DOF_SECT_OPTDESC)
                        continue;

                break;
        }

        for (offs = 0; offs < sec->dofs_size; offs += sec->dofs_entsize) {
                dof_optdesc_t *opt = (dof_optdesc_t *)(uintptr_t)
                    ((uintptr_t)dof + sec->dofs_offset + offs);

                if (opt->dofo_strtab != DOF_SECIDX_NONE)
                        continue;

                if (opt->dofo_option >= DTRACEOPT_MAX)
                        continue;

                dtp->dt_options[opt->dofo_option] = opt->dofo_value;
        }

        free(dof);
        return (0);
}

typedef struct dt_option {
        const char *o_name;
        int (*o_func)(dtrace_hdl_t *, const char *, uintptr_t);
        uintptr_t o_option;
} dt_option_t;

/*
 * Compile-time options.
 */
static const dt_option_t _dtrace_ctoptions[] = {
        { "aggpercpu", dt_opt_agg, DTRACE_A_PERCPU },
        { "amin", dt_opt_amin },
        { "argref", dt_opt_cflags, DTRACE_C_ARGREF },
        { "core", dt_opt_core },
        { "cpp", dt_opt_cflags, DTRACE_C_CPP },
        { "cpphdrs", dt_opt_cpp_hdrs },
        { "cpppath", dt_opt_cpp_path },
        { "ctypes", dt_opt_ctypes },
        { "defaultargs", dt_opt_cflags, DTRACE_C_DEFARG },
        { "dtypes", dt_opt_dtypes },
        { "debug", dt_opt_debug },
        { "define", dt_opt_cpp_opts, (uintptr_t)"-D" },
        { "droptags", dt_opt_droptags },
        { "empty", dt_opt_cflags, DTRACE_C_EMPTY },
        { "encoding", dt_opt_encoding },
        { "errtags", dt_opt_cflags, DTRACE_C_ETAGS },
        { "evaltime", dt_opt_evaltime },
        { "incdir", dt_opt_cpp_opts, (uintptr_t)"-I" },
        { "iregs", dt_opt_iregs },
        { "kdefs", dt_opt_invcflags, DTRACE_C_KNODEF },
        { "knodefs", dt_opt_cflags, DTRACE_C_KNODEF },
        { "late", dt_opt_xlate },
        { "lazyload", dt_opt_lazyload },
        { "ldpath", dt_opt_ld_path },
        { "libdir", dt_opt_libdir },
        { "linkmode", dt_opt_linkmode },
        { "linktype", dt_opt_linktype },
        { "nolibs", dt_opt_cflags, DTRACE_C_NOLIBS },
        { "pgmax", dt_opt_pgmax },
        { "pspec", dt_opt_cflags, DTRACE_C_PSPEC },
        { "setenv", dt_opt_setenv, 1 },
        { "stdc", dt_opt_stdc },
        { "strip", dt_opt_dflags, DTRACE_D_STRIP },
        { "syslibdir", dt_opt_syslibdir },
        { "tree", dt_opt_tree },
        { "tregs", dt_opt_tregs },
        { "udefs", dt_opt_invcflags, DTRACE_C_UNODEF },
        { "undef", dt_opt_cpp_opts, (uintptr_t)"-U" },
        { "unodefs", dt_opt_cflags, DTRACE_C_UNODEF },
        { "unsetenv", dt_opt_setenv, 0 },
        { "verbose", dt_opt_cflags, DTRACE_C_DIFV },
        { "version", dt_opt_version },
        { "zdefs", dt_opt_cflags, DTRACE_C_ZDEFS },
        { NULL }
};

/*
 * Run-time options.
 */
static const dt_option_t _dtrace_rtoptions[] = {
        { "aggsize", dt_opt_size, DTRACEOPT_AGGSIZE },
        { "bufsize", dt_opt_size, DTRACEOPT_BUFSIZE },
        { "bufpolicy", dt_opt_bufpolicy, DTRACEOPT_BUFPOLICY },
        { "bufresize", dt_opt_bufresize, DTRACEOPT_BUFRESIZE },
        { "cleanrate", dt_opt_rate, DTRACEOPT_CLEANRATE },
        { "cpu", dt_opt_runtime, DTRACEOPT_CPU },
        { "destructive", dt_opt_runtime, DTRACEOPT_DESTRUCTIVE },
        { "dynvarsize", dt_opt_size, DTRACEOPT_DYNVARSIZE },
        { "grabanon", dt_opt_runtime, DTRACEOPT_GRABANON },
        { "jstackframes", dt_opt_runtime, DTRACEOPT_JSTACKFRAMES },
        { "jstackstrsize", dt_opt_size, DTRACEOPT_JSTACKSTRSIZE },
        { "nspec", dt_opt_runtime, DTRACEOPT_NSPEC },
        { "specsize", dt_opt_size, DTRACEOPT_SPECSIZE },
        { "stackframes", dt_opt_runtime, DTRACEOPT_STACKFRAMES },
        { "statusrate", dt_opt_rate, DTRACEOPT_STATUSRATE },
        { "strsize", dt_opt_strsize, DTRACEOPT_STRSIZE },
        { "ustackframes", dt_opt_runtime, DTRACEOPT_USTACKFRAMES },
        { "temporal", dt_opt_runtime, DTRACEOPT_TEMPORAL },
        { NULL }
};

/*
 * Dynamic run-time options.
 */
static const dt_option_t _dtrace_drtoptions[] = {
        { "agghist", dt_opt_runtime, DTRACEOPT_AGGHIST },
        { "aggpack", dt_opt_runtime, DTRACEOPT_AGGPACK },
        { "aggrate", dt_opt_rate, DTRACEOPT_AGGRATE },
        { "aggsortkey", dt_opt_runtime, DTRACEOPT_AGGSORTKEY },
        { "aggsortkeypos", dt_opt_runtime, DTRACEOPT_AGGSORTKEYPOS },
        { "aggsortpos", dt_opt_runtime, DTRACEOPT_AGGSORTPOS },
        { "aggsortrev", dt_opt_runtime, DTRACEOPT_AGGSORTREV },
        { "aggzoom", dt_opt_runtime, DTRACEOPT_AGGZOOM },
        { "flowindent", dt_opt_runtime, DTRACEOPT_FLOWINDENT },
        { "quiet", dt_opt_runtime, DTRACEOPT_QUIET },
        { "rawbytes", dt_opt_runtime, DTRACEOPT_RAWBYTES },
        { "stackindent", dt_opt_runtime, DTRACEOPT_STACKINDENT },
        { "switchrate", dt_opt_rate, DTRACEOPT_SWITCHRATE },
        { NULL }
};

int
dtrace_getopt(dtrace_hdl_t *dtp, const char *opt, dtrace_optval_t *val)
{
        const dt_option_t *op;

        if (opt == NULL)
                return (dt_set_errno(dtp, EINVAL));

        /*
         * We only need to search the run-time options -- it's not legal
         * to get the values of compile-time options.
         */
        for (op = _dtrace_rtoptions; op->o_name != NULL; op++) {
                if (strcmp(op->o_name, opt) == 0) {
                        *val = dtp->dt_options[op->o_option];
                        return (0);
                }
        }

        for (op = _dtrace_drtoptions; op->o_name != NULL; op++) {
                if (strcmp(op->o_name, opt) == 0) {
                        *val = dtp->dt_options[op->o_option];
                        return (0);
                }
        }

        return (dt_set_errno(dtp, EDT_BADOPTNAME));
}

int
dtrace_setopt(dtrace_hdl_t *dtp, const char *opt, const char *val)
{
        const dt_option_t *op;

        if (opt == NULL)
                return (dt_set_errno(dtp, EINVAL));

        for (op = _dtrace_ctoptions; op->o_name != NULL; op++) {
                if (strcmp(op->o_name, opt) == 0)
                        return (op->o_func(dtp, val, op->o_option));
        }

        for (op = _dtrace_drtoptions; op->o_name != NULL; op++) {
                if (strcmp(op->o_name, opt) == 0)
                        return (op->o_func(dtp, val, op->o_option));
        }

        for (op = _dtrace_rtoptions; op->o_name != NULL; op++) {
                if (strcmp(op->o_name, opt) == 0) {
                        /*
                         * Only dynamic run-time options may be set while
                         * tracing is active.
                         */
                        if (dtp->dt_active)
                                return (dt_set_errno(dtp, EDT_ACTIVE));

                        return (op->o_func(dtp, val, op->o_option));
                }
        }

        return (dt_set_errno(dtp, EDT_BADOPTNAME));
}