Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/psutil-7.0.1-py3.11-linux-x86_64.egg/psutil/_psposix.py: 23%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

86 statements  

1# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. 

2# Use of this source code is governed by a BSD-style license that can be 

3# found in the LICENSE file. 

4 

5"""Routines common to all posix systems.""" 

6 

7import enum 

8import glob 

9import os 

10import signal 

11import time 

12 

13from ._common import MACOS 

14from ._common import TimeoutExpired 

15from ._common import memoize 

16from ._common import sdiskusage 

17from ._common import usage_percent 

18 

19if MACOS: 

20 from . import _psutil_osx 

21 

22 

23__all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map'] 

24 

25 

26def pid_exists(pid): 

27 """Check whether pid exists in the current process table.""" 

28 if pid == 0: 

29 # According to "man 2 kill" PID 0 has a special meaning: 

30 # it refers to <<every process in the process group of the 

31 # calling process>> so we don't want to go any further. 

32 # If we get here it means this UNIX platform *does* have 

33 # a process with id 0. 

34 return True 

35 try: 

36 os.kill(pid, 0) 

37 except ProcessLookupError: 

38 return False 

39 except PermissionError: 

40 # EPERM clearly means there's a process to deny access to 

41 return True 

42 # According to "man 2 kill" possible error values are 

43 # (EINVAL, EPERM, ESRCH) 

44 else: 

45 return True 

46 

47 

48Negsignal = enum.IntEnum( 

49 'Negsignal', {x.name: -x.value for x in signal.Signals} 

50) 

51 

52 

53def negsig_to_enum(num): 

54 """Convert a negative signal value to an enum.""" 

55 try: 

56 return Negsignal(num) 

57 except ValueError: 

58 return num 

59 

60 

61def wait_pid( 

62 pid, 

63 timeout=None, 

64 proc_name=None, 

65 _waitpid=os.waitpid, 

66 _timer=getattr(time, 'monotonic', time.time), # noqa: B008 

67 _min=min, 

68 _sleep=time.sleep, 

69 _pid_exists=pid_exists, 

70): 

71 """Wait for a process PID to terminate. 

72 

73 If the process terminated normally by calling exit(3) or _exit(2), 

74 or by returning from main(), the return value is the positive integer 

75 passed to *exit(). 

76 

77 If it was terminated by a signal it returns the negated value of the 

78 signal which caused the termination (e.g. -SIGTERM). 

79 

80 If PID is not a children of os.getpid() (current process) just 

81 wait until the process disappears and return None. 

82 

83 If PID does not exist at all return None immediately. 

84 

85 If *timeout* != None and process is still alive raise TimeoutExpired. 

86 timeout=0 is also possible (either return immediately or raise). 

87 """ 

88 if pid <= 0: 

89 # see "man waitpid" 

90 msg = "can't wait for PID 0" 

91 raise ValueError(msg) 

92 interval = 0.0001 

93 flags = 0 

94 if timeout is not None: 

95 flags |= os.WNOHANG 

96 stop_at = _timer() + timeout 

97 

98 def sleep(interval): 

99 # Sleep for some time and return a new increased interval. 

100 if timeout is not None: 

101 if _timer() >= stop_at: 

102 raise TimeoutExpired(timeout, pid=pid, name=proc_name) 

103 _sleep(interval) 

104 return _min(interval * 2, 0.04) 

105 

106 # See: https://linux.die.net/man/2/waitpid 

107 while True: 

108 try: 

109 retpid, status = os.waitpid(pid, flags) 

110 except InterruptedError: 

111 interval = sleep(interval) 

112 except ChildProcessError: 

113 # This has two meanings: 

114 # - PID is not a child of os.getpid() in which case 

115 # we keep polling until it's gone 

116 # - PID never existed in the first place 

117 # In both cases we'll eventually return None as we 

118 # can't determine its exit status code. 

119 while _pid_exists(pid): 

120 interval = sleep(interval) 

121 return None 

122 else: 

123 if retpid == 0: 

124 # WNOHANG flag was used and PID is still running. 

125 interval = sleep(interval) 

126 continue 

127 

128 if os.WIFEXITED(status): 

129 # Process terminated normally by calling exit(3) or _exit(2), 

130 # or by returning from main(). The return value is the 

131 # positive integer passed to *exit(). 

132 return os.WEXITSTATUS(status) 

133 elif os.WIFSIGNALED(status): 

134 # Process exited due to a signal. Return the negative value 

135 # of that signal. 

136 return negsig_to_enum(-os.WTERMSIG(status)) 

137 # elif os.WIFSTOPPED(status): 

138 # # Process was stopped via SIGSTOP or is being traced, and 

139 # # waitpid() was called with WUNTRACED flag. PID is still 

140 # # alive. From now on waitpid() will keep returning (0, 0) 

141 # # until the process state doesn't change. 

142 # # It may make sense to catch/enable this since stopped PIDs 

143 # # ignore SIGTERM. 

144 # interval = sleep(interval) 

145 # continue 

146 # elif os.WIFCONTINUED(status): 

147 # # Process was resumed via SIGCONT and waitpid() was called 

148 # # with WCONTINUED flag. 

149 # interval = sleep(interval) 

150 # continue 

151 else: 

152 # Should never happen. 

153 msg = f"unknown process exit status {status!r}" 

154 raise ValueError(msg) 

155 

156 

157def disk_usage(path): 

158 """Return disk usage associated with path. 

159 Note: UNIX usually reserves 5% disk space which is not accessible 

160 by user. In this function "total" and "used" values reflect the 

161 total and used disk space whereas "free" and "percent" represent 

162 the "free" and "used percent" user disk space. 

163 """ 

164 st = os.statvfs(path) 

165 # Total space which is only available to root (unless changed 

166 # at system level). 

167 total = st.f_blocks * st.f_frsize 

168 # Remaining free space usable by root. 

169 avail_to_root = st.f_bfree * st.f_frsize 

170 # Remaining free space usable by user. 

171 avail_to_user = st.f_bavail * st.f_frsize 

172 # Total space being used in general. 

173 used = total - avail_to_root 

174 if MACOS: 

175 # see: https://github.com/giampaolo/psutil/pull/2152 

176 used = _psutil_osx.disk_usage_used(path, used) 

177 # Total space which is available to user (same as 'total' but 

178 # for the user). 

179 total_user = used + avail_to_user 

180 # User usage percent compared to the total amount of space 

181 # the user can use. This number would be higher if compared 

182 # to root's because the user has less space (usually -5%). 

183 usage_percent_user = usage_percent(used, total_user, round_=1) 

184 

185 # NB: the percentage is -5% than what shown by df due to 

186 # reserved blocks that we are currently not considering: 

187 # https://github.com/giampaolo/psutil/issues/829#issuecomment-223750462 

188 return sdiskusage( 

189 total=total, used=used, free=avail_to_user, percent=usage_percent_user 

190 ) 

191 

192 

193@memoize 

194def get_terminal_map(): 

195 """Get a map of device-id -> path as a dict. 

196 Used by Process.terminal(). 

197 """ 

198 ret = {} 

199 ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*') 

200 for name in ls: 

201 assert name not in ret, name 

202 try: 

203 ret[os.stat(name).st_rdev] = name 

204 except FileNotFoundError: 

205 pass 

206 return ret