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