fosl 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/fosl/namespace.rb +2 -0
- data/lib/fosl/parser.rb +162 -0
- data/lib/fosl/process.rb +18 -0
- metadata +70 -0
data/lib/fosl/parser.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
require "fosl/namespace"
|
2
|
+
require "fosl/process"
|
3
|
+
|
4
|
+
class FOSL::Parser
|
5
|
+
# Fields are separated by newlines or null
|
6
|
+
# Fields start with a character followed by the data
|
7
|
+
|
8
|
+
# These are the fields according to the lsof manpage.
|
9
|
+
# If you want to implement one, you should write a 'parse_<field letter>'
|
10
|
+
# method. It should return a hash of key => value you want to save.
|
11
|
+
#
|
12
|
+
# # The following copied mostly verbatim from the lsof manpage.
|
13
|
+
# - a file access mode
|
14
|
+
# - c process command name (all characters from proc or user structure)
|
15
|
+
# - C file structure share count
|
16
|
+
# - d file's device character code
|
17
|
+
# - D file's major/minor device number (0x<hexadecimal>)
|
18
|
+
# - f file descriptor
|
19
|
+
# - F file structure address (0x<hexadecimal>)
|
20
|
+
# - G file flaGs (0x<hexadecimal>; names if +fg follows)
|
21
|
+
# - i file's inode number
|
22
|
+
# - k link count
|
23
|
+
# - l file's lock status
|
24
|
+
# - L process login name
|
25
|
+
# - m marker between repeated output
|
26
|
+
# - n file name, comment, Internet address
|
27
|
+
# - N node identifier (ox<hexadecimal>
|
28
|
+
# - o file's offset (decimal)
|
29
|
+
# - p process ID (always selected)
|
30
|
+
# - g process group ID
|
31
|
+
# - P protocol name
|
32
|
+
# - r raw device number (0x<hexadecimal>)
|
33
|
+
# - R parent process ID
|
34
|
+
# - s file's size (decimal)
|
35
|
+
# - S file's stream identification
|
36
|
+
# - t file's type
|
37
|
+
# - T TCP/TPI information, identified by prefixes (the
|
38
|
+
# - u process user ID
|
39
|
+
# - z Solaris 10 and higher zone name
|
40
|
+
# - Z SELinux security context (inhibited when SELinux is disabled)
|
41
|
+
|
42
|
+
# T is various network/tcp/socket information.
|
43
|
+
def parse_T(data)
|
44
|
+
prefix, value = data.split("=")
|
45
|
+
case prefix
|
46
|
+
when "ST" ; prefix = :state
|
47
|
+
when "QR" ; prefix = :read_queue
|
48
|
+
when "QS" ; prefix = :send_queue
|
49
|
+
|
50
|
+
# (sissel) I don't know the values of these fields. Feel free
|
51
|
+
# to implement them and send me patches.
|
52
|
+
#when "SO" ; prefix = :socket_options
|
53
|
+
#when "SS" ; prefix = :socket_State
|
54
|
+
#when "TF" ; prefix = :tcp_flags
|
55
|
+
#when "WR" ; prefix = :read_window
|
56
|
+
#when "WW" ; prefix = :write_window
|
57
|
+
end
|
58
|
+
return { prefix => value }
|
59
|
+
end # def parse_T
|
60
|
+
|
61
|
+
# The file's type
|
62
|
+
def parse_t(data)
|
63
|
+
return { :type => data }
|
64
|
+
end
|
65
|
+
|
66
|
+
# The protocol name
|
67
|
+
def parse_P(data)
|
68
|
+
return { :protocol => data }
|
69
|
+
end
|
70
|
+
|
71
|
+
# the pid
|
72
|
+
def parse_p(data)
|
73
|
+
new_pid(data.to_i)
|
74
|
+
return :new_pid
|
75
|
+
end
|
76
|
+
|
77
|
+
# the file name or identifier
|
78
|
+
def parse_n(data)
|
79
|
+
return { :name => data }
|
80
|
+
end
|
81
|
+
|
82
|
+
# file descriptor (or 'cwd' etc...)
|
83
|
+
def parse_f(data)
|
84
|
+
new_file
|
85
|
+
|
86
|
+
# Convert to int it looks like a number.
|
87
|
+
if data.to_i != 0 or data == "0"
|
88
|
+
data = data.to_i
|
89
|
+
end
|
90
|
+
|
91
|
+
return { :fd => data }
|
92
|
+
end
|
93
|
+
|
94
|
+
# The command name
|
95
|
+
def parse_c(data)
|
96
|
+
@current_process.command = data
|
97
|
+
return nil
|
98
|
+
end
|
99
|
+
|
100
|
+
# state helper, creates a new process
|
101
|
+
def new_pid(pid)
|
102
|
+
new_file # push the last file (if any) onto the last process
|
103
|
+
@current_process = FOSL::Process.new(pid)
|
104
|
+
end
|
105
|
+
|
106
|
+
# state helper, creates a new file hash
|
107
|
+
def new_file
|
108
|
+
if !@current_file.nil? && !@current_file.empty?
|
109
|
+
@current_process.files << @current_file
|
110
|
+
end
|
111
|
+
|
112
|
+
@current_file = {}
|
113
|
+
end
|
114
|
+
|
115
|
+
# Parse output from an lsof(1) run. You must run
|
116
|
+
# This output must be from lsof run with this flag '-F Pcfnt0'
|
117
|
+
def parse(data)
|
118
|
+
if data[0..0] != "p"
|
119
|
+
raise "Expected first character to be 'p'. Unexpected data input - #{data[0..30]}..."
|
120
|
+
end
|
121
|
+
|
122
|
+
result = Hash.new { |h,k| h[k] = FOSL::Process.new(k) }
|
123
|
+
|
124
|
+
data.split(/[\n\0]/).each do |field|
|
125
|
+
next if field.empty?
|
126
|
+
type = field[0 .. 0]
|
127
|
+
value = field[1 .. -1]
|
128
|
+
|
129
|
+
method = "parse_#{type}".to_sym
|
130
|
+
if self.respond_to?(method)
|
131
|
+
r = self.send(method, value)
|
132
|
+
#p field => r
|
133
|
+
if r.is_a?(Hash)
|
134
|
+
@current_file.merge!(r)
|
135
|
+
elsif r == :new_pid
|
136
|
+
result[@current_process.pid] = @current_process
|
137
|
+
end
|
138
|
+
else
|
139
|
+
$stderr.puts "Unsupported field type '#{type}': #{field.inspect}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# push last file
|
144
|
+
new_file
|
145
|
+
|
146
|
+
return result
|
147
|
+
end # def parse
|
148
|
+
|
149
|
+
# Helper for running lsof.
|
150
|
+
# Returns the same thing as 'parse'
|
151
|
+
#
|
152
|
+
# Example:
|
153
|
+
# lsof("-i :443")
|
154
|
+
def lsof(args="")
|
155
|
+
output = `lsof -F PcfntT0 #{args}`
|
156
|
+
# Should we raise an exception, or just return empty results, on failure?
|
157
|
+
if $?.exitstatus != 0
|
158
|
+
raise "lsof exited with status #{$?.exitstatus}"
|
159
|
+
end
|
160
|
+
return self.parse(output)
|
161
|
+
end
|
162
|
+
end # class FOSL::Parser
|
data/lib/fosl/process.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "fosl/namespace"
|
2
|
+
|
3
|
+
class FOSL::Process
|
4
|
+
attr_reader :files
|
5
|
+
attr_reader :pid
|
6
|
+
attr_accessor :command
|
7
|
+
|
8
|
+
def initialize(pid)
|
9
|
+
@command = nil
|
10
|
+
@pid = pid
|
11
|
+
@files = []
|
12
|
+
end
|
13
|
+
|
14
|
+
# helpers
|
15
|
+
def listeners
|
16
|
+
@files.find_all { |f| f[:state] == "LISTEN" }
|
17
|
+
end
|
18
|
+
end # class FOSL::Process
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fosl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jordan Sissel
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-18 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: ""
|
23
|
+
email: jls@semicomplete.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/fosl/parser.rb
|
32
|
+
- lib/fosl/namespace.rb
|
33
|
+
- lib/fosl/process.rb
|
34
|
+
has_rdoc: true
|
35
|
+
homepage: https://github.com/jordansissel/ruby-lsof
|
36
|
+
licenses: []
|
37
|
+
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
hash: 3
|
50
|
+
segments:
|
51
|
+
- 0
|
52
|
+
version: "0"
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 3
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.5.1
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: fosl - a ruby api for reading lsof(1) output
|
69
|
+
test_files: []
|
70
|
+
|