Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/psutil/_psposix.py: 63%

94 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

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 glob 

8import os 

9import signal 

10import sys 

11import time 

12 

13from ._common import MACOS 

14from ._common import TimeoutExpired 

15from ._common import memoize 

16from ._common import sdiskusage 

17from ._common import usage_percent 

18from ._compat import PY3 

19from ._compat import ChildProcessError 

20from ._compat import FileNotFoundError 

21from ._compat import InterruptedError 

22from ._compat import PermissionError 

23from ._compat import ProcessLookupError 

24from ._compat import unicode 

25 

26 

27if MACOS: 

28 from . import _psutil_osx 

29 

30 

31if sys.version_info >= (3, 4): 

32 import enum 

33else: 

34 enum = None 

35 

36 

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

38 

39 

40def pid_exists(pid): 

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

42 if pid == 0: 

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

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

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

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

47 # a process with id 0. 

48 return True 

49 try: 

50 os.kill(pid, 0) 

51 except ProcessLookupError: 

52 return False 

53 except PermissionError: 

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

55 return True 

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

57 # (EINVAL, EPERM, ESRCH) 

58 else: 

59 return True 

60 

61 

62# Python 3.5 signals enum (contributed by me ^^): 

63# https://bugs.python.org/issue21076 

64if enum is not None and hasattr(signal, "Signals"): 

65 Negsignal = enum.IntEnum( 

66 'Negsignal', dict([(x.name, -x.value) for x in signal.Signals])) 

67 

68 def negsig_to_enum(num): 

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

70 try: 

71 return Negsignal(num) 

72 except ValueError: 

73 return num 

74else: # pragma: no cover 

75 def negsig_to_enum(num): 

76 return num 

77 

78 

79def wait_pid(pid, timeout=None, proc_name=None, 

80 _waitpid=os.waitpid, 

81 _timer=getattr(time, 'monotonic', time.time), 

82 _min=min, 

83 _sleep=time.sleep, 

84 _pid_exists=pid_exists): 

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

86 

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

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

89 passed to *exit(). 

90 

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

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

93 

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

95 wait until the process disappears and return None. 

96 

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

98 

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

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

101 """ 

102 if pid <= 0: 

103 raise ValueError("can't wait for PID 0") # see "man waitpid" 

104 interval = 0.0001 

105 flags = 0 

106 if timeout is not None: 

107 flags |= os.WNOHANG 

108 stop_at = _timer() + timeout 

109 

110 def sleep(interval): 

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

112 if timeout is not None: 

113 if _timer() >= stop_at: 

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

115 _sleep(interval) 

116 return _min(interval * 2, 0.04) 

117 

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

119 while True: 

120 try: 

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

122 except InterruptedError: 

123 interval = sleep(interval) 

124 except ChildProcessError: 

125 # This has two meanings: 

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

127 # we keep polling until it's gone 

128 # - PID never existed in the first place 

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

130 # can't determine its exit status code. 

131 while _pid_exists(pid): 

132 interval = sleep(interval) 

133 return 

134 else: 

135 if retpid == 0: 

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

137 interval = sleep(interval) 

138 continue 

139 elif os.WIFEXITED(status): 

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

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

142 # positive integer passed to *exit(). 

143 return os.WEXITSTATUS(status) 

144 elif os.WIFSIGNALED(status): 

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

146 # of that signal. 

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

148 # elif os.WIFSTOPPED(status): 

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

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

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

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

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

154 # # ignore SIGTERM. 

155 # interval = sleep(interval) 

156 # continue 

157 # elif os.WIFCONTINUED(status): 

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

159 # # with WCONTINUED flag. 

160 # interval = sleep(interval) 

161 # continue 

162 else: 

163 # Should never happen. 

164 raise ValueError("unknown process exit status %r" % status) 

165 

166 

167def disk_usage(path): 

168 """Return disk usage associated with path. 

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

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

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

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

173 """ 

174 if PY3: 

175 st = os.statvfs(path) 

176 else: # pragma: no cover 

177 # os.statvfs() does not support unicode on Python 2: 

178 # - https://github.com/giampaolo/psutil/issues/416 

179 # - http://bugs.python.org/issue18695 

180 try: 

181 st = os.statvfs(path) 

182 except UnicodeEncodeError: 

183 if isinstance(path, unicode): 

184 try: 

185 path = path.encode(sys.getfilesystemencoding()) 

186 except UnicodeEncodeError: 

187 pass 

188 st = os.statvfs(path) 

189 else: 

190 raise 

191 

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

193 # at system level). 

194 total = (st.f_blocks * st.f_frsize) 

195 # Remaining free space usable by root. 

196 avail_to_root = (st.f_bfree * st.f_frsize) 

197 # Remaining free space usable by user. 

198 avail_to_user = (st.f_bavail * st.f_frsize) 

199 # Total space being used in general. 

200 used = (total - avail_to_root) 

201 if MACOS: 

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

203 used = _psutil_osx.disk_usage_used(path, used) 

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

205 # for the user). 

206 total_user = used + avail_to_user 

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

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

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

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

211 

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

213 # reserved blocks that we are currently not considering: 

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

215 return sdiskusage( 

216 total=total, used=used, free=avail_to_user, percent=usage_percent_user) 

217 

218 

219@memoize 

220def get_terminal_map(): 

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

222 Used by Process.terminal() 

223 """ 

224 ret = {} 

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

226 for name in ls: 

227 assert name not in ret, name 

228 try: 

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

230 except FileNotFoundError: 

231 pass 

232 return ret