root/usr/src/cmd/truss/listopts.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 (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <memory.h>
#include <sys/types.h>
#include <signal.h>
#include <libproc.h>
#include "ramdata.h"
#include "systable.h"
#include "proto.h"

/* XXX A bug in the <string.h> header file requires this */
extern char *strtok_r(char *s1, const char *s2, char **lasts);

/*
 * option procesing ---
 * Routines for scanning syscall, signal, fault
 * and file descriptor lists.
 */

/*
 * Function prototypes for static routines in this module.
 */
void    upcase(char *);

const char white[] = " \t\n";   /* white space characters */
const char sepr[] = " ,\t\n";   /* list separator characters */
const char csepr[] = " :,\t\n"; /* same, with ':' added */

/*
 * Scan list of syscall names.
 * Return 0 on success, != 0 on any failure.
 */
int
syslist(char *str,                      /* string of syscall names */
        sysset_t *setp,                 /* syscall set */
        int *fp)                        /* first-time flag */
{
        char *name;
        int exclude = FALSE;
        int rc = 0;
        char *lasts;

        name = strtok_r(str, sepr, &lasts);

        if (name != NULL && *name == '!') {     /* exclude from set */
                exclude = TRUE;
                if (*++name == '\0')
                        name = strtok_r(NULL, sepr, &lasts);
        } else if (!*fp) {      /* first time, clear the set */
                premptyset(setp);
                *fp = TRUE;
        }

        for (; name; name = strtok_r(NULL, sepr, &lasts)) {
                int sys;
                int sysx;
                int sysxx;
                int sys64;
                char *next;

                if (*name == '!') {     /* exclude remainder from set */
                        exclude = TRUE;
                        while (*++name == '!')
                                /* empty */;
                        if (*name == '\0')
                                continue;
                }

                sys = strtol(name, &next, 0);
                sysx = sysxx = sys64 = 0;
                if (sys < 0 || sys > PRMAXSYS || *next != '\0')
                        sys = 0;
                if (sys == 0) {
                        const struct systable *stp = systable;
                        for (; sys == 0 && stp->nargs >= 0; stp++)
                                if (stp->name && strcmp(stp->name, name) == 0)
                                        sys = stp-systable;
                }
                if (sys == 0) {
                        const struct sysalias *sap = sysalias;
                        for (; sys == 0 && sap->name; sap++)
                                if (strcmp(sap->name, name) == 0)
                                        sys = sap->number;
                }
                if (sys > 0 && sys <= PRMAXSYS) {
                        switch (sys) {
                        case SYS_fstatat:       /* set both if either */
                        case SYS_fstatat64:
                                sys = SYS_fstatat;
                                sys64 = SYS_fstatat64;
                                goto def;

                        case SYS_stat:          /* set all if either */
                        case SYS_stat64:
                                sys = SYS_stat;
                                sys64 = SYS_stat64;
                                sysx = SYS_fstatat;
                                sysxx = SYS_fstatat64;
                                goto def;

                        case SYS_lstat:         /* set all if either */
                        case SYS_lstat64:
                                sys = SYS_lstat;
                                sys64 = SYS_lstat64;
                                sysx = SYS_fstatat;
                                sysxx = SYS_fstatat64;
                                goto def;

                        case SYS_fstat:         /* set all if either */
                        case SYS_fstat64:
                                sys = SYS_fstat;
                                sys64 = SYS_fstat64;
                                sysx = SYS_fstatat;
                                sysxx = SYS_fstatat64;
                                goto def;

                        case SYS_getdents:      /* set both if either */
                        case SYS_getdents64:
                                sys = SYS_getdents;
                                sys64 = SYS_getdents64;
                                goto def;

                        case SYS_mmap:          /* set both if either */
                        case SYS_mmap64:
                                sys = SYS_mmap;
                                sys64 = SYS_mmap64;
                                goto def;

                        case SYS_statvfs:       /* set both if either */
                        case SYS_statvfs64:
                                sys = SYS_statvfs;
                                sys64 = SYS_statvfs64;
                                goto def;

                        case SYS_fstatvfs:      /* set both if either */
                        case SYS_fstatvfs64:
                                sys = SYS_fstatvfs;
                                sys64 = SYS_fstatvfs64;
                                goto def;

                        case SYS_setrlimit:     /* set both if either */
                        case SYS_setrlimit64:
                                sys = SYS_setrlimit;
                                sys64 = SYS_setrlimit64;
                                goto def;

                        case SYS_getrlimit:     /* set both if either */
                        case SYS_getrlimit64:
                                sys = SYS_getrlimit;
                                sys64 = SYS_getrlimit64;
                                goto def;

                        case SYS_pread:         /* set both if either */
                        case SYS_pread64:
                                sys = SYS_pread;
                                sys64 = SYS_pread64;
                                goto def;

                        case SYS_pwrite:        /* set both if either */
                        case SYS_pwrite64:
                                sys = SYS_pwrite;
                                sys64 = SYS_pwrite64;
                                goto def;

                        case SYS_openat:        /* set all if any */
                        case SYS_openat64:
                        case SYS_open:
                        case SYS_open64:
                                sys = SYS_openat;
                                sys64 = SYS_openat64;
                                sysx = SYS_open;
                                sysxx = SYS_open64;
                                goto def;

                        case SYS_forksys:       /* set both if either */
                        case SYS_vfork:
                                sysx = SYS_forksys;
                                sys = SYS_vfork;
                                goto def;

                        case SYS_sigprocmask:   /* set both if either */
                        case SYS_lwp_sigmask:
                                sysx = SYS_sigprocmask;
                                sys = SYS_lwp_sigmask;
                                goto def;

                        case SYS_lseek:         /* set both if either */
                        case SYS_llseek:
                                sysx = SYS_lseek;
                                sys = SYS_llseek;
                                goto def;

                        case SYS_rename:        /* set both */
                                sysx = SYS_renameat;
                                goto def;

                        case SYS_link:          /* set both */
                                sysx = SYS_linkat;
                                goto def;

                        case SYS_unlink:        /* set both */
                        case SYS_rmdir:         /* set both */
                                sysx = SYS_unlinkat;
                                goto def;

                        case SYS_symlink:       /* set both */
                                sysx = SYS_symlinkat;
                                goto def;

                        case SYS_readlink:      /* set both */
                                sysx = SYS_readlinkat;
                                goto def;

                        case SYS_chmod:         /* set both */
                        case SYS_fchmod:        /* set both */
                                sysx = SYS_fchmodat;
                                goto def;

                        case SYS_chown:         /* set both */
                        case SYS_lchown:        /* set both */
                        case SYS_fchown:        /* set both */
                                sysx = SYS_fchownat;
                                goto def;

                        case SYS_mkdir:         /* set both */
                                sysx = SYS_mkdirat;
                                goto def;

                        case SYS_mknod:         /* set both */
                                sysx = SYS_mknodat;
                                goto def;

                        case SYS_access:        /* set both */
                                sysx = SYS_faccessat;
                                goto def;

                        default:
                        def:
                                if (exclude) {
                                        prdelset(setp, sys);
                                        if (sysx)
                                                prdelset(setp, sysx);
                                        if (sysxx)
                                                prdelset(setp, sysxx);
                                        if (sys64)
                                                prdelset(setp, sys64);
                                } else {
                                        praddset(setp, sys);
                                        if (sysx)
                                                praddset(setp, sysx);
                                        if (sysxx)
                                                praddset(setp, sysxx);
                                        if (sys64)
                                                praddset(setp, sys64);
                                }
                                break;
                        }
                } else if (strcmp(name, "all") == 0 ||
                    strcmp(name, "ALL") == 0) {
                        if (exclude) {
                                premptyset(setp);
                        } else {
                                prfillset(setp);
                        }
                } else {
                        (void) fprintf(stderr,
                            "%s: unrecognized syscall: %s\n",
                            command, name);
                        rc = -1;
                }
        }

        return (rc);
}

/*
 * List of signals to trace.
 * Return 0 on success, != 0 on any failure.
 */
int
siglist(private_t *pri,
        char *str,                      /* string of signal names */
        sigset_t *setp,                 /* signal set */
        int *fp)                        /* first-time flag */
{
        char *name;
        int exclude = FALSE;
        int rc = 0;
        char *lasts;

        upcase(str);
        name = strtok_r(str, sepr, &lasts);

        if (name != NULL && *name == '!') {     /* exclude from set */
                exclude = TRUE;
                if (*++name == '\0')
                        name = strtok_r(NULL, sepr, &lasts);
        } else if (!*fp) {      /* first time, clear the set */
                premptyset(setp);
                *fp = TRUE;
        }

        for (; name; name = strtok_r(NULL, sepr, &lasts)) {
                int sig;
                char *next;

                if (*name == '!') {     /* exclude remainder from set */
                        exclude = TRUE;
                        while (*++name == '!')
                                /* empty */;
                        if (*name == '\0')
                                continue;
                }

                sig = strtol(name, &next, 0);
                if (sig <= 0 || sig > PRMAXSIG || *next != '\0') {
                        for (sig = 1; sig <= PRMAXSIG; sig++) {
                                const char *sname = rawsigname(pri, sig);
                                if (sname == NULL)
                                        continue;
                                if (strcmp(sname, name) == 0 ||
                                    strcmp(sname+3, name) == 0)
                                        break;
                        }
                        if (sig > PRMAXSIG)
                                sig = 0;
                }
                if (sig > 0 && sig <= PRMAXSIG) {
                        if (exclude) {
                                prdelset(setp, sig);
                        } else {
                                praddset(setp, sig);
                        }
                } else if (strcmp(name, "ALL") == 0) {
                        if (exclude) {
                                premptyset(setp);
                        } else {
                                prfillset(setp);
                        }
                } else {
                        (void) fprintf(stderr,
                            "%s: unrecognized signal name/number: %s\n",
                            command, name);
                        rc = -1;
                }
        }

        return (rc);
}

/*
 * List of faults to trace.
 * return 0 on success, != 0 on any failure.
 */
int
fltlist(char *str,                      /* string of fault names */
        fltset_t *setp,                 /* fault set */
        int *fp)                        /* first-time flag */
{
        char *name;
        int exclude = FALSE;
        int rc = 0;
        char *lasts;

        upcase(str);
        name = strtok_r(str, sepr, &lasts);

        if (name != NULL && *name == '!') {     /* exclude from set */
                exclude = TRUE;
                if (*++name == '\0')
                        name = strtok_r(NULL, sepr, &lasts);
        } else if (!*fp) {      /* first time, clear the set */
                premptyset(setp);
                *fp = TRUE;
        }

        for (; name; name = strtok_r(NULL, sepr, &lasts)) {
                int flt;
                char *next;

                if (*name == '!') {     /* exclude remainder from set */
                        exclude = TRUE;
                        while (*++name == '!')
                                /* empty */;
                        if (*name == '\0')
                                continue;
                }

                flt = strtol(name, &next, 0);
                if (flt <= 0 || flt > PRMAXFAULT || *next != '\0') {
                        for (flt = 1; flt <= PRMAXFAULT; flt++) {
                                char fname[32];

                                if (proc_fltname(flt, fname,
                                    sizeof (fname)) == NULL)
                                        continue;

                                if (strcmp(fname, name) == 0 ||
                                    strcmp(fname+3, name) == 0)
                                        break;
                        }
                        if (flt > PRMAXFAULT)
                                flt = 0;
                }
                if (flt > 0 && flt <= PRMAXFAULT) {
                        if (exclude) {
                                prdelset(setp, flt);
                        } else {
                                praddset(setp, flt);
                        }
                } else if (strcmp(name, "ALL") == 0) {
                        if (exclude) {
                                premptyset(setp);
                        } else {
                                prfillset(setp);
                        }
                } else {
                        (void) fprintf(stderr,
                            "%s: unrecognized fault name/number: %s\n",
                            command, name);
                        rc = -1;
                }
        }

        return (rc);
}

/*
 * Gather file descriptors to dump.
 * Return 0 on success, != 0 on any failure.
 */
int
fdlist(char *str,               /* string of filedescriptors */
        fileset_t *setp)        /* set of boolean flags */
{
        char *name;
        int exclude = FALSE;
        int rc = 0;
        char *lasts;

        upcase(str);
        name = strtok_r(str, sepr, &lasts);

        if (name != NULL && *name == '!') {     /* exclude from set */
                exclude = TRUE;
                if (*++name == '\0')
                        name = strtok_r(NULL, sepr, &lasts);
        }

        for (; name; name = strtok_r(NULL, sepr, &lasts)) {
                int fd;
                char *next;

                if (*name == '!') {     /* exclude remainder from set */
                        exclude = TRUE;
                        while (*++name == '!')
                                /* empty */;
                        if (*name == '\0')
                                continue;
                }

                fd = strtol(name, &next, 0);
                if (fd >= 0 && fd < NOFILES_MAX && *next == '\0') {
                        fd++;
                        if (exclude) {
                                prdelset(setp, fd);
                        } else {
                                praddset(setp, fd);
                        }
                } else if (strcmp(name, "ALL") == 0) {
                        if (exclude) {
                                premptyset(setp);
                        } else {
                                prfillset(setp);
                        }
                } else {
                        (void) fprintf(stderr,
                            "%s: filedescriptor not in range[0..%d]: %s\n",
                            command, NOFILES_MAX-1, name);
                        rc = -1;
                }
        }

        return (rc);
}

void
upcase(char *str)
{
        int c;

        while ((c = *str) != '\0')
                *str++ = toupper(c);
}

/*
 * 'arg' points to a string like:
 *      libc,libnsl,... : printf,read,write,...
 * or
 *      libc,libnsl,... :: printf,read,write,...
 * with possible filename pattern-matching metacharacters.
 *
 * Assumption:  No library or function name can contain ',' or ':'.
 */
int
liblist(char *arg, int hang)
{
        const char *star = "*";
        struct dynpat *Dyp;
        char *pat;
        char *fpat;
        char *lasts;
        uint_t maxpat;

        /* append a new dynpat structure to the end of the Dynpat list */
        Dyp = my_malloc(sizeof (struct dynpat), NULL);
        Dyp->next = NULL;
        if (Lastpat == NULL)
                Dynpat = Lastpat = Dyp;
        else {
                Lastpat->next = Dyp;
                Lastpat = Dyp;
        }
        Dyp->flag = hang? BPT_HANG : 0;
        Dyp->exclude_lib = 0;
        Dyp->exclude = 0;
        Dyp->internal = 0;
        Dyp->Dp = NULL;

        /*
         * Find the beginning of the filename patterns
         * and null-terminate the library name patterns.
         */
        if ((fpat = strchr(arg, ':')) != NULL)
                *fpat++ = '\0';

        /*
         * Library name patterns.
         */
        pat = strtok_r(arg, sepr, &lasts);

        /* '!' introduces an exclusion list */
        if (pat != NULL && *pat == '!') {
                Dyp->exclude_lib = 1;
                pat += strspn(pat, "!");
                if (*pat == '\0')
                        pat = strtok_r(NULL, sepr, &lasts);
                /* force exclusion of all functions as well */
                Dyp->exclude = 1;
                Dyp->internal = 1;
                fpat = NULL;
        }

        if (pat == NULL) {
                /* empty list means all libraries */
                Dyp->libpat = my_malloc(sizeof (char *), NULL);
                Dyp->libpat[0] = star;
                Dyp->nlibpat = 1;
        } else {
                /*
                 * We are now at the library list.
                 * Generate the list and count the library name patterns.
                 */
                maxpat = 1;
                Dyp->libpat = my_malloc(maxpat * sizeof (char *), NULL);
                Dyp->nlibpat = 0;
                Dyp->libpat[Dyp->nlibpat++] = pat;
                while ((pat = strtok_r(NULL, sepr, &lasts)) != NULL) {
                        if (Dyp->nlibpat == maxpat) {
                                maxpat *= 2;
                                Dyp->libpat = my_realloc(Dyp->libpat,
                                    maxpat * sizeof (char *), NULL);
                        }
                        Dyp->libpat[Dyp->nlibpat++] = pat;
                }
        }

        /*
         * Function name patterns.
         */
        if (fpat == NULL)
                pat = NULL;
        else {
                /*
                 * We have already seen a ':'.  Look for another.
                 * Double ':' means trace internal calls.
                 */
                fpat += strspn(fpat, white);
                if (*fpat == ':') {
                        Dyp->internal = 1;
                        *fpat++ = '\0';
                }
                pat = strtok_r(fpat, csepr, &lasts);
        }

        /* '!' introduces an exclusion list */
        if (pat != NULL && *pat == '!') {
                Dyp->exclude = 1;
                Dyp->internal = 1;
                pat += strspn(pat, "!");
                if (*pat == '\0')
                        pat = strtok_r(NULL, sepr, &lasts);
        }

        if (pat == NULL) {
                /* empty function list means exclude all functions */
                Dyp->sympat = my_malloc(sizeof (char *), NULL);
                Dyp->sympat[0] = star;
                Dyp->nsympat = 1;
        } else {
                /*
                 * We are now at the function list.
                 * Generate the list and count the symbol name patterns.
                 */
                maxpat = 1;
                Dyp->sympat = my_malloc(maxpat * sizeof (char *), NULL);
                Dyp->nsympat = 0;
                Dyp->sympat[Dyp->nsympat++] = pat;
                while ((pat = strtok_r(NULL, sepr, &lasts)) != NULL) {
                        if (Dyp->nsympat == maxpat) {
                                maxpat *= 2;
                                Dyp->sympat = my_realloc(Dyp->sympat,
                                    maxpat * sizeof (char *), NULL);
                        }
                        Dyp->sympat[Dyp->nsympat++] = pat;
                }
        }

        return (0);
}