process_tail 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8de878e91fb0d39a77238a19bcb567e6257ed449
4
+ data.tar.gz: 9b4353b4c9e7cd4ed6ee44cdc9796310448912a3
5
+ SHA512:
6
+ metadata.gz: dec2d6a557f9c826234c0fdbc0d4bc19eca4e35c28d303d63a9e7078e3e93c72684ea469eb6ae6581a97e2598602b89380d60b19301139ab6b812fcfa0fdc360
7
+ data.tar.gz: b71a3dc8ca8bccd242ccfa2148e777c3063b3941a169b50fe09858b25e23df0921f9e42bafc022830d75417cad0e8c51e66996fdc85d6702b50dc74813ac9136
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in process_tail.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Hika Hibariya
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,45 @@
1
+ # ProcessTail
2
+
3
+ https://github.com/hibariya/process_tail
4
+
5
+ ## Description
6
+
7
+ ProcessTail traces other process' write(2) system call and copy its buffer.
8
+ So you can get other process outputs.
9
+
10
+ ## Problems
11
+
12
+ * Mac OSX and Windows are not supported at the moment
13
+ * SEGV occures occasionally
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'process_tail'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install process_tail
30
+
31
+ ## Usage
32
+
33
+ ```ruby
34
+ ProcessTail.open pid, :stdout do |io|
35
+ puts "Recent stdout of #{pid}: #{io.gets}"
36
+ end
37
+ ```
38
+
39
+ ## Contributing
40
+
41
+ 1. Fork it ( https://github.com/hibariya/process_tail/fork )
42
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
43
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
44
+ 4. Push to the branch (`git push origin my-new-feature`)
45
+ 5. Create a new Pull Request
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rake/extensiontask'
4
+
5
+ Rake::ExtensionTask.new 'process_tail' do |ext|
6
+ ext.lib_dir = 'lib/process_tail'
7
+ end
8
+
9
+ RSpec::Core::RakeTask.new(:spec)
10
+
11
+ task default: [:compile, :spec]
@@ -0,0 +1,13 @@
1
+ require 'mkmf'
2
+
3
+ %w(string.h sys/ptrace.h sys/syscall.h sys/types.h sys/user.h sys/wait.h ruby.h ruby/thread.h).each do |h|
4
+ abort "missing #{h}" unless have_header(h)
5
+ end
6
+
7
+ abort 'missing ptrace' unless have_func('ptrace', 'sys/ptrace.h')
8
+
9
+ %w(orig_rax rdi rdx rsi rdx).each do |m|
10
+ abort "missing #{m}" unless have_struct_member('struct user_regs_struct', m, 'sys/user.h')
11
+ end
12
+
13
+ create_makefile 'process_tail/process_tail'
@@ -0,0 +1,157 @@
1
+ #include <errno.h>
2
+ #include <stdio.h>
3
+ #include <stdlib.h>
4
+ #include <string.h>
5
+ #include <sys/ptrace.h>
6
+ #include <sys/syscall.h>
7
+ #include <sys/types.h>
8
+ #include <sys/user.h>
9
+ #include <sys/wait.h>
10
+ #include <ruby.h>
11
+ #include <ruby/thread.h>
12
+
13
+ struct
14
+ process_tail_args {
15
+ pid_t pid;
16
+ int *status;
17
+ unsigned int fd;
18
+ VALUE io;
19
+ };
20
+
21
+ static void *
22
+ process_tail_waitpid(void *argp)
23
+ {
24
+ struct process_tail_args *args = argp;
25
+
26
+ waitpid(args->pid, args->status, WUNTRACED | WCONTINUED);
27
+
28
+ return NULL;
29
+ }
30
+
31
+ static int
32
+ process_tail_attach(void *argp)
33
+ {
34
+ struct process_tail_args *args = argp;
35
+
36
+ if (ptrace(PTRACE_ATTACH, args->pid, NULL, NULL) < 0) {
37
+ rb_sys_fail("PTRACE_ATTACH");
38
+ return -1;
39
+ }
40
+
41
+ rb_thread_call_without_gvl(process_tail_waitpid, (void *)argp, RUBY_UBF_PROCESS, NULL);
42
+
43
+ return 0;
44
+ }
45
+
46
+ static VALUE
47
+ process_tail_detach(VALUE argp)
48
+ {
49
+ struct process_tail_args *args = (void *)argp;
50
+
51
+ ptrace(PTRACE_DETACH, args->pid, NULL, NULL);
52
+
53
+ return Qnil;
54
+ }
55
+
56
+ static void
57
+ process_tail_get_data(pid_t pid, long addr, char *string, int len)
58
+ {
59
+ long data;
60
+ int i;
61
+
62
+ for (i = 0; i < len; i += sizeof(long)) {
63
+ data = ptrace(PTRACE_PEEKDATA, pid, addr + i);
64
+
65
+ memcpy(string + i, &data, sizeof(long));
66
+ }
67
+
68
+ string[len] = '\0';
69
+ }
70
+
71
+ static VALUE
72
+ process_tail_loop(VALUE argp)
73
+ {
74
+ struct process_tail_args *args = (void *)argp;
75
+ struct user_regs_struct regs;
76
+ int in_syscall = 0;
77
+ int signal = 0;
78
+ int status;
79
+ char *string = NULL;
80
+
81
+ args->status = &status;
82
+
83
+ while (ptrace(PTRACE_SYSCALL, args->pid, NULL, signal) == 0) {
84
+ rb_thread_call_without_gvl(process_tail_waitpid, args, RUBY_UBF_PROCESS, NULL);
85
+
86
+ signal = 0;
87
+
88
+ if (WIFEXITED(status)) {
89
+ return Qnil;
90
+ }
91
+
92
+ if (!WIFSTOPPED(status)) {
93
+ continue;
94
+ }
95
+
96
+ switch (WSTOPSIG(status)) {
97
+ case SIGTRAP:
98
+ ptrace(PTRACE_GETREGS, args->pid, 0, &regs);
99
+
100
+ if (regs.orig_rax != SYS_write) {
101
+ continue;
102
+ }
103
+
104
+ in_syscall = 1 - in_syscall;
105
+ if (in_syscall && (args->fd == 0 || regs.rdi == args->fd)) {
106
+ string = malloc(regs.rdx + sizeof(long));
107
+ process_tail_get_data(args->pid, regs.rsi, string, regs.rdx);
108
+
109
+ if (rb_funcall(args->io, rb_intern("closed?"), 0) == Qtrue) {
110
+ return Qnil;
111
+ }
112
+
113
+ rb_io_write(args->io, rb_str_new_cstr(string));
114
+
115
+ free(string);
116
+ }
117
+ case SIGVTALRM:
118
+ break;
119
+ case SIGSTOP:
120
+ break;
121
+ default:
122
+ signal = WSTOPSIG(status);
123
+ }
124
+
125
+ rb_thread_schedule();
126
+ }
127
+
128
+ return Qnil;
129
+ }
130
+
131
+ static VALUE
132
+ process_tail_trace(VALUE klass, VALUE pidp, VALUE fdp, VALUE io)
133
+ {
134
+ pid_t pid = (pid_t)FIX2INT(pidp);
135
+ unsigned int fd = (unsigned int)FIX2INT(fdp);
136
+
137
+ struct process_tail_args args = {pid, NULL, fd, io};
138
+
139
+ Check_Type(pidp, T_FIXNUM);
140
+ Check_Type(fdp, T_FIXNUM);
141
+ Check_Type(io, T_FILE);
142
+
143
+ if (process_tail_attach((void *)&args) == 0) {
144
+ rb_ensure(process_tail_loop, (VALUE)&args, process_tail_detach, (VALUE)&args);
145
+ }
146
+
147
+ return Qnil;
148
+ }
149
+
150
+ void
151
+ Init_process_tail()
152
+ {
153
+ VALUE ProcessTail;
154
+
155
+ ProcessTail = rb_define_module("ProcessTail");
156
+ rb_define_singleton_method(ProcessTail, "do_trace", process_tail_trace, 3);
157
+ }
@@ -0,0 +1,50 @@
1
+ require 'process_tail/process_tail'
2
+ require 'process_tail/version'
3
+
4
+ module ProcessTail
5
+ class << self
6
+ def open(pid, fd = :stdout)
7
+ read_io, thread = trace(pid, fd)
8
+
9
+ block_given? ? yield(read_io) : read_io
10
+ ensure
11
+ finalize = -> {
12
+ read_io.close unless read_io.closed?
13
+ thread.kill
14
+ }
15
+
16
+ block_given? ? finalize.call : at_exit(&finalize)
17
+ end
18
+
19
+ def trace(pid, fd = :stdout)
20
+ read_io, write_io = IO.pipe
21
+
22
+ thread = Thread.fork {
23
+ begin
24
+ do_trace pid, extract_fd(fd), write_io
25
+ ensure
26
+ begin
27
+ [read_io, write_io].each do |io|
28
+ io.close unless io.closed?
29
+ end
30
+ rescue IOError; end
31
+ end
32
+ }
33
+
34
+ [read_io, thread]
35
+ end
36
+
37
+ def extract_fd(name)
38
+ case name
39
+ when :stdout
40
+ 1
41
+ when :stderr
42
+ 2
43
+ when :all
44
+ 0
45
+ else
46
+ Integer(name)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ module ProcessTail
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'process_tail/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'process_tail'
8
+ spec.version = ProcessTail::VERSION
9
+ spec.authors = ['Hika Hibariya']
10
+ spec.email = ['hibariya@gmail.com']
11
+ spec.summary = %q{Get other process outputs.}
12
+ spec.homepage = 'https://github.com/hibariya/process_tail'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.extensions = %w(ext/process_tail/extconf.rb)
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.7'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rake-compiler', '~> 0.9.5'
25
+ spec.add_development_dependency 'rspec', '~> 3.1.0'
26
+ end
@@ -0,0 +1,66 @@
1
+ require 'process_tail'
2
+
3
+ describe ProcessTail do
4
+ def process_maker(&fn)
5
+ -> {
6
+ Process.fork {
7
+ trap :USR1 do
8
+ sleep 0.1 # XXX :(
9
+
10
+ fn.call if fn
11
+ end
12
+
13
+ sleep
14
+ }
15
+ }
16
+ end
17
+
18
+ def expect_same_output_and_no_error(fd, method, string)
19
+ greeting = process_maker { send(method, string) }
20
+ pid = greeting.call
21
+ io, th = ProcessTail.trace(pid, fd)
22
+
23
+ Process.kill :USR1, pid # resume child
24
+
25
+ "#{string}\n".each_line do |expected|
26
+ expect(io.gets).to eq expected
27
+ end
28
+
29
+ Process.kill :TERM, pid
30
+
31
+ expect { th.join }.to_not raise_error
32
+ end
33
+
34
+ describe '.open' do
35
+ specify 'without block' do
36
+ pid = process_maker.call
37
+
38
+ read_io = ProcessTail.open(pid, :stdout)
39
+
40
+ expect(read_io).to_not be_closed
41
+
42
+ read_io.close
43
+ end
44
+
45
+ specify 'with block' do
46
+ pid = process_maker.call
47
+ read_io = nil
48
+
49
+ ProcessTail.open pid, :stdout do |io|
50
+ read_io = io
51
+
52
+ expect(read_io).to_not be_closed
53
+ end
54
+
55
+ expect(read_io).to be_closed
56
+ end
57
+ end
58
+
59
+ describe '.trace' do
60
+ it do
61
+ expect_same_output_and_no_error :stdout, :puts, 'HELLO'
62
+ expect_same_output_and_no_error :stderr, :warn, 'HELLO'
63
+ expect_same_output_and_no_error :stdout, :puts, "HELLO\nLOVE AND KISSES"
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,9 @@
1
+ RSpec.configure do |config|
2
+ config.expect_with :rspec do |expectations|
3
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
4
+ end
5
+
6
+ config.mock_with :rspec do |mocks|
7
+ mocks.verify_partial_doubles = true
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: process_tail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Hika Hibariya
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-03 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: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake-compiler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.9.5
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.5
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.1.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.1.0
69
+ description:
70
+ email:
71
+ - hibariya@gmail.com
72
+ executables: []
73
+ extensions:
74
+ - ext/process_tail/extconf.rb
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - ext/process_tail/extconf.rb
83
+ - ext/process_tail/process_tail.c
84
+ - lib/process_tail.rb
85
+ - lib/process_tail/version.rb
86
+ - process_tail.gemspec
87
+ - spec/process_tail_spec.rb
88
+ - spec/spec_helper.rb
89
+ homepage: https://github.com/hibariya/process_tail
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.4.5
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Get other process outputs.
113
+ test_files:
114
+ - spec/process_tail_spec.rb
115
+ - spec/spec_helper.rb