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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/ext/sse/extconf.rb +16 -0
  3. data/ext/sse/sse.c +214 -0
  4. metadata +46 -0
@@ -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
@@ -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')
@@ -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: []