/* * 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) 1997, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, Joyent, Inc. All rights reserved. * Copyright 2019, Carlos Neira * Copyright 2019 OmniOS Community Edition (OmniOSce) Association. * Copyright 2020 Oxide Computer Company */ #include #define __EXTENSIONS__ #include #undef __EXTENSIONS__ #include #include #include "libproc.h" #include static const char * rawfltname(int flt) { const char *name; switch (flt) { case FLTILL: name = "FLTILL"; break; case FLTPRIV: name = "FLTPRIV"; break; case FLTBPT: name = "FLTBPT"; break; case FLTTRACE: name = "FLTTRACE"; break; case FLTACCESS: name = "FLTACCESS"; break; case FLTBOUNDS: name = "FLTBOUNDS"; break; case FLTIOVF: name = "FLTIOVF"; break; case FLTIZDIV: name = "FLTIZDIV"; break; case FLTFPE: name = "FLTFPE"; break; case FLTSTACK: name = "FLTSTACK"; break; case FLTPAGE: name = "FLTPAGE"; break; case FLTWATCH: name = "FLTWATCH"; break; case FLTCPCOVF: name = "FLTCPCOVF"; break; default: name = NULL; break; } return (name); } /* * Return the name of a fault. * Manufacture a name for unknown fault. */ char * proc_fltname(int flt, char *buf, size_t bufsz) { const char *name = rawfltname(flt); size_t len; if (bufsz == 0) /* force a program failure */ return (NULL); if (name != NULL) { len = strlen(name); (void) strncpy(buf, name, bufsz); } else { len = snprintf(buf, bufsz, "FLT#%d", flt); } if (len >= bufsz) /* ensure null-termination */ buf[bufsz-1] = '\0'; return (buf); } /* * Return the name of a signal. * Manufacture a name for unknown signal. */ char * proc_signame(int sig, char *buf, size_t bufsz) { char name[SIG2STR_MAX+4]; size_t len; if (bufsz == 0) /* force a program failure */ return (NULL); /* sig2str() omits the leading "SIG" */ (void) strcpy(name, "SIG"); if (sig2str(sig, name+3) == 0) { len = strlen(name); (void) strncpy(buf, name, bufsz); } else { len = snprintf(buf, bufsz, "SIG#%d", sig); } if (len >= bufsz) /* ensure null-termination */ buf[bufsz-1] = '\0'; return (buf); } static const char *const systable[] = { NULL, /* 0 */ "_exit", /* 1 */ NULL, /* 2 */ "read", /* 3 */ "write", /* 4 */ "open", /* 5 */ "close", /* 6 */ "linkat", /* 7 */ NULL, /* 8 */ "link", /* 9 */ "unlink", /* 10 */ "symlinkat", /* 11 */ "chdir", /* 12 */ "time", /* 13 */ "mknod", /* 14 */ "chmod", /* 15 */ "chown", /* 16 */ "brk", /* 17 */ "stat", /* 18 */ "lseek", /* 19 */ "getpid", /* 20 */ "mount", /* 21 */ "readlinkat", /* 22 */ "setuid", /* 23 */ "getuid", /* 24 */ "stime", /* 25 */ "ptrace", /* 26 */ "alarm", /* 27 */ "fstat", /* 28 */ "pause", /* 29 */ NULL, /* 30 */ "stty", /* 31 */ "gtty", /* 32 */ "access", /* 33 */ "nice", /* 34 */ "statfs", /* 35 */ "sync", /* 36 */ "kill", /* 37 */ "fstatfs", /* 38 */ "pgrpsys", /* 39 */ "uucopystr", /* 40 */ NULL, /* 41 */ "pipe", /* 42 */ "times", /* 43 */ "profil", /* 44 */ "faccessat", /* 45 */ "setgid", /* 46 */ "getgid", /* 47 */ "mknodat", /* 48 */ "msgsys", /* 49 */ "sysi86", /* 50 */ "acct", /* 51 */ "shmsys", /* 52 */ "semsys", /* 53 */ "ioctl", /* 54 */ "uadmin", /* 55 */ "fchownat", /* 56 */ "utssys", /* 57 */ "fdsync", /* 58 */ "execve", /* 59 */ "umask", /* 60 */ "chroot", /* 61 */ "fcntl", /* 62 */ "ulimit", /* 63 */ "renameat", /* 64 */ "unlinkat", /* 65 */ "fstatat", /* 66 */ "fstatat64", /* 67 */ "openat", /* 68 */ "openat64", /* 69 */ "tasksys", /* 70 */ "acctctl", /* 71 */ "exacctsys", /* 72 */ "getpagesizes", /* 73 */ "rctlsys", /* 74 */ "issetugid", /* 75 */ "fsat", /* 76 */ "lwp_park", /* 77 */ "sendfilev", /* 78 */ "rmdir", /* 79 */ "mkdir", /* 80 */ "getdents", /* 81 */ "privsys", /* 82 */ "ucredsys", /* 83 */ "sysfs", /* 84 */ "getmsg", /* 85 */ "putmsg", /* 86 */ NULL, /* 87 */ "lstat", /* 88 */ "symlink", /* 89 */ "readlink", /* 90 */ "setgroups", /* 91 */ "getgroups", /* 92 */ "fchmod", /* 93 */ "fchown", /* 94 */ "sigprocmask", /* 95 */ "sigsuspend", /* 96 */ "sigaltstack", /* 97 */ "sigaction", /* 98 */ "sigpending", /* 99 */ "context", /* 100 */ "fchmodat", /* 101 */ "mkdirat", /* 102 */ "statvfs", /* 103 */ "fstatvfs", /* 104 */ "getloadavg", /* 105 */ "nfssys", /* 106 */ "waitid", /* 107 */ "sigsendsys", /* 108 */ "hrtsys", /* 109 */ "acancel", /* 110 */ "async", /* 111 */ "priocntlsys", /* 112 */ "pathconf", /* 113 */ "mincore", /* 114 */ "mmap", /* 115 */ "mprotect", /* 116 */ "munmap", /* 117 */ "fpathconf", /* 118 */ "vfork", /* 119 */ "fchdir", /* 120 */ "readv", /* 121 */ "writev", /* 122 */ "preadv", /* 123 */ "pwritev", /* 124 */ "upanic", /* 125 */ "getrandom", /* 126 */ "mmapobj", /* 127 */ "setrlimit", /* 128 */ "getrlimit", /* 129 */ "lchown", /* 130 */ "memcntl", /* 131 */ "getpmsg", /* 132 */ "putpmsg", /* 133 */ "rename", /* 134 */ "uname", /* 135 */ "setegid", /* 136 */ "sysconfig", /* 137 */ "adjtime", /* 138 */ "systeminfo", /* 139 */ "sharefs", /* 140 */ "seteuid", /* 141 */ "forksys", /* 142 */ NULL, /* 143 */ "sigtimedwait", /* 144 */ "lwp_info", /* 145 */ "yield", /* 146 */ NULL, /* 147 */ "lwp_sema_post", /* 148 */ "lwp_sema_trywait", /* 149 */ "lwp_detatch", /* 150 */ "corectl", /* 151 */ "modctl", /* 152 */ "fchroot", /* 153 */ NULL, /* 154 */ "vhangup", /* 155 */ "gettimeofday", /* 156 */ "getitimer", /* 157 */ "setitimer", /* 158 */ "lwp_create", /* 159 */ "lwp_exit", /* 160 */ "lwp_suspend", /* 161 */ "lwp_continue", /* 162 */ "lwp_kill", /* 163 */ "lwp_self", /* 164 */ "lwp_sigmask", /* 165 */ "lwp_private", /* 166 */ "lwp_wait", /* 167 */ "lwp_mutex_wakeup", /* 168 */ NULL, /* 169 */ "lwp_cond_wait", /* 170 */ "lwp_cond_signal", /* 171 */ "lwp_cond_broadcast", /* 172 */ "pread", /* 173 */ "pwrite", /* 174 */ "llseek", /* 175 */ "inst_sync", /* 176 */ "brand", /* 177 */ "kaio", /* 178 */ "cpc", /* 179 */ "lgrpsys", /* 180 */ "rusagesys", /* 181 */ "portfs", /* 182 */ "pollsys", /* 183 */ "labelsys", /* 184 */ "acl", /* 185 */ "auditsys", /* 186 */ "processor_bind", /* 187 */ "processor_info", /* 188 */ "p_online", /* 189 */ "sigqueue", /* 190 */ "clock_gettime", /* 191 */ "clock_settime", /* 192 */ "clock_getres", /* 193 */ "timer_create", /* 194 */ "timer_delete", /* 195 */ "timer_settime", /* 196 */ "timer_gettime", /* 197 */ "timer_getoverrun", /* 198 */ "nanosleep", /* 199 */ "facl", /* 200 */ "door", /* 201 */ "setreuid", /* 202 */ "setregid", /* 203 */ "install_utrap", /* 204 */ "signotify", /* 205 */ "schedctl", /* 206 */ "pset", /* 207 */ "sparc_utrap_install", /* 208 */ "resolvepath", /* 209 */ "lwp_mutex_timedlock", /* 210 */ "lwp_sema_timedwait", /* 211 */ "lwp_rwlock_sys", /* 212 */ "getdents64", /* 213 */ "mmap64", /* 214 */ "stat64", /* 215 */ "lstat64", /* 216 */ "fstat64", /* 217 */ "statvfs64", /* 218 */ "fstatvfs64", /* 219 */ "setrlimit64", /* 220 */ "getrlimit64", /* 221 */ "pread64", /* 222 */ "pwrite64", /* 223 */ NULL, /* 224 */ "open64", /* 225 */ "rpcmod", /* 226 */ "zone", /* 227 */ "autofssys", /* 228 */ "getcwd", /* 229 */ "so_socket", /* 230 */ "so_socketpair", /* 231 */ "bind", /* 232 */ "listen", /* 233 */ "accept", /* 234 */ "connect", /* 235 */ "shutdown", /* 236 */ "recv", /* 237 */ "recvfrom", /* 238 */ "recvmsg", /* 239 */ "send", /* 240 */ "sendmsg", /* 241 */ "sendto", /* 242 */ "getpeername", /* 243 */ "getsockname", /* 244 */ "getsockopt", /* 245 */ "setsockopt", /* 246 */ "sockconfig", /* 247 */ "ntp_gettime", /* 248 */ "ntp_adjtime", /* 249 */ "lwp_mutex_unlock", /* 250 */ "lwp_mutex_trylock", /* 251 */ "lwp_mutex_register", /* 252 */ "cladm", /* 253 */ "uucopy", /* 254 */ "umount2" /* 255 */ }; /* SYSEND == max syscall number + 1 */ #define SYSEND (sizeof (systable) / sizeof (systable[0])) /* * Return the name of a system call. * Manufacture a name for unknown system call. */ char * proc_sysname(int sys, char *buf, size_t bufsz) { const char *name; size_t len; if (bufsz == 0) /* force a program failure */ return (NULL); if (sys >= 0 && sys < SYSEND) name = systable[sys]; else name = NULL; if (name != NULL) { len = strlen(name); (void) strncpy(buf, name, bufsz); } else { len = snprintf(buf, bufsz, "SYS#%d", sys); } if (len >= bufsz) /* ensure null-termination */ buf[bufsz-1] = '\0'; return (buf); } /* * Convert a string representation of a fault to the corresponding number. */ int proc_str2flt(const char *str, int *fltnum) { char *next; int i; i = strtol(str, &next, 0); if (i > 0 && i <= PRMAXFAULT && *next == '\0') { *fltnum = i; return (0); } for (i = 1; i <= PRMAXFAULT; i++) { const char *s = rawfltname(i); if (s && (strcasecmp(s, str) == 0 || strcasecmp(s + 3, str) == 0)) { *fltnum = i; return (0); } } return (-1); } /* * Convert a string representation of a signal to the signal number. This * functionality is already available in libc, but the interface doesn't * optionally accept a "SIG" prefix. We strip that first, and then call libc. */ int proc_str2sig(const char *str, int *signum) { if (strncasecmp(str, "SIG", 3) == 0) str += 3; /* skip prefix */ return (str2sig(str, signum)); } /* * Convert a string representation of a system call to the corresponding number. * We do this by performing a simple linear search of the table above. */ int proc_str2sys(const char *str, int *sysnum) { char *next; int i; i = strtol(str, &next, 0); if (i > 0 && i <= PRMAXSYS && *next == '\0') { *sysnum = i; return (0); } for (i = 1; i < SYSEND; i++) { if (systable[i] != NULL && strcmp(systable[i], str) == 0) { *sysnum = i; return (0); } } return (-1); } /* * Convert a fltset_t to a string representation consisting of canonical * machine fault names separated by the given delimeter string. If * m is non-zero (TRUE), set members are printed. If m is zero (FALSE), set * non-members are printed. If the specified buf is too small to hold the * complete formatted set, NULL is returned; otherwise buf is returned. */ char * proc_fltset2str(const fltset_t *set, const char *delim, int m, char *buf, size_t len) { char name[FLT2STR_MAX], *p = buf; size_t n; int i; if (buf == NULL || len < 1) { errno = EINVAL; return (NULL); } buf[0] = '\0'; /* Set first byte to \0 */ for (i = 1; i <= PRMAXFAULT; i++) { if ((prismember(set, i) != 0) ^ (m == 0)) { (void) proc_fltname(i, name, sizeof (name)); if (buf[0] != '\0') n = snprintf(p, len, "%s%s", delim, name); else n = snprintf(p, len, "%s", name); if (n != strlen(p)) { errno = ENAMETOOLONG; /* Output was truncated */ return (NULL); } len -= n; p += n; } } return (buf); } /* * Convert a sigset_t to a string representation consisting of canonical signal * names (without the SIG prefix). Parameters and return values analogous to * proc_fltset2str(). */ char * proc_sigset2str(const sigset_t *set, const char *delim, int m, char *buf, size_t len) { char name[SIG2STR_MAX], *p = buf; size_t n; int i; if (buf == NULL || len < 1) { errno = EINVAL; return (NULL); } m = (m != 0); /* Make sure m is 0 or 1 */ buf[0] = '\0'; /* Set first byte to \0 */ /* * Unlike proc_fltset2str() and proc_sysset2str(), we don't loop * until i <= NSIG here, because sigismember() rejects i == NSIG. */ for (i = 1; i < NSIG; i++) { if (sigismember(set, i) == m) { (void) sig2str(i, name); if (buf[0] != '\0') n = snprintf(p, len, "%s%s", delim, name); else n = snprintf(p, len, "%s", name); if (n != strlen(p)) { errno = ENAMETOOLONG; /* Output was truncated */ return (NULL); } len -= n; p += n; } } return (buf); } /* * Convert a sysset_t to a string representation consisting of canonical system * call names. Parameters and return values analogous to proc_fltset2str(). */ char * proc_sysset2str(const sysset_t *set, const char *delim, int m, char *buf, size_t len) { char name[SYS2STR_MAX], *p = buf; size_t n; int i; if (buf == NULL || len < 1) { errno = EINVAL; return (NULL); } buf[0] = '\0'; /* Set first byte to \0 */ for (i = 1; i <= PRMAXSYS; i++) { if ((prismember(set, i) != 0) ^ (m == 0)) { (void) proc_sysname(i, name, sizeof (name)); if (buf[0] != '\0') n = snprintf(p, len, "%s%s", delim, name); else n = snprintf(p, len, "%s", name); if (n != strlen(p)) { errno = ENAMETOOLONG; /* Output was truncated */ return (NULL); } len -= n; p += n; } } return (buf); } /* * Convert a string representation of a fault set (names separated by * one or more of the given delimeters) to a fltset_t. * If m is non-zero (TRUE), members of the string representation are set. * If m is zero (FALSE), non-members of the string representation are set. * This function returns NULL for success. Otherwise it returns a pointer * to the token of the string that couldn't be identified as a string * representation of a fault. */ char * proc_str2fltset(const char *s, const char *delim, int m, fltset_t *set) { char *p, *q, *t; int flt; if (m) { premptyset(set); } else { prfillset(set); } t = strdupa(s); for (p = strtok_r(t, delim, &q); p != NULL; p = strtok_r(NULL, delim, &q)) { if (proc_str2flt(p, &flt) == -1) { errno = EINVAL; return ((char *)s + (p - t)); } if (m) praddset(set, flt); else prdelset(set, flt); } return (NULL); } /* * Convert a string representation of a signal set (names with or without the * SIG prefix separated by one or more of the given delimeters) to a sigset_t. * Parameters and return values analogous to proc_str2fltset(). */ char * proc_str2sigset(const char *s, const char *delim, int m, sigset_t *set) { char *p, *q, *t; int sig; if (m) { premptyset(set); } else { prfillset(set); } t = strdupa(s); for (p = strtok_r(t, delim, &q); p != NULL; p = strtok_r(NULL, delim, &q)) { if (proc_str2sig(p, &sig) == -1) { errno = EINVAL; return ((char *)s + (p - t)); } if (m) praddset(set, sig); else prdelset(set, sig); } return (NULL); } /* * Convert a string representation of a system call set (names separated by * one or more of the given delimeters) to a sysset_t. Parameters and return * values analogous to proc_str2fltset(). */ char * proc_str2sysset(const char *s, const char *delim, int m, sysset_t *set) { char *p, *q, *t; int sys; if (m) { premptyset(set); } else { prfillset(set); } t = strdupa(s); for (p = strtok_r(t, delim, &q); p != NULL; p = strtok_r(NULL, delim, &q)) { if (proc_str2sys(p, &sys) == -1) { errno = EINVAL; return ((char *)s + (p - t)); } if (m) praddset(set, sys); else prdelset(set, sys); } return (NULL); } /* * Returns a string representation of a process data model. * See for possible values. */ char * proc_dmodelname(int dmodel, char *buf, size_t bufsz) { static const char *const dmdls[] = { "PR_MODEL_UNKNOWN", "PR_MODEL_ILP32", "PR_MODEL_LP64", NULL }; size_t len; if (bufsz == 0) return (NULL); if (dmodel > PR_MODEL_LP64 || dmodel < PR_MODEL_UNKNOWN) { len = snprintf(buf, bufsz, "DMODEL#%d", dmodel); } else { len = strlen(dmdls[dmodel]); (void) strncpy(buf, dmdls[dmodel], bufsz); } if (len >= bufsz) buf[bufsz-1] = '\0'; return (buf); }