-
-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathpsutil.py
More file actions
128 lines (105 loc) · 5.15 KB
/
Copy pathpsutil.py
File metadata and controls
128 lines (105 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#! /usr/bin/env python3
# -*- coding: utf-8; py-indent-offset: 4 -*-
#
# Author: Linuxfabrik GmbH, Zurich, Switzerland
# Contact: info (at) linuxfabrik (dot) ch
# https://www.linuxfabrik.ch/
# License: The Unlicense, see LICENSE file.
# https://github.com/Linuxfabrik/lib/blob/main/CONTRIBUTING.md
"""Wrapper library for functions from psutil."""
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
__version__ = '2026082601'
import os
import sys
from collections import namedtuple
from . import user
from .globals import STATE_UNKNOWN
try:
import psutil
except ImportError:
print('Python module "psutil" is not installed.')
sys.exit(STATE_UNKNOWN)
# The fields a caller gets back. psutil's own partition tuple is not reused, because its
# shape moves between releases: psutil 5.9 carries `maxfile` and `maxpath` next to these
# four and psutil 7 does not, so building one from four values raises there.
sdiskpart = namedtuple('sdiskpart', ['device', 'mountpoint', 'fstype', 'opts'])
def get_partitions(ignore=None, include_all=False):
"""
Return all mounted disk partitions as a list of named tuples, including device, mount point,
filesystem type and mount options, similar to the `df` command on UNIX.
Listing the partitions never waits on the filesystems it lists. `psutil.disk_partitions()`
looks up `os.pathconf()` on every mount point it returns, to fill in the `maxfile` and
`maxpath` fields, and that lookup blocks on a network filesystem whose server has stopped
answering: merely asking what is mounted then never comes back. Those two fields are
dropped here and the lookup with them, so the answer comes from the mount table alone.
### Parameters
- **ignore** (`list`, optional): A list of strings to ignore. Any partition whose mount
point contains any of the strings in this list will be excluded from the result.
Defaults to an empty list.
- **include_all** (`bool`, optional): Return every mounted filesystem instead of the
physical devices only. The default leaves out the pseudo and memory filesystems, and
with them the network filesystems, which the kernel also lists as `nodev`.
Defaults to False.
### Returns
- **list**: A list of named tuples representing the disk partitions, each containing:
- **device**: The device name (e.g., `/dev/sda1`).
- **mountpoint**: The mount point (e.g., `/`).
- **fstype**: The filesystem type (e.g., `ext4`).
- **opts**: The mount options (e.g., `rw,relatime`).
### Example
>>> get_partitions(['/mnt'])
[sdiskpart(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,relatime')]
"""
if ignore is None:
ignore = []
ignore = list(filter(None, ignore))
try:
# psutil's platform module is not its published interface, so a layout that does
# not match falls back to the documented function, blocking lookups and all.
parts = psutil._psplatform.disk_partitions(include_all)
except Exception:
parts = psutil.disk_partitions(all=include_all)
return [
sdiskpart(part.device, part.mountpoint, part.fstype, part.opts)
for part in parts
if not any(item in part.mountpoint for item in ignore)
]
def get_process_accounts(names):
"""
Return the account names the running processes of a program use.
Which account a program's processes actually run under answers a different question
than which one its configuration names: a process that never dropped its privileges
still shows the account it kept.
The listing is confined to this process's own mount namespace. A program running in
a container appears in the host's process list too, under a user id mapped into the
host's range, and counting that as an account of the host's own installation reports
a stray user that does not exist there. Two processes in the same mount namespace see
the same filesystem, which is what makes them part of the same installation.
### Parameters
- **names** (`iterable`): The process names to count, for example
`('httpd', 'apache2')`. Matched exactly against the name the kernel reports, which
is the executable rather than the command line.
### Returns
- **list**: The account name of every matching process, one entry per process and
therefore with repeats. A process that vanished while the list was being built, or
that this user may not inspect, is left out rather than reported as an unknown
account.
### Example
>>> get_process_accounts(('httpd', 'apache2'))
['root', 'apache', 'apache']
"""
names = tuple(names)
namespace = user.own_mount_namespace()
accounts = []
for proc in psutil.process_iter(['name', 'pid', 'username']):
try:
if proc.info['name'] not in names:
continue
if namespace is not None:
own = os.readlink('/proc/{}/ns/mnt'.format(proc.info['pid']))
if own != namespace:
continue
accounts.append(proc.info['username'])
except (psutil.NoSuchProcess, psutil.AccessDenied, OSError):
continue
return accounts