]> git.saurik.com Git - apple/system_cmds.git/blob - lskq.tproj/lskq.c
system_cmds-790.tar.gz
[apple/system_cmds.git] / lskq.tproj / lskq.c
1 /*
2 * Copyright (c) 2015-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <inttypes.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <strings.h>
29 #include <assert.h>
30 #include <errno.h>
31
32 #include <sys/types.h>
33 #include <sys/event.h>
34 #include <sys/time.h>
35 #include <sys/proc_info.h>
36 #include <sys/param.h>
37 #include <pthread/pthread.h>
38 #include <mach/message.h>
39 #define PRIVATE
40 #include <libproc.h>
41 #undef PRIVATE
42 #include <os/assumes.h>
43 #include <os/overflow.h>
44
45 #include "common.h"
46
47 #define ARRAYLEN(x) (sizeof((x))/sizeof((x[0])))
48
49 /* command line options */
50 static int verbose;
51 static int all_pids;
52 static int ignore_empty;
53 static int raw;
54
55 static char *self = "lskq";
56
57 static inline const char *
58 filt_name(int16_t filt)
59 {
60 static char unkn_filt[32];
61 int idx = -filt;
62 if (idx >= 0 && idx < ARRAYLEN(filt_strs)) {
63 return filt_strs[idx];
64 } else {
65 snprintf(unkn_filt, sizeof(unkn_filt), "%i (?)", idx);
66 return unkn_filt;
67 }
68 }
69
70 static inline const char *
71 fdtype_str(uint32_t type)
72 {
73 static char unkn_fdtype[32];
74 if (type < ARRAYLEN(fdtype_strs)) {
75 return fdtype_strs[type];
76 } else {
77 snprintf(unkn_fdtype, sizeof(unkn_fdtype), "%i (?)", type);
78 return unkn_fdtype;
79 }
80 }
81
82 static char *
83 fflags_build(struct kevent_extinfo *info, char *str, int len)
84 {
85 unsigned ff = info->kqext_sfflags;
86
87 switch (info->kqext_kev.filter) {
88
89 case EVFILT_READ: {
90 snprintf(str, len, "%c ",
91 (ff & NOTE_LOWAT) ? 'l' : '-'
92 );
93 break;
94 }
95
96 case EVFILT_MACHPORT: {
97 snprintf(str, len, "%c ",
98 (ff & MACH_RCV_MSG) ? 'r' : '-'
99 );
100 break;
101 }
102
103 case EVFILT_VNODE: {
104 snprintf(str, len, "%c%c%c%c%c%c%c",
105 (ff & NOTE_DELETE) ? 'd' : '-',
106 (ff & NOTE_WRITE) ? 'w' : '-',
107 (ff & NOTE_EXTEND) ? 'e' : '-',
108 (ff & NOTE_ATTRIB) ? 'a' : '-',
109 (ff & NOTE_LINK) ? 'l' : '-',
110 (ff & NOTE_RENAME) ? 'r' : '-',
111 (ff & NOTE_REVOKE) ? 'v' : '-'
112 );
113 break;
114 }
115
116 case EVFILT_PROC: {
117 /* NOTE_REAP is deprecated, but we still want to show if it's used */
118 #pragma clang diagnostic push
119 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
120 snprintf(str, len, "%c%c%c%c%c%c%c",
121 (ff & NOTE_EXIT) ? 'x' : '-',
122 (ff & NOTE_EXITSTATUS) ? 't' : '-',
123 (ff & NOTE_EXIT_DETAIL)? 'd' : '-',
124 (ff & NOTE_FORK) ? 'f' : '-',
125 (ff & NOTE_EXEC) ? 'e' : '-',
126 (ff & NOTE_SIGNAL) ? 's' : '-',
127 (ff & NOTE_REAP) ? 'r' : '-'
128 );
129 break;
130 #pragma clang diagnostic pop
131 }
132
133 case EVFILT_TIMER: {
134 snprintf(str, len, "%c%c%c%c%c ",
135 (ff & NOTE_SECONDS) ? 's' :
136 (ff & NOTE_USECONDS) ? 'u' :
137 (ff & NOTE_NSECONDS) ? 'n' : '?',
138 (ff & NOTE_ABSOLUTE) ? 'a' : '-',
139 (ff & NOTE_CRITICAL) ? 'c' : '-',
140 (ff & NOTE_BACKGROUND) ? 'b' : '-',
141 (ff & NOTE_LEEWAY) ? 'l' : '-'
142 );
143 break;
144 }
145
146 case EVFILT_USER:
147 snprintf(str, len, "%c%c%c ",
148 (ff & NOTE_TRIGGER) ? 't' : '-',
149 (ff & NOTE_FFAND) ? 'a' : '-',
150 (ff & NOTE_FFOR) ? 'o' : '-'
151 );
152 break;
153
154 case EVFILT_WORKLOOP:
155 snprintf(str, len, "%c%c%c%c ",
156 (ff & NOTE_WL_THREAD_REQUEST) ? 't' :
157 (ff & NOTE_WL_SYNC_WAIT) ? 'w' : '-',
158 (ff & NOTE_WL_SYNC_WAKE) ? '!' : '-',
159 (ff & NOTE_WL_UPDATE_QOS) ? 'q' : '-',
160 (ff & NOTE_WL_UPDATE_OWNER) ? 'O' :
161 (ff & NOTE_WL_DISCOVER_OWNER) ? 'o' : '-'
162 );
163 break;
164
165 default:
166 snprintf(str, len, "");
167 break;
168 };
169
170 return str;
171 }
172
173
174 static inline int
175 filter_is_fd_type(int filter)
176 {
177 switch (filter) {
178 case EVFILT_VNODE ... EVFILT_READ:
179 case EVFILT_SOCK:
180 case EVFILT_NW_CHANNEL:
181 return 1;
182 default:
183 return 0;
184 }
185 }
186
187 static const char *
188 thread_qos_name(uint8_t th_qos)
189 {
190 switch (th_qos) {
191 case 0: return "--";
192 case 1: return "MT";
193 case 2: return "BG";
194 case 3: return "UT";
195 case 4: return "DF";
196 case 5: return "IN";
197 case 6: return "UI";
198 case 7: return "MG";
199 default: return "??";
200 }
201 }
202
203 /*
204 * find index of fd in a list of fdinfo of length nfds
205 */
206 static inline int
207 fd_list_getfd(struct proc_fdinfo *fds, int nfds, int fd)
208 {
209 int i;
210 for (i = 0; i < nfds; i++) {
211 if (fds[i].proc_fd == fd) {
212 return i;
213 }
214 }
215
216 return -1;
217 }
218
219 /*
220 * left truncate URL-form process names
221 */
222 static const char *
223 shorten_procname(const char *proc, int width)
224 {
225 if (strcasestr(proc, "com.") == proc) {
226 long len = strlen(proc);
227 if (len > width) {
228 return &proc[len - width];
229 } else {
230 return proc;
231 }
232 } else {
233 return proc;
234 }
235 }
236
237 /*
238 * stringify knote ident where possible (signals, processes)
239 */
240 static void
241 print_ident(uint64_t ident, int16_t filter, int width)
242 {
243 if (raw) {
244 printf("%#*llx ", width, ident);
245 return;
246 }
247
248 switch (filter) {
249
250 case EVFILT_SIGNAL:
251 case EVFILT_PROC: {
252 char str[128] = "";
253 char num[128];
254 char out[128];
255 int numlen = sprintf(num, "%llu", ident);
256 int strwidth = width - numlen - 1; // add room for a space
257
258 if (filter == EVFILT_SIGNAL) {
259 if (ident < ARRAYLEN(sig_strs)) {
260 snprintf(str, strwidth + 1, "%s", sig_strs[ident]);
261 }
262 } else {
263 /* FIXME: this should be cached */
264 struct proc_bsdinfo bsdinfo;
265 int ret = proc_pidinfo((int)ident, PROC_PIDTBSDINFO, 0, &bsdinfo, sizeof(bsdinfo));
266 if (ret == sizeof(bsdinfo)) {
267 char *procname = bsdinfo.pbi_name;
268 if (strlen(procname) == 0) {
269 procname = bsdinfo.pbi_comm;
270 }
271 snprintf(str, strwidth + 1, "%s", shorten_procname(procname, strwidth));
272 }
273 }
274
275 if (str[0] != '\0') {
276 snprintf(out, width + 1, "%-*s %s", strwidth, str, num);
277 } else {
278 snprintf(out, width + 1, "%s", num);
279 }
280
281 printf("%*s ", width, out);
282 break;
283 }
284
285 case EVFILT_MACHPORT:
286 case EVFILT_TIMER:
287 /* hex, to match lsmp */
288 printf("%#*llx ", width, ident);
289 break;
290
291 case EVFILT_WORKLOOP:
292 printf("%#*llx ", width, ident);
293 break;
294
295 default:
296 printf("%*llu ", width, ident);
297 break;
298 }
299
300 }
301
302 static void
303 print_kqid(int state, uint64_t kqid)
304 {
305 if (state & KQ_WORKQ) {
306 printf("%18s ", "wq");
307 } else if (state & KQ_WORKLOOP) {
308 printf("%#18" PRIx64 " ", kqid);
309 } else {
310 printf("fd %15" PRIi64 " ", kqid);
311 }
312 }
313
314 #define PROCNAME_WIDTH 20
315
316 static void
317 print_kq_info(int pid, const char *procname, uint64_t kqid, int state)
318 {
319 if (raw) {
320 printf("%5u ", pid);
321 print_kqid(state, kqid);
322 printf("%#10x ", state);
323 } else {
324 char tmpstr[PROCNAME_WIDTH+1];
325 strlcpy(tmpstr, shorten_procname(procname, PROCNAME_WIDTH), PROCNAME_WIDTH+1);
326 printf("%-*s ", PROCNAME_WIDTH, tmpstr);
327 printf("%5u ", pid);
328 print_kqid(state, kqid);
329 printf(" %c%c%c ",
330 (state & KQ_SLEEP) ? 'k' : '-',
331 (state & KQ_SEL) ? 's' : '-',
332 (state & KQ_WORKQ) ? 'q' :
333 (state & KQ_WORKLOOP) ? 'l' : '-'
334 );
335 }
336 }
337
338 enum kqtype {
339 KQTYPE_FD,
340 KQTYPE_DYNAMIC
341 };
342
343 static int
344 process_kqueue(int pid, const char *procname, enum kqtype type, uint64_t kqid,
345 struct proc_fdinfo *fdlist, int nfds)
346 {
347 int ret, i, nknotes;
348 char tmpstr[256];
349 int maxknotes = 256; /* arbitrary starting point */
350 int kq_state;
351 bool is_kev_64, is_kev_qos;
352 int err = 0;
353 bool overflow = false;
354 int fd;
355 bool dynkq_printed = false;
356
357 /*
358 * get the basic kqueue info
359 */
360 struct kqueue_fdinfo kqfdinfo = {};
361 struct kqueue_dyninfo kqinfo = {};
362 switch (type) {
363 case KQTYPE_FD:
364 ret = proc_pidfdinfo(pid, (int)kqid, PROC_PIDFDKQUEUEINFO, &kqfdinfo, sizeof(kqfdinfo));
365 fd = (int)kqid;
366 break;
367 case KQTYPE_DYNAMIC:
368 ret = proc_piddynkqueueinfo(pid, PROC_PIDDYNKQUEUE_INFO, kqid, &kqinfo, sizeof(kqinfo));
369 break;
370 default:
371 os_crash("invalid kqueue type");
372 }
373
374 if (type == KQTYPE_FD && (int)kqid != -1) {
375 if (ret != sizeof(kqfdinfo)) {
376 /* every proc has an implicit workq kqueue, dont warn if its unused */
377 fprintf(stderr, "WARN: FD table changed (pid %i, kq %i)\n", pid,
378 fd);
379 }
380 } else if (type == KQTYPE_DYNAMIC) {
381 if (ret < sizeof(struct kqueue_info)) {
382 fprintf(stderr, "WARN: kqueue missing (pid %i, kq %#" PRIx64 ")\n",
383 pid, kqid);
384 } else {
385 kqfdinfo.kqueueinfo = kqinfo.kqdi_info;
386 }
387 if (verbose && ret >= sizeof(struct kqueue_dyninfo)) {
388 print_kq_info(pid, procname, kqid, kqinfo.kqdi_info.kq_state);
389
390 printf("%18s ", " "); // ident
391 printf("%-9s ", " "); // filter
392 dynkq_printed = true;
393
394 if (raw) {
395 printf("%-10s ", " "); // fflags
396 printf("%-10s ", " "); // flags
397 printf("%-10s ", " "); // evst
398 } else {
399 printf("%-8s ", " "); // fdtype
400 printf("%-7s ", " "); // fflags
401 printf("%-15s ", " "); // flags
402 printf("%-17s ", " "); // evst
403 }
404
405 int qos = MAX(MAX(kqinfo.kqdi_events_qos, kqinfo.kqdi_async_qos),
406 kqinfo.kqdi_sync_waiter_qos);
407 printf("%3s ", thread_qos_name(qos));
408 printf("%-18s ", " "); // data
409 printf("%-18s ", " "); // udata
410 printf("%#18llx ", kqinfo.kqdi_servicer); // ext0
411 printf("%#18llx ", kqinfo.kqdi_owner); // ext1
412 printf("\n");
413 }
414 }
415
416 /*
417 * get extended kqueue info
418 */
419 struct kevent_extinfo *kqextinfo = NULL;
420 again:
421 if (!kqextinfo) {
422 kqextinfo = malloc(sizeof(struct kevent_extinfo) * maxknotes);
423 }
424 if (!kqextinfo) {
425 err = errno;
426 perror("failed allocating memory");
427 goto out;
428 }
429
430 errno = 0;
431 switch (type) {
432 case KQTYPE_FD:
433 nknotes = proc_pidfdinfo(pid, fd, PROC_PIDFDKQUEUE_EXTINFO,
434 kqextinfo, sizeof(struct kevent_extinfo) * maxknotes);
435 break;
436 case KQTYPE_DYNAMIC:
437 nknotes = proc_piddynkqueueinfo(pid, PROC_PIDDYNKQUEUE_EXTINFO, kqid,
438 kqextinfo, sizeof(struct kevent_extinfo) * maxknotes);
439 break;
440 default:
441 os_crash("invalid kqueue type");
442 }
443
444 if (nknotes <= 0) {
445 if (errno == 0) {
446 /* proc_*() can't distinguish between error and empty list */
447 } else if (errno == EAGAIN) {
448 goto again;
449 } else if (errno == EBADF) {
450 fprintf(stderr, "WARN: FD table changed (pid %i, kq %#" PRIx64 ")\n", pid, kqid);
451 goto out;
452 } else {
453 err = errno;
454 perror("failed to get extended kqueue info");
455 goto out;
456 }
457 }
458
459 if (nknotes > maxknotes) {
460 maxknotes = nknotes + 16; /* arbitrary safety margin */
461 free(kqextinfo);
462 kqextinfo = NULL;
463 goto again;
464 }
465
466 if (nknotes >= PROC_PIDFDKQUEUE_KNOTES_MAX) {
467 overflow = true;
468 }
469
470 kq_state = kqfdinfo.kqueueinfo.kq_state;
471 is_kev_64 = (kq_state & PROC_KQUEUE_64);
472 is_kev_qos = (kq_state & PROC_KQUEUE_QOS);
473
474 if (nknotes == 0) {
475 if (!ignore_empty && !dynkq_printed) {
476 /* for empty kqueues, print a single empty entry */
477 print_kq_info(pid, procname, kqid, kq_state);
478 printf("%18s \n", "-");
479 }
480 goto out;
481 }
482
483 for (i = 0; i < nknotes; i++) {
484 struct kevent_extinfo *info = &kqextinfo[i];
485
486 print_kq_info(pid, procname, kqid, kqfdinfo.kqueueinfo.kq_state);
487 print_ident(info->kqext_kev.ident, info->kqext_kev.filter, 18);
488 printf("%-9s ", filt_name(info->kqext_kev.filter));
489
490 if (raw) {
491 printf("%#10x ", info->kqext_sfflags);
492 printf("%#10x ", info->kqext_kev.flags);
493 printf("%#10x ", info->kqext_status);
494 } else {
495 /* for kevents attached to file descriptors, print the type of FD (file, socket, etc) */
496 const char *fdstr = "";
497 if (filter_is_fd_type(info->kqext_kev.filter)) {
498 fdstr = "<unkn>";
499 int knfd = fd_list_getfd(fdlist, nfds, (int)info->kqext_kev.ident);
500 if (knfd >= 0) {
501 fdstr = fdtype_str(fdlist[knfd].proc_fdtype);
502 }
503 }
504 printf("%-8s ", fdstr);
505
506 /* print filter flags */
507 printf("%7s ", fflags_build(info, tmpstr, sizeof(tmpstr)));
508
509 /* print generic flags */
510 unsigned flg = info->kqext_kev.flags;
511 printf("%c%c%c%c %c%c%c%c %c%c%c%c%c ",
512 (flg & EV_ADD) ? 'a' : '-',
513 (flg & EV_ENABLE) ? 'n' : '-',
514 (flg & EV_DISABLE) ? 'd' : '-',
515 (flg & EV_DELETE) ? 'x' : '-',
516
517 (flg & EV_RECEIPT) ? 'r' : '-',
518 (flg & EV_ONESHOT) ? '1' : '-',
519 (flg & EV_CLEAR) ? 'c' : '-',
520 (flg & EV_DISPATCH) ? 's' : '-',
521
522 (flg & EV_UDATA_SPECIFIC) ? 'u' : '-',
523 (flg & EV_FLAG0) ? 'p' : '-',
524 (flg & EV_FLAG1) ? 'b' : '-',
525 (flg & EV_EOF) ? 'o' : '-',
526 (flg & EV_ERROR) ? 'e' : '-'
527 );
528
529 unsigned st = info->kqext_status;
530 printf("%c%c%c%c %c%c%c%c %c%c%c%c %c%c ",
531 (st & KN_ACTIVE) ? 'a' : '-',
532 (st & KN_QUEUED) ? 'q' : '-',
533 (st & KN_DISABLED) ? 'd' : '-',
534 (st & KN_STAYACTIVE) ? 's' : '-',
535
536 (st & KN_DROPPING) ? 'd' : '-',
537 (st & KN_USEWAIT) ? 'w' : '-',
538 (st & KN_ATTACHING) ? 'c' : '-',
539 (st & KN_ATTACHED) ? 'a' : '-',
540
541 (st & KN_DISPATCH) ? 's' : '-',
542 (st & KN_UDATA_SPECIFIC) ? 'u' : '-',
543 (st & KN_SUPPRESSED) ? 'p' : '-',
544 (st & KN_STOLENDROP) ? 't' : '-',
545
546 (st & KN_REQVANISH) ? 'v' : '-',
547 (st & KN_VANISHED) ? 'n' : '-'
548 );
549 }
550
551 printf("%3s ", thread_qos_name(info->kqext_kev.qos));
552
553 printf("%#18llx ", (unsigned long long)info->kqext_kev.data);
554
555 if (verbose) {
556 printf("%#18llx ", (unsigned long long)info->kqext_kev.udata);
557 if (is_kev_qos || is_kev_64) {
558 printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[0]);
559 printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[1]);
560
561 if (is_kev_qos) {
562 printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[2]);
563 printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[3]);
564 printf("%#10lx ", (unsigned long)info->kqext_kev.xflags);
565 }
566 }
567 }
568
569 printf("\n");
570 }
571
572 if (overflow) {
573 printf(" ***** output truncated (>=%i knotes on kq %" PRIu64 ", proc %i) *****\n",
574 nknotes, kqid, pid);
575 }
576
577 out:
578 if (kqextinfo) {
579 free(kqextinfo);
580 kqextinfo = NULL;
581 }
582
583 return err;
584 }
585
586 static int
587 pid_kqids(pid_t pid, kqueue_id_t **kqids_out)
588 {
589 static int kqids_len = 256;
590 static kqueue_id_t *kqids = NULL;
591 static uint32_t kqids_size;
592
593 int nkqids;
594
595 retry:
596 if (os_mul_overflow(sizeof(kqueue_id_t), kqids_len, &kqids_size)) {
597 assert(kqids_len > PROC_PIDDYNKQUEUES_MAX);
598 kqids_len = PROC_PIDDYNKQUEUES_MAX;
599 goto retry;
600 }
601 if (!kqids) {
602 kqids = malloc(kqids_size);
603 os_assert(kqids != NULL);
604 }
605
606 nkqids = proc_list_dynkqueueids(pid, kqids, kqids_size);
607 if (nkqids > kqids_len && kqids_len < PROC_PIDDYNKQUEUES_MAX) {
608 kqids_len *= 2;
609 if (kqids_len > PROC_PIDDYNKQUEUES_MAX) {
610 kqids_len = PROC_PIDDYNKQUEUES_MAX;
611 }
612 free(kqids);
613 kqids = NULL;
614 goto retry;
615 }
616
617 *kqids_out = kqids;
618 return MIN(nkqids, kqids_len);
619 }
620
621 static int
622 process_pid(pid_t pid)
623 {
624 int i, nfds, nkqids;
625 kqueue_id_t *kqids;
626 int ret = 0;
627 int maxfds = 256; /* arbitrary starting point */
628 struct proc_fdinfo *fdlist = NULL;
629
630 /* enumerate file descriptors */
631 again:
632 if (!fdlist) {
633 fdlist = malloc(sizeof(struct proc_fdinfo) * maxfds);
634 }
635 if (!fdlist) {
636 ret = errno;
637 perror("failed to allocate");
638 goto out;
639 }
640
641 nfds = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdlist,
642 sizeof(struct proc_fdinfo) * maxfds);
643 if (nfds <= 0) {
644 ret = errno;
645 fprintf(stderr, "%s: failed enumerating file descriptors of process %i: %s",
646 self, pid, strerror(ret));
647 if (ret == EPERM && geteuid() != 0) {
648 fprintf(stderr, " (are you root?)");
649 }
650 fprintf(stderr, "\n");
651 goto out;
652 }
653
654 nfds /= sizeof(struct proc_fdinfo);
655 if (nfds >= maxfds) {
656 maxfds = nfds + 16;
657 free(fdlist);
658 fdlist = NULL;
659 goto again;
660 }
661
662 /* get bsdinfo for the process name */
663 struct proc_bsdinfo bsdinfo;
664 ret = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &bsdinfo, sizeof(bsdinfo));
665 if (ret != sizeof(bsdinfo)) {
666 perror("failed retrieving process info");
667 ret = -1;
668 goto out;
669 }
670
671 char *procname = bsdinfo.pbi_name;
672 if (strlen(procname) == 0) {
673 procname = bsdinfo.pbi_comm;
674 }
675
676 /* handle the special workq kq */
677 ret = process_kqueue(pid, procname, KQTYPE_FD, -1, fdlist, nfds);
678 if (ret) {
679 goto out;
680 }
681
682 for (i = 0; i < nfds; i++) {
683 if (fdlist[i].proc_fdtype == PROX_FDTYPE_KQUEUE) {
684 ret = process_kqueue(pid, procname, KQTYPE_FD,
685 (uint64_t)fdlist[i].proc_fd, fdlist, nfds);
686 if (ret) {
687 goto out;
688 }
689 }
690 }
691
692 nkqids = pid_kqids(pid, &kqids);
693
694 for (i = 0; i < nkqids; i++) {
695 ret = process_kqueue(pid, procname, KQTYPE_DYNAMIC, kqids[i], fdlist, nfds);
696 if (ret) {
697 goto out;
698 }
699 }
700
701 if (nkqids >= PROC_PIDDYNKQUEUES_MAX) {
702 printf(" ***** output truncated (>=%i dynamic kqueues in proc %i) *****\n",
703 nkqids, pid);
704 }
705
706 out:
707 if (fdlist) {
708 free(fdlist);
709 fdlist = NULL;
710 }
711
712 return ret;
713 }
714
715 static int
716 process_all_pids(void)
717 {
718 int i, npids;
719 int ret = 0;
720 int maxpids = 2048;
721 int *pids = NULL;
722
723 again:
724 if (!pids) {
725 pids = malloc(sizeof(int) * maxpids);
726 }
727 if (!pids) {
728 perror("failed allocating pids[]");
729 goto out;
730 }
731
732 errno = 0;
733 npids = proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(int) * maxpids);
734 if (npids <= 0) {
735 if (errno == 0) {
736 /* empty pid list */
737 } else if (errno == EAGAIN) {
738 goto again;
739 } else {
740 ret = errno;
741 perror("failed enumerating pids");
742 goto out;
743 }
744 }
745
746 npids /= sizeof(int);
747 if (npids >= maxpids) {
748 maxpids = npids + 16;
749 free(pids);
750 pids = NULL;
751 goto again;
752 }
753
754 for (i = 0; i < npids; i++) {
755 /* listpids gives us pid 0 for some reason */
756 if (pids[i]) {
757 ret = process_pid(pids[i]);
758 /* ignore races with processes exiting */
759 if (ret && ret != ESRCH) {
760 goto out;
761 }
762 }
763 }
764
765 out:
766 if (pids) {
767 free(pids);
768 pids = NULL;
769 }
770
771 return ret;
772 }
773
774 static void
775 cheatsheet(void)
776 {
777 fprintf(stderr, "\nFilter-independent flags:\n\n\
778 \033[1m\
779 command pid kq kqst ident filter fdtype fflags flags evst\033[0m\n\
780 \033[1m\
781 -------------------- ----- ------------------ ---- ------------------ --------- -------- ------- --------------- -----------------\033[0m\n\
782 ┌ EV_UDATA_SPECIFIC\n\
783 EV_DISPATCH ┐ │┌ EV_FLAG0 (EV_POLL)\n\
784 EV_CLEAR ┐│ ││┌ EV_FLAG1 (EV_OOBAND)\n\
785 EV_ONESHOT ┐││ │││┌ EV_EOF\n\
786 EV_RECEIPT ┐│││ ││││┌ EV_ERROR\n\
787 ││││ │││││\n\
788 \033[1m\
789 launchd 1 4 ks- netbiosd 250 PROC ------- andx r1cs upboe aqds dwca supt vn\033[0m \n\
790 │ │││ ││││ ││││ ││││ ││││ ││\n\
791 kqueue file descriptor/dynamic ID ┘ │││ EV_ADD ┘│││ KN_ACTIVE ┘│││ ││││ ││││ ││\n\
792 KQ_SLEEP ┘││ EV_ENABLE ┘││ KN_QUEUED ┘││ ││││ ││││ ││\n\
793 KQ_SEL ┘│ EV_DISABLE ┘│ KN_DISABLED ┘│ ││││ ││││ │└ KN_VANISHED\n\
794 KQ_WORKQ (q) ┤ EV_DELETE ┘ KN_STAYACTIVE ┘ ││││ ││││ └ KN_REQVANISH\n\
795 KQ_WORKLOOP (l) ┘ ││││ ││││\n\
796 KN_DROPPING ┘│││ │││└ KN_STOLENDROP\n\
797 KN_USEWAIT ┘││ ││└ KN_SUPPRESSED\n\
798 KN_ATTACHING ┘│ │└ KN_UDATA_SPECIFIC\n\
799 KN_ATTACHED ┘ └ KN_DISPATCH\n\
800 \n");
801 }
802
803 static void
804 usage(void)
805 {
806 fprintf(stderr, "usage: %s [-vher] [-a | -p <pid>]\n", self);
807 }
808
809 static void
810 print_header(void)
811 {
812 if (raw) {
813 printf(" pid kq kqst knid filter fflags flags evst qos data");
814 } else {
815 printf("command pid kq kqst knid filter fdtype fflags flags evst qos data");
816 }
817
818 if (verbose) {
819 printf(" udata servicer / ext0 owner / ext1 ext2 ext3 xflags");
820 }
821
822 printf("\n");
823
824 if (raw) {
825 printf("----- ------------------ ---------- ------------------ --------- ---------- ---------- ---------- --- ------------------");
826 } else {
827 printf("-------------------- ----- ------------------ ---- ------------------ --------- -------- ------- --------------- ----------------- --- ------------------");
828 }
829
830 if (verbose) {
831 printf(" ------------------ ------------------ ------------------ ------------------ ------------------ ----------");
832 }
833 printf("\n");
834 }
835
836 int
837 main(int argc, char *argv[])
838 {
839 pid_t pid = 0;
840 int opt;
841
842 setlinebuf(stdout);
843
844 if (argc > 0) {
845 self = argv[0];
846 }
847
848 while ((opt = getopt(argc, argv, "eahvrp:")) != -1) {
849 switch (opt) {
850 case 'a':
851 all_pids = 1;
852 break;
853 case 'v':
854 verbose++;
855 break;
856 case 'p':
857 pid = atoi(optarg);
858 break;
859 case 'e':
860 ignore_empty = 1;
861 break;
862 case 'h':
863 usage();
864 cheatsheet();
865 return 0;
866 case 'r':
867 raw = 1;
868 break;
869 case '?':
870 default:
871 usage();
872 return 1;
873 }
874 }
875
876 argc -= optind;
877 argv += optind;
878
879 if (argc == 1) {
880 /* also allow lskq <pid> */
881 if (pid || all_pids) {
882 usage();
883 return 1;
884 }
885
886 pid = atoi(argv[0]);
887 } else if (argc > 1) {
888 usage();
889 return 1;
890 }
891
892 /* exactly one of -p or -a is required */
893 if (!pid && !all_pids) {
894 usage();
895 return 1;
896 } else if (pid && all_pids) {
897 usage();
898 return 1;
899 }
900
901 print_header();
902
903 if (all_pids) {
904 return process_all_pids();
905 } else {
906 return process_pid(pid);
907 }
908
909 return 0;
910 }