1
2 #ifndef _GNU_SOURCE
3 #define _GNU_SOURCE 1
4 #endif
5
6 #include <assert.h>
7 #include <errno.h>
8 #include <limits.h> // for INT_MAX
9 #include <stdbool.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <signal.h>
13 #include <sys/sysctl.h>
14 #include <libproc.h>
15 #include <string.h>
16
17 #include "process_info.h"
18 #include "../../common.h"
19 #include "../../posix.h"
20
21 /*
22 * Returns a list of all BSD processes on the system. This routine
23 * allocates the list and puts it in *procList and a count of the
24 * number of entries in *procCount. You are responsible for freeing
25 * this list (use "free" from System framework).
26 * On success, the function returns 0.
27 * On error, the function returns a BSD errno value.
28 */
ps__get_proc_list(kinfo_proc ** procList,size_t * procCount)29 int ps__get_proc_list(kinfo_proc **procList, size_t *procCount) {
30 int mib3[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
31 size_t size, size2;
32 void *ptr;
33 int err;
34 int lim = 8; // some limit
35
36 assert( procList != NULL);
37 assert(*procList == NULL);
38 assert(procCount != NULL);
39
40 *procCount = 0;
41
42 /*
43 * We start by calling sysctl with ptr == NULL and size == 0.
44 * That will succeed, and set size to the appropriate length.
45 * We then allocate a buffer of at least that size and call
46 * sysctl with that buffer. If that succeeds, we're done.
47 * If that call fails with ENOMEM, we throw the buffer away
48 * and try again.
49 * Note that the loop calls sysctl with NULL again. This is
50 * is necessary because the ENOMEM failure case sets size to
51 * the amount of data returned, not the amount of data that
52 * could have been returned.
53 */
54 while (lim-- > 0) {
55 size = 0;
56 if (sysctl((int *)mib3, 3, NULL, &size, NULL, 0) == -1)
57 return errno;
58 size2 = size + (size >> 3); // add some
59 if (size2 > size) {
60 ptr = malloc(size2);
61 if (ptr == NULL)
62 ptr = malloc(size);
63 else
64 size = size2;
65 }
66 else {
67 ptr = malloc(size);
68 }
69 if (ptr == NULL)
70 return ENOMEM;
71
72 if (sysctl((int *)mib3, 3, ptr, &size, NULL, 0) == -1) {
73 err = errno;
74 free(ptr);
75 if (err != ENOMEM)
76 return err;
77 }
78 else {
79 *procList = (kinfo_proc *)ptr;
80 *procCount = size / sizeof(kinfo_proc);
81 return 0;
82 }
83 }
84 return ENOMEM;
85 }
86
87
88 // Read the maximum argument size for processes
ps__get_argmax()89 int ps__get_argmax() {
90 int argmax;
91 int mib[] = { CTL_KERN, KERN_ARGMAX };
92 size_t size = sizeof(argmax);
93
94 if (sysctl(mib, 2, &argmax, &size, NULL, 0) == 0)
95 return argmax;
96 return 0;
97 }
98
99 // return process args as a character vector
ps__get_cmdline(long pid)100 SEXP ps__get_cmdline(long pid) {
101 int mib[3];
102 int nargs;
103 int idx;
104 size_t len;
105 char *procargs = NULL;
106 char *arg_ptr;
107 char *arg_end;
108 char *curr_arg;
109 size_t argmax;
110
111 SEXP arg = R_NilValue;
112 SEXP retlist = R_NilValue;
113
114 // special case for PID 0 (kernel_task) where cmdline cannot be fetched
115 if (pid == 0) {
116 ps__access_denied("");
117 return R_NilValue;
118 }
119
120 // read argmax and allocate memory for argument space.
121 argmax = ps__get_argmax();
122 if (! argmax) {
123 ps__set_error_from_errno();
124 return R_NilValue;
125 }
126
127 procargs = (char *) malloc(argmax);
128 if (NULL == procargs) {
129 ps__no_memory("");
130 return R_NilValue;
131 }
132
133 PROTECT_PTR(procargs);
134
135 // read argument space
136 mib[0] = CTL_KERN;
137 mib[1] = KERN_PROCARGS2;
138 mib[2] = (pid_t)pid;
139 if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) {
140 // In case of zombie process we'll get EINVAL, we fix this.
141 if (errno == EINVAL) errno = ESRCH;
142 UNPROTECT(1);
143 ps__set_error_from_errno();
144 return R_NilValue;
145 }
146
147 arg_end = &procargs[argmax];
148 // copy the number of arguments to nargs
149 memcpy(&nargs, procargs, sizeof(nargs));
150
151 arg_ptr = procargs + sizeof(nargs);
152 len = strlen(arg_ptr);
153 arg_ptr += len + 1;
154
155 if (arg_ptr == arg_end) {
156 UNPROTECT(1);
157 return allocVector(STRSXP, 0);
158 }
159
160 // skip ahead to the first argument
161 for (; arg_ptr < arg_end; arg_ptr++) {
162 if (*arg_ptr != '\0')
163 break;
164 }
165
166 // iterate through arguments
167 curr_arg = arg_ptr;
168 idx = 0;
169 PROTECT(retlist = allocVector(STRSXP, nargs));
170 while (arg_ptr < arg_end && nargs > 0) {
171 if (*arg_ptr++ == '\0') {
172 PROTECT(arg = ps__str_to_utf8(curr_arg));
173 SET_STRING_ELT(retlist, idx++, STRING_ELT(arg, 0));
174 UNPROTECT(1);
175 // iterate to next arg and decrement # of args
176 curr_arg = arg_ptr;
177 nargs--;
178 }
179 }
180
181 UNPROTECT(2);
182 return retlist;
183 }
184
185 // return process environment as a character vector
ps__get_environ(long pid)186 SEXP ps__get_environ(long pid) {
187 int mib[3];
188 int nargs, nenv;
189 char *procargs = NULL;
190 char *arg_ptr;
191 char *arg_end;
192 char *env_start;
193 size_t argmax;
194 SEXP ret = NULL;
195
196 // special case for PID 0 (kernel_task) where cmdline cannot be fetched
197 if (pid == 0) {
198 ps__access_denied("");
199 goto ret;
200 }
201
202 // read argmax and allocate memory for argument space.
203 argmax = ps__get_argmax();
204 if (! argmax) {
205 ps__set_error_from_errno();
206 return R_NilValue;
207 }
208
209 procargs = (char *) malloc(argmax);
210 if (NULL == procargs) {
211 ps__no_memory("");
212 return R_NilValue;
213 }
214
215 PROTECT_PTR(procargs);
216
217 // read argument space
218 mib[0] = CTL_KERN;
219 mib[1] = KERN_PROCARGS2;
220 mib[2] = (pid_t)pid;
221 if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) {
222 // In case of zombie process we'll get EINVAL, fix this.
223 if (errno == EINVAL) errno = ESRCH;
224 UNPROTECT(1);
225 ps__set_error_from_errno();
226 return R_NilValue;
227 }
228
229 arg_end = &procargs[argmax];
230 // copy the number of arguments to nargs
231 memcpy(&nargs, procargs, sizeof(nargs));
232
233 // skip executable path
234 arg_ptr = procargs + sizeof(nargs);
235 arg_ptr = memchr(arg_ptr, '\0', arg_end - arg_ptr);
236
237 if (arg_ptr == NULL || arg_ptr == arg_end)
238 goto empty;
239
240 // skip ahead to the first argument
241 for (; arg_ptr < arg_end; arg_ptr++) {
242 if (*arg_ptr != '\0')
243 break;
244 }
245
246 // iterate through arguments
247 while (arg_ptr < arg_end && nargs > 0) {
248 if (*arg_ptr++ == '\0')
249 nargs--;
250 }
251
252 // build an environment variable block
253 env_start = arg_ptr;
254
255 /* Count the number of env vars first */
256 nenv = 0;
257 while (*arg_ptr != '\0' && arg_ptr < arg_end) {
258 char *s = memchr(arg_ptr + 1, '\0', arg_end - arg_ptr);
259
260 if (s == NULL)
261 break;
262
263 nenv++;
264 arg_ptr = s + 1;
265 }
266
267 PROTECT(ret = allocVector(STRSXP, nenv));
268 arg_ptr = env_start;
269 nenv = 0;
270 while (*arg_ptr != '\0' && arg_ptr < arg_end) {
271 char *s = memchr(arg_ptr + 1, '\0', arg_end - arg_ptr);
272
273 if (s == NULL)
274 break;
275
276 SET_STRING_ELT(ret, nenv++, Rf_mkCharLen(arg_ptr, (int)(s - arg_ptr)));
277
278 arg_ptr = s + 1;
279 }
280
281 UNPROTECT(2);
282 return ret;
283
284 empty:
285 UNPROTECT(1);
286
287 ret:
288 return allocVector(STRSXP, 0);
289 }
290
291
ps__get_kinfo_proc(long pid,struct kinfo_proc * kp)292 int ps__get_kinfo_proc(long pid, struct kinfo_proc *kp) {
293 int mib[4];
294 size_t len;
295 mib[0] = CTL_KERN;
296 mib[1] = KERN_PROC;
297 mib[2] = KERN_PROC_PID;
298 mib[3] = (pid_t) pid;
299
300 // fetch the info with sysctl()
301 len = sizeof(struct kinfo_proc);
302
303 // now read the data from sysctl
304 if (sysctl(mib, 4, kp, &len, NULL, 0) == -1) {
305 // raise an exception and throw errno as the error
306 ps__set_error_from_errno();
307 return -1;
308 }
309
310 // sysctl succeeds but len is zero, happens when process has gone away
311 if (len == 0) {
312 ps__no_such_process(pid, 0);
313 return -1;
314 }
315 return 0;
316 }
317
318
319 /*
320 * A wrapper around proc_pidinfo().
321 * Returns 0 on failure (and error gets already set).
322 */
ps__proc_pidinfo(long pid,int flavor,uint64_t arg,void * pti,int size)323 int ps__proc_pidinfo(long pid, int flavor, uint64_t arg, void *pti, int size) {
324 errno = 0;
325 int ret = proc_pidinfo((int)pid, flavor, arg, pti, size);
326 if ((ret <= 0) || ((unsigned long)ret < sizeof(pti))) {
327 ps__raise_for_pid(pid, "proc_pidinfo()");
328 return 0;
329 }
330 return ret;
331 }
332