process_observer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +22 -0
- data/LICENSE +21 -0
- data/README.md +22 -0
- data/Rakefile +14 -0
- data/bin/console +7 -0
- data/lib/process_observer/exceptions.rb +27 -0
- data/lib/process_observer/linux.rb +42 -0
- data/lib/process_observer/log.rb +32 -0
- data/lib/process_observer/main.rb +57 -0
- data/lib/process_observer/os.rb +34 -0
- data/lib/process_observer/process.rb +232 -0
- data/lib/process_observer/version.rb +7 -0
- data/lib/process_observer/windows.rb +157 -0
- data/lib/process_observer.rb +12 -0
- data/process_observer.gemspec +32 -0
- data/test/test_process_observer.rb +9 -0
- data/test/test_windows_utility.rb +17 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0db88799a99a3c3460d56f238c79855d55f3193efb73c3e3f3353b9392605383
|
4
|
+
data.tar.gz: b30813b905a168e86ecb874e883e8bbe989bf40542b0eeb66e79adfac05a0640
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 532689e20fd3c59307c037cee48b7e698c36d5beda99a07dc62f7720b92626c6e48fd8c84f8fb0fcc8058982e31cb745636c479685b4ec5adf7bbf5453659126
|
7
|
+
data.tar.gz: 79f1831d0c430ac1504a4a4140f7efd68a416c19f38771b8c26e56b088ab1409e21b94832a6a62d4d1ec96a60a071ab80625e99e79c4e2a4f97536aca13ffedb
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
process_observer (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
minitest (5.11.3)
|
10
|
+
rake (12.3.3)
|
11
|
+
|
12
|
+
PLATFORMS
|
13
|
+
x64-mingw32
|
14
|
+
|
15
|
+
DEPENDENCIES
|
16
|
+
bundler (~> 2.0)
|
17
|
+
minitest (~> 5.0)
|
18
|
+
process_observer!
|
19
|
+
rake (~> 12.0)
|
20
|
+
|
21
|
+
BUNDLED WITH
|
22
|
+
2.0.2
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Fizvlad
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# ProcessObserver
|
2
|
+
|
3
|
+
This small gem may be used to obtain information about processes suing OS specific
|
4
|
+
console applications. Currently it is `tasklist` for Windows and `ps` for Linux.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
You can install this gem executing `gem install process_observer` in console.
|
9
|
+
|
10
|
+
Then simply include it with `include "process_observer"`
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
You can find documentation here: https://www.rubydoc.info/gems/process_observer.
|
15
|
+
|
16
|
+
## Contributing
|
17
|
+
|
18
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/fizvlad/process_observer/issues.
|
19
|
+
|
20
|
+
## License
|
21
|
+
|
22
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module ProcessObserver
|
2
|
+
|
3
|
+
##
|
4
|
+
# Module with exceptions which might be raised.
|
5
|
+
module Exceptions
|
6
|
+
|
7
|
+
##
|
8
|
+
# String contains some special character which is not allowed.
|
9
|
+
class SpecialCharacterError < ArgumentError; end
|
10
|
+
|
11
|
+
##
|
12
|
+
# This platform is currently unsupported.
|
13
|
+
class UnsupportedPlatformError < ArgumentError; end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Expected to see single process.
|
17
|
+
class MultipleProcessError < RuntimeError; end
|
18
|
+
|
19
|
+
##
|
20
|
+
# UTF-8 encoding is required.
|
21
|
+
class EncodingError < RuntimeError; end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
include Exceptions
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ProcessObserver
|
2
|
+
|
3
|
+
##
|
4
|
+
# Module with methods to interact with Linux.
|
5
|
+
module Linux
|
6
|
+
|
7
|
+
##
|
8
|
+
# Process list executable.
|
9
|
+
EXE = "ps"
|
10
|
+
|
11
|
+
##
|
12
|
+
# Output format.
|
13
|
+
OUTPUT_FORMAT = "-o \"pid,comm,stat,time,rss\""
|
14
|
+
|
15
|
+
##
|
16
|
+
# Call processes list executable with provided options string.
|
17
|
+
#
|
18
|
+
# @param options [String] options string.
|
19
|
+
#
|
20
|
+
# @return [Array<Process>] array of processes.
|
21
|
+
def self.call(options = "-A")
|
22
|
+
raise ArgumentError, "Provide options string", caller if options.to_s.empty?
|
23
|
+
command = "#{EXE} #{options} #{OUTPUT_FORMAT}"
|
24
|
+
Log.debug "Executing: #{command}"
|
25
|
+
re = `#{command}`.chomp
|
26
|
+
csv = CSV.parse(re.each_line.map { |line| line.strip.squeeze(" ") }.join("\n"), col_sep: " ")
|
27
|
+
|
28
|
+
enum = csv.to_a.drop(1) # Skip header
|
29
|
+
enum.map do |data|
|
30
|
+
LinuxProcess.new(
|
31
|
+
pid: data[0],
|
32
|
+
comm: data[1],
|
33
|
+
stat: data[2],
|
34
|
+
time: data[3],
|
35
|
+
rss: data[4]
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module ProcessObserver
|
4
|
+
|
5
|
+
##
|
6
|
+
# Logging methods
|
7
|
+
module Log
|
8
|
+
|
9
|
+
##
|
10
|
+
# Loggers
|
11
|
+
@@loggers = {
|
12
|
+
debug: Logger.new(STDOUT),
|
13
|
+
warn: Logger.new(STDERR)
|
14
|
+
}
|
15
|
+
@@loggers[:debug].level = Logger::DEBUG
|
16
|
+
@@loggers[:warn].level = Logger::WARN
|
17
|
+
|
18
|
+
##
|
19
|
+
# Send warning.
|
20
|
+
def self.warn(*args)
|
21
|
+
@@loggers[:warn].warn(args.join("\n"))
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Send debug message.
|
26
|
+
def self.debug(*args)
|
27
|
+
@@loggers[:debug].debug(args.join("\n")) if $DEBUG
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module ProcessObserver
|
2
|
+
|
3
|
+
##
|
4
|
+
# List of all active processes.
|
5
|
+
#
|
6
|
+
# @return [Array<Process>]
|
7
|
+
def self.all
|
8
|
+
case
|
9
|
+
when OS.windows?
|
10
|
+
Windows.call
|
11
|
+
when OS.linux?
|
12
|
+
Linux.call
|
13
|
+
else
|
14
|
+
raise UnsupportedPlatformError, "This platform is currently unsupported", caller
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# List of all running processes.
|
20
|
+
#
|
21
|
+
# @return [Array<Process>]
|
22
|
+
def self.running
|
23
|
+
case
|
24
|
+
when OS.windows?
|
25
|
+
Windows.call(fi: "STATUS eq RUNNING")
|
26
|
+
when OS.linux?
|
27
|
+
Linux.call.select { |process| process.status.include?("R") }
|
28
|
+
else
|
29
|
+
raise UnsupportedPlatformError, "This platform is currently unsupported", caller
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Requests info about current Ruby process.
|
35
|
+
#
|
36
|
+
# @return [Process]
|
37
|
+
def self.this
|
38
|
+
pid = ::Process.pid
|
39
|
+
case
|
40
|
+
when OS.windows?
|
41
|
+
arr = Windows.call(fi: "PID eq #{pid}")
|
42
|
+
when OS.linux?
|
43
|
+
arr = Linux.call.select { |process| process.pid == pid }
|
44
|
+
else
|
45
|
+
raise UnsupportedPlatformError, "This platform is currently unsupported", caller
|
46
|
+
end
|
47
|
+
raise MultipleProcessError, "Expected single process, got: #{arr.size}", caller if arr.size != 1
|
48
|
+
arr[0]
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# @return [Integer] used memory in KB.
|
53
|
+
def self.used_memory
|
54
|
+
self.all.sum(0, &:memory)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ProcessObserver
|
2
|
+
|
3
|
+
##
|
4
|
+
# Module with methods to identify current platform.
|
5
|
+
#
|
6
|
+
# Source: https://stackoverflow.com/a/171011/9293044
|
7
|
+
module OS
|
8
|
+
|
9
|
+
##
|
10
|
+
# Whether Windows is used.
|
11
|
+
def OS.windows?
|
12
|
+
(/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Whether Mac is used.
|
17
|
+
def OS.mac?
|
18
|
+
(/darwin/ =~ RUBY_PLATFORM) != nil
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Whether Unix (not Windows) is used.
|
23
|
+
def OS.unix?
|
24
|
+
!OS.windows?
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Whether Linux (Unix but not Mac) is used.
|
29
|
+
def OS.linux?
|
30
|
+
OS.unix? and not OS.mac?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
module ProcessObserver
|
2
|
+
|
3
|
+
##
|
4
|
+
# Class representing process.
|
5
|
+
class Process
|
6
|
+
|
7
|
+
##
|
8
|
+
# @return [String] name name of the executable or command.
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
##
|
12
|
+
# @return [Integer] pid process ID.
|
13
|
+
attr_reader :pid
|
14
|
+
|
15
|
+
##
|
16
|
+
# @return [Integer, nil] memory amount of consumed memory.
|
17
|
+
attr_reader :memory
|
18
|
+
|
19
|
+
##
|
20
|
+
# Initialize new process.
|
21
|
+
#
|
22
|
+
# @param options [Hash]
|
23
|
+
#
|
24
|
+
# @option options [String] name name of the executable or command.
|
25
|
+
# @option options [Integer] pid process ID.
|
26
|
+
# @option options [Integer, nil] memory amount of consumed memory.
|
27
|
+
def initialize(options)
|
28
|
+
@name = options[:image_name].to_s
|
29
|
+
@pid = options[:pid].to_i
|
30
|
+
@memory = options[:memory] ? options[:memory].to_i : nil
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Class representing process in Windows.
|
37
|
+
class WindowsProcess < Process
|
38
|
+
|
39
|
+
##
|
40
|
+
# @return [String] name of the executable.
|
41
|
+
attr_reader :image_name
|
42
|
+
|
43
|
+
##
|
44
|
+
# @return [Integer] process ID.
|
45
|
+
attr_reader :pid
|
46
|
+
|
47
|
+
##
|
48
|
+
# @return [String, nil] session name.
|
49
|
+
attr_reader :session_name
|
50
|
+
|
51
|
+
##
|
52
|
+
# @return [Integer, nil] session number.
|
53
|
+
attr_reader :session
|
54
|
+
|
55
|
+
##
|
56
|
+
# @return [Integer, nil] memory usage in KB.
|
57
|
+
attr_reader :mem_usage
|
58
|
+
|
59
|
+
##
|
60
|
+
# @return [String, nil] process status.
|
61
|
+
attr_reader :status
|
62
|
+
|
63
|
+
##
|
64
|
+
# @return [String, nil] user which started process.
|
65
|
+
attr_reader :user_name
|
66
|
+
|
67
|
+
##
|
68
|
+
# @return [String, nil] process runtime.
|
69
|
+
attr_reader :cpu_time
|
70
|
+
|
71
|
+
##
|
72
|
+
# @return [String, nil] title of process window.
|
73
|
+
attr_reader :window_title
|
74
|
+
|
75
|
+
##
|
76
|
+
# @return [Array<String>] services.
|
77
|
+
attr_reader :services
|
78
|
+
|
79
|
+
##
|
80
|
+
# @return [Array<String>] used DLLs.
|
81
|
+
attr_reader :modules
|
82
|
+
|
83
|
+
##
|
84
|
+
# @return [String, nil] name of app package name.
|
85
|
+
attr_reader :package_name
|
86
|
+
|
87
|
+
##
|
88
|
+
# Initialize new process.
|
89
|
+
#
|
90
|
+
# @param options [Hash]
|
91
|
+
#
|
92
|
+
# @option options [String] image_name name of the executable.
|
93
|
+
# @option options [Integer] pid process ID.
|
94
|
+
# @option options [String, nil] session_name session name.
|
95
|
+
# @option options [Integer, nil] session session number.
|
96
|
+
# @option options [Integer, nil] mem_usage memory usage in KB.
|
97
|
+
# @option options [String, nil] status process status.
|
98
|
+
# @option options [String, nil] user_name user which started process.
|
99
|
+
# @option options [String, nil] cpu_time process runtime.
|
100
|
+
# @option options [String, nil] window_title title of process window.
|
101
|
+
# @option options [Array<String>, nil] services services.
|
102
|
+
# @option options [Array<String>, nil] modules used DLLs.
|
103
|
+
# @option options [String, nil] package_name name of app package name.
|
104
|
+
def initialize(options)
|
105
|
+
@image_name = options[:image_name].to_s
|
106
|
+
@pid = options[:pid].to_i
|
107
|
+
@session_name = options[:session_name] ? options[:session_name].to_s : nil
|
108
|
+
@session = options[:session] ? options[:session].to_i : nil
|
109
|
+
@mem_usage = options[:mem_usage] ? options[:mem_usage].to_s.gsub(/[^\d]/, "").to_i : nil
|
110
|
+
@status = options[:status] ? options[:status].to_s : nil
|
111
|
+
@user_name = options[:user_name] ? options[:user_name].to_s : nil
|
112
|
+
@cpu_time = options[:cpu_time] ? options[:cpu_time].to_s : nil
|
113
|
+
@window_title = options[:window_title] ? options[:window_title].to_s : nil
|
114
|
+
@services = options[:services] ? options[:services].to_s.split(",") : []
|
115
|
+
@modules = options[:modules] ? options[:modules].to_s.split(",") : []
|
116
|
+
@package_name = options[:package_name] ? options[:package_name].to_s : nil
|
117
|
+
|
118
|
+
super(
|
119
|
+
name: @image_name,
|
120
|
+
pid: @pid,
|
121
|
+
memory: @mem_usage
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# @return [String] PID and name of process.
|
127
|
+
def to_s
|
128
|
+
"Process ##{@pid} #{@image_name}"
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Inspect all stored data.
|
133
|
+
#
|
134
|
+
# @param [String] splitter
|
135
|
+
#
|
136
|
+
# @return [String] all accessable info in human-readable form.
|
137
|
+
def inspect(splitter = "; ")
|
138
|
+
to_s +
|
139
|
+
(@session_name || @session ? "#{splitter}Session: #{@session_name}(#{@session})" : "") +
|
140
|
+
(@mem_usage ? "#{splitter}Memory usage: #{@mem_usage} KB" : "") +
|
141
|
+
(@status ? "#{splitter}Status: #{@status}" : "") +
|
142
|
+
(@user_name || @cpu_time ? "#{splitter}Launched by: #{@user_name}, runs for #{@cpu_time}" : "") +
|
143
|
+
(@window_title ? "#{splitter}Title: #{@window_title}" : "") +
|
144
|
+
(@services.empty? ? "" : "#{splitter}Services: #{@services.join(",")}") +
|
145
|
+
(@modules.empty? ? "" : "#{splitter}Modules: #{@modules.join(",")}") +
|
146
|
+
(@package_name ? "#{splitter}Package name: #{@package_name}" : "")
|
147
|
+
end
|
148
|
+
|
149
|
+
##
|
150
|
+
# Compare with other process by PID.
|
151
|
+
def ==(other)
|
152
|
+
WindowsProcess === other && other.pid == @pid
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# Class representing process in Unix.
|
159
|
+
class LinuxProcess < Process
|
160
|
+
|
161
|
+
##
|
162
|
+
# @return [String] command which launched process.
|
163
|
+
attr_reader :comm
|
164
|
+
|
165
|
+
##
|
166
|
+
# @return [Integer] process ID.
|
167
|
+
attr_reader :pid
|
168
|
+
|
169
|
+
##
|
170
|
+
# @return [String, nil] process status.
|
171
|
+
attr_reader :stat
|
172
|
+
|
173
|
+
##
|
174
|
+
# @return [String, nil] amount of time process is running.
|
175
|
+
attr_reader :time
|
176
|
+
|
177
|
+
##
|
178
|
+
# @return [Integer, nil] amount of used CPU memory in KB.
|
179
|
+
attr_reader :rss
|
180
|
+
|
181
|
+
##
|
182
|
+
# Initialize new process.
|
183
|
+
#
|
184
|
+
# @param options [Hash]
|
185
|
+
#
|
186
|
+
# @option options [String] comm command which launched process.
|
187
|
+
# @option options [Integer] pid process ID.
|
188
|
+
# @option options [String, nil] stat process status.
|
189
|
+
# @option options [String, nil] time amount of time process is running.
|
190
|
+
# @option options [Integer, nil] rss amount of used CPU memory in KB.
|
191
|
+
def initialize(options)
|
192
|
+
@comm = options[:comm].to_s
|
193
|
+
@pid = options[:pid].to_i
|
194
|
+
@stat = options[:stat] ? options[:stat].to_s : nil
|
195
|
+
@time = options[:time] ? options[:time].to_s : nil
|
196
|
+
@rss = options[:rss] ? options[:rss].to_i : nil
|
197
|
+
|
198
|
+
super(
|
199
|
+
name: @comm,
|
200
|
+
pid: @pid,
|
201
|
+
memory: @rss
|
202
|
+
)
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# @return [String] PID and command of process.
|
207
|
+
def to_s
|
208
|
+
"Process ##{@pid} #{@comm}"
|
209
|
+
end
|
210
|
+
|
211
|
+
##
|
212
|
+
# Inspect all stored data.
|
213
|
+
#
|
214
|
+
# @param [String] splitter
|
215
|
+
#
|
216
|
+
# @return [String] all accessable info in human-readable form.
|
217
|
+
def inspect(splitter = "; ")
|
218
|
+
to_s +
|
219
|
+
(@stat ? "#{splitter}Status: #{@stat}" : "") +
|
220
|
+
(@time ? "#{splitter}Time running: #{@time}" : "") +
|
221
|
+
(@rss ? "#{splitter}Memory: #{@rss} KB" : "")
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# Compare with other process by PID.
|
226
|
+
def ==(other)
|
227
|
+
LinuxProcess === other && other.pid == @pid
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require "csv"
|
2
|
+
|
3
|
+
module ProcessObserver
|
4
|
+
|
5
|
+
##
|
6
|
+
# Module with methods to interact with Windows.
|
7
|
+
module Windows
|
8
|
+
|
9
|
+
##
|
10
|
+
# Task list executable.
|
11
|
+
EXE = "tasklist"
|
12
|
+
|
13
|
+
##
|
14
|
+
# Method to turn hash with call parameters into call string argument.
|
15
|
+
#
|
16
|
+
# @note: this method currently raises error when meet string with double quote.
|
17
|
+
#
|
18
|
+
# @param hash [Hash] not empty hash.
|
19
|
+
#
|
20
|
+
# @return [String]
|
21
|
+
def self.to_arg(hash)
|
22
|
+
raise ArgumentError, "Empty hash", caller if hash.empty?
|
23
|
+
|
24
|
+
hash.map do |key, value|
|
25
|
+
case value
|
26
|
+
when TrueClass, FalseClass, NilClass # Boolean or nil
|
27
|
+
value ? "/#{key}" : ""
|
28
|
+
when String
|
29
|
+
raise Exceptions::SpecialCharacterError, "Please don't use double quotes in string", caller if value.include?('"')
|
30
|
+
"/#{key} \"#{value}\""
|
31
|
+
else
|
32
|
+
"/#{key} #{value}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
.join(" ").strip.gsub(/\s+/, " ")
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Call task list executable with provided options.
|
40
|
+
#
|
41
|
+
# @param options [Hash] hash with call options.
|
42
|
+
#
|
43
|
+
# @option options [Boolean] svc (false) return services for each process.
|
44
|
+
# @option options [Boolean] apps (false) return store processes.
|
45
|
+
# @option options [Boolean] v (false) return extended info.
|
46
|
+
# @option options [true, String, nil] m (nil) return tasks using given module or all modules if +true+.
|
47
|
+
# @option options [String, nil] fi (nil) filter.
|
48
|
+
# @option options [:csv, :table, :list] fo (:csv) format of output.
|
49
|
+
# if +:csv+ is chosen output will be parsed with CSV library and
|
50
|
+
# returned as array of {Process}, otherwise whole string will be returned.
|
51
|
+
#
|
52
|
+
# @return [String, Array<Process>] call output
|
53
|
+
def self.call(options = {})
|
54
|
+
raise EncodingError, "Please use `chcp 65001` to switch to UTF-8 first" if `chcp`.slice(/\d+/).to_i != 65001
|
55
|
+
|
56
|
+
call_params = {}
|
57
|
+
call_params[:svc] = !!options[:svc]
|
58
|
+
call_params[:apps] = !!options[:apps]
|
59
|
+
call_params[:v] = !!options[:v]
|
60
|
+
case
|
61
|
+
when options[:m].nil? then call_params[:m] = nil
|
62
|
+
when options[:m] == true then call_params[:m] = true
|
63
|
+
else call_params[:m] = options[:m].to_s
|
64
|
+
end
|
65
|
+
call_params[:fi] = options[:fi].to_s unless options[:fi].nil?
|
66
|
+
|
67
|
+
call_params[:fo] = options[:fo] || :csv
|
68
|
+
raise ArgumentError, "Unknown format option #{call_params[:fo]}", caller unless [:csv, :table, :list].include?(call_params[:fo])
|
69
|
+
|
70
|
+
Log.debug "Call parameters: #{call_params}"
|
71
|
+
command = call_params.empty? ? "#{EXE}" : "#{EXE} #{to_arg(call_params)}"
|
72
|
+
Log.debug "Executing: #{command}"
|
73
|
+
re = `#{command}`.chomp
|
74
|
+
|
75
|
+
if call_params[:fo] == :csv
|
76
|
+
csv = CSV.parse(re)
|
77
|
+
enum = csv.to_a.drop(1) # Skip header
|
78
|
+
case
|
79
|
+
when call_params[:v] && call_params[:apps]
|
80
|
+
# Extended info fow apps
|
81
|
+
enum.map do |data|
|
82
|
+
WindowsProcess.new(
|
83
|
+
image_name: data[0],
|
84
|
+
pid: data[1],
|
85
|
+
session_name: data[2],
|
86
|
+
session: data[3],
|
87
|
+
mem_usage: data[4],
|
88
|
+
status: data[5],
|
89
|
+
user_name: data[6],
|
90
|
+
cpu_time: data[7],
|
91
|
+
window_title: data[8],
|
92
|
+
package_name: data[9]
|
93
|
+
)
|
94
|
+
end
|
95
|
+
when call_params[:v]
|
96
|
+
# Extended info
|
97
|
+
enum.map do |data|
|
98
|
+
WindowsProcess.new(
|
99
|
+
image_name: data[0],
|
100
|
+
pid: data[1],
|
101
|
+
session_name: data[2],
|
102
|
+
session: data[3],
|
103
|
+
mem_usage: data[4],
|
104
|
+
status: data[5],
|
105
|
+
user_name: data[6],
|
106
|
+
cpu_time: data[7],
|
107
|
+
window_title: data[8]
|
108
|
+
)
|
109
|
+
end
|
110
|
+
when call_params[:apps]
|
111
|
+
# Apps
|
112
|
+
enum.map do |data|
|
113
|
+
WindowsProcess.new(
|
114
|
+
image_name: data[0],
|
115
|
+
pid: data[1],
|
116
|
+
mem_usage: data[2],
|
117
|
+
package_name: data[3]
|
118
|
+
)
|
119
|
+
end
|
120
|
+
when call_params[:m]
|
121
|
+
# Modules
|
122
|
+
enum.map do |data|
|
123
|
+
WindowsProcess.new(
|
124
|
+
image_name: data[0],
|
125
|
+
pid: data[1],
|
126
|
+
modules: data[2]
|
127
|
+
)
|
128
|
+
end
|
129
|
+
when call_params[:svc]
|
130
|
+
# Services
|
131
|
+
enum.map do |data|
|
132
|
+
WindowsProcess.new(
|
133
|
+
image_name: data[0],
|
134
|
+
pid: data[1],
|
135
|
+
services: data[2]
|
136
|
+
)
|
137
|
+
end
|
138
|
+
else
|
139
|
+
# Regular mode
|
140
|
+
enum.map do |data|
|
141
|
+
WindowsProcess.new(
|
142
|
+
image_name: data[0],
|
143
|
+
pid: data[1],
|
144
|
+
session_name: data[2],
|
145
|
+
session: data[3],
|
146
|
+
mem_usage: data[4]
|
147
|
+
)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
else
|
151
|
+
re.strip
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require_relative "./process_observer/version"
|
2
|
+
require_relative "./process_observer/main"
|
3
|
+
require_relative "./process_observer/exceptions"
|
4
|
+
require_relative "./process_observer/process"
|
5
|
+
require_relative "./process_observer/os"
|
6
|
+
require_relative "./process_observer/windows"
|
7
|
+
require_relative "./process_observer/linux"
|
8
|
+
require_relative "./process_observer/log"
|
9
|
+
|
10
|
+
##
|
11
|
+
# Module containing everything.
|
12
|
+
module ProcessObserver; end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "process_observer/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "process_observer"
|
7
|
+
spec.version = ProcessObserver::VERSION
|
8
|
+
spec.authors = ["Fizvlad"]
|
9
|
+
spec.email = ["fizvlad@mail.ru"]
|
10
|
+
|
11
|
+
spec.summary = "Small gem to get info about current list of processes."
|
12
|
+
spec.homepage = "https://github.com/fizvlad/process-observer-rb"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
+
spec.metadata["source_code_uri"] = "https://github.com/fizvlad/process-observer-rb"
|
17
|
+
spec.metadata["changelog_uri"] = "https://github.com/fizvlad/process-observer-rb/releases"
|
18
|
+
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
|
+
end
|
24
|
+
spec.test_files = Dir[ "test/**/test_*.rb" ]
|
25
|
+
spec.require_paths = ["lib"]
|
26
|
+
|
27
|
+
spec.required_ruby_version = ">=2.5.0"
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
30
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
31
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative "./helper"
|
2
|
+
|
3
|
+
class ProcessObserverTest < Minitest::Test
|
4
|
+
|
5
|
+
def test_hash_to_str
|
6
|
+
assert_equal("/one_param 42", ProcessObserver::Windows.to_arg(one_param: 42))
|
7
|
+
assert_equal("/one_param", ProcessObserver::Windows.to_arg(one_param: true))
|
8
|
+
assert_equal("/first /second", ProcessObserver::Windows.to_arg(first: true, second: true))
|
9
|
+
assert_equal("/first", ProcessObserver::Windows.to_arg(first: true, second: false))
|
10
|
+
assert_equal("/first", ProcessObserver::Windows.to_arg(first: true, second: nil))
|
11
|
+
assert_equal("/first /second \"str example\"", ProcessObserver::Windows.to_arg(first: true, second: "str example"))
|
12
|
+
assert_raises(ProcessObserver::SpecialCharacterError) do
|
13
|
+
ProcessObserver::Windows.to_arg(first: true, second: "str with quote: \"")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: process_observer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Fizvlad
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-08-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '12.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '12.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- fizvlad@mail.ru
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- Gemfile
|
64
|
+
- Gemfile.lock
|
65
|
+
- LICENSE
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- bin/console
|
69
|
+
- lib/process_observer.rb
|
70
|
+
- lib/process_observer/exceptions.rb
|
71
|
+
- lib/process_observer/linux.rb
|
72
|
+
- lib/process_observer/log.rb
|
73
|
+
- lib/process_observer/main.rb
|
74
|
+
- lib/process_observer/os.rb
|
75
|
+
- lib/process_observer/process.rb
|
76
|
+
- lib/process_observer/version.rb
|
77
|
+
- lib/process_observer/windows.rb
|
78
|
+
- process_observer.gemspec
|
79
|
+
- test/test_process_observer.rb
|
80
|
+
- test/test_windows_utility.rb
|
81
|
+
homepage: https://github.com/fizvlad/process-observer-rb
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
metadata:
|
85
|
+
homepage_uri: https://github.com/fizvlad/process-observer-rb
|
86
|
+
source_code_uri: https://github.com/fizvlad/process-observer-rb
|
87
|
+
changelog_uri: https://github.com/fizvlad/process-observer-rb/releases
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.5.0
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubygems_version: 3.0.4
|
104
|
+
signing_key:
|
105
|
+
specification_version: 4
|
106
|
+
summary: Small gem to get info about current list of processes.
|
107
|
+
test_files:
|
108
|
+
- test/test_process_observer.rb
|
109
|
+
- test/test_windows_utility.rb
|