sse 1.0.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/ext/sse/extconf.rb +16 -0
- data/ext/sse/sse.c +214 -0
- metadata +46 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: cacf1342bcfbfa1b2bda184e4b3429b929bbdaf0
|
|
4
|
+
data.tar.gz: b44c93e823f9e10ea98a64c5b2bf03cf124d1a7e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5411de312e3e9b64b8ea7da6444f38452602a3a7b3f6ec5a003a6aa6e6b9143d29997bce79eeaf233d01c9dd6ada73d2a9a76ebc95fa1226bd9d933debf6d819
|
|
7
|
+
data.tar.gz: 0cf7e670f95a4ff498806cb4ad64af82ac3b7c4651da48eec7a18bf174e0ad53b82306c7a5d2e508d4adb3359a0ffe64abd100058e8c1699c721fbea9ba1df6a
|
data/ext/sse/extconf.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'mkmf'
|
|
2
|
+
|
|
3
|
+
[
|
|
4
|
+
'ruby.h', 'stdio.h', 'stdlib.h', 'string.h',
|
|
5
|
+
'errno.h', 'time.h', 'signal.h'
|
|
6
|
+
].each { |header| abort("missing library #{header}") unless have_header(header) }
|
|
7
|
+
|
|
8
|
+
[
|
|
9
|
+
'close', 'difftime', 'dup2', 'execvp',
|
|
10
|
+
'fork', 'malloc', 'pipe', 'read',
|
|
11
|
+
'realloc', 'select', 'strerror', 'strlen',
|
|
12
|
+
'strncpy', 'strtok', 'time', 'waitpid',
|
|
13
|
+
'write'
|
|
14
|
+
].each { |func| abort("missing #{func}\(\)") unless have_func(func) }
|
|
15
|
+
|
|
16
|
+
create_makefile('sse/sse')
|
data/ext/sse/sse.c
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <stdio.h>
|
|
3
|
+
#include <stdlib.h>
|
|
4
|
+
#include <string.h>
|
|
5
|
+
#include <errno.h>
|
|
6
|
+
#include <time.h>
|
|
7
|
+
#include <signal.h>
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* Module handle
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
static VALUE sse_module;
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* All exceptions for this module
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
static VALUE sse_FileNotFoundError;
|
|
20
|
+
static VALUE sse_ForkFailureError;
|
|
21
|
+
static VALUE sse_PipeFailureError;
|
|
22
|
+
static VALUE sse_WriteFailureError;
|
|
23
|
+
static VALUE sse_ReadFailureError;
|
|
24
|
+
static VALUE sse_WaitpidFailureError;
|
|
25
|
+
static VALUE sse_TimeoutError;
|
|
26
|
+
|
|
27
|
+
/*
|
|
28
|
+
* General utilities
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
char **split(char *str, char *sep) {
|
|
32
|
+
char *arg = strtok(str, sep);
|
|
33
|
+
size_t arg_list_size = 1;
|
|
34
|
+
char **arg_list = malloc(sizeof(char*));
|
|
35
|
+
|
|
36
|
+
arg_list[0] = malloc(strlen(arg)+1);
|
|
37
|
+
strncpy(arg_list[0], arg, strlen(arg));
|
|
38
|
+
arg_list[0][strlen(arg)] = '\0';
|
|
39
|
+
|
|
40
|
+
while((arg = strtok(NULL, sep)) != NULL) {
|
|
41
|
+
arg_list = realloc(arg_list, (sizeof(char*))*(arg_list_size+2));
|
|
42
|
+
arg_list[arg_list_size] = malloc(strlen(arg));
|
|
43
|
+
strncpy(arg_list[arg_list_size], arg, strlen(arg));
|
|
44
|
+
arg_list[arg_list_size][strlen(arg)] = '\0';
|
|
45
|
+
++arg_list_size;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
arg_list = realloc(arg_list, (sizeof(char*))*(arg_list_size));
|
|
49
|
+
arg_list[arg_list_size] = malloc(sizeof(char*));
|
|
50
|
+
arg_list[arg_list_size] = (char*)NULL;
|
|
51
|
+
|
|
52
|
+
return arg_list;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* Ruby classes and functions
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
VALUE rb_execute_sh(VALUE self, VALUE rb_stdin, VALUE rb_timeout, VALUE rb_command) {
|
|
60
|
+
int stdin_ends[2];
|
|
61
|
+
int stdout_ends[2];
|
|
62
|
+
int stderr_ends[2];
|
|
63
|
+
|
|
64
|
+
if(pipe(stdin_ends) == -1 || pipe(stdout_ends) == -1 || pipe(stderr_ends) == -1) {
|
|
65
|
+
rb_raise(sse_PipeFailureError, "%s:%d (%d) pipe: %s\n", __FILE__, __LINE__, errno, strerror(errno));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
char **command_list = split(StringValueCStr(rb_command), (char*)" ");
|
|
69
|
+
char *stdin_buf = StringValueCStr(rb_stdin);
|
|
70
|
+
pid_t pid = fork();
|
|
71
|
+
|
|
72
|
+
if(pid > 0) {
|
|
73
|
+
size_t stdout_buf_size = 0;
|
|
74
|
+
size_t stderr_buf_size = 0;
|
|
75
|
+
size_t stdout_buf_max = 1;
|
|
76
|
+
size_t stderr_buf_max = 1;
|
|
77
|
+
char *stdout_buf = malloc(1);
|
|
78
|
+
char *stderr_buf = malloc(1);
|
|
79
|
+
|
|
80
|
+
stdout_buf[0] = '\0';
|
|
81
|
+
stderr_buf[0] = '\0';
|
|
82
|
+
|
|
83
|
+
time_t start = time(NULL);
|
|
84
|
+
time_t timeout = NUM2INT(rb_timeout);
|
|
85
|
+
int sent_stdin = 0;
|
|
86
|
+
|
|
87
|
+
close(stdin_ends[0]);
|
|
88
|
+
close(stdout_ends[1]);
|
|
89
|
+
close(stderr_ends[1]);
|
|
90
|
+
|
|
91
|
+
while(difftime(time(NULL), start) < timeout) {
|
|
92
|
+
int status = waitpid(pid, NULL, WNOHANG);
|
|
93
|
+
|
|
94
|
+
if(status == -1) {
|
|
95
|
+
rb_raise(sse_WaitpidFailureError, "%s:%d (%d) waitpid: %s\n", __FILE__, __LINE__, errno, strerror(errno));
|
|
96
|
+
} else if(status == 0) {
|
|
97
|
+
fd_set write_set;
|
|
98
|
+
fd_set read_set;
|
|
99
|
+
struct timeval select_timeout = { 0, 0 };
|
|
100
|
+
int maxfd = (stdin_ends[1] > stdout_ends[0] ? (stdin_ends[1] > stderr_ends[0] ? stdin_ends[1] : stderr_ends[0]) : (stdout_ends[0] > stderr_ends[0] ? stdout_ends[0] : stderr_ends[0]));
|
|
101
|
+
|
|
102
|
+
FD_ZERO(&write_set);
|
|
103
|
+
FD_ZERO(&read_set);
|
|
104
|
+
|
|
105
|
+
if(sent_stdin == 0) {
|
|
106
|
+
FD_SET(stdin_ends[1], &write_set);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
FD_SET(stdout_ends[0], &read_set);
|
|
110
|
+
FD_SET(stderr_ends[0], &read_set);
|
|
111
|
+
|
|
112
|
+
select(maxfd+1, &read_set, &write_set, NULL, &select_timeout);
|
|
113
|
+
|
|
114
|
+
if(FD_ISSET(stdin_ends[1], &write_set)) {
|
|
115
|
+
sent_stdin = 1;
|
|
116
|
+
|
|
117
|
+
if(write(stdin_ends[1], stdin_buf, strlen(stdin_buf)) == -1) {
|
|
118
|
+
rb_raise(sse_WriteFailureError, "%s:%d (%d) write: %s\n", __FILE__, __LINE__, errno, strerror(errno));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
close(stdin_ends[1]);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if(FD_ISSET(stdout_ends[0], &read_set)) {
|
|
125
|
+
char tmp_buf[251];
|
|
126
|
+
ssize_t bytes_read = read(stdout_ends[0], tmp_buf, 250);
|
|
127
|
+
|
|
128
|
+
if(bytes_read == -1) {
|
|
129
|
+
rb_raise(sse_ReadFailureError, "%s:%d (%d) read: %s\n", __FILE__, __LINE__, errno, strerror(errno));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
tmp_buf[bytes_read] = '\0';
|
|
133
|
+
|
|
134
|
+
while((stdout_buf_size + bytes_read+1) >= stdout_buf_max) {
|
|
135
|
+
stdout_buf_max *= 2;
|
|
136
|
+
stdout_buf = realloc(stdout_buf, stdout_buf_max);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
strncpy(stdout_buf, tmp_buf, bytes_read);
|
|
140
|
+
stdout_buf_size += bytes_read;
|
|
141
|
+
stdout_buf[stdout_buf_size] = '\0';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if(FD_ISSET(stderr_ends[0], &read_set)) {
|
|
145
|
+
char tmp_buf[251];
|
|
146
|
+
ssize_t bytes_read = read(stderr_ends[0], tmp_buf, 250);
|
|
147
|
+
|
|
148
|
+
if(bytes_read == -1) {
|
|
149
|
+
rb_raise(sse_ReadFailureError, "%s:%d (%d) read: %s\n", __FILE__, __LINE__, errno, strerror(errno));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
tmp_buf[bytes_read] = '\0';
|
|
153
|
+
|
|
154
|
+
while((stderr_buf_size + bytes_read+1) >= stderr_buf_max) {
|
|
155
|
+
stderr_buf_max *= 2;
|
|
156
|
+
stderr_buf = realloc(stderr_buf, stderr_buf_max);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
strncpy(stderr_buf, tmp_buf, bytes_read);
|
|
160
|
+
stderr_buf_size += bytes_read;
|
|
161
|
+
stderr_buf[stderr_buf_size] = '\0';
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
close(stdout_ends[0]);
|
|
165
|
+
close(stderr_ends[1]);
|
|
166
|
+
VALUE result = rb_ary_new2(3);
|
|
167
|
+
rb_ary_store(result, 0, INT2NUM(difftime(time(NULL), start)));
|
|
168
|
+
rb_ary_store(result, 1, rb_str_new_cstr(stdout_buf));
|
|
169
|
+
rb_ary_store(result, 2, rb_str_new_cstr(stderr_buf));
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
kill(SIGINT, pid);
|
|
175
|
+
rb_raise(sse_TimeoutError, "Proccess took too long to finish.\n");
|
|
176
|
+
} else if(pid == 0) {
|
|
177
|
+
dup2(stdin_ends[0], STDIN_FILENO);
|
|
178
|
+
dup2(stdout_ends[1], STDOUT_FILENO);
|
|
179
|
+
dup2(stderr_ends[1], STDERR_FILENO);
|
|
180
|
+
close(stdin_ends[1]);
|
|
181
|
+
close(stdout_ends[0]);
|
|
182
|
+
close(stderr_ends[0]);
|
|
183
|
+
|
|
184
|
+
if(execvp(command_list[0], command_list) == -1) {
|
|
185
|
+
rb_raise(sse_ForkFailureError, "%s:%d (%d) execvp: %s\n", __FILE__, __LINE__, errno, strerror(errno));
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
rb_raise(sse_FileNotFoundError, "%s:%d (%d) execvp: %s\n", __FILE__, __LINE__, errno, strerror(errno));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
VALUE thunderfury_blessed_blade_of_the_windseeker = rb_ary_new2(3);
|
|
192
|
+
rb_ary_store(thunderfury_blessed_blade_of_the_windseeker, 0, INT2NUM(42));
|
|
193
|
+
rb_ary_store(thunderfury_blessed_blade_of_the_windseeker, 1, rb_str_new_cstr("this is stdout"));
|
|
194
|
+
rb_ary_store(thunderfury_blessed_blade_of_the_windseeker, 2, rb_str_new_cstr("this is stderr"));
|
|
195
|
+
return thunderfury_blessed_blade_of_the_windseeker;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/*
|
|
199
|
+
* Initialize the module
|
|
200
|
+
*/
|
|
201
|
+
|
|
202
|
+
void Init_sse() {
|
|
203
|
+
sse_module = rb_define_module("sse");
|
|
204
|
+
|
|
205
|
+
sse_FileNotFoundError = rb_define_class_under(sse_module, "FileNotFoundError", rb_eStandardError);
|
|
206
|
+
sse_ForkFailureError = rb_define_class_under(sse_module, "ForkFailureError", rb_eStandardError);
|
|
207
|
+
sse_PipeFailureError = rb_define_class_under(sse_module, "PipeFailureError", rb_eStandardError);
|
|
208
|
+
sse_WriteFailureError = rb_define_class_under(sse_module, "WriteFailureError", rb_eStandardError);
|
|
209
|
+
sse_ReadFailureError = rb_define_class_under(sse_module, "ReadFailureError", rb_eStandardError);
|
|
210
|
+
sse_WaitpidFailureError = rb_define_class_under(sse_module, "WaitpidFailureError", rb_eStandardError);
|
|
211
|
+
sse_TimeoutError = rb_define_class_under(sse_module, "TimeoutError", rb_eStandardError);
|
|
212
|
+
|
|
213
|
+
rb_define_global_function("execute_sh", rb_execute_sh, 3);
|
|
214
|
+
}
|
metadata
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: sse
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Nicholas Chambers
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2016-10-22 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: A ruby gem for safe and secure execution of POSIX sh.
|
|
14
|
+
email: nchambers@spookyinternet.com
|
|
15
|
+
executables: []
|
|
16
|
+
extensions:
|
|
17
|
+
- ext/sse/extconf.rb
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- ext/sse/extconf.rb
|
|
21
|
+
- ext/sse/sse.c
|
|
22
|
+
homepage: http://spookypac.com/
|
|
23
|
+
licenses:
|
|
24
|
+
- MIT
|
|
25
|
+
metadata: {}
|
|
26
|
+
post_install_message:
|
|
27
|
+
rdoc_options: []
|
|
28
|
+
require_paths:
|
|
29
|
+
- lib
|
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - ">="
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: '0'
|
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
requirements: []
|
|
41
|
+
rubyforge_project:
|
|
42
|
+
rubygems_version: 2.4.8
|
|
43
|
+
signing_key:
|
|
44
|
+
specification_version: 4
|
|
45
|
+
summary: Safe Shell Execution
|
|
46
|
+
test_files: []
|