1
2 #ifndef _GNU_SOURCE
3 #define _GNU_SOURCE 1
4 #endif
5
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <sys/sysctl.h>
9 #include <sys/proc_info.h>
10 #include <sys/types.h>
11 #include <libproc.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <utmpx.h>
15 #include <arpa/inet.h>
16
17 #include <mach/mach.h>
18 #include <mach/mach_vm.h>
19
20 #include "ps-internal.h"
21 #include "arch/macos/process_info.h"
22
23 #define PS__TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
24
25 #define PS__CHECK_KINFO(kp, handle) \
26 if (PS__TV2DOUBLE(kp.kp_proc.p_starttime) != handle->create_time) { \
27 ps__no_such_process(handle->pid, 0); \
28 ps__throw_error(); \
29 }
30
31 #define PS__CHECK_HANDLE(handle) \
32 do { \
33 struct kinfo_proc kp; \
34 if (ps__get_kinfo_proc(handle->pid, &kp) == -1) { \
35 ps__set_error_from_errno(); \
36 ps__throw_error(); \
37 } \
38 PS__CHECK_KINFO(kp, handle); \
39 } while (0)
40
41 #define PS__GET_STATUS(stat, result, error) \
42 switch(stat) { \
43 case SIDL: result = mkString("idle"); break; \
44 case SRUN: result = mkString("running"); break; \
45 case SSLEEP: result = mkString("sleeping"); break; \
46 case SSTOP: result = mkString("stopped"); break; \
47 case SZOMB: result = mkString("zombie"); break; \
48 default: error; \
49 }
50
ps__check_for_zombie(ps_handle_t * handle,int err)51 void ps__check_for_zombie(ps_handle_t *handle, int err) {
52 struct kinfo_proc kp;
53 int ret;
54
55 if (handle->pid == 0) {
56 ps__access_denied("");
57 err = 1;
58
59 } else if (errno == 0 || errno == ESRCH) {
60
61 ret = ps__get_kinfo_proc(handle->pid, &kp);
62 if ((ret == -1) ||
63 (PS__TV2DOUBLE(kp.kp_proc.p_starttime) != handle->create_time)) {
64 ps__no_such_process(handle->pid, 0);
65 err = 1;
66 } else if (kp.kp_proc.p_stat == SZOMB) {
67 ps__zombie_process(handle->pid);
68 err = 1;
69 } else {
70 ps__access_denied("");
71 }
72
73 } else {
74 ps__set_error_from_errno();
75 }
76
77 if (err) ps__throw_error();
78 }
79
psll_finalizer(SEXP p)80 void psll_finalizer(SEXP p) {
81 ps_handle_t *handle = R_ExternalPtrAddr(p);
82 if (handle) free(handle);
83 }
84
psll_handle(SEXP pid,SEXP time)85 SEXP psll_handle(SEXP pid, SEXP time) {
86 pid_t cpid = isNull(pid) ? getpid() : INTEGER(pid)[0];
87 double ctime;
88 ps_handle_t *handle;
89 SEXP res;
90
91 if (!isNull(time)) {
92 ctime = REAL(time)[0];
93 } else {
94 struct kinfo_proc kp;
95 if (ps__get_kinfo_proc(cpid, &kp) == -1) ps__throw_error();
96 ctime = (double) PS__TV2DOUBLE(kp.kp_proc.p_starttime);
97 }
98
99 handle = malloc(sizeof(ps_handle_t));
100
101 if (!handle) {
102 ps__no_memory("");
103 ps__throw_error();
104 }
105
106 handle->pid = cpid;
107 handle->create_time = ctime;
108 handle->gone = 0;
109
110 PROTECT(res = R_MakeExternalPtr(handle, R_NilValue, R_NilValue));
111 R_RegisterCFinalizerEx(res, psll_finalizer, /* onexit */ 0);
112 setAttrib(res, R_ClassSymbol, mkString("ps_handle"));
113
114 UNPROTECT(1);
115 return res;
116 }
117
psll_format(SEXP p)118 SEXP psll_format(SEXP p) {
119 ps_handle_t *handle = R_ExternalPtrAddr(p);
120 struct kinfo_proc kp;
121 SEXP name, status, result;
122
123 if (!handle) error("Process pointer cleaned up already");
124
125 if (ps__get_kinfo_proc(handle->pid, &kp) == -1) {
126 PROTECT(name = mkString("???"));
127 PROTECT(status = mkString("terminated"));
128 } else {
129 PROTECT(name = ps__str_to_utf8(kp.kp_proc.p_comm));
130 PS__GET_STATUS(kp.kp_proc.p_stat, status, status = mkString("unknown"));
131 PROTECT(status);
132 }
133 PROTECT(result = ps__build_list("OldO", name, (long) handle->pid,
134 handle->create_time, status));
135
136 /* We do not check that the pid is still valid here, because we want
137 to be able to format & print processes that have finished already. */
138
139 UNPROTECT(3);
140 return result;
141 }
142
143
psll_parent(SEXP p)144 SEXP psll_parent(SEXP p) {
145 ps_handle_t *handle = R_ExternalPtrAddr(p);
146 struct kinfo_proc kp;
147 SEXP ppid, parent;
148
149 if (!handle) error("Process pointer cleaned up already");
150
151 if (ps__get_kinfo_proc(handle->pid, &kp) == -1) ps__throw_error();
152 PS__CHECK_KINFO(kp, handle);
153
154 /* TODO: this is a race condition, because the parent process might
155 have just quit, so psll_handle() might fail. If this happens, then
156 we should try to query the ppid again. */
157
158 PROTECT(ppid = ScalarInteger(kp.kp_eproc.e_ppid));
159 PROTECT(parent = psll_handle(ppid, R_NilValue));
160
161 UNPROTECT(2);
162 return parent;
163 }
164
165
psll_ppid(SEXP p)166 SEXP psll_ppid(SEXP p) {
167 ps_handle_t *handle = R_ExternalPtrAddr(p);
168 struct kinfo_proc kp;
169
170 if (!handle) error("Process pointer cleaned up already");
171
172 if (ps__get_kinfo_proc(handle->pid, &kp) == -1) ps__throw_error();
173 PS__CHECK_KINFO(kp, handle);
174
175 return ScalarInteger(kp.kp_eproc.e_ppid);
176 }
177
178
psll_is_running(SEXP p)179 SEXP psll_is_running(SEXP p) {
180 ps_handle_t *handle = R_ExternalPtrAddr(p);
181 struct kinfo_proc kp;
182 double ctime;
183
184 if (!handle) error("Process pointer cleaned up already");
185
186 if (handle->gone) return ScalarLogical(0);
187 if (ps__get_kinfo_proc(handle->pid, &kp) == -1) return ScalarLogical(0);
188
189 ctime = (double) PS__TV2DOUBLE(kp.kp_proc.p_starttime);
190 return ScalarLogical(ctime == handle->create_time);
191 }
192
193
psll_name(SEXP p)194 SEXP psll_name(SEXP p) {
195 ps_handle_t *handle = R_ExternalPtrAddr(p);
196 struct kinfo_proc kp;
197
198 if (!handle) error("Process pointer cleaned up already");
199
200 if (ps__get_kinfo_proc(handle->pid, &kp) == -1) ps__throw_error();
201 PS__CHECK_KINFO(kp, handle);
202 return ps__str_to_utf8(kp.kp_proc.p_comm);
203 }
204
205
psll_exe(SEXP p)206 SEXP psll_exe(SEXP p) {
207 ps_handle_t *handle = R_ExternalPtrAddr(p);
208 int ret;
209 char buf[PROC_PIDPATHINFO_MAXSIZE];
210
211 if (!handle) error("Process pointer cleaned up already");
212
213 ret = proc_pidpath(handle->pid, &buf, sizeof(buf));
214
215 if (ret == 0) ps__check_for_zombie(handle, 1);
216
217 PS__CHECK_HANDLE(handle);
218
219 return ps__str_to_utf8(buf);
220 }
221
psll_cmdline(SEXP p)222 SEXP psll_cmdline(SEXP p) {
223 ps_handle_t *handle = R_ExternalPtrAddr(p);
224 SEXP result;
225
226 if (!handle) error("Process pointer cleaned up already");
227
228 result = ps__get_cmdline(handle->pid);
229
230 if (isNull(result)) ps__check_for_zombie(handle, 1);
231
232 PROTECT(result);
233 PS__CHECK_HANDLE(handle);
234 UNPROTECT(1);
235 return result;
236 }
237
238
psll_status(SEXP p)239 SEXP psll_status(SEXP p) {
240 ps_handle_t *handle = R_ExternalPtrAddr(p);
241 struct kinfo_proc kp;
242 SEXP result;
243
244 if (!handle) error("Process pointer cleaned up already");
245
246 if (ps__get_kinfo_proc(handle->pid, &kp) == -1) {
247 handle->gone = 1;
248 ps__no_such_process(handle->pid, 0);
249 ps__throw_error();
250 }
251
252 PS__CHECK_KINFO(kp, handle);
253
254 PS__GET_STATUS(kp.kp_proc.p_stat, result, error("Unknown process status"));
255
256 return result;
257 }
258
259
psll_username(SEXP p)260 SEXP psll_username(SEXP p) {
261 ps_handle_t *handle = R_ExternalPtrAddr(p);
262 struct kinfo_proc kp;
263 SEXP ruid, pw, result;
264
265 if (!handle) error("Process pointer cleaned up already");
266
267 if (ps__get_kinfo_proc(handle->pid, &kp) == -1) ps__throw_error();
268
269 PS__CHECK_KINFO(kp, handle);
270
271 PROTECT(ruid = ScalarInteger(kp.kp_eproc.e_pcred.p_ruid));
272 PROTECT(pw = ps__get_pw_uid(ruid));
273 PROTECT(result = VECTOR_ELT(pw, 0));
274
275 UNPROTECT(3);
276 return result;
277 }
278
279
psll_cwd(SEXP p)280 SEXP psll_cwd(SEXP p) {
281 ps_handle_t *handle = R_ExternalPtrAddr(p);
282
283 if (!handle) error("Process pointer cleaned up already");
284
285 struct proc_vnodepathinfo pathinfo;
286
287 if (ps__proc_pidinfo(handle->pid, PROC_PIDVNODEPATHINFO, 0, &pathinfo,
288 sizeof(pathinfo)) <= 0) {
289 ps__check_for_zombie(handle, 1);
290 }
291
292 PS__CHECK_HANDLE(handle);
293 return ps__str_to_utf8(pathinfo.pvi_cdir.vip_path);
294 }
295
296
psll_uids(SEXP p)297 SEXP psll_uids(SEXP p) {
298 ps_handle_t *handle = R_ExternalPtrAddr(p);
299 struct kinfo_proc kp;
300 SEXP result, names;
301
302 if (!handle) error("Process pointer cleaned up already");
303
304 if (ps__get_kinfo_proc(handle->pid, &kp) == -1) ps__throw_error();
305
306 PS__CHECK_KINFO(kp, handle);
307
308 PROTECT(result = allocVector(INTSXP, 3));
309 INTEGER(result)[0] = kp.kp_eproc.e_pcred.p_ruid;
310 INTEGER(result)[1] = kp.kp_eproc.e_ucred.cr_uid;
311 INTEGER(result)[2] = kp.kp_eproc.e_pcred.p_svuid;
312 PROTECT(names = ps__build_string("real", "effective", "saved", NULL));
313 setAttrib(result, R_NamesSymbol, names);
314
315 UNPROTECT(2);
316 return result;
317 }
318
319
psll_gids(SEXP p)320 SEXP psll_gids(SEXP p) {
321 ps_handle_t *handle = R_ExternalPtrAddr(p);
322 struct kinfo_proc kp;
323 SEXP result, names;
324
325 if (!handle) error("Process pointer cleaned up already");
326
327 if (ps__get_kinfo_proc(handle->pid, &kp) == -1) ps__throw_error();
328
329 PS__CHECK_KINFO(kp, handle);
330
331 PROTECT(result = allocVector(INTSXP, 3));
332 INTEGER(result)[0] = kp.kp_eproc.e_pcred.p_rgid;
333 INTEGER(result)[1] = kp.kp_eproc.e_ucred.cr_groups[0];
334 INTEGER(result)[2] = kp.kp_eproc.e_pcred.p_svgid;
335 PROTECT(names = ps__build_string("real", "effective", "saved", NULL));
336 setAttrib(result, R_NamesSymbol, names);
337
338 UNPROTECT(2);
339 return result;
340 }
341
342
psll_terminal(SEXP p)343 SEXP psll_terminal(SEXP p) {
344 ps_handle_t *handle = R_ExternalPtrAddr(p);
345 struct kinfo_proc kp;
346
347 if (!handle) error("Process pointer cleaned up already");
348
349 if (ps__get_kinfo_proc(handle->pid, &kp) == -1) ps__throw_error();
350
351 PS__CHECK_KINFO(kp, handle);
352
353 if (kp.kp_eproc.e_tdev != -1) {
354 return ScalarInteger(kp.kp_eproc.e_tdev);
355 } else {
356 return ScalarInteger(NA_INTEGER);
357 }
358 }
359
360
psll_environ(SEXP p)361 SEXP psll_environ(SEXP p) {
362 ps_handle_t *handle = R_ExternalPtrAddr(p);
363 SEXP result;
364
365 if (!handle) error("Process pointer cleaned up already");
366
367 result = ps__get_environ(handle->pid);
368
369 if (isNull(result)) ps__check_for_zombie(handle, 1);
370
371 PROTECT(result);
372 PS__CHECK_HANDLE(handle);
373 UNPROTECT(1);
374 return result;
375 }
376
377
378
psll_num_threads(SEXP p)379 SEXP psll_num_threads(SEXP p) {
380 ps_handle_t *handle = R_ExternalPtrAddr(p);
381 struct proc_taskinfo pti;
382
383 if (!handle) error("Process pointer cleaned up already");
384
385 if (ps__proc_pidinfo(handle->pid, PROC_PIDTASKINFO, 0, &pti,
386 sizeof(pti)) <= 0) {
387 ps__check_for_zombie(handle, 1);
388 }
389
390 PS__CHECK_HANDLE(handle);
391
392 return ScalarInteger(pti.pti_threadnum);
393 }
394
395
psll_cpu_times(SEXP p)396 SEXP psll_cpu_times(SEXP p) {
397 ps_handle_t *handle = R_ExternalPtrAddr(p);
398 struct proc_taskinfo pti;
399 SEXP result, names;
400
401 if (!handle) error("Process pointer cleaned up already");
402
403 if (ps__proc_pidinfo(handle->pid, PROC_PIDTASKINFO, 0, &pti,
404 sizeof(pti)) <= 0) {
405 ps__check_for_zombie(handle, 1);
406 }
407
408 PS__CHECK_HANDLE(handle);
409
410 PROTECT(result = allocVector(REALSXP, 4));
411 REAL(result)[0] = (double) pti.pti_total_user / 1000000000.0;
412 REAL(result)[1] = (double) pti.pti_total_system / 1000000000.0;
413 REAL(result)[2] = REAL(result)[3] = NA_REAL;
414 PROTECT(names = ps__build_string("user", "system", "children_user",
415 "children_system", NULL));
416 setAttrib(result, R_NamesSymbol, names);
417
418 UNPROTECT(2);
419 return result;
420 }
421
422
psll_memory_info(SEXP p)423 SEXP psll_memory_info(SEXP p) {
424 ps_handle_t *handle = R_ExternalPtrAddr(p);
425 struct proc_taskinfo pti;
426 SEXP result, names;
427
428 if (!handle) error("Process pointer cleaned up already");
429
430 if (ps__proc_pidinfo(handle->pid, PROC_PIDTASKINFO, 0, &pti,
431 sizeof(pti)) <= 0) {
432 ps__check_for_zombie(handle, 1);
433 }
434
435 PS__CHECK_HANDLE(handle);
436
437 PROTECT(result = allocVector(REALSXP, 4));
438 REAL(result)[0] = (double) pti.pti_resident_size;
439 REAL(result)[1] = (double) pti.pti_virtual_size;
440 REAL(result)[2] = (double) pti.pti_faults;
441 REAL(result)[3] = (double) pti.pti_pageins;
442 PROTECT(names = ps__build_string("rss", "vms", "pfaults", "pageins", NULL));
443 setAttrib(result, R_NamesSymbol, names);
444
445 UNPROTECT(2);
446 return result;
447 }
448
ps__boot_time()449 SEXP ps__boot_time() {
450 #define MIB_SIZE 2
451 int mib[MIB_SIZE];
452 size_t size;
453 struct timeval boottime;
454 double unixtime = 0.0;
455
456 mib[0] = CTL_KERN;
457 mib[1] = KERN_BOOTTIME;
458 size = sizeof(boottime);
459 if (sysctl(mib, MIB_SIZE, &boottime, &size, NULL, 0) != -1) {
460 unixtime = boottime.tv_sec + boottime.tv_usec / 1.e6;
461 } else {
462 ps__set_error_from_errno();
463 ps__throw_error();
464 }
465
466 return ScalarReal(unixtime);
467 }
468
ps__cpu_count_logical()469 SEXP ps__cpu_count_logical() {
470 int num = 0;
471 size_t size = sizeof(int);
472
473 if (sysctlbyname("hw.logicalcpu", &num, &size, NULL, 2))
474 return ScalarInteger(NA_INTEGER);
475 else
476 return ScalarInteger(num);
477 }
478
ps__cpu_count_physical()479 SEXP ps__cpu_count_physical() {
480 int num = 0;
481 size_t size = sizeof(int);
482
483 if (sysctlbyname("hw.physicalcpu", &num, &size, NULL, 0))
484 return ScalarInteger(NA_INTEGER);
485 else
486 return ScalarInteger(num);
487 }
488
ps__kill_if_env(SEXP marker,SEXP after,SEXP pid,SEXP sig)489 SEXP ps__kill_if_env(SEXP marker, SEXP after, SEXP pid, SEXP sig) {
490 const char *cmarker = CHAR(STRING_ELT(marker, 0));
491 pid_t cpid = INTEGER(pid)[0];
492 int csig = INTEGER(sig)[0];
493 SEXP env;
494 size_t i, len;
495
496 PROTECT(env = ps__get_environ(cpid));
497 if (isNull(env)) {
498 ps__set_error_from_errno();
499 ps__throw_error();
500 }
501
502 len = LENGTH(env);
503
504 for (i = 0; i < len; i++) {
505 if (strstr(CHAR(STRING_ELT(env, i)), cmarker)) {
506 struct kinfo_proc kp;
507 int kpret = ps__get_kinfo_proc(cpid, &kp);
508 int ret = kill(cpid, csig);
509
510 if (ret == -1) {
511 if (errno == ESRCH) {
512 ps__no_such_process(cpid, 0);
513 } else if (errno == EPERM || errno == EACCES) {
514 ps__access_denied("");
515 } else {
516 ps__set_error_from_errno();
517 }
518 ps__throw_error();
519 }
520
521 UNPROTECT(1);
522
523 if (kpret != -1) {
524 return ps__str_to_utf8(kp.kp_proc.p_comm);
525 } else {
526 return mkString("???");
527 }
528 }
529 }
530
531 UNPROTECT(1);
532 return R_NilValue;
533 }
534
ps__find_if_env(SEXP marker,SEXP after,SEXP pid)535 SEXP ps__find_if_env(SEXP marker, SEXP after, SEXP pid) {
536 const char *cmarker = CHAR(STRING_ELT(marker, 0));
537 pid_t cpid = INTEGER(pid)[0];
538 SEXP env;
539 size_t i, len;
540 SEXP phandle;
541 ps_handle_t *handle;
542
543 PROTECT(phandle = psll_handle(pid, R_NilValue));
544 handle = R_ExternalPtrAddr(phandle);
545
546 PROTECT(env = ps__get_environ(cpid));
547 if (isNull(env)) {
548 ps__set_error_from_errno();
549 ps__throw_error();
550 }
551
552 len = LENGTH(env);
553
554 for (i = 0; i < len; i++) {
555 if (strstr(CHAR(STRING_ELT(env, i)), cmarker)) {
556 UNPROTECT(2);
557 PS__CHECK_HANDLE(handle);
558 return phandle;
559 }
560 }
561
562 UNPROTECT(2);
563 return R_NilValue;
564 }
565
psll_num_fds(SEXP p)566 SEXP psll_num_fds(SEXP p) {
567 ps_handle_t *handle = R_ExternalPtrAddr(p);
568 struct proc_fdinfo *fds_pointer;
569 int pidinfo_result;
570 int num;
571 pid_t pid;
572
573 if (!handle) error("Process pointer cleaned up already");
574
575 pid = handle->pid;
576
577 pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
578 if (pidinfo_result <= 0) ps__check_for_zombie(handle, 1);
579
580 fds_pointer = malloc(pidinfo_result);
581 if (fds_pointer == NULL) {
582 ps__no_memory("");
583 ps__throw_error();
584 }
585
586 pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer,
587 pidinfo_result);
588
589 if (pidinfo_result <= 0) {
590 free(fds_pointer);
591 ps__check_for_zombie(handle, 1);
592 }
593
594 num = (pidinfo_result / PROC_PIDLISTFD_SIZE);
595 free(fds_pointer);
596
597 PS__CHECK_HANDLE(handle);
598
599 return ScalarInteger(num);
600 }
601
psll_open_files(SEXP p)602 SEXP psll_open_files(SEXP p) {
603 ps_handle_t *handle = R_ExternalPtrAddr(p);
604
605 long pid;
606 int pidinfo_result;
607 int iterations;
608 int i;
609 unsigned long nb;
610
611 struct proc_fdinfo *fds_pointer = NULL;
612 struct proc_fdinfo *fdp_pointer;
613 struct vnode_fdinfowithpath vi;
614
615 SEXP result;
616
617 if (!handle) error("Process pointer cleaned up already");
618
619 pid = handle->pid;
620
621 pidinfo_result = ps__proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
622 if (pidinfo_result <= 0) goto error;
623
624 fds_pointer = malloc(pidinfo_result);
625 if (fds_pointer == NULL) {
626 ps__no_memory("");
627 goto error;
628 }
629 pidinfo_result = ps__proc_pidinfo(
630 pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result);
631
632 if (pidinfo_result <= 0) goto error;
633
634 iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);
635
636 PROTECT(result = allocVector(VECSXP, iterations));
637
638 for (i = 0; i < iterations; i++) {
639 fdp_pointer = &fds_pointer[i];
640
641 if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE) {
642 errno = 0;
643 nb = proc_pidfdinfo((pid_t)pid,
644 fdp_pointer->proc_fd,
645 PROC_PIDFDVNODEPATHINFO,
646 &vi,
647 sizeof(vi));
648
649 // --- errors checking
650 if ((nb <= 0) || nb < sizeof(vi)) {
651 if ((errno == ENOENT) || (errno == EBADF)) {
652 // no such file or directory or bad file descriptor;
653 // let's assume the file has been closed or removed
654 continue;
655 } else {
656 ps__set_error(
657 "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed for %d", (int) pid);
658 goto error;
659 }
660 }
661 // --- /errors checking
662
663 SET_VECTOR_ELT(
664 result, i,
665 ps__build_list("si", vi.pvip.vip_path, (int) fdp_pointer->proc_fd));
666 }
667 }
668
669 free(fds_pointer);
670
671 PS__CHECK_HANDLE(handle);
672
673 UNPROTECT(1);
674 return result;
675
676 error:
677 if (fds_pointer != NULL) free(fds_pointer);
678 ps__check_for_zombie(handle, 1);
679 return R_NilValue;
680 }
681
psll_connections(SEXP p)682 SEXP psll_connections(SEXP p) {
683 ps_handle_t *handle = R_ExternalPtrAddr(p);
684 long pid;
685 int pidinfo_result;
686 int iterations;
687 int i;
688 unsigned long nb;
689
690 struct proc_fdinfo *fds_pointer = NULL;
691 struct proc_fdinfo *fdp_pointer;
692 struct socket_fdinfo si;
693
694 SEXP result;
695
696 if (!handle) error("Process pointer cleaned up already");
697
698 pid = handle->pid;
699
700 if (pid == 0) return allocVector(VECSXP, 0);
701
702 pidinfo_result = ps__proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
703 if (pidinfo_result <= 0) goto error;
704
705 fds_pointer = malloc(pidinfo_result);
706 if (fds_pointer == NULL) {
707 ps__no_memory("");
708 ps__throw_error();
709 }
710
711 pidinfo_result = ps__proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer,
712 pidinfo_result);
713 if (pidinfo_result <= 0) goto error;
714
715 iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);
716 PROTECT(result = allocVector(VECSXP, iterations));
717 for (i = 0; i < iterations; i++) {
718 fdp_pointer = &fds_pointer[i];
719
720 if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET) {
721 errno = 0;
722 nb = proc_pidfdinfo((pid_t)pid, fdp_pointer->proc_fd,
723 PROC_PIDFDSOCKETINFO, &si, sizeof(si));
724
725 // --- errors checking
726 if ((nb <= 0) || (nb < sizeof(si))) {
727 if (errno == EBADF) {
728 // let's assume socket has been closed
729 continue;
730 } else {
731 ps__set_error("proc_pidinfo(PROC_PIDFDSOCKETINFO) failed for %d",
732 (int) pid);
733 goto error;
734 }
735 }
736 // --- /errors checking
737
738 //
739 int fd, family, type, lport, rport, state;
740 char lip[512], rip[512];
741
742 SEXP tuple;
743
744 fd = (int)fdp_pointer->proc_fd;
745 family = si.psi.soi_family;
746 type = si.psi.soi_type;
747
748 if ((family == AF_INET) || (family == AF_INET6)) {
749 if (family == AF_INET) {
750 inet_ntop(AF_INET,
751 &si.psi.soi_proto.pri_tcp.tcpsi_ini. \
752 insi_laddr.ina_46.i46a_addr4,
753 lip,
754 sizeof(lip));
755 inet_ntop(AF_INET,
756 &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr. \
757 ina_46.i46a_addr4,
758 rip,
759 sizeof(rip));
760 } else {
761 inet_ntop(AF_INET6,
762 &si.psi.soi_proto.pri_tcp.tcpsi_ini. \
763 insi_laddr.ina_6,
764 lip, sizeof(lip));
765 inet_ntop(AF_INET6,
766 &si.psi.soi_proto.pri_tcp.tcpsi_ini. \
767 insi_faddr.ina_6,
768 rip, sizeof(rip));
769 }
770
771 // check for inet_ntop failures
772 if (errno != 0) {
773 ps__set_error_from_errno(0);
774 goto error;
775 }
776
777 lport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport);
778 rport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport);
779 if (type == SOCK_STREAM)
780 state = (int)si.psi.soi_proto.pri_tcp.tcpsi_state;
781 else
782 state = NA_INTEGER;
783
784 // construct the python list
785 PROTECT(tuple = ps__build_list("iiisisii", fd, family, type,
786 lip, lport, rip, rport,state));
787 SET_VECTOR_ELT(result, i, tuple);
788 UNPROTECT(1);
789
790 } else if (family == AF_UNIX) {
791 SEXP laddr, raddr;
792 PROTECT(laddr = ps__str_to_utf8(si.psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path));
793 PROTECT(raddr = ps__str_to_utf8(si.psi.soi_proto.pri_un.unsi_caddr.ua_sun.sun_path));
794
795 // construct the python list
796 PROTECT(tuple = ps__build_list("iiiOiOii", fd, family, type,
797 laddr, 0, raddr, 0, NA_INTEGER));
798
799 SET_VECTOR_ELT(result, i, tuple);
800 UNPROTECT(3);
801 }
802 }
803 }
804
805 free(fds_pointer);
806
807 PS__CHECK_HANDLE(handle);
808
809 UNPROTECT(1);
810 return result;
811
812 error:
813 if (fds_pointer) free(fds_pointer);
814 ps__check_for_zombie(handle, 1);
815 return R_NilValue;
816 }
817
ps__users()818 SEXP ps__users() {
819 struct utmpx *utx;
820 SEXP result;
821 PROTECT_INDEX pidx;
822 int len = 10, num = 0;
823
824 PROTECT_WITH_INDEX(result = allocVector(VECSXP, len), &pidx);
825
826 while ((utx = getutxent()) != NULL) {
827
828 if (utx->ut_type != USER_PROCESS) continue;
829
830 if (++num == len) {
831 len *= 2;
832 REPROTECT(result = Rf_lengthgets(result, len), pidx);
833 }
834 SET_VECTOR_ELT(
835 result, num,
836 ps__build_list("sssdi", utx->ut_user, utx->ut_line, utx->ut_host,
837 (double) PS__TV2DOUBLE(utx->ut_tv), utx->ut_pid));
838 }
839
840 endutxent();
841 UNPROTECT(1);
842 return result;
843 }
844
ps__disk_partitions(SEXP all)845 SEXP ps__disk_partitions(SEXP all) {
846 int num;
847 int i;
848 int len;
849 uint64_t flags;
850 char opts[400];
851 struct statfs *fs = NULL;
852 SEXP result;
853
854 // get the number of mount points
855 num = getfsstat(NULL, 0, MNT_NOWAIT);
856 if (num == -1) {
857 ps__set_error_from_errno();
858 goto error;
859 }
860
861 len = sizeof(*fs) * num;
862 fs = malloc(len);
863 if (fs == NULL) {
864 ps__no_memory("");
865 goto error;
866 }
867
868 num = getfsstat(fs, len, MNT_NOWAIT);
869 if (num == -1) {
870 ps__set_error_from_errno();
871 goto error;
872 }
873
874 PROTECT(result = allocVector(VECSXP, num));
875
876 for (i = 0; i < num; i++) {
877 opts[0] = 0;
878 flags = fs[i].f_flags;
879
880 // see sys/mount.h
881 if (flags & MNT_RDONLY)
882 strlcat(opts, "ro", sizeof(opts));
883 else
884 strlcat(opts, "rw", sizeof(opts));
885 if (flags & MNT_SYNCHRONOUS)
886 strlcat(opts, ",sync", sizeof(opts));
887 if (flags & MNT_NOEXEC)
888 strlcat(opts, ",noexec", sizeof(opts));
889 if (flags & MNT_NOSUID)
890 strlcat(opts, ",nosuid", sizeof(opts));
891 if (flags & MNT_UNION)
892 strlcat(opts, ",union", sizeof(opts));
893 if (flags & MNT_ASYNC)
894 strlcat(opts, ",async", sizeof(opts));
895 if (flags & MNT_EXPORTED)
896 strlcat(opts, ",exported", sizeof(opts));
897 if (flags & MNT_QUARANTINE)
898 strlcat(opts, ",quarantine", sizeof(opts));
899 if (flags & MNT_LOCAL)
900 strlcat(opts, ",local", sizeof(opts));
901 if (flags & MNT_QUOTA)
902 strlcat(opts, ",quota", sizeof(opts));
903 if (flags & MNT_ROOTFS)
904 strlcat(opts, ",rootfs", sizeof(opts));
905 if (flags & MNT_DOVOLFS)
906 strlcat(opts, ",dovolfs", sizeof(opts));
907 if (flags & MNT_DONTBROWSE)
908 strlcat(opts, ",dontbrowse", sizeof(opts));
909 if (flags & MNT_IGNORE_OWNERSHIP)
910 strlcat(opts, ",ignore-ownership", sizeof(opts));
911 if (flags & MNT_AUTOMOUNTED)
912 strlcat(opts, ",automounted", sizeof(opts));
913 if (flags & MNT_JOURNALED)
914 strlcat(opts, ",journaled", sizeof(opts));
915 if (flags & MNT_NOUSERXATTR)
916 strlcat(opts, ",nouserxattr", sizeof(opts));
917 if (flags & MNT_DEFWRITE)
918 strlcat(opts, ",defwrite", sizeof(opts));
919 if (flags & MNT_MULTILABEL)
920 strlcat(opts, ",multilabel", sizeof(opts));
921 if (flags & MNT_NOATIME)
922 strlcat(opts, ",noatime", sizeof(opts));
923 if (flags & MNT_UPDATE)
924 strlcat(opts, ",update", sizeof(opts));
925 if (flags & MNT_RELOAD)
926 strlcat(opts, ",reload", sizeof(opts));
927 if (flags & MNT_FORCE)
928 strlcat(opts, ",force", sizeof(opts));
929 if (flags & MNT_CMDFLAGS)
930 strlcat(opts, ",cmdflags", sizeof(opts));
931
932 SET_VECTOR_ELT(
933 result, i,
934 ps__build_list("ssss", fs[i].f_mntfromname, fs[i].f_mntonname,
935 fs[i].f_fstypename, opts));
936 }
937
938 free(fs);
939 UNPROTECT(1);
940 return result;
941
942 error:
943 if (fs != NULL) free(fs);
944 ps__throw_error();
945 return R_NilValue;
946 }
947
ps__sys_vminfo(vm_statistics_data_t * vmstat)948 int ps__sys_vminfo(vm_statistics_data_t *vmstat) {
949 kern_return_t ret;
950 mach_msg_type_number_t count = sizeof(*vmstat) / sizeof(integer_t);
951 mach_port_t mport = mach_host_self();
952
953 ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)vmstat, &count);
954 if (ret != KERN_SUCCESS) {
955 ps__set_error(
956 "host_statistics(HOST_VM_INFO) syscall failed: %s",
957 mach_error_string(ret)
958 );
959 return 1;
960 }
961 mach_port_deallocate(mach_task_self(), mport);
962 return 0;
963 }
964
ps__system_memory()965 SEXP ps__system_memory() {
966 int mib[2];
967 uint64_t total;
968 size_t len = sizeof(total);
969 vm_statistics_data_t vm;
970 int pagesize = getpagesize();
971 // physical mem
972 mib[0] = CTL_HW;
973 mib[1] = HW_MEMSIZE;
974
975 // This is also available as sysctlbyname("hw.memsize").
976 if (sysctl(mib, 2, &total, &len, NULL, 0)) {
977 if (errno != 0) {
978 ps__set_error_from_errno();
979 } else {
980 ps__set_error("sysctl(HW_MEMSIZE) syscall failed");
981 }
982 ps__throw_error();
983 }
984
985 // vm
986 if (ps__sys_vminfo(&vm)) ps__throw_error();
987
988 return ps__build_named_list(
989 "dddddd",
990 "total", (double) total,
991 "active", (double) vm.active_count * pagesize,
992 "inactive", (double) vm.inactive_count * pagesize,
993 "wired", (double) vm.wire_count * pagesize,
994 "free", (double) vm.free_count * pagesize,
995 "speculative", (double) vm.speculative_count * pagesize
996 );
997 }
998
ps__system_swap()999 SEXP ps__system_swap() {
1000 int mib[2];
1001 size_t size;
1002 struct xsw_usage totals;
1003 vm_statistics_data_t vm;
1004 int pagesize = getpagesize();
1005
1006 mib[0] = CTL_VM;
1007 mib[1] = VM_SWAPUSAGE;
1008 size = sizeof(totals);
1009 if (sysctl(mib, 2, &totals, &size, NULL, 0) == -1) {
1010 if (errno != 0) {
1011 ps__set_error_from_errno();
1012 } else {
1013 ps__set_error("sysctl(VM_SWAPUSAGE) syscall failed");
1014 }
1015 ps__throw_error();
1016 }
1017
1018 // vm
1019 if (ps__sys_vminfo(&vm)) ps__throw_error();
1020
1021 return ps__build_named_list(
1022 "ddddd",
1023 "total", (double) totals.xsu_total,
1024 "used", (double) totals.xsu_used,
1025 "free", (double) totals.xsu_avail,
1026 "sin", (double) vm.pageins * pagesize,
1027 "sout", (double) vm.pageouts * pagesize
1028 );
1029 }
1030
1031 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
1032
ps__loadavg(SEXP counter_name)1033 SEXP ps__loadavg(SEXP counter_name) {
1034 struct loadavg info;
1035 size_t size = sizeof(info);
1036 int which[] = {CTL_VM, VM_LOADAVG};
1037
1038 if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) < 0) {
1039 ps__set_error_from_errno();
1040 ps__throw_error();
1041 }
1042
1043 SEXP ret = PROTECT(allocVector(REALSXP, 3));
1044 REAL(ret)[0] = (double) info.ldavg[0] / info.fscale;
1045 REAL(ret)[1] = (double) info.ldavg[1] / info.fscale;
1046 REAL(ret)[2] = (double) info.ldavg[2] / info.fscale;
1047
1048 UNPROTECT(1);
1049 return ret;
1050 }
1051
ps__system_cpu_times()1052 SEXP ps__system_cpu_times() {
1053 mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
1054 kern_return_t error;
1055 host_cpu_load_info_data_t r_load;
1056
1057 mach_port_t host_port = mach_host_self();
1058 error = host_statistics(host_port, HOST_CPU_LOAD_INFO,
1059 (host_info_t)&r_load, &count);
1060
1061 mach_port_deallocate(mach_task_self(), host_port);
1062
1063 if (error != KERN_SUCCESS) {
1064 ps__set_error_from_errno();
1065 ps__throw_error();
1066 }
1067
1068 const char *nms[] = { "user", "nice", "system", "idle", "" };
1069 SEXP ret = PROTECT(Rf_mkNamed(REALSXP, nms));
1070
1071 REAL(ret)[0] = (double) r_load.cpu_ticks[CPU_STATE_USER] / CLK_TCK;
1072 REAL(ret)[1] = (double) r_load.cpu_ticks[CPU_STATE_NICE] / CLK_TCK;
1073 REAL(ret)[2] = (double) r_load.cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK;
1074 REAL(ret)[3] = (double) r_load.cpu_ticks[CPU_STATE_IDLE] / CLK_TCK;
1075
1076 UNPROTECT(1);
1077 return ret;
1078 }
1079