/*
* 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);
}