process_tail 0.0.1 → 0.0.2
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 +4 -4
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/README.md +2 -1
- data/bin/process_tail +11 -0
- data/ext/process_tail/extconf.rb +3 -2
- data/ext/process_tail/process_tail.c +190 -83
- data/lib/process_tail/version.rb +1 -1
- data/lib/process_tail.rb +46 -15
- data/spec/process_tail_spec.rb +62 -10
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85d86bdf54735a348134ab9abceec3610882fe23
|
4
|
+
data.tar.gz: 9f1e65346bd355e4fb1b9f9c83fdf8c45a16a224
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3da11ae5cece8697449bcea8307bc34e1af5b6cb3a39834e96d56f2b32d6306e4ac9070909c79bab0f2486f319a15de408d13df6982d9d3fa367163920c8387
|
7
|
+
data.tar.gz: 30b6556d39cf4b06169e518a69fe7a8c1b0b03a05e0f0db97bcd6abafee42625622048885fbc7452dfbf48cec4915983c2feb10aa6aae5ea132a7a7b8c770b56
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# ProcessTail
|
1
|
+
# ProcessTail [](https://travis-ci.org/hibariya/process_tail)
|
2
2
|
|
3
3
|
https://github.com/hibariya/process_tail
|
4
4
|
|
@@ -11,6 +11,7 @@ So you can get other process outputs.
|
|
11
11
|
|
12
12
|
* Mac OSX and Windows are not supported at the moment
|
13
13
|
* SEGV occures occasionally
|
14
|
+
* Multithreaded process support
|
14
15
|
|
15
16
|
## Installation
|
16
17
|
|
data/bin/process_tail
ADDED
data/ext/process_tail/extconf.rb
CHANGED
@@ -4,10 +4,11 @@ require 'mkmf'
|
|
4
4
|
abort "missing #{h}" unless have_header(h)
|
5
5
|
end
|
6
6
|
|
7
|
-
abort 'missing ptrace' unless have_func('ptrace', 'sys/ptrace.h')
|
8
|
-
|
9
7
|
%w(orig_rax rdi rdx rsi rdx).each do |m|
|
10
8
|
abort "missing #{m}" unless have_struct_member('struct user_regs_struct', m, 'sys/user.h')
|
11
9
|
end
|
12
10
|
|
11
|
+
abort 'missing __WALL' unless have_const('__WALL', 'sys/wait.h')
|
12
|
+
abort 'missing PTRACE_O_TRACESYSGOOD' unless have_const('PTRACE_O_TRACESYSGOOD', 'sys/ptrace.h')
|
13
|
+
|
13
14
|
create_makefile 'process_tail/process_tail'
|
@@ -1,5 +1,3 @@
|
|
1
|
-
#include <errno.h>
|
2
|
-
#include <stdio.h>
|
3
1
|
#include <stdlib.h>
|
4
2
|
#include <string.h>
|
5
3
|
#include <sys/ptrace.h>
|
@@ -10,148 +8,257 @@
|
|
10
8
|
#include <ruby.h>
|
11
9
|
#include <ruby/thread.h>
|
12
10
|
|
13
|
-
struct
|
14
|
-
process_tail_args {
|
11
|
+
typedef struct pt_tracee {
|
15
12
|
pid_t pid;
|
13
|
+
int activated;
|
14
|
+
int dead;
|
15
|
+
struct pt_tracee *next;
|
16
|
+
} pt_tracee_t;
|
17
|
+
|
18
|
+
typedef struct pt_wait_args {
|
16
19
|
int *status;
|
17
|
-
|
18
|
-
|
19
|
-
};
|
20
|
+
pid_t pid;
|
21
|
+
} pt_wait_args_t;
|
20
22
|
|
21
|
-
static
|
22
|
-
|
23
|
+
static pt_tracee_t *
|
24
|
+
pt_tracee_add(pt_tracee_t **headpp, pid_t pid)
|
23
25
|
{
|
24
|
-
|
26
|
+
pt_tracee_t *tracee = malloc(sizeof(pt_tracee_t));
|
25
27
|
|
26
|
-
|
28
|
+
if (tracee == NULL) {
|
29
|
+
exit(EXIT_FAILURE);
|
30
|
+
}
|
27
31
|
|
28
|
-
|
32
|
+
tracee->pid = pid;
|
33
|
+
tracee->activated = 0;
|
34
|
+
tracee->dead = 0;
|
35
|
+
tracee->next = (struct pt_tracee *)*headpp;
|
36
|
+
|
37
|
+
(*headpp) = tracee;
|
38
|
+
|
39
|
+
return tracee;
|
40
|
+
}
|
41
|
+
|
42
|
+
static pt_tracee_t *
|
43
|
+
pt_tracee_find_or_add(pt_tracee_t **headpp, pid_t pid)
|
44
|
+
{
|
45
|
+
pt_tracee_t *tracee = *headpp;
|
46
|
+
|
47
|
+
while (tracee != NULL) {
|
48
|
+
if (tracee->pid == pid) {
|
49
|
+
return tracee;
|
50
|
+
}
|
51
|
+
|
52
|
+
tracee = tracee->next;
|
53
|
+
}
|
54
|
+
|
55
|
+
return pt_tracee_add(headpp, pid);
|
29
56
|
}
|
30
57
|
|
31
58
|
static int
|
32
|
-
|
59
|
+
pt_tracee_wipedoutq(pt_tracee_t *headp)
|
33
60
|
{
|
34
|
-
|
61
|
+
pt_tracee_t *tracee = headp;
|
35
62
|
|
36
|
-
|
37
|
-
|
38
|
-
|
63
|
+
while (tracee != NULL) {
|
64
|
+
if (tracee->dead == 0) {
|
65
|
+
return 0;
|
66
|
+
}
|
67
|
+
|
68
|
+
tracee = tracee->next;
|
39
69
|
}
|
40
70
|
|
41
|
-
|
71
|
+
return 1;
|
72
|
+
}
|
73
|
+
|
74
|
+
static void
|
75
|
+
pt_tracee_free(pt_tracee_t **headpp)
|
76
|
+
{
|
77
|
+
pt_tracee_t *tracee = *headpp;
|
42
78
|
|
43
|
-
|
79
|
+
if (tracee->next) {
|
80
|
+
pt_tracee_free(&tracee->next);
|
81
|
+
}
|
82
|
+
|
83
|
+
free(tracee);
|
84
|
+
(*headpp) = NULL;
|
44
85
|
}
|
45
86
|
|
46
|
-
static
|
47
|
-
|
87
|
+
static int
|
88
|
+
pt_io_closedq(VALUE io)
|
48
89
|
{
|
49
|
-
|
90
|
+
return rb_funcall(io, rb_intern("closed?"), 0) == Qtrue;
|
91
|
+
}
|
50
92
|
|
51
|
-
|
93
|
+
static void *
|
94
|
+
pt_wait(void *argp)
|
95
|
+
{
|
96
|
+
pt_wait_args_t *args = (pt_wait_args_t *)argp;
|
52
97
|
|
53
|
-
|
98
|
+
args->pid = waitpid(-1, args->status, __WALL);
|
99
|
+
|
100
|
+
return NULL;
|
54
101
|
}
|
55
102
|
|
56
103
|
static void
|
57
|
-
|
104
|
+
pt_get_data(pid_t pid, long addr, char *string, int length)
|
58
105
|
{
|
59
|
-
long data;
|
60
106
|
int i;
|
107
|
+
long data;
|
61
108
|
|
62
|
-
for (i = 0; i <
|
109
|
+
for (i = 0; i < length; i += sizeof(long)) {
|
63
110
|
data = ptrace(PTRACE_PEEKDATA, pid, addr + i);
|
64
111
|
|
65
112
|
memcpy(string + i, &data, sizeof(long));
|
66
113
|
}
|
67
114
|
|
68
|
-
string[
|
115
|
+
string[length] = '\0';
|
69
116
|
}
|
70
117
|
|
71
|
-
static
|
72
|
-
|
118
|
+
static void
|
119
|
+
pt_ptrace_syscall(pid_t pid, long signal)
|
73
120
|
{
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
121
|
+
ptrace(PTRACE_SYSCALL, pid, 0, signal);
|
122
|
+
|
123
|
+
rb_thread_schedule();
|
124
|
+
}
|
125
|
+
|
126
|
+
static void
|
127
|
+
pt_loop(unsigned int fd, VALUE write_io, VALUE read_io, VALUE wait_queue, pt_tracee_t *tracee_headp)
|
128
|
+
{
|
129
|
+
char *string = NULL;
|
130
|
+
int syscall = 0;
|
78
131
|
int status;
|
79
|
-
|
132
|
+
long signal;
|
133
|
+
pid_t pid;
|
134
|
+
pt_tracee_t *tracee;
|
135
|
+
pt_wait_args_t pt_wait_args = {&status};
|
80
136
|
|
81
|
-
|
137
|
+
// NOTE: system call: eax, arguments: rdi, rsi, rdx, r10, r8, r9
|
138
|
+
struct user_regs_struct regs;
|
82
139
|
|
83
|
-
while (
|
84
|
-
rb_thread_call_without_gvl(
|
140
|
+
while (1) {
|
141
|
+
rb_thread_call_without_gvl(pt_wait, &pt_wait_args, RUBY_UBF_PROCESS, NULL);
|
85
142
|
|
86
143
|
signal = 0;
|
144
|
+
pid = pt_wait_args.pid;
|
145
|
+
tracee = pt_tracee_find_or_add(&tracee_headp, pid);
|
146
|
+
|
147
|
+
if (pt_io_closedq(read_io)) {
|
148
|
+
pt_tracee_free(&tracee_headp);
|
87
149
|
|
88
|
-
|
89
|
-
return Qnil;
|
150
|
+
return;
|
90
151
|
}
|
91
152
|
|
92
|
-
if (
|
153
|
+
if (tracee->activated == 0) {
|
154
|
+
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD);
|
155
|
+
tracee->activated = 1;
|
156
|
+
|
157
|
+
rb_funcall(wait_queue, rb_intern("enq"), 1, INT2FIX((int)pid));
|
158
|
+
pt_ptrace_syscall(pid, signal);
|
159
|
+
|
93
160
|
continue;
|
94
161
|
}
|
95
162
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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);
|
163
|
+
if (WIFEXITED(status) || WIFSIGNALED(status)) {
|
164
|
+
tracee->dead = 1;
|
165
|
+
|
166
|
+
if (pt_tracee_wipedoutq(tracee_headp)) {
|
167
|
+
pt_tracee_free(&tracee_headp);
|
168
|
+
|
169
|
+
return;
|
170
|
+
}
|
171
|
+
|
172
|
+
continue;
|
123
173
|
}
|
124
174
|
|
125
|
-
|
126
|
-
|
175
|
+
if (!(WIFSTOPPED(status) && WSTOPSIG(status) & 0x80)) {
|
176
|
+
signal = WSTOPSIG(status);
|
177
|
+
pt_ptrace_syscall(pid, signal);
|
127
178
|
|
128
|
-
|
179
|
+
continue;
|
180
|
+
}
|
181
|
+
|
182
|
+
ptrace(PTRACE_GETREGS, pid, 0, ®s);
|
183
|
+
|
184
|
+
if (regs.orig_rax != SYS_write) {
|
185
|
+
pt_ptrace_syscall(pid, signal);
|
186
|
+
|
187
|
+
continue;
|
188
|
+
}
|
189
|
+
|
190
|
+
syscall = 1 - syscall;
|
191
|
+
if (syscall && (fd == 0 || regs.rdi == fd)) {
|
192
|
+
string = malloc(regs.rdx + sizeof(long));
|
193
|
+
|
194
|
+
if (string == NULL) {
|
195
|
+
exit(EXIT_FAILURE);
|
196
|
+
}
|
197
|
+
|
198
|
+
pt_get_data(pid, regs.rsi, string, regs.rdx);
|
199
|
+
|
200
|
+
if (pt_io_closedq(read_io)) {
|
201
|
+
free(string);
|
202
|
+
pt_tracee_free(&tracee_headp);
|
203
|
+
|
204
|
+
return;
|
205
|
+
}
|
206
|
+
|
207
|
+
rb_io_write(write_io, rb_str_new_cstr(string));
|
208
|
+
|
209
|
+
free(string);
|
210
|
+
}
|
211
|
+
|
212
|
+
pt_ptrace_syscall(pid, signal);
|
213
|
+
}
|
129
214
|
}
|
130
215
|
|
131
216
|
static VALUE
|
132
|
-
|
217
|
+
pt_attach(VALUE klass, VALUE pidv)
|
133
218
|
{
|
134
|
-
|
135
|
-
unsigned int fd = (unsigned int)FIX2INT(fdp);
|
219
|
+
int pid = (pid_t)FIX2INT(pidv);
|
136
220
|
|
137
|
-
|
221
|
+
Check_Type(pidv, T_FIXNUM);
|
138
222
|
|
139
|
-
|
140
|
-
|
141
|
-
Check_Type(io, T_FILE);
|
223
|
+
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
|
224
|
+
rb_sys_fail("PTRACE_ATTACH");
|
142
225
|
|
143
|
-
|
144
|
-
rb_ensure(process_tail_loop, (VALUE)&args, process_tail_detach, (VALUE)&args);
|
226
|
+
return Qnil;
|
145
227
|
}
|
146
228
|
|
229
|
+
return Qtrue;
|
230
|
+
}
|
231
|
+
|
232
|
+
static VALUE
|
233
|
+
pt_detach(VALUE klass, VALUE pidv)
|
234
|
+
{
|
235
|
+
Check_Type(pidv, T_FIXNUM);
|
236
|
+
|
237
|
+
ptrace(PTRACE_DETACH, (pid_t)FIX2INT(pidv), NULL, NULL);
|
238
|
+
|
239
|
+
return Qnil;
|
240
|
+
}
|
241
|
+
|
242
|
+
static VALUE
|
243
|
+
pt_trace(VALUE klass, VALUE fdp, VALUE write_io, VALUE read_io, VALUE wait_queue)
|
244
|
+
{
|
245
|
+
pt_tracee_t *tracee_headp = NULL;
|
246
|
+
|
247
|
+
Check_Type(fdp, T_FIXNUM);
|
248
|
+
Check_Type(write_io, T_FILE);
|
249
|
+
Check_Type(read_io, T_FILE);
|
250
|
+
|
251
|
+
pt_loop((unsigned int)FIX2INT(fdp), write_io, read_io, wait_queue, tracee_headp);
|
252
|
+
|
147
253
|
return Qnil;
|
148
254
|
}
|
149
255
|
|
150
256
|
void
|
151
257
|
Init_process_tail()
|
152
258
|
{
|
153
|
-
VALUE ProcessTail;
|
259
|
+
VALUE ProcessTail = rb_define_module("ProcessTail");
|
154
260
|
|
155
|
-
ProcessTail
|
156
|
-
rb_define_singleton_method(ProcessTail, "do_trace",
|
261
|
+
rb_define_singleton_method(ProcessTail, "attach", pt_attach, 1);
|
262
|
+
rb_define_singleton_method(ProcessTail, "do_trace", pt_trace, 4);
|
263
|
+
rb_define_singleton_method(ProcessTail, "detach", pt_detach, 1);
|
157
264
|
}
|
data/lib/process_tail/version.rb
CHANGED
data/lib/process_tail.rb
CHANGED
@@ -1,37 +1,68 @@
|
|
1
|
+
require 'thread'
|
1
2
|
require 'process_tail/process_tail'
|
2
3
|
require 'process_tail/version'
|
3
4
|
|
4
5
|
module ProcessTail
|
6
|
+
TRACE_LOCK = Mutex.new # NOTE For now, ProcessTail can't trace multiple processes at the same time
|
7
|
+
|
5
8
|
class << self
|
6
9
|
def open(pid, fd = :stdout)
|
7
|
-
|
10
|
+
io = trace(pid, fd)
|
8
11
|
|
9
|
-
block_given? ? yield(
|
12
|
+
block_given? ? yield(io) : io
|
10
13
|
ensure
|
11
|
-
|
12
|
-
read_io.close unless read_io.closed?
|
13
|
-
thread.kill
|
14
|
-
}
|
15
|
-
|
16
|
-
block_given? ? finalize.call : at_exit(&finalize)
|
14
|
+
io.close if block_given? && !io.closed?
|
17
15
|
end
|
18
16
|
|
19
17
|
def trace(pid, fd = :stdout)
|
18
|
+
TRACE_LOCK.synchronize {
|
19
|
+
trace_without_lock(pid, fd)
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def trace_without_lock(pid, fd)
|
20
26
|
read_io, write_io = IO.pipe
|
27
|
+
task_ids = extract_task_ids(pid)
|
28
|
+
wait_queue = Queue.new
|
21
29
|
|
22
30
|
thread = Thread.fork {
|
23
31
|
begin
|
24
|
-
|
32
|
+
extract_task_ids(pid).each do |tid|
|
33
|
+
attach tid
|
34
|
+
end
|
35
|
+
|
36
|
+
do_trace extract_fd(fd), write_io, read_io, wait_queue
|
25
37
|
ensure
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
38
|
+
write_io.close unless write_io.closed?
|
39
|
+
|
40
|
+
task_ids.each do |tid|
|
41
|
+
detach tid
|
42
|
+
end
|
31
43
|
end
|
32
44
|
}
|
33
45
|
|
34
|
-
|
46
|
+
at_exit do
|
47
|
+
thread.kill
|
48
|
+
thread.join
|
49
|
+
end
|
50
|
+
|
51
|
+
wait_all_attach task_ids, wait_queue
|
52
|
+
|
53
|
+
read_io
|
54
|
+
end
|
55
|
+
|
56
|
+
def wait_all_attach(task_ids, waitq)
|
57
|
+
task_ids.size.times do
|
58
|
+
waitq.deq
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def extract_task_ids(pid)
|
63
|
+
Dir.open("/proc/#{pid}/task") {|dir|
|
64
|
+
dir.entries.grep(/^\d+$/).map(&:to_i)
|
65
|
+
}
|
35
66
|
end
|
36
67
|
|
37
68
|
def extract_fd(name)
|
data/spec/process_tail_spec.rb
CHANGED
@@ -1,24 +1,27 @@
|
|
1
1
|
require 'process_tail'
|
2
|
+
require 'timeout'
|
2
3
|
|
3
4
|
describe ProcessTail do
|
4
5
|
def process_maker(&fn)
|
5
6
|
-> {
|
6
|
-
Process.fork {
|
7
|
+
pid = Process.fork {
|
7
8
|
trap :USR1 do
|
8
|
-
sleep 0.1 # XXX :(
|
9
|
-
|
10
9
|
fn.call if fn
|
11
10
|
end
|
12
11
|
|
13
12
|
sleep
|
14
13
|
}
|
14
|
+
|
15
|
+
$recorded_children << pid
|
16
|
+
|
17
|
+
pid
|
15
18
|
}
|
16
19
|
end
|
17
20
|
|
18
21
|
def expect_same_output_and_no_error(fd, method, string)
|
19
|
-
greeting
|
20
|
-
pid
|
21
|
-
io
|
22
|
+
greeting = process_maker { send(method, string) }
|
23
|
+
pid = greeting.call
|
24
|
+
io = ProcessTail.trace(pid, fd)
|
22
25
|
|
23
26
|
Process.kill :USR1, pid # resume child
|
24
27
|
|
@@ -26,9 +29,23 @@ describe ProcessTail do
|
|
26
29
|
expect(io.gets).to eq expected
|
27
30
|
end
|
28
31
|
|
29
|
-
|
32
|
+
io.close
|
33
|
+
end
|
34
|
+
|
35
|
+
around do |example|
|
36
|
+
begin
|
37
|
+
$recorded_children = []
|
38
|
+
|
39
|
+
# at least every examples should be finished within 5 seconds
|
40
|
+
timeout 5 do
|
41
|
+
example.run
|
42
|
+
end
|
30
43
|
|
31
|
-
|
44
|
+
ensure
|
45
|
+
$recorded_children.each do |child|
|
46
|
+
Process.kill :TERM, child
|
47
|
+
end
|
48
|
+
end
|
32
49
|
end
|
33
50
|
|
34
51
|
describe '.open' do
|
@@ -46,7 +63,7 @@ describe ProcessTail do
|
|
46
63
|
pid = process_maker.call
|
47
64
|
read_io = nil
|
48
65
|
|
49
|
-
ProcessTail.open pid, :stdout
|
66
|
+
ProcessTail.open pid, :stdout do |io|
|
50
67
|
read_io = io
|
51
68
|
|
52
69
|
expect(read_io).to_not be_closed
|
@@ -57,9 +74,44 @@ describe ProcessTail do
|
|
57
74
|
end
|
58
75
|
|
59
76
|
describe '.trace' do
|
60
|
-
|
77
|
+
specify 'simple stdout' do
|
61
78
|
expect_same_output_and_no_error :stdout, :puts, 'HELLO'
|
79
|
+
end
|
80
|
+
|
81
|
+
specify 'simple stderr' do
|
62
82
|
expect_same_output_and_no_error :stderr, :warn, 'HELLO'
|
83
|
+
end
|
84
|
+
|
85
|
+
specify 'multithreaded' do
|
86
|
+
pid = Process.fork {
|
87
|
+
trap :USR1 do
|
88
|
+
puts 'HELLO'
|
89
|
+
end
|
90
|
+
|
91
|
+
Thread.fork do
|
92
|
+
trap :USR2 do
|
93
|
+
puts 'HELLO'
|
94
|
+
end
|
95
|
+
|
96
|
+
sleep
|
97
|
+
end
|
98
|
+
|
99
|
+
sleep
|
100
|
+
}
|
101
|
+
|
102
|
+
$recorded_children << pid
|
103
|
+
io = ProcessTail.trace(pid, :stdout)
|
104
|
+
|
105
|
+
Process.kill :USR1, pid
|
106
|
+
Process.kill :USR2, pid
|
107
|
+
|
108
|
+
expect(io.gets).to eq "HELLO\n"
|
109
|
+
expect(io.gets).to eq "HELLO\n"
|
110
|
+
|
111
|
+
io.close
|
112
|
+
end
|
113
|
+
|
114
|
+
specify 'multiline' do
|
63
115
|
expect_same_output_and_no_error :stdout, :puts, "HELLO\nLOVE AND KISSES"
|
64
116
|
end
|
65
117
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: process_tail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hika Hibariya
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -69,16 +69,19 @@ dependencies:
|
|
69
69
|
description:
|
70
70
|
email:
|
71
71
|
- hibariya@gmail.com
|
72
|
-
executables:
|
72
|
+
executables:
|
73
|
+
- process_tail
|
73
74
|
extensions:
|
74
75
|
- ext/process_tail/extconf.rb
|
75
76
|
extra_rdoc_files: []
|
76
77
|
files:
|
77
78
|
- ".gitignore"
|
79
|
+
- ".travis.yml"
|
78
80
|
- Gemfile
|
79
81
|
- LICENSE.txt
|
80
82
|
- README.md
|
81
83
|
- Rakefile
|
84
|
+
- bin/process_tail
|
82
85
|
- ext/process_tail/extconf.rb
|
83
86
|
- ext/process_tail/process_tail.c
|
84
87
|
- lib/process_tail.rb
|