This file is indexed.

/usr/share/arm/util/procTools.py is in tor-arm 1.4.5.0-1.1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
"""
Helper functions for querying process and system information from the /proc
contents. Fetching information this way provides huge performance benefits
over lookups via system utilities (ps, netstat, etc). For instance, resolving
connections this way cuts the runtime by around 90% verses the alternatives.
These functions may not work on all platforms (only Linux?).

All functions raise IOErrors if unable to read their respective proc files.

The method for reading these files (and some of the code) are borrowed from
psutil:
https://code.google.com/p/psutil/
which was written by Jay Loden, Dave Daeschler, Giampaolo Rodola' and is under
the BSD license.
"""

import os
import sys
import time
import socket
import base64

from util import enum, log

# cached system values
SYS_START_TIME, SYS_PHYSICAL_MEMORY = None, None
CLOCK_TICKS = os.sysconf(os.sysconf_names["SC_CLK_TCK"])
Stat = enum.Enum("COMMAND", "CPU_UTIME", "CPU_STIME", "START_TIME")

CONFIG = {"queries.useProc": True,
          "log.procCallMade": log.DEBUG}

def loadConfig(config):
  config.update(CONFIG)

def isProcAvailable():
  """
  Provides true if configured to use proc resolution and it's available on the
  platform, false otherwise.
  """
  
  return CONFIG["queries.useProc"] and os.uname()[0] == "Linux"

def getSystemStartTime():
  """
  Provides the unix time (seconds since epoch) when the system started.
  """
  
  global SYS_START_TIME
  if not SYS_START_TIME:
    startTime = time.time()
    statFile = open('/proc/stat')
    statLines = statFile.readlines()
    statFile.close()
    
    for line in statLines:
      if line.startswith('btime'):
        SYS_START_TIME = float(line.strip().split()[1])
        break
    
    _logProcRuntime("system start time", "/proc/stat[btime]", startTime)
  
  return SYS_START_TIME

def getPhysicalMemory():
  """
  Provides the total physical memory on the system in bytes.
  """
  
  global SYS_PHYSICAL_MEMORY
  if not SYS_PHYSICAL_MEMORY:
    startTime = time.time()
    memFile = open('/proc/meminfo')
    memLines = memFile.readlines()
    memFile.close()
    
    for line in memLines:
      if line.startswith('MemTotal:'):
        SYS_PHYSICAL_MEMORY = int(line.split()[1]) * 1024
    
    _logProcRuntime("system physical memory", "/proc/meminfo[MemTotal]", startTime)
  
  return SYS_PHYSICAL_MEMORY

def getPwd(pid):
  """
  Provides the current working directory for the given process.
  
  Arguments:
    pid - queried process
  """
  
  startTime = time.time()
  if pid == 0: cwd = ""
  else: cwd = os.readlink("/proc/%s/cwd" % pid)
  _logProcRuntime("cwd", "/proc/%s/cwd" % pid, startTime)
  return cwd

def getUid(pid):
  """
  Provides the user ID the given process is running under. This is None if it
  can't be determined.
  
  Arguments:
    pid - queried process
  """
  
  startTime = time.time()
  statusFile = open("/proc/%s/status" % pid)
  statusFileLines = statusFile.readlines()
  statusFile.close()
  
  result = None
  for line in statusFileLines:
    if line.startswith("Uid:"):
      lineComp = line.split()
      
      if len(lineComp) >= 2 and lineComp[1].isdigit():
        result = lineComp[1]
  
  _logProcRuntime("uid", "/proc/%s/status[Uid]" % pid, startTime)
  return result

def getMemoryUsage(pid):
  """
  Provides the memory usage in bytes for the given process of the form:
  (residentSize, virtualSize)
  
  Arguments:
    pid - queried process
  """
  
  # checks if this is the kernel process
  if pid == 0: return (0, 0)
  
  startTime = time.time()
  statusFile = open("/proc/%s/status" % pid)
  statusFileLines = statusFile.readlines()
  statusFile.close()
  
  residentSize, virtualSize = None, None
  for line in statusFileLines:
    if line.startswith("VmRSS"):
      residentSize = int(line.split()[1]) * 1024
      if virtualSize != None: break
    elif line.startswith("VmSize:"):
      virtualSize = int(line.split()[1]) * 1024
      if residentSize != None: break
  
  _logProcRuntime("memory usage", "/proc/%s/status[VmRSS|VmSize]" % pid, startTime)
  return (residentSize, virtualSize)

