process_observer 0.1.0
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.
- 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
|