From 5fd83771641d15c418f747bd343ba6738d3875f7 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Sun, 9 May 2021 14:20:58 -0400 Subject: Import macOS userland adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106 --- system_cmds/lskq.tproj/lskq.c | 963 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 963 insertions(+) create mode 100644 system_cmds/lskq.tproj/lskq.c (limited to 'system_cmds/lskq.tproj/lskq.c') diff --git a/system_cmds/lskq.tproj/lskq.c b/system_cmds/lskq.tproj/lskq.c new file mode 100644 index 0000000..a48bb26 --- /dev/null +++ b/system_cmds/lskq.tproj/lskq.c @@ -0,0 +1,963 @@ +/* + * Copyright (c) 2015-2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#define PRIVATE +#include +#undef PRIVATE +#include +#include + +#include "common.h" + +#define ARRAYLEN(x) (sizeof((x))/sizeof((x[0]))) + +/* command line options */ +static int verbose; +static int all_pids; +static int ignore_empty; +static int raw; + +static char *self = "lskq"; + +static inline const char * +filt_name(int16_t filt) +{ + static char unkn_filt[32]; + int idx = -filt; + if (idx >= 0 && idx < ARRAYLEN(filt_strs)) { + return filt_strs[idx]; + } else { + snprintf(unkn_filt, sizeof(unkn_filt), "%i (?)", idx); + return unkn_filt; + } +} + +static inline const char * +fdtype_str(uint32_t type) +{ + static char unkn_fdtype[32]; + if (type < ARRAYLEN(fdtype_strs)) { + return fdtype_strs[type]; + } else { + snprintf(unkn_fdtype, sizeof(unkn_fdtype), "%i (?)", type); + return unkn_fdtype; + } +} + +static char * +fflags_build(struct kevent_extinfo *info, char *str, int len) +{ + unsigned ff = info->kqext_sfflags; + + switch (info->kqext_kev.filter) { + + case EVFILT_READ: { + snprintf(str, len, "%c ", + (ff & NOTE_LOWAT) ? 'l' : '-' + ); + break; + } + + case EVFILT_MACHPORT: { + snprintf(str, len, "%c ", + (ff & MACH_RCV_MSG) ? 'r' : '-' + ); + break; + } + + case EVFILT_VNODE: { + snprintf(str, len, "%c%c%c%c%c%c%c%c", + (ff & NOTE_DELETE) ? 'd' : '-', + (ff & NOTE_WRITE) ? 'w' : '-', + (ff & NOTE_EXTEND) ? 'e' : '-', + (ff & NOTE_ATTRIB) ? 'a' : '-', + (ff & NOTE_LINK) ? 'l' : '-', + (ff & NOTE_RENAME) ? 'r' : '-', + (ff & NOTE_REVOKE) ? 'v' : '-', + (ff & NOTE_FUNLOCK) ? 'u' : '-' + ); + break; + } + + case EVFILT_PROC: { +/* NOTE_REAP is deprecated, but we still want to show if it's used */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + snprintf(str, len, "%c%c%c%c%c%c%c", + (ff & NOTE_EXIT) ? 'x' : '-', + (ff & NOTE_EXITSTATUS) ? 't' : '-', + (ff & NOTE_EXIT_DETAIL)? 'd' : '-', + (ff & NOTE_FORK) ? 'f' : '-', + (ff & NOTE_EXEC) ? 'e' : '-', + (ff & NOTE_SIGNAL) ? 's' : '-', + (ff & NOTE_REAP) ? 'r' : '-' + ); + break; +#pragma clang diagnostic pop + } + + case EVFILT_TIMER: { + snprintf(str, len, "%c%c%c%c%c ", + (ff & NOTE_SECONDS) ? 's' : + (ff & NOTE_USECONDS) ? 'u' : + (ff & NOTE_NSECONDS) ? 'n' : + (ff & NOTE_MACHTIME) ? 'm' : '?', + (ff & NOTE_ABSOLUTE) ? 'a' : + (ff & NOTE_MACH_CONTINUOUS_TIME) ? 'A' : '-', + (ff & NOTE_CRITICAL) ? 'c' : '-', + (ff & NOTE_BACKGROUND) ? 'b' : '-', + (ff & NOTE_LEEWAY) ? 'l' : '-' + ); + break; + } + + case EVFILT_USER: + snprintf(str, len, "%c%c%c ", + (ff & NOTE_TRIGGER) ? 't' : '-', + (ff & NOTE_FFAND) ? 'a' : '-', + (ff & NOTE_FFOR) ? 'o' : '-' + ); + break; + + case EVFILT_WORKLOOP: + snprintf(str, len, "%c%c%c%c%c ", + (ff & NOTE_WL_THREAD_REQUEST) ? 't' : + (ff & NOTE_WL_SYNC_WAIT) ? 'w' : + (ff & NOTE_WL_SYNC_IPC) ? 'i' : '-', + (ff & NOTE_WL_SYNC_WAKE) ? 'W' : '-', + (ff & NOTE_WL_UPDATE_QOS) ? 'q' : '-', + (ff & NOTE_WL_DISCOVER_OWNER) ? 'o' : '-', + (ff & NOTE_WL_IGNORE_ESTALE) ? 'e' : '-' + ); + break; + + default: + snprintf(str, len, ""); + break; + }; + + return str; +} + + +static inline int +filter_is_fd_type(int filter) +{ + switch (filter) { + case EVFILT_VNODE ... EVFILT_READ: + case EVFILT_SOCK: + case EVFILT_NW_CHANNEL: + return 1; + default: + return 0; + } +} + +static const char * +thread_qos_name(uint8_t th_qos) +{ + switch (th_qos) { + case 0: return "--"; + case 1: return "MT"; + case 2: return "BG"; + case 3: return "UT"; + case 4: return "DF"; + case 5: return "IN"; + case 6: return "UI"; + case 7: return "MG"; + default: return "??"; + } +} + +/* + * find index of fd in a list of fdinfo of length nfds + */ +static inline int +fd_list_getfd(struct proc_fdinfo *fds, int nfds, int fd) +{ + int i; + for (i = 0; i < nfds; i++) { + if (fds[i].proc_fd == fd) { + return i; + } + } + + return -1; +} + +/* + * left truncate URL-form process names + */ +static const char * +shorten_procname(const char *proc, int width) +{ + if (strcasestr(proc, "com.") == proc) { + long len = strlen(proc); + if (len > width) { + return &proc[len - width]; + } else { + return proc; + } + } else { + return proc; + } +} + +/* + * stringify knote ident where possible (signals, processes) + */ +static void +print_ident(uint64_t ident, int16_t filter, int width) +{ + if (raw) { + printf("%#*llx ", width, ident); + return; + } + + switch (filter) { + + case EVFILT_SIGNAL: + case EVFILT_PROC: { + char str[128] = ""; + char num[128]; + char out[128]; + int numlen = sprintf(num, "%llu", ident); + int strwidth = width - numlen - 1; // add room for a space + + if (filter == EVFILT_SIGNAL) { + if (ident < ARRAYLEN(sig_strs)) { + snprintf(str, strwidth + 1, "%s", sig_strs[ident]); + } + } else { + /* FIXME: this should be cached */ + struct proc_bsdinfo bsdinfo; + int ret = proc_pidinfo((int)ident, PROC_PIDTBSDINFO, 0, &bsdinfo, sizeof(bsdinfo)); + if (ret == sizeof(bsdinfo)) { + char *procname = bsdinfo.pbi_name; + if (strlen(procname) == 0) { + procname = bsdinfo.pbi_comm; + } + snprintf(str, strwidth + 1, "%s", shorten_procname(procname, strwidth)); + } + } + + if (str[0] != '\0') { + snprintf(out, width + 1, "%-*s %s", strwidth, str, num); + } else { + snprintf(out, width + 1, "%s", num); + } + + printf("%*s ", width, out); + break; + } + + case EVFILT_MACHPORT: + case EVFILT_TIMER: + /* hex, to match lsmp */ + printf("%#*llx ", width, ident); + break; + + case EVFILT_WORKLOOP: + printf("%#*llx ", width, ident); + break; + + default: + printf("%*llu ", width, ident); + break; + } + +} + +static void +print_kqid(int state, uint64_t kqid) +{ + if (state & KQ_WORKQ) { + printf("%18s ", "wq"); + } else if (state & KQ_WORKLOOP) { + printf("%#18" PRIx64 " ", kqid); + } else { + printf("fd %15" PRIi64 " ", kqid); + } +} + +#define PROCNAME_WIDTH 20 + +static void +print_kq_info(int pid, const char *procname, uint64_t kqid, int state) +{ + if (raw) { + printf("%5u ", pid); + print_kqid(state, kqid); + printf("%#10x ", state); + } else { + char tmpstr[PROCNAME_WIDTH+1]; + strlcpy(tmpstr, shorten_procname(procname, PROCNAME_WIDTH), PROCNAME_WIDTH+1); + printf("%-*s ", PROCNAME_WIDTH, tmpstr); + printf("%5u ", pid); + print_kqid(state, kqid); + printf(" %c%c%c ", + (state & KQ_SLEEP) ? 'k' : '-', + (state & KQ_SEL) ? 's' : '-', + (state & KQ_WORKQ) ? 'q' : + (state & KQ_WORKLOOP) ? 'l' : '-' + ); + } +} + +enum kqtype { + KQTYPE_FD, + KQTYPE_DYNAMIC +}; + +#define POLICY_TIMESHARE 1 +#define POLICY_RR 2 +#define POLICY_FIFO 4 + +static int +process_kqueue(int pid, const char *procname, enum kqtype type, uint64_t kqid, + struct proc_fdinfo *fdlist, int nfds) +{ + int ret, i, nknotes; + char tmpstr[256]; + int maxknotes = 256; /* arbitrary starting point */ + int kq_state; + bool is_kev_64, is_kev_qos; + int err = 0; + bool overflow = false; + int fd; + bool dynkq_printed = false; + + /* + * get the basic kqueue info + */ + struct kqueue_fdinfo kqfdinfo = {}; + struct kqueue_dyninfo kqinfo = {}; + switch (type) { + case KQTYPE_FD: + ret = proc_pidfdinfo(pid, (int)kqid, PROC_PIDFDKQUEUEINFO, &kqfdinfo, sizeof(kqfdinfo)); + fd = (int)kqid; + break; + case KQTYPE_DYNAMIC: + ret = proc_piddynkqueueinfo(pid, PROC_PIDDYNKQUEUE_INFO, kqid, &kqinfo, sizeof(kqinfo)); + break; + default: + os_crash("invalid kqueue type"); + } + + if (type == KQTYPE_FD && (int)kqid != -1) { + if (ret != sizeof(kqfdinfo)) { + /* every proc has an implicit workq kqueue, dont warn if its unused */ + fprintf(stderr, "WARN: FD table changed (pid %i, kq %i)\n", pid, + fd); + } + } else if (type == KQTYPE_DYNAMIC) { + if (ret < sizeof(struct kqueue_info)) { + fprintf(stderr, "WARN: kqueue missing (pid %i, kq %#" PRIx64 ")\n", + pid, kqid); + } else { + kqfdinfo.kqueueinfo = kqinfo.kqdi_info; + } + if (verbose && ret >= sizeof(struct kqueue_dyninfo)) { + print_kq_info(pid, procname, kqid, kqinfo.kqdi_info.kq_state); + + if (kqinfo.kqdi_owner) { + printf("%#18llx ", kqinfo.kqdi_owner); // ident + printf("%-9s ", "WL owned"); // filter + } else if (kqinfo.kqdi_servicer) { + printf("%#18llx ", kqinfo.kqdi_servicer); // ident + printf("%-9s ", "WL"); // filter + } else { + printf("%18s ", "-"); // ident + printf("%-9s ", "WL"); // filter + } + dynkq_printed = true; + + if (raw) { + printf("%-10s ", " "); // fflags + printf("%-10s ", " "); // flags + printf("%-10s ", " "); // evst + } else { + const char *reqstate = "???"; + + switch (kqinfo.kqdi_request_state) { + case WORKQ_TR_STATE_IDLE: + reqstate = ""; + break; + case WORKQ_TR_STATE_NEW: + reqstate = "new"; + break; + case WORKQ_TR_STATE_QUEUED: + reqstate = "queued"; + break; + case WORKQ_TR_STATE_CANCELED: + reqstate = "canceled"; + break; + case WORKQ_TR_STATE_BINDING: + reqstate = "binding"; + break; + case WORKQ_TR_STATE_BOUND: + reqstate = "bound"; + break; + } + + printf("%-8s ", reqstate); // fdtype + char policy_type; + switch (kqinfo.kqdi_pol) { + case POLICY_RR: + policy_type = 'R'; + break; + case POLICY_FIFO: + policy_type = 'F'; + case POLICY_TIMESHARE: + case 0: + default: + policy_type = '-'; + break; + } + snprintf(tmpstr, 4, "%c%c%c", (kqinfo.kqdi_pri == 0)?'-':'P', policy_type, (kqinfo.kqdi_cpupercent == 0)?'-':'%'); + printf("%-7s ", tmpstr); // fflags + printf("%-15s ", " "); // flags + printf("%-15s ", " "); // evst + } + + if (!raw && kqinfo.kqdi_pri != 0) { + printf("%3d ", kqinfo.kqdi_pri); //qos + } else { + int qos = MAX(MAX(kqinfo.kqdi_events_qos, kqinfo.kqdi_async_qos), + kqinfo.kqdi_sync_waiter_qos); + printf("%3s ", thread_qos_name(qos)); //qos + } + printf("\n"); + } + } + + /* + * get extended kqueue info + */ + struct kevent_extinfo *kqextinfo = NULL; + again: + if (!kqextinfo) { + kqextinfo = malloc(sizeof(struct kevent_extinfo) * maxknotes); + } + if (!kqextinfo) { + err = errno; + perror("failed allocating memory"); + goto out; + } + + errno = 0; + switch (type) { + case KQTYPE_FD: + nknotes = proc_pidfdinfo(pid, fd, PROC_PIDFDKQUEUE_EXTINFO, + kqextinfo, sizeof(struct kevent_extinfo) * maxknotes); + break; + case KQTYPE_DYNAMIC: + nknotes = proc_piddynkqueueinfo(pid, PROC_PIDDYNKQUEUE_EXTINFO, kqid, + kqextinfo, sizeof(struct kevent_extinfo) * maxknotes); + break; + default: + os_crash("invalid kqueue type"); + } + + if (nknotes <= 0) { + if (errno == 0) { + /* proc_*() can't distinguish between error and empty list */ + } else if (errno == EAGAIN) { + goto again; + } else if (errno == EBADF) { + fprintf(stderr, "WARN: FD table changed (pid %i, kq %#" PRIx64 ")\n", pid, kqid); + goto out; + } else { + err = errno; + perror("failed to get extended kqueue info"); + goto out; + } + } + + if (nknotes > maxknotes) { + maxknotes = nknotes + 16; /* arbitrary safety margin */ + free(kqextinfo); + kqextinfo = NULL; + goto again; + } + + if (nknotes >= PROC_PIDFDKQUEUE_KNOTES_MAX) { + overflow = true; + } + + kq_state = kqfdinfo.kqueueinfo.kq_state; + is_kev_64 = (kq_state & PROC_KQUEUE_64); + is_kev_qos = (kq_state & PROC_KQUEUE_QOS); + + if (nknotes == 0) { + if (!ignore_empty && !dynkq_printed) { + /* for empty kqueues, print a single empty entry */ + print_kq_info(pid, procname, kqid, kq_state); + printf("%18s \n", "-"); + } + goto out; + } + + for (i = 0; i < nknotes; i++) { + struct kevent_extinfo *info = &kqextinfo[i]; + + print_kq_info(pid, procname, kqid, kqfdinfo.kqueueinfo.kq_state); + print_ident(info->kqext_kev.ident, info->kqext_kev.filter, 18); + printf("%-9s ", filt_name(info->kqext_kev.filter)); + + if (raw) { + printf("%#10x ", info->kqext_sfflags); + printf("%#10x ", info->kqext_kev.flags); + printf("%#10x ", info->kqext_status); + } else { + /* for kevents attached to file descriptors, print the type of FD (file, socket, etc) */ + const char *fdstr = ""; + if (filter_is_fd_type(info->kqext_kev.filter)) { + fdstr = ""; + int knfd = fd_list_getfd(fdlist, nfds, (int)info->kqext_kev.ident); + if (knfd >= 0) { + fdstr = fdtype_str(fdlist[knfd].proc_fdtype); + } + } + printf("%-8s ", fdstr); + + /* print filter flags */ + printf("%7s ", fflags_build(info, tmpstr, sizeof(tmpstr))); + + /* print generic flags */ + unsigned flg = info->kqext_kev.flags; + printf("%c%c%c%c %c%c%c%c %c%c%c%c%c ", + (flg & EV_ADD) ? 'a' : '-', + (flg & EV_ENABLE) ? 'n' : '-', + (flg & EV_DISABLE) ? 'd' : '-', + (flg & EV_DELETE) ? 'x' : '-', + + (flg & EV_RECEIPT) ? 'r' : '-', + (flg & EV_ONESHOT) ? '1' : '-', + (flg & EV_CLEAR) ? 'c' : '-', + (flg & EV_DISPATCH) ? 's' : '-', + + (flg & EV_UDATA_SPECIFIC) ? 'u' : '-', + (flg & EV_FLAG0) ? 'p' : '-', + (flg & EV_FLAG1) ? 'b' : '-', + (flg & EV_EOF) ? 'o' : '-', + (flg & EV_ERROR) ? 'e' : '-' + ); + + unsigned st = info->kqext_status; + printf("%c%c%c%c%c %c%c%c%c %c%c%c ", + (st & KN_ACTIVE) ? 'a' : '-', + (st & KN_QUEUED) ? 'q' : '-', + (st & KN_DISABLED) ? 'd' : '-', + (st & KN_SUPPRESSED) ? 'p' : '-', + (st & KN_STAYACTIVE) ? 's' : '-', + + (st & KN_DROPPING) ? 'd' : '-', + (st & KN_LOCKED) ? 'l' : '-', + (st & KN_POSTING) ? 'P' : '-', + (st & KN_MERGE_QOS) ? 'm' : '-', + + (st & KN_DEFERDELETE) ? 'D' : '-', + (st & KN_REQVANISH) ? 'v' : '-', + (st & KN_VANISHED) ? 'n' : '-' + ); + } + + printf("%3s ", thread_qos_name(info->kqext_kev.qos)); + + printf("%#18llx ", (unsigned long long)info->kqext_kev.data); + + if (verbose) { + printf("%#18llx ", (unsigned long long)info->kqext_kev.udata); + if (is_kev_qos || is_kev_64) { + printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[0]); + printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[1]); + + if (is_kev_qos) { + printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[2]); + printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[3]); + printf("%#10lx ", (unsigned long)info->kqext_kev.xflags); + } + } + } + + printf("\n"); + } + + if (overflow) { + printf(" ***** output truncated (>=%i knotes on kq %" PRIu64 ", proc %i) *****\n", + nknotes, kqid, pid); + } + + out: + if (kqextinfo) { + free(kqextinfo); + kqextinfo = NULL; + } + + return err; +} + +static int +pid_kqids(pid_t pid, kqueue_id_t **kqids_out) +{ + static int kqids_len = 256; + static kqueue_id_t *kqids = NULL; + static uint32_t kqids_size; + + int nkqids; + +retry: + if (os_mul_overflow(sizeof(kqueue_id_t), kqids_len, &kqids_size)) { + assert(kqids_len > PROC_PIDDYNKQUEUES_MAX); + kqids_len = PROC_PIDDYNKQUEUES_MAX; + goto retry; + } + if (!kqids) { + kqids = malloc(kqids_size); + os_assert(kqids != NULL); + } + + nkqids = proc_list_dynkqueueids(pid, kqids, kqids_size); + if (nkqids > kqids_len && kqids_len < PROC_PIDDYNKQUEUES_MAX) { + kqids_len *= 2; + if (kqids_len > PROC_PIDDYNKQUEUES_MAX) { + kqids_len = PROC_PIDDYNKQUEUES_MAX; + } + free(kqids); + kqids = NULL; + goto retry; + } + + *kqids_out = kqids; + return MIN(nkqids, kqids_len); +} + +static int +process_pid(pid_t pid) +{ + int i, nfds, nkqids; + kqueue_id_t *kqids; + int ret = 0; + int maxfds = 256; /* arbitrary starting point */ + struct proc_fdinfo *fdlist = NULL; + + /* enumerate file descriptors */ + again: + if (!fdlist) { + fdlist = malloc(sizeof(struct proc_fdinfo) * maxfds); + } + if (!fdlist) { + ret = errno; + perror("failed to allocate"); + goto out; + } + + nfds = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdlist, + sizeof(struct proc_fdinfo) * maxfds); + if (nfds <= 0) { + ret = errno; + fprintf(stderr, "%s: failed enumerating file descriptors of process %i: %s", + self, pid, strerror(ret)); + if (ret == EPERM && geteuid() != 0) { + fprintf(stderr, " (are you root?)"); + } + fprintf(stderr, "\n"); + goto out; + } + + nfds /= sizeof(struct proc_fdinfo); + if (nfds >= maxfds) { + maxfds = nfds + 16; + free(fdlist); + fdlist = NULL; + goto again; + } + + /* get bsdinfo for the process name */ + struct proc_bsdinfo bsdinfo; + ret = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &bsdinfo, sizeof(bsdinfo)); + if (ret != sizeof(bsdinfo)) { + perror("failed retrieving process info"); + ret = -1; + goto out; + } + + char *procname = bsdinfo.pbi_name; + if (strlen(procname) == 0) { + procname = bsdinfo.pbi_comm; + } + + /* handle the special workq kq */ + ret = process_kqueue(pid, procname, KQTYPE_FD, -1, fdlist, nfds); + if (ret) { + goto out; + } + + for (i = 0; i < nfds; i++) { + if (fdlist[i].proc_fdtype == PROX_FDTYPE_KQUEUE) { + ret = process_kqueue(pid, procname, KQTYPE_FD, + (uint64_t)fdlist[i].proc_fd, fdlist, nfds); + if (ret) { + goto out; + } + } + } + + nkqids = pid_kqids(pid, &kqids); + + for (i = 0; i < nkqids; i++) { + ret = process_kqueue(pid, procname, KQTYPE_DYNAMIC, kqids[i], fdlist, nfds); + if (ret) { + goto out; + } + } + + if (nkqids >= PROC_PIDDYNKQUEUES_MAX) { + printf(" ***** output truncated (>=%i dynamic kqueues in proc %i) *****\n", + nkqids, pid); + } + + out: + if (fdlist) { + free(fdlist); + fdlist = NULL; + } + + return ret; +} + +static int +process_all_pids(void) +{ + int i, npids; + int ret = 0; + int maxpids = 2048; + int *pids = NULL; + + again: + if (!pids) { + pids = malloc(sizeof(int) * maxpids); + } + if (!pids) { + perror("failed allocating pids[]"); + goto out; + } + + errno = 0; + npids = proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(int) * maxpids); + if (npids <= 0) { + if (errno == 0) { + /* empty pid list */ + } else if (errno == EAGAIN) { + goto again; + } else { + ret = errno; + perror("failed enumerating pids"); + goto out; + } + } + + npids /= sizeof(int); + if (npids >= maxpids) { + maxpids = npids + 16; + free(pids); + pids = NULL; + goto again; + } + + for (i = 0; i < npids; i++) { + /* listpids gives us pid 0 for some reason */ + if (pids[i]) { + ret = process_pid(pids[i]); + /* ignore races with processes exiting */ + if (ret && ret != ESRCH) { + goto out; + } + } + } + +out: + if (pids) { + free(pids); + pids = NULL; + } + + return ret; +} + +static void +cheatsheet(void) +{ + const char *bold = "\033[1m"; + const char *reset = "\033[0m"; + if (!isatty(STDERR_FILENO)) { + bold = reset = ""; + } + + fprintf(stderr, "\nFilter-independent flags:\n\n\ +%s\ +command pid kq kqst knid filter fdtype fflags flags evst qos%s\n%s\ +-------------------- ----- ------------------ ---- ------------------ --------- -------- ------- --------------- -------------- ---%s\n\ + ┌ EV_UDATA_SPECIFIC\n\ + EV_DISPATCH ┐ │┌ EV_FLAG0 (EV_POLL)\n\ + EV_CLEAR ┐│ ││┌ EV_FLAG1 (EV_OOBAND)\n\ + EV_ONESHOT ┐││ │││┌ EV_EOF\n\ + EV_RECEIPT ┐│││ ││││┌ EV_ERROR\n\ + ││││ │││││\n%s\ +launchd 1 4 ks- netbiosd 250 PROC ------- andx r1cs upboe aqdps dlPm Dvn IN%s\n\ + │ │││ ││││ │││││ ││││ │││\n\ + kqueue file descriptor/dynamic ID ┘ │││ EV_ADD ┘│││ KN_ACTIVE ┘││││ ││││ ││└ KN_VANISHED\n\ + KQ_SLEEP ┘││ EV_ENABLE ┘││ KN_QUEUED ┘│││ ││││ │└ KN_REQVANISH\n\ + KQ_SEL ┘│ EV_DISABLE ┘│ KN_DISABLED ┘││ ││││ └ KN_DEFERDELETE\n\ + KQ_WORKQ (q) ┤ EV_DELETE ┘ KN_SUPPRESSED ┘│ ││││\n\ + KQ_WORKLOOP (l) ┘ KN_STAYACTIVE ┘ ││││\n\ + ││││\n\ + KN_DROPPING ┘││└ KN_MERGE_QOS\n\ + KN_LOCKED ┘└ KN_POSTING\n\ + \n", bold, reset, bold, reset, bold, reset); +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-vher] [-a | -p ]\n", self); +} + +static void +print_header(void) +{ + if (raw) { + printf(" pid kq kqst knid filter fflags flags evst qos data"); + } else { + printf("command pid kq kqst knid filter fdtype fflags flags evst qos data"); + } + + if (verbose) { + printf(" udata ext0 ext1 ext2 ext3 xflags"); + } + + printf("\n"); + + if (raw) { + printf("----- ------------------ ---------- ------------------ --------- ---------- ---------- ---------- --- ------------------"); + } else { + printf("-------------------- ----- ------------------ ---- ------------------ --------- -------- ------- --------------- -------------- --- ------------------"); + } + + if (verbose) { + printf(" ------------------ ------------------ ------------------ ------------------ ------------------ ----------"); + } + printf("\n"); +} + +int +main(int argc, char *argv[]) +{ + pid_t pid = 0; + int opt; + + setlinebuf(stdout); + + if (argc > 0) { + self = argv[0]; + } + + while ((opt = getopt(argc, argv, "eahvrp:")) != -1) { + switch (opt) { + case 'a': + all_pids = 1; + break; + case 'v': + verbose++; + break; + case 'p': + pid = atoi(optarg); + break; + case 'e': + ignore_empty = 1; + break; + case 'h': + usage(); + cheatsheet(); + return 0; + case 'r': + raw = 1; + break; + case '?': + default: + usage(); + return 1; + } + } + + argc -= optind; + argv += optind; + + if (argc == 1) { + /* also allow lskq */ + if (pid || all_pids) { + usage(); + return 1; + } + + pid = atoi(argv[0]); + } else if (argc > 1) { + usage(); + return 1; + } + + /* exactly one of -p or -a is required */ + if (!pid && !all_pids) { + usage(); + return 1; + } else if (pid && all_pids) { + usage(); + return 1; + } + + print_header(); + + if (all_pids) { + return process_all_pids(); + } else { + return process_pid(pid); + } + + return 0; +} -- cgit v1.2.3-56-ge451