1 /*
2  * Copyright (c) 2009, Giampaolo Rodola'
3  * Copyright (c) 2017, Arnon Yaari
4  * All rights reserved.
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 /*
10  * AIX support is experimental at this time.
11  * The following functions and methods are unsupported on the AIX platform:
12  * - psutil.Process.memory_maps
13  *
14  * Known limitations:
15  * - psutil.Process.io_counters read count is always 0
16  * - psutil.Process.io_counters may not be available on older AIX versions
17  * - psutil.Process.threads may not be available on older AIX versions
18  * - psutil.net_io_counters may not be available on older AIX versions
19  * - reading basic process info may fail or return incorrect values when
20  *   process is starting (see IBM APAR IV58499 - fixed in newer AIX versions)
21  * - sockets and pipes may not be counted in num_fds (fixed in newer AIX
22  *    versions)
23  *
24  * Useful resources:
25  * - proc filesystem: http://www-01.ibm.com/support/knowledgecenter/
26  *       ssw_aix_72/com.ibm.aix.files/proc.htm
27  * - libperfstat: http://www-01.ibm.com/support/knowledgecenter/
28  *       ssw_aix_72/com.ibm.aix.files/libperfstat.h.htm
29  */
30 
31 #include <Python.h>
32 #include <sys/limits.h>
33 #include <sys/proc.h>
34 #include <sys/procfs.h>
35 #include <sys/socket.h>
36 #include <sys/stat.h>
37 #include <sys/sysinfo.h>
38 #include <sys/thread.h>
39 #include <sys/types.h>
40 #include <fcntl.h>
41 #include <utmp.h>
42 #include <utmpx.h>
43 #include <mntent.h>
44 #include <sys/ioctl.h>
45 #include <sys/tihdr.h>
46 #include <stropts.h>
47 #include <netinet/tcp_fsm.h>
48 #include <arpa/inet.h>
49 #include <net/if.h>
50 #include <libperfstat.h>
51 #include <unistd.h>
52 
53 #include "_psutil_common.h"
54 #include "_psutil_posix.h"
55 #include "arch/aix/ifaddrs.h"
56 #include "arch/aix/net_connections.h"
57 #include "arch/aix/common.h"
58 
59 
60 #define TV2DOUBLE(t)   (((t).tv_nsec * 0.000000001) + (t).tv_sec)
61 
62 /*
63  * Read a file content and fills a C structure with it.
64  */
65 int
psutil_file_to_struct(char * path,void * fstruct,size_t size)66 psutil_file_to_struct(char *path, void *fstruct, size_t size) {
67     int fd;
68     size_t nbytes;
69     fd = open(path, O_RDONLY);
70     if (fd == -1) {
71         PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
72         return 0;
73     }
74     nbytes = read(fd, fstruct, size);
75     if (nbytes <= 0) {
76         close(fd);
77         PyErr_SetFromErrno(PyExc_OSError);
78         return 0;
79     }
80     if (nbytes != size) {
81         close(fd);
82         PyErr_SetString(PyExc_RuntimeError, "structure size mismatch");
83         return 0;
84     }
85     close(fd);
86     return nbytes;
87 }
88 
89 
90 /*
91  * Return process ppid, rss, vms, ctime, nice, nthreads, status and tty
92  * as a Python tuple.
93  */
94 static PyObject *
psutil_proc_basic_info(PyObject * self,PyObject * args)95 psutil_proc_basic_info(PyObject *self, PyObject *args) {
96     int pid;
97     char path[100];
98     psinfo_t info;
99     pstatus_t status;
100     const char *procfs_path;
101 
102     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
103         return NULL;
104 
105     sprintf(path, "%s/%i/psinfo", procfs_path, pid);
106     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
107         return NULL;
108 
109     if (info.pr_nlwp == 0 && info.pr_lwp.pr_lwpid == 0) {
110         // From the /proc docs: "If the process is a zombie, the pr_nlwp
111         // and pr_lwp.pr_lwpid flags are zero."
112         status.pr_stat = SZOMB;
113     } else if (info.pr_flag & SEXIT) {
114         // "exiting" processes don't have /proc/<pid>/status
115         // There are other "exiting" processes that 'ps' shows as "active"
116         status.pr_stat = SACTIVE;
117     } else {
118         sprintf(path, "%s/%i/status", procfs_path, pid);
119         if (! psutil_file_to_struct(path, (void *)&status, sizeof(status)))
120             return NULL;
121     }
122 
123     return Py_BuildValue("KKKdiiiK",
124         (unsigned long long) info.pr_ppid,      // parent pid
125         (unsigned long long) info.pr_rssize,    // rss
126         (unsigned long long) info.pr_size,      // vms
127         TV2DOUBLE(info.pr_start),               // create time
128         (int) info.pr_lwp.pr_nice,              // nice
129         (int) info.pr_nlwp,                     // no. of threads
130         (int) status.pr_stat,                   // status code
131         (unsigned long long)info.pr_ttydev      // tty nr
132         );
133 }
134 
135 
136 /*
137  * Return process name as a Python string.
138  */
139 static PyObject *
psutil_proc_name(PyObject * self,PyObject * args)140 psutil_proc_name(PyObject *self, PyObject *args) {
141     int pid;
142     char path[100];
143     psinfo_t info;
144     const char *procfs_path;
145 
146     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
147         return NULL;
148     sprintf(path, "%s/%i/psinfo", procfs_path, pid);
149     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
150         return NULL;
151 
152     return PyUnicode_DecodeFSDefaultAndSize(info.pr_fname, PRFNSZ);
153 }
154 
155 
156 /*
157  * Return process command line arguments as a Python list
158  */
159 static PyObject *
psutil_proc_args(PyObject * self,PyObject * args)160 psutil_proc_args(PyObject *self, PyObject *args) {
161     int pid;
162     PyObject *py_retlist = PyList_New(0);
163     PyObject *py_arg = NULL;
164     struct procsinfo procbuf;
165     long arg_max;
166     char *argbuf = NULL;
167     char *curarg = NULL;
168     int ret;
169 
170     if (py_retlist == NULL)
171         return NULL;
172     if (!PyArg_ParseTuple(args, "i", &pid))
173         goto error;
174     arg_max = sysconf(_SC_ARG_MAX);
175     argbuf = malloc(arg_max);
176     if (argbuf == NULL) {
177         PyErr_NoMemory();
178         goto error;
179     }
180 
181     procbuf.pi_pid = pid;
182     ret = getargs(&procbuf, sizeof(procbuf), argbuf, ARG_MAX);
183     if (ret == -1) {
184         PyErr_SetFromErrno(PyExc_OSError);
185         goto error;
186     }
187 
188     curarg = argbuf;
189     /* getargs will always append an extra NULL to end the arg list,
190      * even if the buffer is not big enough (even though it is supposed
191      * to be) so the following 'while' is safe */
192     while (*curarg != '\0') {
193         py_arg = PyUnicode_DecodeFSDefault(curarg);
194         if (!py_arg)
195             goto error;
196         if (PyList_Append(py_retlist, py_arg))
197             goto error;
198         Py_DECREF(py_arg);
199         curarg = strchr(curarg, '\0') + 1;
200     }
201 
202     free(argbuf);
203 
204     return py_retlist;
205 
206 error:
207     if (argbuf != NULL)
208         free(argbuf);
209     Py_XDECREF(py_retlist);
210     Py_XDECREF(py_arg);
211     return NULL;
212 }
213 
214 
215 /*
216  * Return process environment variables as a Python dict
217  */
218 static PyObject *
psutil_proc_environ(PyObject * self,PyObject * args)219 psutil_proc_environ(PyObject *self, PyObject *args) {
220     int pid;
221     PyObject *py_retdict = PyDict_New();
222     PyObject *py_key = NULL;
223     PyObject *py_val = NULL;
224     struct procsinfo procbuf;
225     long env_max;
226     char *envbuf = NULL;
227     char *curvar = NULL;
228     char *separator = NULL;
229     int ret;
230 
231     if (py_retdict == NULL)
232         return NULL;
233     if (!PyArg_ParseTuple(args, "i", &pid))
234         goto error;
235     env_max = sysconf(_SC_ARG_MAX);
236     envbuf = malloc(env_max);
237     if (envbuf == NULL) {
238         PyErr_NoMemory();
239         goto error;
240     }
241 
242     procbuf.pi_pid = pid;
243     ret = getevars(&procbuf, sizeof(procbuf), envbuf, ARG_MAX);
244     if (ret == -1) {
245         PyErr_SetFromErrno(PyExc_OSError);
246         goto error;
247     }
248 
249     curvar = envbuf;
250     /* getevars will always append an extra NULL to end the arg list,
251      * even if the buffer is not big enough (even though it is supposed
252      * to be) so the following 'while' is safe */
253     while (*curvar != '\0') {
254         separator = strchr(curvar, '=');
255         if (separator != NULL) {
256             py_key = PyUnicode_DecodeFSDefaultAndSize(
257                 curvar,
258                 (Py_ssize_t)(separator - curvar)
259             );
260             if (!py_key)
261                 goto error;
262             py_val = PyUnicode_DecodeFSDefault(separator + 1);
263             if (!py_val)
264                 goto error;
265             if (PyDict_SetItem(py_retdict, py_key, py_val))
266                 goto error;
267             Py_CLEAR(py_key);
268             Py_CLEAR(py_val);
269         }
270         curvar = strchr(curvar, '\0') + 1;
271     }
272 
273     free(envbuf);
274 
275     return py_retdict;
276 
277 error:
278     if (envbuf != NULL)
279         free(envbuf);
280     Py_XDECREF(py_retdict);
281     Py_XDECREF(py_key);
282     Py_XDECREF(py_val);
283     return NULL;
284 }
285 
286 
287 #ifdef CURR_VERSION_THREAD
288 
289 /*
290  * Retrieves all threads used by process returning a list of tuples
291  * including thread id, user time and system time.
292  */
293 static PyObject *
psutil_proc_threads(PyObject * self,PyObject * args)294 psutil_proc_threads(PyObject *self, PyObject *args) {
295     long pid;
296     PyObject *py_retlist = PyList_New(0);
297     PyObject *py_tuple = NULL;
298     perfstat_thread_t *threadt = NULL;
299     perfstat_id_t id;
300     int i, rc, thread_count;
301 
302     if (py_retlist == NULL)
303         return NULL;
304     if (! PyArg_ParseTuple(args, "l", &pid))
305         goto error;
306 
307     /* Get the count of threads */
308     thread_count = perfstat_thread(NULL, NULL, sizeof(perfstat_thread_t), 0);
309     if (thread_count <= 0) {
310         PyErr_SetFromErrno(PyExc_OSError);
311         goto error;
312     }
313 
314     /* Allocate enough memory */
315     threadt = (perfstat_thread_t *)calloc(thread_count,
316         sizeof(perfstat_thread_t));
317     if (threadt == NULL) {
318         PyErr_NoMemory();
319         goto error;
320     }
321 
322     strcpy(id.name, "");
323     rc = perfstat_thread(&id, threadt, sizeof(perfstat_thread_t),
324         thread_count);
325     if (rc <= 0) {
326         PyErr_SetFromErrno(PyExc_OSError);
327         goto error;
328     }
329 
330     for (i = 0; i < thread_count; i++) {
331         if (threadt[i].pid != pid)
332             continue;
333 
334         py_tuple = Py_BuildValue("Idd",
335                                  threadt[i].tid,
336                                  threadt[i].ucpu_time,
337                                  threadt[i].scpu_time);
338         if (py_tuple == NULL)
339             goto error;
340         if (PyList_Append(py_retlist, py_tuple))
341             goto error;
342         Py_DECREF(py_tuple);
343     }
344     free(threadt);
345     return py_retlist;
346 
347 error:
348     Py_XDECREF(py_tuple);
349     Py_DECREF(py_retlist);
350     if (threadt != NULL)
351         free(threadt);
352     return NULL;
353 }
354 
355 #endif
356 
357 
358 #ifdef CURR_VERSION_PROCESS
359 
360 static PyObject *
psutil_proc_io_counters(PyObject * self,PyObject * args)361 psutil_proc_io_counters(PyObject *self, PyObject *args) {
362     long pid;
363     int rc;
364     perfstat_process_t procinfo;
365     perfstat_id_t id;
366     if (! PyArg_ParseTuple(args, "l", &pid))
367         return NULL;
368 
369     snprintf(id.name, sizeof(id.name), "%ld", pid);
370     rc = perfstat_process(&id, &procinfo, sizeof(perfstat_process_t), 1);
371     if (rc <= 0) {
372         PyErr_SetFromErrno(PyExc_OSError);
373         return NULL;
374     }
375 
376     return Py_BuildValue("(KKKK)",
377                          procinfo.inOps,      // XXX always 0
378                          procinfo.outOps,
379                          procinfo.inBytes,    // XXX always 0
380                          procinfo.outBytes);
381 }
382 
383 #endif
384 
385 
386 /*
387  * Return process user and system CPU times as a Python tuple.
388  */
389 static PyObject *
psutil_proc_cpu_times(PyObject * self,PyObject * args)390 psutil_proc_cpu_times(PyObject *self, PyObject *args) {
391     int pid;
392     char path[100];
393     pstatus_t info;
394     const char *procfs_path;
395 
396     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
397         return NULL;
398     sprintf(path, "%s/%i/status", procfs_path, pid);
399     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
400         return NULL;
401     // results are more precise than os.times()
402     return Py_BuildValue("dddd",
403                          TV2DOUBLE(info.pr_utime),
404                          TV2DOUBLE(info.pr_stime),
405                          TV2DOUBLE(info.pr_cutime),
406                          TV2DOUBLE(info.pr_cstime));
407 }
408 
409 
410 /*
411  * Return process uids/gids as a Python tuple.
412  */
413 static PyObject *
psutil_proc_cred(PyObject * self,PyObject * args)414 psutil_proc_cred(PyObject *self, PyObject *args) {
415     int pid;
416     char path[100];
417     prcred_t info;
418     const char *procfs_path;
419 
420     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
421         return NULL;
422     sprintf(path, "%s/%i/cred", procfs_path, pid);
423     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
424         return NULL;
425     return Py_BuildValue("iiiiii",
426                          info.pr_ruid, info.pr_euid, info.pr_suid,
427                          info.pr_rgid, info.pr_egid, info.pr_sgid);
428 }
429 
430 
431 /*
432  * Return process voluntary and involuntary context switches as a Python tuple.
433  */
434 static PyObject *
psutil_proc_num_ctx_switches(PyObject * self,PyObject * args)435 psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) {
436     PyObject *py_tuple = NULL;
437     pid32_t requested_pid;
438     pid32_t pid = 0;
439     int np = 0;
440     struct procentry64 *processes = (struct procentry64 *)NULL;
441     struct procentry64 *p;
442 
443     if (! PyArg_ParseTuple(args, "i", &requested_pid))
444         return NULL;
445 
446     processes = psutil_read_process_table(&np);
447     if (!processes)
448         return NULL;
449 
450     /* Loop through processes */
451     for (p = processes; np > 0; np--, p++) {
452         pid = p->pi_pid;
453         if (requested_pid != pid)
454             continue;
455         py_tuple = Py_BuildValue("LL",
456             (long long) p->pi_ru.ru_nvcsw,    /* voluntary context switches */
457             (long long) p->pi_ru.ru_nivcsw);  /* involuntary */
458         free(processes);
459         return py_tuple;
460     }
461 
462     /* finished iteration without finding requested pid */
463     free(processes);
464     return NoSuchProcess("psutil_read_process_table (no PID found)");
465 }
466 
467 
468 /*
469  * Return users currently connected on the system.
470  */
471 static PyObject *
psutil_users(PyObject * self,PyObject * args)472 psutil_users(PyObject *self, PyObject *args) {
473     struct utmpx *ut;
474     PyObject *py_retlist = PyList_New(0);
475     PyObject *py_tuple = NULL;
476     PyObject *py_username = NULL;
477     PyObject *py_tty = NULL;
478     PyObject *py_hostname = NULL;
479     PyObject *py_user_proc = NULL;
480 
481     if (py_retlist == NULL)
482         return NULL;
483 
484     setutxent();
485     while (NULL != (ut = getutxent())) {
486         if (ut->ut_type == USER_PROCESS)
487             py_user_proc = Py_True;
488         else
489             py_user_proc = Py_False;
490         py_username = PyUnicode_DecodeFSDefault(ut->ut_user);
491         if (! py_username)
492             goto error;
493         py_tty = PyUnicode_DecodeFSDefault(ut->ut_line);
494         if (! py_tty)
495             goto error;
496         py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host);
497         if (! py_hostname)
498             goto error;
499         py_tuple = Py_BuildValue(
500             "(OOOfOi)",
501             py_username,              // username
502             py_tty,                   // tty
503             py_hostname,              // hostname
504             (float)ut->ut_tv.tv_sec,  // tstamp
505             py_user_proc,             // (bool) user process
506             ut->ut_pid                // process id
507         );
508         if (py_tuple == NULL)
509             goto error;
510         if (PyList_Append(py_retlist, py_tuple))
511             goto error;
512         Py_CLEAR(py_username);
513         Py_CLEAR(py_tty);
514         Py_CLEAR(py_hostname);
515         Py_CLEAR(py_tuple);
516     }
517     endutxent();
518 
519     return py_retlist;
520 
521 error:
522     Py_XDECREF(py_username);
523     Py_XDECREF(py_tty);
524     Py_XDECREF(py_hostname);
525     Py_XDECREF(py_tuple);
526     Py_DECREF(py_retlist);
527     if (ut != NULL)
528         endutxent();
529     return NULL;
530 }
531 
532 
533 /*
534  * Return disk mounted partitions as a list of tuples including device,
535  * mount point and filesystem type.
536  */
537 static PyObject *
psutil_disk_partitions(PyObject * self,PyObject * args)538 psutil_disk_partitions(PyObject *self, PyObject *args) {
539     FILE *file = NULL;
540     struct mntent * mt = NULL;
541     PyObject *py_dev = NULL;
542     PyObject *py_mountp = NULL;
543     PyObject *py_tuple = NULL;
544     PyObject *py_retlist = PyList_New(0);
545 
546     if (py_retlist == NULL)
547         return NULL;
548 
549     file = setmntent(MNTTAB, "rb");
550     if (file == NULL) {
551         PyErr_SetFromErrno(PyExc_OSError);
552         goto error;
553     }
554     mt = getmntent(file);
555     while (mt != NULL) {
556         py_dev = PyUnicode_DecodeFSDefault(mt->mnt_fsname);
557         if (! py_dev)
558             goto error;
559         py_mountp = PyUnicode_DecodeFSDefault(mt->mnt_dir);
560         if (! py_mountp)
561             goto error;
562         py_tuple = Py_BuildValue(
563             "(OOss)",
564             py_dev,         // device
565             py_mountp,      // mount point
566             mt->mnt_type,   // fs type
567             mt->mnt_opts);  // options
568         if (py_tuple == NULL)
569             goto error;
570         if (PyList_Append(py_retlist, py_tuple))
571             goto error;
572         Py_CLEAR(py_dev);
573         Py_CLEAR(py_mountp);
574         Py_CLEAR(py_tuple);
575         mt = getmntent(file);
576     }
577     endmntent(file);
578     return py_retlist;
579 
580 error:
581     Py_XDECREF(py_dev);
582     Py_XDECREF(py_mountp);
583     Py_XDECREF(py_tuple);
584     Py_DECREF(py_retlist);
585     if (file != NULL)
586         endmntent(file);
587     return NULL;
588 }
589 
590 
591 #if defined(CURR_VERSION_NETINTERFACE) && CURR_VERSION_NETINTERFACE >= 3
592 
593 /*
594  * Return a list of tuples for network I/O statistics.
595  */
596 static PyObject *
psutil_net_io_counters(PyObject * self,PyObject * args)597 psutil_net_io_counters(PyObject *self, PyObject *args) {
598     perfstat_netinterface_t *statp = NULL;
599     int tot, i;
600     perfstat_id_t first;
601 
602     PyObject *py_retdict = PyDict_New();
603     PyObject *py_ifc_info = NULL;
604 
605     if (py_retdict == NULL)
606         return NULL;
607 
608     /* check how many perfstat_netinterface_t structures are available */
609     tot = perfstat_netinterface(
610         NULL, NULL, sizeof(perfstat_netinterface_t), 0);
611     if (tot == 0) {
612         // no network interfaces - return empty dict
613         return py_retdict;
614     }
615     if (tot < 0) {
616         PyErr_SetFromErrno(PyExc_OSError);
617         goto error;
618     }
619     statp = (perfstat_netinterface_t *)
620         malloc(tot * sizeof(perfstat_netinterface_t));
621     if (statp == NULL) {
622         PyErr_NoMemory();
623         goto error;
624     }
625     strcpy(first.name, FIRST_NETINTERFACE);
626     tot = perfstat_netinterface(&first, statp,
627         sizeof(perfstat_netinterface_t), tot);
628     if (tot < 0) {
629         PyErr_SetFromErrno(PyExc_OSError);
630         goto error;
631     }
632 
633     for (i = 0; i < tot; i++) {
634         py_ifc_info = Py_BuildValue("(KKKKKKKK)",
635             statp[i].obytes,      /* number of bytes sent on interface */
636             statp[i].ibytes,      /* number of bytes received on interface */
637             statp[i].opackets,    /* number of packets sent on interface */
638             statp[i].ipackets,    /* number of packets received on interface */
639             statp[i].ierrors,     /* number of input errors on interface */
640             statp[i].oerrors,     /* number of output errors on interface */
641             statp[i].if_iqdrops,  /* Dropped on input, this interface */
642             statp[i].xmitdrops    /* number of packets not transmitted */
643            );
644         if (!py_ifc_info)
645             goto error;
646         if (PyDict_SetItemString(py_retdict, statp[i].name, py_ifc_info))
647             goto error;
648         Py_DECREF(py_ifc_info);
649     }
650 
651     free(statp);
652     return py_retdict;
653 
654 error:
655     if (statp != NULL)
656         free(statp);
657     Py_XDECREF(py_ifc_info);
658     Py_DECREF(py_retdict);
659     return NULL;
660 }
661 
662 #endif
663 
664 
665 static PyObject*
psutil_net_if_stats(PyObject * self,PyObject * args)666 psutil_net_if_stats(PyObject* self, PyObject* args) {
667     char *nic_name;
668     int sock = 0;
669     int ret;
670     int mtu;
671     struct ifreq ifr;
672     PyObject *py_is_up = NULL;
673     PyObject *py_retlist = NULL;
674 
675     if (! PyArg_ParseTuple(args, "s", &nic_name))
676         return NULL;
677 
678     sock = socket(AF_INET, SOCK_DGRAM, 0);
679     if (sock == -1)
680         goto error;
681 
682     PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
683 
684     // is up?
685     ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
686     if (ret == -1)
687         goto error;
688     if ((ifr.ifr_flags & IFF_UP) != 0)
689         py_is_up = Py_True;
690     else
691         py_is_up = Py_False;
692     Py_INCREF(py_is_up);
693 
694     // MTU
695     ret = ioctl(sock, SIOCGIFMTU, &ifr);
696     if (ret == -1)
697         goto error;
698     mtu = ifr.ifr_mtu;
699 
700     close(sock);
701     py_retlist = Py_BuildValue("[Oi]", py_is_up, mtu);
702     if (!py_retlist)
703         goto error;
704     Py_DECREF(py_is_up);
705     return py_retlist;
706 
707 error:
708     Py_XDECREF(py_is_up);
709     if (sock != 0)
710         close(sock);
711     PyErr_SetFromErrno(PyExc_OSError);
712     return NULL;
713 }
714 
715 
716 static PyObject *
psutil_boot_time(PyObject * self,PyObject * args)717 psutil_boot_time(PyObject *self, PyObject *args) {
718     float boot_time = 0.0;
719     struct utmpx *ut;
720 
721     setutxent();
722     while (NULL != (ut = getutxent())) {
723         if (ut->ut_type == BOOT_TIME) {
724             boot_time = (float)ut->ut_tv.tv_sec;
725             break;
726         }
727     }
728     endutxent();
729     if (boot_time == 0.0) {
730         /* could not find BOOT_TIME in getutxent loop */
731         PyErr_SetString(PyExc_RuntimeError, "can't determine boot time");
732         return NULL;
733     }
734     return Py_BuildValue("f", boot_time);
735 }
736 
737 
738 /*
739  * Return a Python list of tuple representing per-cpu times
740  */
741 static PyObject *
psutil_per_cpu_times(PyObject * self,PyObject * args)742 psutil_per_cpu_times(PyObject *self, PyObject *args) {
743     int ncpu, rc, i;
744     long ticks;
745     perfstat_cpu_t *cpu = NULL;
746     perfstat_id_t id;
747     PyObject *py_retlist = PyList_New(0);
748     PyObject *py_cputime = NULL;
749 
750     if (py_retlist == NULL)
751         return NULL;
752 
753     /* get the number of ticks per second */
754     ticks = sysconf(_SC_CLK_TCK);
755     if (ticks < 0) {
756         PyErr_SetFromErrno(PyExc_OSError);
757         goto error;
758     }
759 
760     /* get the number of cpus in ncpu */
761     ncpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
762     if (ncpu <= 0){
763         PyErr_SetFromErrno(PyExc_OSError);
764         goto error;
765     }
766 
767     /* allocate enough memory to hold the ncpu structures */
768     cpu = (perfstat_cpu_t *) malloc(ncpu * sizeof(perfstat_cpu_t));
769     if (cpu == NULL) {
770         PyErr_NoMemory();
771         goto error;
772     }
773 
774     strcpy(id.name, "");
775     rc = perfstat_cpu(&id, cpu, sizeof(perfstat_cpu_t), ncpu);
776 
777     if (rc <= 0) {
778         PyErr_SetFromErrno(PyExc_OSError);
779         goto error;
780     }
781 
782     for (i = 0; i < ncpu; i++) {
783         py_cputime = Py_BuildValue(
784             "(dddd)",
785             (double)cpu[i].user / ticks,
786             (double)cpu[i].sys / ticks,
787             (double)cpu[i].idle / ticks,
788             (double)cpu[i].wait / ticks);
789         if (!py_cputime)
790             goto error;
791         if (PyList_Append(py_retlist, py_cputime))
792             goto error;
793         Py_DECREF(py_cputime);
794     }
795     free(cpu);
796     return py_retlist;
797 
798 error:
799     Py_XDECREF(py_cputime);
800     Py_DECREF(py_retlist);
801     if (cpu != NULL)
802         free(cpu);
803     return NULL;
804 }
805 
806 
807 /*
808  * Return disk IO statistics.
809  */
810 static PyObject *
psutil_disk_io_counters(PyObject * self,PyObject * args)811 psutil_disk_io_counters(PyObject *self, PyObject *args) {
812     PyObject *py_retdict = PyDict_New();
813     PyObject *py_disk_info = NULL;
814     perfstat_disk_t *diskt = NULL;
815     perfstat_id_t id;
816     int i, rc, disk_count;
817 
818     if (py_retdict == NULL)
819         return NULL;
820 
821     /* Get the count of disks */
822     disk_count = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0);
823     if (disk_count <= 0) {
824         PyErr_SetFromErrno(PyExc_OSError);
825         goto error;
826     }
827 
828     /* Allocate enough memory */
829     diskt = (perfstat_disk_t *)calloc(disk_count,
830         sizeof(perfstat_disk_t));
831     if (diskt == NULL) {
832         PyErr_NoMemory();
833         goto error;
834     }
835 
836     strcpy(id.name, FIRST_DISK);
837     rc = perfstat_disk(&id, diskt, sizeof(perfstat_disk_t),
838         disk_count);
839     if (rc <= 0) {
840         PyErr_SetFromErrno(PyExc_OSError);
841         goto error;
842     }
843 
844     for (i = 0; i < disk_count; i++) {
845         py_disk_info = Py_BuildValue(
846             "KKKKKK",
847             diskt[i].__rxfers,
848             diskt[i].xfers - diskt[i].__rxfers,
849             diskt[i].rblks * diskt[i].bsize,
850             diskt[i].wblks * diskt[i].bsize,
851             diskt[i].rserv / 1000 / 1000,  // from nano to milli secs
852             diskt[i].wserv / 1000 / 1000   // from nano to milli secs
853         );
854         if (py_disk_info == NULL)
855             goto error;
856         if (PyDict_SetItemString(py_retdict, diskt[i].name,
857                                  py_disk_info))
858             goto error;
859         Py_DECREF(py_disk_info);
860     }
861     free(diskt);
862     return py_retdict;
863 
864 error:
865     Py_XDECREF(py_disk_info);
866     Py_DECREF(py_retdict);
867     if (diskt != NULL)
868         free(diskt);
869     return NULL;
870 }
871 
872 
873 /*
874  * Return virtual memory usage statistics.
875  */
876 static PyObject *
psutil_virtual_mem(PyObject * self,PyObject * args)877 psutil_virtual_mem(PyObject *self, PyObject *args) {
878     int rc;
879     long pagesize = psutil_getpagesize();
880     perfstat_memory_total_t memory;
881 
882     rc = perfstat_memory_total(
883         NULL, &memory, sizeof(perfstat_memory_total_t), 1);
884     if (rc <= 0){
885         PyErr_SetFromErrno(PyExc_OSError);
886         return NULL;
887     }
888 
889     return Py_BuildValue("KKKKK",
890         (unsigned long long) memory.real_total * pagesize,
891         (unsigned long long) memory.real_avail * pagesize,
892         (unsigned long long) memory.real_free * pagesize,
893         (unsigned long long) memory.real_pinned * pagesize,
894         (unsigned long long) memory.real_inuse * pagesize
895     );
896 }
897 
898 
899 /*
900  * Return stats about swap memory.
901  */
902 static PyObject *
psutil_swap_mem(PyObject * self,PyObject * args)903 psutil_swap_mem(PyObject *self, PyObject *args) {
904     int rc;
905     long pagesize = psutil_getpagesize();
906     perfstat_memory_total_t memory;
907 
908     rc = perfstat_memory_total(
909         NULL, &memory, sizeof(perfstat_memory_total_t), 1);
910     if (rc <= 0){
911         PyErr_SetFromErrno(PyExc_OSError);
912         return NULL;
913     }
914 
915     return Py_BuildValue("KKKK",
916         (unsigned long long) memory.pgsp_total * pagesize,
917         (unsigned long long) memory.pgsp_free * pagesize,
918         (unsigned long long) memory.pgins * pagesize,
919         (unsigned long long) memory.pgouts * pagesize
920     );
921 }
922 
923 
924 /*
925  * Return CPU statistics.
926  */
927 static PyObject *
psutil_cpu_stats(PyObject * self,PyObject * args)928 psutil_cpu_stats(PyObject *self, PyObject *args) {
929     int ncpu, rc, i;
930     // perfstat_cpu_total_t doesn't have invol/vol cswitch, only pswitch
931     // which is apparently something else. We have to sum over all cpus
932     perfstat_cpu_t *cpu = NULL;
933     perfstat_id_t id;
934     u_longlong_t cswitches = 0;
935     u_longlong_t devintrs = 0;
936     u_longlong_t softintrs = 0;
937     u_longlong_t syscall = 0;
938 
939     /* get the number of cpus in ncpu */
940     ncpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
941     if (ncpu <= 0){
942         PyErr_SetFromErrno(PyExc_OSError);
943         goto error;
944     }
945 
946     /* allocate enough memory to hold the ncpu structures */
947     cpu = (perfstat_cpu_t *) malloc(ncpu * sizeof(perfstat_cpu_t));
948     if (cpu == NULL) {
949         PyErr_NoMemory();
950         goto error;
951     }
952 
953     strcpy(id.name, "");
954     rc = perfstat_cpu(&id, cpu, sizeof(perfstat_cpu_t), ncpu);
955 
956     if (rc <= 0) {
957         PyErr_SetFromErrno(PyExc_OSError);
958         goto error;
959     }
960 
961     for (i = 0; i < ncpu; i++) {
962         cswitches += cpu[i].invol_cswitch + cpu[i].vol_cswitch;
963         devintrs += cpu[i].devintrs;
964         softintrs += cpu[i].softintrs;
965         syscall += cpu[i].syscall;
966     }
967 
968     free(cpu);
969 
970     return Py_BuildValue(
971         "KKKK",
972         cswitches,
973         devintrs,
974         softintrs,
975         syscall
976     );
977 
978 error:
979     if (cpu != NULL)
980         free(cpu);
981     return NULL;
982 }
983 
984 
985 /*
986  * define the psutil C module methods and initialize the module.
987  */
988 static PyMethodDef
989 PsutilMethods[] =
990 {
991     // --- process-related functions
992     {"proc_basic_info", psutil_proc_basic_info, METH_VARARGS,
993      "Return process ppid, rss, vms, ctime, nice, nthreads, status and tty"},
994     {"proc_name", psutil_proc_name, METH_VARARGS,
995      "Return process name."},
996     {"proc_args", psutil_proc_args, METH_VARARGS,
997      "Return process command line arguments."},
998     {"proc_environ", psutil_proc_environ, METH_VARARGS,
999      "Return process environment variables."},
1000     {"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS,
1001      "Return process user and system CPU times."},
1002     {"proc_cred", psutil_proc_cred, METH_VARARGS,
1003      "Return process uids/gids."},
1004 #ifdef CURR_VERSION_THREAD
1005     {"proc_threads", psutil_proc_threads, METH_VARARGS,
1006      "Return process threads"},
1007 #endif
1008 #ifdef CURR_VERSION_PROCESS
1009     {"proc_io_counters", psutil_proc_io_counters, METH_VARARGS,
1010      "Get process I/O counters."},
1011 #endif
1012     {"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS,
1013      "Get process I/O counters."},
1014 
1015     // --- system-related functions
1016     {"users", psutil_users, METH_VARARGS,
1017      "Return currently connected users."},
1018     {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
1019      "Return disk partitions."},
1020     {"boot_time", psutil_boot_time, METH_VARARGS,
1021      "Return system boot time in seconds since the EPOCH."},
1022     {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
1023      "Return system per-cpu times as a list of tuples"},
1024     {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
1025      "Return a Python dict of tuples for disk I/O statistics."},
1026     {"virtual_mem", psutil_virtual_mem, METH_VARARGS,
1027      "Return system virtual memory usage statistics"},
1028     {"swap_mem", psutil_swap_mem, METH_VARARGS,
1029      "Return stats about swap memory, in bytes"},
1030 #if defined(CURR_VERSION_NETINTERFACE) && CURR_VERSION_NETINTERFACE >= 3
1031     {"net_io_counters", psutil_net_io_counters, METH_VARARGS,
1032      "Return a Python dict of tuples for network I/O statistics."},
1033 #endif
1034     {"net_connections", psutil_net_connections, METH_VARARGS,
1035      "Return system-wide connections"},
1036     {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
1037      "Return NIC stats (isup, mtu)"},
1038     {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
1039      "Return CPU statistics"},
1040 
1041     // --- others
1042     {"set_testing", psutil_set_testing, METH_NOARGS,
1043      "Set psutil in testing mode"},
1044 
1045     {NULL, NULL, 0, NULL}
1046 };
1047 
1048 
1049 struct module_state {
1050     PyObject *error;
1051 };
1052 
1053 #if PY_MAJOR_VERSION >= 3
1054 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
1055 #else
1056 #define GETSTATE(m) (&_state)
1057 #endif
1058 
1059 #ifdef __cplusplus
1060 extern "C" {
1061 #endif
1062 
1063 #if PY_MAJOR_VERSION >= 3
1064 
1065 static int
psutil_aix_traverse(PyObject * m,visitproc visit,void * arg)1066 psutil_aix_traverse(PyObject *m, visitproc visit, void *arg) {
1067     Py_VISIT(GETSTATE(m)->error);
1068     return 0;
1069 }
1070 
1071 static int
psutil_aix_clear(PyObject * m)1072 psutil_aix_clear(PyObject *m) {
1073     Py_CLEAR(GETSTATE(m)->error);
1074     return 0;
1075 }
1076 
1077 static struct PyModuleDef moduledef = {
1078     PyModuleDef_HEAD_INIT,
1079     "psutil_aix",
1080     NULL,
1081     sizeof(struct module_state),
1082     PsutilMethods,
1083     NULL,
1084     psutil_aix_traverse,
1085     psutil_aix_clear,
1086     NULL
1087 };
1088 
1089 #define INITERROR return NULL
1090 
PyInit__psutil_aix(void)1091 PyMODINIT_FUNC PyInit__psutil_aix(void)
1092 
1093 #else
1094 #define INITERROR return
1095 
1096 void init_psutil_aix(void)
1097 #endif
1098 {
1099 #if PY_MAJOR_VERSION >= 3
1100     PyObject *module = PyModule_Create(&moduledef);
1101 #else
1102     PyObject *module = Py_InitModule("_psutil_aix", PsutilMethods);
1103 #endif
1104     PyModule_AddIntConstant(module, "version", PSUTIL_VERSION);
1105 
1106     PyModule_AddIntConstant(module, "SIDL", SIDL);
1107     PyModule_AddIntConstant(module, "SZOMB", SZOMB);
1108     PyModule_AddIntConstant(module, "SACTIVE", SACTIVE);
1109     PyModule_AddIntConstant(module, "SSWAP", SSWAP);
1110     PyModule_AddIntConstant(module, "SSTOP", SSTOP);
1111 
1112     PyModule_AddIntConstant(module, "TCPS_CLOSED", TCPS_CLOSED);
1113     PyModule_AddIntConstant(module, "TCPS_CLOSING", TCPS_CLOSING);
1114     PyModule_AddIntConstant(module, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT);
1115     PyModule_AddIntConstant(module, "TCPS_LISTEN", TCPS_LISTEN);
1116     PyModule_AddIntConstant(module, "TCPS_ESTABLISHED", TCPS_ESTABLISHED);
1117     PyModule_AddIntConstant(module, "TCPS_SYN_SENT", TCPS_SYN_SENT);
1118     PyModule_AddIntConstant(module, "TCPS_SYN_RCVD", TCPS_SYN_RECEIVED);
1119     PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1);
1120     PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2);
1121     PyModule_AddIntConstant(module, "TCPS_LAST_ACK", TCPS_LAST_ACK);
1122     PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT);
1123     PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
1124 
1125     psutil_setup();
1126 
1127     if (module == NULL)
1128         INITERROR;
1129 #if PY_MAJOR_VERSION >= 3
1130     return module;
1131 #endif
1132 }
1133 
1134 #ifdef __cplusplus
1135 }
1136 #endif
1137