1 /* 2 * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. 3 * Use of this source code is governed by a BSD-style license that can be 4 * found in the LICENSE file. 5 * 6 * Linux-specific functions. 7 */ 8 9 #ifndef _GNU_SOURCE 10 #define _GNU_SOURCE 1 11 #endif 12 #include <Python.h> 13 #include <errno.h> 14 #include <stdlib.h> 15 #include <mntent.h> 16 #include <features.h> 17 #include <utmp.h> 18 #include <sched.h> 19 #include <linux/version.h> 20 #include <sys/syscall.h> 21 #include <sys/sysinfo.h> 22 #include <sys/ioctl.h> 23 #include <sys/socket.h> 24 #include <linux/sockios.h> 25 #include <linux/if.h> 26 #include <sys/resource.h> 27 28 // see: https://github.com/giampaolo/psutil/issues/659 29 #ifdef PSUTIL_ETHTOOL_MISSING_TYPES 30 #include <linux/types.h> 31 typedef __u64 u64; 32 typedef __u32 u32; 33 typedef __u16 u16; 34 typedef __u8 u8; 35 #endif 36 /* Avoid redefinition of struct sysinfo with musl libc */ 37 #define _LINUX_SYSINFO_H 38 #include <linux/ethtool.h> 39 40 /* The minimum number of CPUs allocated in a cpu_set_t */ 41 static const int NCPUS_START = sizeof(unsigned long) * CHAR_BIT; 42 43 // Linux >= 2.6.13 44 #define PSUTIL_HAVE_IOPRIO defined(__NR_ioprio_get) && defined(__NR_ioprio_set) 45 46 // Should exist starting from CentOS 6 (year 2011). 47 #ifdef CPU_ALLOC 48 #define PSUTIL_HAVE_CPU_AFFINITY 49 #endif 50 51 #include "_psutil_common.h" 52 #include "_psutil_posix.h" 53 54 // May happen on old RedHat versions, see: 55 // https://github.com/giampaolo/psutil/issues/607 56 #ifndef DUPLEX_UNKNOWN 57 #define DUPLEX_UNKNOWN 0xff 58 #endif 59 60 61 #if PSUTIL_HAVE_IOPRIO 62 enum { 63 IOPRIO_WHO_PROCESS = 1, 64 }; 65 66 static inline int 67 ioprio_get(int which, int who) { 68 return syscall(__NR_ioprio_get, which, who); 69 } 70 71 static inline int 72 ioprio_set(int which, int who, int ioprio) { 73 return syscall(__NR_ioprio_set, which, who, ioprio); 74 } 75 76 #define IOPRIO_CLASS_SHIFT 13 77 #define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) 78 79 #define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT) 80 #define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK) 81 #define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data) 82 83 84 /* 85 * Return a (ioclass, iodata) Python tuple representing process I/O priority. 86 */ 87 static PyObject * 88 psutil_proc_ioprio_get(PyObject *self, PyObject *args) { 89 pid_t pid; 90 int ioprio, ioclass, iodata; 91 if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) 92 return NULL; 93 ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid); 94 if (ioprio == -1) 95 return PyErr_SetFromErrno(PyExc_OSError); 96 ioclass = IOPRIO_PRIO_CLASS(ioprio); 97 iodata = IOPRIO_PRIO_DATA(ioprio); 98 return Py_BuildValue("ii", ioclass, iodata); 99 } 100 101 102 /* 103 * A wrapper around ioprio_set(); sets process I/O priority. 104 * ioclass can be either IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE 105 * or 0. iodata goes from 0 to 7 depending on ioclass specified. 106 */ 107 static PyObject * 108 psutil_proc_ioprio_set(PyObject *self, PyObject *args) { 109 pid_t pid; 110 int ioprio, ioclass, iodata; 111 int retval; 112 113 if (! PyArg_ParseTuple( 114 args, _Py_PARSE_PID "ii", &pid, &ioclass, &iodata)) { 115 return NULL; 116 } 117 ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata); 118 retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio); 119 if (retval == -1) 120 return PyErr_SetFromErrno(PyExc_OSError); 121 Py_RETURN_NONE; 122 } 123 #endif 124 125 126 /* 127 * Return disk mounted partitions as a list of tuples including device, 128 * mount point and filesystem type 129 */ 130 static PyObject * 131 psutil_disk_partitions(PyObject *self, PyObject *args) { 132 FILE *file = NULL; 133 struct mntent *entry; 134 char *mtab_path; 135 PyObject *py_dev = NULL; 136 PyObject *py_mountp = NULL; 137 PyObject *py_tuple = NULL; 138 PyObject *py_retlist = PyList_New(0); 139 140 if (py_retlist == NULL) 141 return NULL; 142 143 if (!PyArg_ParseTuple(args, "s", &mtab_path)) 144 return NULL; 145 146 Py_BEGIN_ALLOW_THREADS 147 file = setmntent(mtab_path, "r"); 148 Py_END_ALLOW_THREADS 149 if ((file == 0) || (file == NULL)) { 150 psutil_debug("setmntent() failed"); 151 PyErr_SetFromErrnoWithFilename(PyExc_OSError, mtab_path); 152 goto error; 153 } 154 155 while ((entry = getmntent(file))) { 156 if (entry == NULL) { 157 PyErr_Format(PyExc_RuntimeError, "getmntent() syscall failed"); 158 goto error; 159 } 160 py_dev = PyUnicode_DecodeFSDefault(entry->mnt_fsname); 161 if (! py_dev) 162 goto error; 163 py_mountp = PyUnicode_DecodeFSDefault(entry->mnt_dir); 164 if (! py_mountp) 165 goto error; 166 py_tuple = Py_BuildValue("(OOss)", 167 py_dev, // device 168 py_mountp, // mount point 169 entry->mnt_type, // fs type 170 entry->mnt_opts); // options 171 if (! py_tuple) 172 goto error; 173 if (PyList_Append(py_retlist, py_tuple)) 174 goto error; 175 Py_CLEAR(py_dev); 176 Py_CLEAR(py_mountp); 177 Py_CLEAR(py_tuple); 178 } 179 endmntent(file); 180 return py_retlist; 181 182 error: 183 if (file != NULL) 184 endmntent(file); 185 Py_XDECREF(py_dev); 186 Py_XDECREF(py_mountp); 187 Py_XDECREF(py_tuple); 188 Py_DECREF(py_retlist); 189 return NULL; 190 } 191 192 193 /* 194 * A wrapper around sysinfo(), return system memory usage statistics. 195 */ 196 static PyObject * 197 psutil_linux_sysinfo(PyObject *self, PyObject *args) { 198 struct sysinfo info; 199 200 if (sysinfo(&info) != 0) 201 return PyErr_SetFromErrno(PyExc_OSError); 202 // note: boot time might also be determined from here 203 return Py_BuildValue( 204 "(kkkkkkI)", 205 info.totalram, // total 206 info.freeram, // free 207 info.bufferram, // buffer 208 info.sharedram, // shared 209 info.totalswap, // swap tot 210 info.freeswap, // swap free 211 info.mem_unit // multiplier 212 ); 213 } 214 215 216 /* 217 * Return process CPU affinity as a Python list 218 */ 219 #ifdef PSUTIL_HAVE_CPU_AFFINITY 220 221 static PyObject * 222 psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) { 223 int cpu, ncpus, count, cpucount_s; 224 pid_t pid; 225 size_t setsize; 226 cpu_set_t *mask = NULL; 227 PyObject *py_list = NULL; 228 229 if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) 230 return NULL; 231 ncpus = NCPUS_START; 232 while (1) { 233 setsize = CPU_ALLOC_SIZE(ncpus); 234 mask = CPU_ALLOC(ncpus); 235 if (mask == NULL) { 236 psutil_debug("CPU_ALLOC() failed"); 237 return PyErr_NoMemory(); 238 } 239 if (sched_getaffinity(pid, setsize, mask) == 0) 240 break; 241 CPU_FREE(mask); 242 if (errno != EINVAL) 243 return PyErr_SetFromErrno(PyExc_OSError); 244 if (ncpus > INT_MAX / 2) { 245 PyErr_SetString(PyExc_OverflowError, "could not allocate " 246 "a large enough CPU set"); 247 return NULL; 248 } 249 ncpus = ncpus * 2; 250 } 251 252 py_list = PyList_New(0); 253 if (py_list == NULL) 254 goto error; 255 256 cpucount_s = CPU_COUNT_S(setsize, mask); 257 for (cpu = 0, count = cpucount_s; count; cpu++) { 258 if (CPU_ISSET_S(cpu, setsize, mask)) { 259 #if PY_MAJOR_VERSION >= 3 260 PyObject *cpu_num = PyLong_FromLong(cpu); 261 #else 262 PyObject *cpu_num = PyInt_FromLong(cpu); 263 #endif 264 if (cpu_num == NULL) 265 goto error; 266 if (PyList_Append(py_list, cpu_num)) { 267 Py_DECREF(cpu_num); 268 goto error; 269 } 270 Py_DECREF(cpu_num); 271 --count; 272 } 273 } 274 CPU_FREE(mask); 275 return py_list; 276 277 error: 278 if (mask) 279 CPU_FREE(mask); 280 Py_XDECREF(py_list); 281 return NULL; 282 } 283 284 285 /* 286 * Set process CPU affinity; expects a bitmask 287 */ 288 static PyObject * 289 psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { 290 cpu_set_t cpu_set; 291 size_t len; 292 pid_t pid; 293 int i, seq_len; 294 PyObject *py_cpu_set; 295 PyObject *py_cpu_seq = NULL; 296 297 if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &py_cpu_set)) 298 return NULL; 299 300 if (!PySequence_Check(py_cpu_set)) { 301 PyErr_Format(PyExc_TypeError, "sequence argument expected, got %s", 302 Py_TYPE(py_cpu_set)->tp_name); 303 goto error; 304 } 305 306 py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer"); 307 if (!py_cpu_seq) 308 goto error; 309 seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq); 310 CPU_ZERO(&cpu_set); 311 for (i = 0; i < seq_len; i++) { 312 PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i); 313 #if PY_MAJOR_VERSION >= 3 314 long value = PyLong_AsLong(item); 315 #else 316 long value = PyInt_AsLong(item); 317 #endif 318 if ((value == -1) || PyErr_Occurred()) { 319 if (!PyErr_Occurred()) 320 PyErr_SetString(PyExc_ValueError, "invalid CPU value"); 321 goto error; 322 } 323 CPU_SET(value, &cpu_set); 324 } 325 326 len = sizeof(cpu_set); 327 if (sched_setaffinity(pid, len, &cpu_set)) { 328 PyErr_SetFromErrno(PyExc_OSError); 329 goto error; 330 } 331 332 Py_DECREF(py_cpu_seq); 333 Py_RETURN_NONE; 334 335 error: 336 if (py_cpu_seq != NULL) 337 Py_DECREF(py_cpu_seq); 338 return NULL; 339 } 340 #endif /* PSUTIL_HAVE_CPU_AFFINITY */ 341 342 343 /* 344 * Return currently connected users as a list of tuples. 345 */ 346 static PyObject * 347 psutil_users(PyObject *self, PyObject *args) { 348 struct utmp *ut; 349 PyObject *py_retlist = PyList_New(0); 350 PyObject *py_tuple = NULL; 351 PyObject *py_username = NULL; 352 PyObject *py_tty = NULL; 353 PyObject *py_hostname = NULL; 354 PyObject *py_user_proc = NULL; 355 356 if (py_retlist == NULL) 357 return NULL; 358 setutent(); 359 while (NULL != (ut = getutent())) { 360 py_tuple = NULL; 361 py_user_proc = NULL; 362 if (ut->ut_type == USER_PROCESS) 363 py_user_proc = Py_True; 364 else 365 py_user_proc = Py_False; 366 py_username = PyUnicode_DecodeFSDefault(ut->ut_user); 367 if (! py_username) 368 goto error; 369 py_tty = PyUnicode_DecodeFSDefault(ut->ut_line); 370 if (! py_tty) 371 goto error; 372 py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host); 373 if (! py_hostname) 374 goto error; 375 376 py_tuple = Py_BuildValue( 377 "OOOfO" _Py_PARSE_PID, 378 py_username, // username 379 py_tty, // tty 380 py_hostname, // hostname 381 (float)ut->ut_tv.tv_sec, // tstamp 382 py_user_proc, // (bool) user process 383 ut->ut_pid // process id 384 ); 385 if (! py_tuple) 386 goto error; 387 if (PyList_Append(py_retlist, py_tuple)) 388 goto error; 389 Py_CLEAR(py_username); 390 Py_CLEAR(py_tty); 391 Py_CLEAR(py_hostname); 392 Py_CLEAR(py_tuple); 393 } 394 endutent(); 395 return py_retlist; 396 397 error: 398 Py_XDECREF(py_username); 399 Py_XDECREF(py_tty); 400 Py_XDECREF(py_hostname); 401 Py_XDECREF(py_tuple); 402 Py_DECREF(py_retlist); 403 endutent(); 404 return NULL; 405 } 406 407 408 /* 409 * Return stats about a particular network 410 * interface. References: 411 * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py 412 * http://www.i-scream.org/libstatgrab/ 413 */ 414 static PyObject* 415 psutil_net_if_duplex_speed(PyObject* self, PyObject* args) { 416 char *nic_name; 417 int sock = 0; 418 int ret; 419 int duplex; 420 int speed; 421 struct ifreq ifr; 422 struct ethtool_cmd ethcmd; 423 PyObject *py_retlist = NULL; 424 425 if (! PyArg_ParseTuple(args, "s", &nic_name)) 426 return NULL; 427 428 sock = socket(AF_INET, SOCK_DGRAM, 0); 429 if (sock == -1) 430 return PyErr_SetFromOSErrnoWithSyscall("socket()"); 431 PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name)); 432 433 // duplex and speed 434 memset(ðcmd, 0, sizeof ethcmd); 435 ethcmd.cmd = ETHTOOL_GSET; 436 ifr.ifr_data = (void *)ðcmd; 437 ret = ioctl(sock, SIOCETHTOOL, &ifr); 438 439 if (ret != -1) { 440 duplex = ethcmd.duplex; 441 speed = ethcmd.speed; 442 } 443 else { 444 if ((errno == EOPNOTSUPP) || (errno == EINVAL)) { 445 // EOPNOTSUPP may occur in case of wi-fi cards. 446 // For EINVAL see: 447 // https://github.com/giampaolo/psutil/issues/797 448 // #issuecomment-202999532 449 duplex = DUPLEX_UNKNOWN; 450 speed = 0; 451 } 452 else { 453 PyErr_SetFromOSErrnoWithSyscall("ioctl(SIOCETHTOOL)"); 454 goto error; 455 } 456 } 457 458 py_retlist = Py_BuildValue("[ii]", duplex, speed); 459 if (!py_retlist) 460 goto error; 461 close(sock); 462 return py_retlist; 463 464 error: 465 if (sock != -1) 466 close(sock); 467 return NULL; 468 } 469 470 471 /* 472 * Module init. 473 */ 474 475 static PyMethodDef mod_methods[] = { 476 // --- per-process functions 477 478 #if PSUTIL_HAVE_IOPRIO 479 {"proc_ioprio_get", psutil_proc_ioprio_get, METH_VARARGS, 480 "Get process I/O priority"}, 481 {"proc_ioprio_set", psutil_proc_ioprio_set, METH_VARARGS, 482 "Set process I/O priority"}, 483 #endif 484 #ifdef PSUTIL_HAVE_CPU_AFFINITY 485 {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS, 486 "Return process CPU affinity as a Python long (the bitmask)."}, 487 {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS, 488 "Set process CPU affinity; expects a bitmask."}, 489 #endif 490 491 // --- system related functions 492 493 {"disk_partitions", psutil_disk_partitions, METH_VARARGS, 494 "Return disk mounted partitions as a list of tuples including " 495 "device, mount point and filesystem type"}, 496 {"users", psutil_users, METH_VARARGS, 497 "Return currently connected users as a list of tuples"}, 498 {"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS, 499 "Return duplex and speed info about a NIC"}, 500 501 // --- linux specific 502 503 {"linux_sysinfo", psutil_linux_sysinfo, METH_VARARGS, 504 "A wrapper around sysinfo(), return system memory usage statistics"}, 505 // --- others 506 {"set_testing", psutil_set_testing, METH_NOARGS, 507 "Set psutil in testing mode"}, 508 509 {NULL, NULL, 0, NULL} 510 }; 511 512 513 #if PY_MAJOR_VERSION >= 3 514 #define INITERR return NULL 515 516 static struct PyModuleDef moduledef = { 517 PyModuleDef_HEAD_INIT, 518 "_psutil_linux", 519 NULL, 520 -1, 521 mod_methods, 522 NULL, 523 NULL, 524 NULL, 525 NULL 526 }; 527 528 PyObject *PyInit__psutil_linux(void) 529 #else /* PY_MAJOR_VERSION */ 530 #define INITERR return 531 532 void init_psutil_linux(void) 533 #endif /* PY_MAJOR_VERSION */ 534 { 535 #if PY_MAJOR_VERSION >= 3 536 PyObject *mod = PyModule_Create(&moduledef); 537 #else 538 PyObject *mod = Py_InitModule("_psutil_linux", mod_methods); 539 #endif 540 if (mod == NULL) 541 INITERR; 542 543 if (PyModule_AddIntConstant(mod, "version", PSUTIL_VERSION)) INITERR; 544 if (PyModule_AddIntConstant(mod, "DUPLEX_HALF", DUPLEX_HALF)) INITERR; 545 if (PyModule_AddIntConstant(mod, "DUPLEX_FULL", DUPLEX_FULL)) INITERR; 546 if (PyModule_AddIntConstant(mod, "DUPLEX_UNKNOWN", DUPLEX_UNKNOWN)) INITERR; 547 548 psutil_setup(); 549 550 if (mod == NULL) 551 INITERR; 552 #if PY_MAJOR_VERSION >= 3 553 return mod; 554 #endif 555 } 556