-
Notifications
You must be signed in to change notification settings - Fork 53
/
linux_api.py
359 lines (271 loc) · 8.42 KB
/
linux_api.py
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
"""
Author: Armon Dadgar
Start Date: April 7th, 2009
Description:
This file provides a python interface to low-level system call on the Linux platform.
It is designed to abstract away the C-level detail and provide a high-level method of doing
common management tasks.
"""
import os # Provides some convenience functions
import nix_common_api as nix_api # Import the Common API
import textops # Import seattlelib's text processing lib
import portable_popen # For Popen
import platform
# Determine if we are 32 bit or 64 bit
running_32bit = True
architecture = platform.architecture()
if "64" in architecture[0]:
running_32bit = False
# Manually import the common functions we want
exists_outgoing_network_socket = nix_api.exists_outgoing_network_socket
exists_listening_network_socket = nix_api.exists_listening_network_socket
get_available_interfaces = nix_api.get_available_interfaces
# Libc
libc = nix_api.libc
# Functions
myopen = open # This is an annoying restriction of repy
syscall = libc.syscall # syscall function
# Globals
last_stat_data = None # Store the last array of data from _get_proc_info_by_pid
# Constants
JIFFIES_PER_SECOND = 100.0
PAGE_SIZE = os.sysconf('SC_PAGESIZE')
# Get the thread id of the currently executing thread
if running_32bit:
GETTID = 224
else:
GETTID = 186
# Maps each field in /proc/{pid}/stat to an index when split by spaces
FIELDS = {
"pid":0,
"state":1,
"ppid":2,
"pgrp":3,
"session":4,
"tty_nr":5,
"tpgid":6,
"flags":7,
"minflt":8,
"cminflt":9,
"majflt":10,
"cmajflt":11,
"utime":12,
"stime":13,
"cutime":14,
"cstime":15,
"priority":16,
"nice":17,
"num_threads":18,
"itrealvalue":19,
"starttime":20,
"vsize":21,
"rss":22,
"rlim":23,
"startcode":24,
"endcode":25,
"startstack":26,
"kstkesp":27,
"kstkeoip":28,
"signal":29,
"blocked":30,
"sigignore":31,
"sigcatch":32,
"wchan":33,
"nswap":34,
"cnswap":35,
"exit_signal":36,
"processor":37,
"rt_priority":38,
"policy":39,
"delayacct_blkio_ticks":40
}
# Process a /proc/PID/stat or /proc/PID/task/TID/stat file and returns it as an array
def _process_stat_file(file):
# Get the file in proc
fileo = myopen(file,"r")
# Read in all the data
data = fileo.read()
# Close the file object
fileo.close()
# Strip the newline
data = data.strip("\n")
# Remove the substring that says "(python)", since it changes the field alignment
start_index = data.find("(")
if start_index != -1:
end_index = data.find(")", start_index)
data = data[:start_index-1] + data[end_index+1:]
# Break the data into an array by spaces
return data.split(" ")
def _get_proc_info_by_pid(pid):
"""
<Purpose>
Reads in the data from a process stat file, and stores it
<Arguments>
pid: The process identifier for which data should be fetched.
"""
global last_stat_data
# Get the file in proc
file = "/proc/"+str(pid)+"/stat"
# Process the status file
last_stat_data = _process_stat_file(file)
# Check the state, raise an exception if the process is a zombie
if "Z" in last_stat_data[FIELDS["state"]]:
raise Exception, "Queried Process is a zombie (dead)!"
def get_process_cpu_time(pid):
"""
<Purpose>
Returns the total CPU time used by a process.
<Arguments>
pid: The process identifier for the process to query.
<Returns>
The total cpu time.
"""
global last_stat_data
# Update our data
_get_proc_info_by_pid(pid)
# Get the raw usertime and system time
total_time_raw = int(last_stat_data[FIELDS["utime"]])+int(last_stat_data[FIELDS["stime"]])
# Adjust by the number of jiffies per second
total_time = total_time_raw / JIFFIES_PER_SECOND
return total_time
def get_process_rss(force_update=False, pid=None):
"""
<Purpose>
Returns the Resident Set Size of a process. By default, this will
return the information cached by the last call to _get_proc_info_by_pid.
This call is used in get_process_cpu_time.
<Arguments>
force_update:
Allows the caller to force a data update, instead of using the cached data.
pid:
If force_update is True, this parameter must be specified to force the update.
<Returns>
The RSS of the process in bytes.
"""
global last_stat_data
# Check if an update is being forced
if force_update and pid != None:
# Update the info
_get_proc_info_by_pid(pid)
# Fetch the RSS, convert to an integer
rss_pages = int(last_stat_data[FIELDS["rss"]])
rss_bytes = rss_pages * PAGE_SIZE
# Return the info
return rss_bytes
# Get the id of the currently executing thread
def _get_current_thread_id():
# Syscall for GETTID
return syscall(GETTID)
# Get the CPU time of the current thread
def get_current_thread_cpu_time():
"""
<Purpose>
Gets the total CPU time for the currently executing thread.
<Exceptions>
An exception will be raised if something goes wrong.
<Returns>
A floating amount of time in seconds.
"""
# Get the thread id
thread_id = _get_current_thread_id()
# Get our pid
pid = os.getpid()
# Get the file with our status
file = "/proc/"+str(pid)+"/task/"+str(thread_id)+"/stat"
# Process the status file
thread_stat_data = _process_stat_file(file)
# Get the raw usertime and system time
total_time_raw = int(thread_stat_data[FIELDS["utime"]])+int(thread_stat_data[FIELDS["stime"]])
# Adjust by the number of jiffies per second
total_time = total_time_raw / JIFFIES_PER_SECOND
# Return the total time
return total_time
def get_system_uptime():
"""
<Purpose>
Returns the system uptime.
<Exception>
Raises Exception if /proc/uptime is unavailable
<Returns>
The system uptime.
"""
if os.path.exists("/proc/uptime"):
# Open the file
fh = myopen('/proc/uptime', 'r')
# Read in the whole file
data = fh.read()
# Split the file by commas, grap the first number and convert to a float
uptime = float(data.split(" ")[0])
# Close the file
fh.close()
return uptime
else:
raise Exception, "Could not find /proc/uptime!"
def get_uptime_granularity():
"""
<Purpose>
Determines the granularity of the get_system_uptime call.
<Exception>
Raises Exception if /proc/uptime is unavailable
<Returns>
A numerical representation of the minimum granularity.
E.g. 2 digits of granularity would return 0.01
"""
if os.path.exists("/proc/uptime"):
# Open the file
fh = myopen('/proc/uptime', 'r')
# Read in the whole file
data = fh.read()
# Split the file by commas, grap the first number
uptime = data.split(" ")[0]
uptime_digits = len(uptime.split(".")[1])
# Close the file
fh.close()
granularity = uptime_digits
# Convert granularity to a number
return pow(10, 0-granularity)
else:
raise Exception, "Could not find /proc/uptime!"
def get_system_thread_count():
"""
<Purpose>
Returns the number of active threads running on the system.
<Returns>
The thread count.
"""
# Use PS since it is can get the info for us
process = portable_popen.Popen(["ps", "axH"])
ps_output, _ = process.communicate()
# Subtract 1 from the number of lines because the first line is a a table
# header: " PID TTY STAT TIME COMMAND"
threads = len(textops.textops_rawtexttolines(ps_output)) - 1
return threads
def get_interface_ip_addresses(interfaceName):
"""
<Purpose>
Returns the IP address associated with the interface.
<Arguments>
interfaceName: The string name of the interface, e.g. eth0
<Returns>
A list of IP addresses associated with the interface.
"""
# Launch up a shell, get the feed back
# We use ifconfig with the interface name.
ifconfig_process = portable_popen.Popen(["/sbin/ifconfig", interfaceName.strip()])
ifconfig_output, _ = ifconfig_process.communicate()
ifconfig_lines = textops.textops_rawtexttolines(ifconfig_output)
# Look for ipv4 addresses
target_lines = textops.textops_grep("inet", ifconfig_lines)
# and not ipv6
target_lines = textops.textops_grep("inet6", target_lines, exclude=True)
# Only take the ip(s)
target_lines = textops.textops_cut(target_lines, delimiter=":", fields=[1])
target_lines = textops.textops_cut(target_lines, delimiter=" ", fields=[0])
# Create an array for the ip's
ipaddressList = []
for line in target_lines:
# Strip the newline and any spacing
line = line.strip("\n\t ")
ipaddressList.append(line)
# Done, return the interfaces
return ipaddressList