def getStats(pid, *statTypes):
  """
  Provides process specific information. Options are:
  Stat.COMMAND      command name under which the process is running
  Stat.CPU_UTIME    total user time spent on the process
  Stat.CPU_STIME    total system time spent on the process
  Stat.START_TIME   when this process began, in unix time
  
  Arguments:
    pid       - queried process
    statTypes - information to be provided back
  """
  
  startTime = time.time()
  statFilePath = "/proc/%s/stat" % pid
  statFile = open(statFilePath)
  statContents = statFile.read().strip()
  statFile.close()
  
  # contents are of the form:
  # 8438 (tor) S 8407 8438 8407 34818 8438 4202496...
  statComp = []
  cmdStart, cmdEnd = statContents.find("("), statContents.find(")")
  
  if cmdStart != -1 and cmdEnd != -1:
    statComp.append(statContents[:cmdStart])
    statComp.append(statContents[cmdStart + 1:cmdEnd])
    statComp += statContents[cmdEnd + 1:].split()
  
  if len(statComp) != 44:
    raise IOError("stat file had an unexpected format: %s" % statFilePath)
  
  results, queriedStats = [], []
  for statType in statTypes:
    if statType == Stat.COMMAND:
      queriedStats.append("command")
      if pid == 0: results.append("sched")
      else: results.append(statComp[1])
    elif statType == Stat.CPU_UTIME:
      queriedStats.append("utime")
      if pid == 0: results.append("0")
      else: results.append(str(float(statComp[13]) / CLOCK_TICKS))
    elif statType == Stat.CPU_STIME:
      queriedStats.append("stime")
      if pid == 0: results.append("0")
      else: results.append(str(float(statComp[14]) / CLOCK_TICKS))
    elif statType == Stat.START_TIME:
      queriedStats.append("start time")
      if pid == 0: return getSystemStartTime()
      else:
        # According to documentation, starttime is in field 21 and the unit is
        # jiffies (clock ticks). We divide it for clock ticks, then add the
        # uptime to get the seconds since the epoch.
        pStartTime = float(statComp[21]) / CLOCK_TICKS
        results.append(str(pStartTime + getSystemStartTime()))
  
  _logProcRuntime("process %s" % ", ".join(queriedStats), "/proc/%s/stat" % pid, startTime)
  return results

def getConnections(pid):
  """
  Provides a listing of connection tuples of the form:
  [(local_ipAddr1, local_port1, foreign_ipAddr1, foreign_port1), ...]
  
  If the information about a connection can't be queried (often due to
  permission issues) then it's excluded from the listing.
  
  Arguments:
    pid - ID of the process to be resolved
  """
  
  if pid == "0": return []
  
  # fetches the inode numbers for socket file descriptors
  startTime = time.time()
  inodes = []
  for fd in os.listdir("/proc/%s/fd" % pid):
    try:
      # File descriptor link, such as 'socket:[30899]'
      fdName = os.readlink("/proc/%s/fd/%s" % (pid, fd))
      
      if fdName.startswith('socket:['):
        inodes.append(fdName[8:-1])
    except OSError:
      pass # most likely couldn't be read due to permissions
  
  if not inodes:
    # unable to fetch any connections for this process
    return []
  
  # check for the connection information from the /proc/net contents
  conn = []
  for procFilePath in ("/proc/net/tcp", "/proc/net/udp"):
    procFile = open(procFilePath)
    procFile.readline() # skip the first line
    
    for line in procFile:
      _, lAddr, fAddr, status, _, _, _, _, _, inode = line.split()[:10]
      
      if inode in inodes:
        # if a tcp connection, skip if it isn't yet established
        if procFilePath.endswith("/tcp") and status != "01":
          continue
        
        localIp, localPort = _decodeProcAddressEncoding(lAddr)
        foreignIp, foreignPort = _decodeProcAddressEncoding(fAddr)
        conn.append((localIp, localPort, foreignIp, foreignPort))
    
    procFile.close()
  
  _logProcRuntime("process connections", "/proc/net/[tcp|udp]", startTime)
  
  return conn

def _decodeProcAddressEncoding(addr):
  """
  Translates an address entry in the /proc/net/* contents to a human readable
  form, for instance:
  "0500000A:0016" -> ("10.0.0.5", "22")
  
  Reference:
  http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html
  
  Arguments:
    addr - proc address entry to be decoded
  """
  
  ip, port = addr.split(':')
  
  # the port is represented as a two-byte hexadecimal number
  port = str(int(port, 16))
  
  if sys.version_info >= (3,):
    ip = ip.encode('ascii')
  
  # The IPv4 address portion is a little-endian four-byte hexadecimal number.
  # That is, the least significant byte is listed first, so we need to reverse
  # the order of the bytes to convert it to an IP address.
  #
  # This needs to account for the endian ordering as per...
  # http://code.google.com/p/psutil/issues/detail?id=201
  # https://trac.torproject.org/projects/tor/ticket/4777
  
  if sys.byteorder == 'little':
    ip = socket.inet_ntop(socket.AF_INET, base64.b16decode(ip)[::-1])
  else:
    ip = socket.inet_ntop(socket.AF_INET, base64.b16decode(ip))
  
  return (ip, port)

def _logProcRuntime(parameter, procLocation, startTime):
  msg = "proc call (%s): %s (runtime: %0.4f)" % (parameter, procLocation, time.time() - startTime)
  log.log(CONFIG["log.procCallMade"], msg)