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