1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
14 */
15 /*
16 * Copyright (c) 2013 Joyent, Inc. All Rights reserved.
17 * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
18 * Copyright 2021 Oxide Computer Company
19 */
20
21 #include <limits.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <ctype.h>
27 #include <string.h>
28 #include <stddef.h>
29 #include <sys/mkdev.h>
30
31 #include "libproc.h"
32 #include "Pcontrol.h"
33 #include "proc_fd.h"
34
35 /*
36 * Pfdinfo.c - obtain open file information.
37 */
38
39 void
Pinitfd(struct ps_prochandle * P)40 Pinitfd(struct ps_prochandle *P)
41 {
42 list_create(&P->fd_head, sizeof (fd_info_t),
43 offsetof(fd_info_t, fd_list));
44 }
45
46 /*
47 * Allocate an fd_info structure and stick it on the list.
48 * (Unless one already exists.) The list is sorted in
49 * reverse order. We will traverse it in that order later.
50 * This makes the usual ordered insert *fast*.
51 */
52 fd_info_t *
Pfd2info(struct ps_prochandle * P,int fd)53 Pfd2info(struct ps_prochandle *P, int fd)
54 {
55 fd_info_t *fip, *next = NULL;
56
57 for (fip = list_head(&P->fd_head); fip != NULL;
58 fip = list_next(&P->fd_head, fip)) {
59 if (fip->fd_info == NULL)
60 continue;
61
62 if (fip->fd_info->pr_fd == fd) {
63 return (fip);
64 }
65 if (fip->fd_info->pr_fd < fd) {
66 break;
67 }
68 }
69
70 next = fip;
71 if ((fip = calloc(1, sizeof (*fip))) == NULL)
72 return (NULL);
73
74 list_insert_before(&P->fd_head, next, fip);
75 return (fip);
76 }
77
78 static int
fdwalk_cb(const prfdinfo_t * info,void * arg)79 fdwalk_cb(const prfdinfo_t *info, void *arg)
80 {
81 struct ps_prochandle *P = arg;
82 fd_info_t *fip;
83
84 fip = Pfd2info(P, info->pr_fd);
85 if (fip == NULL) {
86 errno = ENOMEM;
87 return (-1);
88 }
89
90 if (fip->fd_info == NULL)
91 fip->fd_info = proc_fdinfo_dup(info);
92
93 if (fip->fd_info == NULL) {
94 errno = ENOMEM;
95 return (-1);
96 }
97
98 return (0);
99 }
100
101 /*
102 * Attempt to load the open file information from a live process.
103 */
104 static void
load_fdinfo(struct ps_prochandle * P)105 load_fdinfo(struct ps_prochandle *P)
106 {
107 /*
108 * In the unlikely case there are *no* file descriptors open,
109 * we will keep rescanning the proc directory, which will be empty.
110 * This is an edge case it isn't worth adding additional state to
111 * to eliminate.
112 */
113 if (!list_is_empty(&P->fd_head))
114 return;
115
116 if (P->state == PS_DEAD || P->state == PS_IDLE)
117 return;
118
119 proc_fdwalk(P->pid, fdwalk_cb, P);
120 }
121
122 int
Pfdinfo_iter(struct ps_prochandle * P,proc_fdinfo_f * func,void * cd)123 Pfdinfo_iter(struct ps_prochandle *P, proc_fdinfo_f *func, void *cd)
124 {
125 fd_info_t *fip;
126 int rv;
127
128 /* Make sure we have live data, if appropriate */
129 load_fdinfo(P);
130
131 /* NB: We walk the list backwards. */
132
133 for (fip = list_tail(&P->fd_head); fip != NULL;
134 fip = list_prev(&P->fd_head, fip)) {
135 if ((rv = func(cd, fip->fd_info)) != 0)
136 return (rv);
137 }
138 return (0);
139 }
140