libsqreen 0.3.0.0.3 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbb56500787a0d15c668257164ca25170ba8121cfca9245b5c4416534245cf8a
4
- data.tar.gz: d238a6f72450a914cf6fe6b2a62aa8fc322972083ab5d6a2e6c9765b461c26f4
3
+ metadata.gz: b2850167f572c0e567ef756d51190d20b5405f891d59de71748b485931793ba4
4
+ data.tar.gz: 47bb4f25a4a71e66bff95c1883eb02109b0e0e56b5d928ff7a7bf3b583d4cb92
5
5
  SHA512:
6
- metadata.gz: c765d809b966a065a1b89df955ece005c668d259a46594f234fa77d25d97e0bf2c6e6178bbbf546424fed649c5b13c864546ddc7267373d7d1eaf923ed2a454c
7
- data.tar.gz: c7a42810d5012ed06f6cbd5bd7ce17a5dd908f2395cca19d43b784847325a10a65f3f23a56cf59a765d3614e6c7422eeabf4386899584bed20319649fff1484d
6
+ metadata.gz: 5191657554a0705f61ad7b78644f51630ebdb337d8e40e24ab1058ce2803cc9e2250e620af55f7417479ef8ec94b906dfd4a1879712bd5332c8cb8d72939b680
7
+ data.tar.gz: 5e7fed1e0ea2f85d4f6e39215bd092a5c4230674901efed98ce648d4be5d294e5bf01668f4cc64a91333ca0bc2a5f5058241da7ce8b83e15afbd5a6cba945922
@@ -0,0 +1,5 @@
1
+ c = RbConfig::MAKEFILE_CONFIG
2
+
3
+ c['CC'] = ENV['LIBSQREEN_CC'] if ENV['LIBSQREEN_CC']
4
+ c['CFLAGS'] += ' ' + ENV['LIBSQREEN_CFLAGS'] if ENV['LIBSQREEN_CFLAGS']
5
+ c['LDFLAGS'] += ' ' + ENV['LIBSQREEN_LDFLAGS'] if ENV['LIBSQREEN_LDFLAGS']
@@ -1,27 +1,75 @@
1
1
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
2
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
3
 
4
+ require_relative '../env_overrides.rb'
4
5
  require 'mkmf'
6
+ require 'shellwords'
5
7
 
6
- $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), '../../ext'))
7
- require 'libsqreen/location'
8
- location = LibSqreen::Location.load!
9
- begin
10
- location.verify!
11
- rescue LibSqreen::Location::Vendor::HeaderNotFound
12
- puts 'could not find header. fallback to stubbing.'
13
- $defs.push('-DLIBSQREEN_STUB')
14
- rescue LibSqreen::Location::Vendor::ArchiveNotFound
15
- puts "could not find binary archive: native features for platform #{RUBY_PLATFORM} not supported. fallback to stubbing."
16
- $defs.push('-DLIBSQREEN_STUB')
17
- else
18
- location.configure
19
-
20
- $CFLAGS += ' -std=c99'
21
- if RUBY_PLATFORM =~ /linux/
22
- version_script_path = File.expand_path(File.join(File.dirname(__FILE__), '../../ext/libsqreen_extension/libsqreen_extension.version'))
23
- $LDFLAGS += " -lpthread -Wl,--version-script='#{version_script_path}'"
8
+ module Paths
9
+ module_function
10
+
11
+ def include_paths
12
+ [File.join(vendored_source_path, 'libsqreen', 'include')]
24
13
  end
14
+
15
+ def object_paths
16
+ objects = [
17
+ File.join(vendored_source_path, 'libsqreen', cpu, platform, libname),
18
+ ]
19
+
20
+ return objects if platform == 'darwin'
21
+
22
+ objects << File.join(vendored_source_path, 'libc++', cpu, platform, 'libc++.a')
23
+ objects << File.join(vendored_source_path, 'libc++', cpu, platform, 'libc++abi.a')
24
+ objects << File.join(vendored_source_path, 'libc++', cpu, platform, 'libunwind.a')
25
+
26
+ objects
27
+ end
28
+
29
+ def has_object_files?
30
+ object_paths.all? { |p| File.file?(p) }
31
+ end
32
+
33
+ def config
34
+ RbConfig::MAKEFILE_CONFIG
35
+ end
36
+
37
+ def platform
38
+ RUBY_PLATFORM =~ /(solaris|darwin|linux(?=-.*$)|linux$)/ && $1
39
+ end
40
+
41
+ def cpu
42
+ RUBY_PLATFORM =~ /^(universal\.|)(.*?)-/ && $2
43
+ end
44
+
45
+ def libname
46
+ "libsqreen.#{config['LIBEXT']}"
47
+ end
48
+
49
+ def vendored_source_path
50
+ File.expand_path('../../../vendor', __FILE__)
51
+ end
52
+ end
53
+
54
+ $INCFLAGS << ' ' + Paths.include_paths.map { |x| '-I' + Shellwords.escape(x) }.join(' ')
55
+ $LDFLAGS << ' ' + Paths.object_paths.map { |x| Shellwords.escape(x) }.join(' ')
56
+
57
+ WARNINGS = %w(no-declaration-after-statement
58
+ unused-parameter missing-prototypes unused-function
59
+ sign-conversion)
60
+
61
+ $CFLAGS += " -std=c99 #{WARNINGS.map { |x| "-W#{x}" }.join(' ')}"
62
+
63
+ src_prefix = nil
64
+ unless Paths.has_object_files?
65
+ puts "WARNING: Could not find binary objects. " \
66
+ "Native features for platform #{RUBY_PLATFORM} not supported. Fallback to stubbing."
67
+ src_prefix = File.join($srcdir, 'stub')
68
+ end
69
+
70
+ if RUBY_PLATFORM =~ /linux/
71
+ version_script_path = File.expand_path(File.join(File.dirname(__FILE__), '../../ext/libsqreen_extension/libsqreen_extension.version'))
72
+ $LDFLAGS += " -lpthread -Wl,--version-script='#{version_script_path}'"
25
73
  end
26
74
 
27
- create_makefile('libsqreen_extension')
75
+ create_makefile('libsqreen_extension', src_prefix)
@@ -3,16 +3,40 @@
3
3
 
4
4
  #include <ruby.h>
5
5
  #include <stdbool.h>
6
+ #include <stdint.h>
7
+ #include <time.h>
6
8
 
7
- #ifndef LIBSQREEN_STUB
9
+ void Init_libsqreen_extension(void);
8
10
 
9
11
  #include <waf.h>
12
+ #include "logging.h"
13
+ #include <ruby/encoding.h>
14
+
15
+ static const rb_encoding *utf8_enc;
16
+ static const rb_encoding *latin1_enc;
17
+ static const rb_encoding *ascii8bit_enc;
18
+ static const rb_encoding *usascii_enc;
19
+ static VALUE utf8_str;
20
+ static VALUE iso8859_1_str;
21
+
22
+ extern VALUE waf_mod;
23
+ VALUE waf_mod;
24
+ static VALUE waf_args_cls;
25
+
26
+ static const PWArgs pw_args_invalid = { .type = PWI_INVALID };
27
+
28
+ static const PWConfig pw_config = {
29
+ .maxArrayLength = UINT64_MAX,
30
+ .maxMapDepth = UINT64_MAX,
31
+ };
32
+
33
+ static VALUE cvt_ruby_str_to_utf8(VALUE value);
10
34
 
11
- /* boxes */
12
35
 
13
36
  // PWArgs box
14
37
  struct libsqreen_waf_args {
15
- void *boxed;
38
+ // owned; value should not be shared
39
+ PWArgs pw_args;
16
40
  };
17
41
 
18
42
  static void
@@ -23,96 +47,77 @@ libsqreen_waf_args_free(void *b) {
23
47
  return;
24
48
  }
25
49
 
26
- if (box->boxed == NULL) {
27
- return;
28
- }
29
-
30
- box->boxed = NULL;
50
+ powerwaf_freeInput(&box->pw_args, false);
51
+ box->pw_args = pw_args_invalid;
31
52
  }
32
53
 
33
54
  static VALUE
34
55
  libsqreen_waf_args_alloc(VALUE klass) {
35
- VALUE obj;
36
- struct libsqreen_waf_args *box;
37
-
38
- obj = Data_Make_Struct(klass, struct libsqreen_waf_args, NULL, libsqreen_waf_args_free, box);
39
-
40
- box->boxed = NULL;
41
-
42
- return obj;
43
- }
56
+ VALUE obj;
57
+ struct libsqreen_waf_args *box;
44
58
 
45
- static VALUE
46
- libsqreen_waf_args_klass() {
47
- VALUE mLibSqreen;
48
- VALUE mWAF;
49
- VALUE cArgs;
59
+ obj = Data_Make_Struct(klass, struct libsqreen_waf_args,
60
+ NULL /* gc_mark */, libsqreen_waf_args_free, box);
50
61
 
51
- mLibSqreen = rb_const_get(rb_cObject, rb_intern("LibSqreen"));
52
- mWAF = rb_const_get(mLibSqreen, rb_intern("WAF"));
53
- cArgs = rb_const_get(mWAF, rb_intern("Args"));
62
+ box->pw_args = pw_args_invalid;
54
63
 
55
- return cArgs;
64
+ return obj;
56
65
  }
57
66
 
58
67
  static VALUE
59
68
  libsqreen_waf_args_new() {
60
- VALUE cArgs;
61
- ID new;
62
- VALUE waf_args;
63
-
64
- cArgs = libsqreen_waf_args_klass();
65
- new = rb_intern("new");
66
- waf_args = rb_funcall(cArgs, new, 0);
69
+ ID new = rb_intern("new");
70
+ VALUE waf_args = rb_funcall(waf_args_cls, new, 0);
67
71
 
68
72
  return waf_args;
69
73
  }
70
74
 
71
75
  static VALUE
72
76
  libsqreen_waf_args_new_from_hash(VALUE hash) {
73
- VALUE cArgs;
74
- ID new;
75
- VALUE waf_args;
76
-
77
- cArgs = libsqreen_waf_args_klass();
78
- new = rb_intern("new");
79
- waf_args = rb_funcall(cArgs, new, 1, hash);
77
+ ID new = rb_intern("new");
78
+ VALUE waf_args = rb_funcall(waf_args_cls, new, 1, hash);
80
79
 
81
80
  return waf_args;
82
81
  }
83
82
 
83
+ // transfers onwership
84
84
  static void
85
- libsqreen_waf_args_set_boxed(VALUE self, PWArgs *args) {
85
+ libsqreen_waf_args_set_boxed(VALUE self, PWArgs args) {
86
86
  struct libsqreen_waf_args *box;
87
87
  Data_Get_Struct(self, struct libsqreen_waf_args, box);
88
- box->boxed = (void *)args;
88
+ box->pw_args = args;
89
89
  }
90
90
 
91
91
  static PWArgs *
92
92
  libsqreen_waf_args_get_boxed(VALUE self) {
93
- PWArgs *args;
94
-
95
93
  struct libsqreen_waf_args *box;
96
94
  Data_Get_Struct(self, struct libsqreen_waf_args, box);
97
- args = (PWArgs *)box->boxed;
98
- return args;
95
+ return &box->pw_args;
99
96
  }
100
97
 
101
- /* wrappers */
102
-
103
- static VALUE
104
- libsqreen_waf_module() {
105
- VALUE mLibSqreen;
106
- VALUE mWAF;
107
-
108
- mLibSqreen = rb_const_get(rb_cObject, rb_intern("LibSqreen"));
109
- mWAF = rb_const_get(mLibSqreen, rb_intern("WAF"));
98
+ // drops the reference, returns it, but doesn't invalidate it
99
+ static PWArgs
100
+ libsqreen_waf_args_relinquish(VALUE self) {
101
+ struct libsqreen_waf_args *box;
102
+ Data_Get_Struct(self, struct libsqreen_waf_args, box);
103
+ PWArgs ret = box->pw_args;
104
+ box->pw_args = pw_args_invalid;
105
+ return ret;
106
+ }
110
107
 
111
- return mWAF;
108
+ static void
109
+ libsqreen_waf_args_invalidate(VALUE self) {
110
+ struct libsqreen_waf_args *box;
111
+ Data_Get_Struct(self, struct libsqreen_waf_args, box);
112
+ libsqreen_waf_args_free(box);
112
113
  }
113
114
 
115
+ /* wrappers */
116
+
114
117
  static VALUE
115
118
  libsqreen_version(VALUE self) {
119
+ (void) self;
120
+
116
121
  PWVersion version;
117
122
  VALUE result;
118
123
 
@@ -127,6 +132,7 @@ libsqreen_version(VALUE self) {
127
132
 
128
133
  static VALUE
129
134
  libsqreen_waf_set(VALUE self, VALUE name, VALUE rules) {
135
+ (void)self;
130
136
  char * pw_name;
131
137
  char * pw_rules;
132
138
  bool pw_result;
@@ -137,7 +143,7 @@ libsqreen_waf_set(VALUE self, VALUE name, VALUE rules) {
137
143
 
138
144
  pw_name = StringValueCStr(name);
139
145
  pw_rules = StringValueCStr(rules);
140
- pw_result = powerwaf_initializePowerWAF(pw_name, pw_rules);
146
+ pw_result = powerwaf_init(pw_name, pw_rules, &pw_config);
141
147
 
142
148
  result = pw_result ? Qtrue : Qfalse;
143
149
 
@@ -146,6 +152,8 @@ libsqreen_waf_set(VALUE self, VALUE name, VALUE rules) {
146
152
 
147
153
  static VALUE
148
154
  libsqreen_waf_delete(VALUE self, VALUE name) {
155
+ (void)self;
156
+
149
157
  char * pw_name;
150
158
 
151
159
  Check_Type(name, T_STRING);
@@ -158,16 +166,17 @@ libsqreen_waf_delete(VALUE self, VALUE name) {
158
166
 
159
167
  static VALUE
160
168
  libsqreen_waf_clear(VALUE self) {
169
+ (void)self;
170
+
161
171
  powerwaf_clearAll();
162
172
 
163
173
  return Qnil;
164
174
  }
165
175
 
166
- static const PWArgs PWArgsNil;
167
-
168
- int on_hash_iteration(VALUE key, VALUE val, VALUE args);
176
+ static int on_hash_iteration(VALUE key, VALUE val, VALUE args);
169
177
 
170
- PWArgs value_to_pw_args(VALUE val) {
178
+ static PWArgs
179
+ value_to_pw_args(VALUE val) {
171
180
  PWArgs pw_val;
172
181
 
173
182
  switch (TYPE(val)) {
@@ -175,9 +184,10 @@ PWArgs value_to_pw_args(VALUE val) {
175
184
  {
176
185
  char *pw_string;
177
186
  size_t pw_len;
187
+ VALUE utf8_val = cvt_ruby_str_to_utf8(val);
178
188
 
179
- pw_string = StringValuePtr(val);
180
- pw_len = RSTRING_LEN(val);
189
+ pw_string = StringValuePtr(utf8_val);
190
+ pw_len = (size_t)RSTRING_LEN(utf8_val);
181
191
  pw_val = powerwaf_createStringWithLength(pw_string, pw_len);
182
192
  }
183
193
  break;
@@ -191,17 +201,24 @@ PWArgs value_to_pw_args(VALUE val) {
191
201
  break;
192
202
  case T_HASH:
193
203
  {
194
- VALUE waf_args;
195
-
196
- pw_val = powerwaf_createMap();
197
- waf_args = libsqreen_waf_args_new();
198
- libsqreen_waf_args_set_boxed(waf_args, &pw_val);
204
+ VALUE waf_args = libsqreen_waf_args_new();
205
+ libsqreen_waf_args_set_boxed(waf_args, powerwaf_createMap());
206
+ // can in principle raise exception, but the WAF allocated
207
+ // memory (included the partial list of allocated values
208
+ // inserted in the map by on_hash_iteration) is already
209
+ // wrapped so is reachable by the GC
199
210
  rb_hash_foreach(val, on_hash_iteration, waf_args);
211
+
212
+ // relinquish ownership to the caller to avoid double frees
213
+ pw_val = libsqreen_waf_args_relinquish(waf_args);
200
214
  }
201
215
  break;
202
216
  case T_ARRAY:
203
217
  {
204
- pw_val = powerwaf_createArray();
218
+ VALUE waf_args = libsqreen_waf_args_new();
219
+ libsqreen_waf_args_set_boxed(waf_args, powerwaf_createArray());
220
+ PWArgs *array_p = libsqreen_waf_args_get_boxed(waf_args);
221
+
205
222
  for (int i = 0; i < RARRAY_LEN(val); i++) {
206
223
  VALUE e;
207
224
  PWArgs pw_e;
@@ -213,44 +230,58 @@ PWArgs value_to_pw_args(VALUE val) {
213
230
  continue;
214
231
  }
215
232
 
233
+ // can in principle raise
216
234
  pw_e = value_to_pw_args(e);
217
- ok = powerwaf_addToPWArgsArray(&pw_val, pw_e);
235
+ ok = powerwaf_addToPWArgsArray(array_p, pw_e);
218
236
  if (!ok) {
219
237
  powerwaf_freeInput(&pw_e, false);
220
238
  }
221
239
  }
240
+
241
+ pw_val = libsqreen_waf_args_relinquish(waf_args);
222
242
  }
223
243
  break;
224
244
  case T_NIL:
225
- pw_val = PWArgsNil;
226
- break;
245
+ /* break intentionally missing */
227
246
  default:
228
- pw_val = PWArgsNil;
247
+ RUBY_LOG(PWL_INFO, "Found argument of unaccepted type: %d", TYPE(val));
248
+ /* 0.6.1 doesn't cancel the whole rule, but it still complains
249
+ * in the log if the values in the top map are missing. Replace all
250
+ * invalid values with an empty map (this uses more maps than needed,
251
+ but also doesn't hurt) */
252
+ pw_val = powerwaf_createMap();
229
253
  break;
230
254
  }
231
255
 
232
256
  return pw_val;
233
257
  }
234
258
 
235
- int on_hash_iteration(VALUE key, VALUE val, VALUE args) {
236
- PWArgs *pw_args;
259
+ static int
260
+ on_hash_iteration(volatile VALUE key, VALUE val, VALUE waf_value) {
237
261
  char *pw_key;
238
- PWArgs pw_val;
262
+ size_t pw_len;
239
263
  bool ok;
240
264
 
241
- Check_Type(key, T_STRING);
242
- // Check_Type(in, libsqreen_waf_args)
265
+ if (RB_TYPE_P(key, T_SYMBOL)) {
266
+ key = rb_sym_to_s(key); // can't fail
267
+ // will go to next if
268
+ }
243
269
 
244
- if (val == Qnil) {
270
+ if (RB_TYPE_P(key, T_STRING)) {
271
+ pw_key = StringValuePtr(key);
272
+ pw_len = (size_t)RSTRING_LEN(key);
273
+ } else {
245
274
  return ST_CONTINUE;
246
275
  }
247
276
 
248
- pw_args = (PWArgs *)libsqreen_waf_args_get_boxed(args);
249
- pw_key = StringValueCStr(key);
250
- pw_val = value_to_pw_args(val);
251
- ok = powerwaf_addToPWArgsMap(pw_args, pw_key, strlen(pw_key), pw_val);
277
+ PWArgs *parent = libsqreen_waf_args_get_boxed(waf_value);
278
+ // can throw, but nothing allocated by powerwaf yet
279
+ PWArgs value_to_add = value_to_pw_args(val);
280
+ // key is volatile because between its last usage
281
+ // and the usage of its component pw_key, GC may run
282
+ ok = powerwaf_addToPWArgsMap(parent, pw_key, pw_len, value_to_add);
252
283
  if (!ok) {
253
- powerwaf_freeInput(&pw_val, false);
284
+ powerwaf_freeInput(&value_to_add, false);
254
285
  }
255
286
 
256
287
  return ST_CONTINUE;
@@ -258,11 +289,7 @@ int on_hash_iteration(VALUE key, VALUE val, VALUE args) {
258
289
 
259
290
  static VALUE
260
291
  libsqreen_waf_args_initialize(VALUE self, VALUE args) {
261
- VALUE hash;
262
- long len;
263
- PWArgs pw_args;
264
-
265
- len = RARRAY_LEN(args);
292
+ long len = RARRAY_LEN(args);
266
293
  if (len > 2) {
267
294
  rb_raise(rb_eArgError, "wrong number of arguments");
268
295
  }
@@ -270,17 +297,18 @@ libsqreen_waf_args_initialize(VALUE self, VALUE args) {
270
297
  return self;
271
298
  }
272
299
 
273
- hash = rb_ary_entry(args, 0);
300
+ VALUE hash = rb_ary_entry(args, 0);
274
301
  Check_Type(hash, T_HASH);
275
302
 
276
- pw_args = value_to_pw_args(hash);
277
- libsqreen_waf_args_set_boxed(self, &pw_args);
303
+ PWArgs pw_args = value_to_pw_args(hash);
304
+ libsqreen_waf_args_set_boxed(self, pw_args);
278
305
 
279
306
  return self;
280
307
  }
281
308
 
282
309
 
283
- VALUE ret_code_to_sym(PW_RET_CODE level) {
310
+ static VALUE
311
+ ret_code_to_sym(PW_RET_CODE level) {
284
312
  VALUE sym;
285
313
 
286
314
  switch (level) {
@@ -312,199 +340,187 @@ VALUE ret_code_to_sym(PW_RET_CODE level) {
312
340
  sym = ID2SYM(rb_intern("block"));
313
341
  break;
314
342
  default:
315
- sym = Qnil;
316
343
  rb_raise(rb_eArgError, "not valid value");
317
- break;
318
344
  }
319
345
 
320
346
  return sym;
321
347
  }
322
348
 
323
- static VALUE
324
- libsqreen_waf_run(VALUE self, VALUE name, VALUE args, VALUE budget) {
325
- VALUE waf_args;
326
- char *pw_name;
327
- PWArgs pw_args;
328
- size_t pw_budget;
329
- PWRet *pw_ret;
330
- VALUE result;
331
349
 
332
- Check_Type(name, T_STRING);
333
- Check_Type(args, T_HASH);
334
- Check_Type(budget, T_FIXNUM);
335
-
336
- waf_args = libsqreen_waf_args_new_from_hash(args);
337
- pw_args = *(PWArgs *)libsqreen_waf_args_get_boxed(waf_args);
338
- pw_budget = FIX2LONG(budget);
339
- pw_name = StringValueCStr(name);
340
- pw_ret = powerwaf_runPowerWAF(pw_name, &pw_args, pw_budget);
350
+ static int64_t
351
+ get_max_run_budget(VALUE arg_max_run_budg) {
352
+ if (NIL_P(arg_max_run_budg)) {
353
+ // use default value provided by waf headers
354
+ RUBY_LOG(PWL_DEBUG, "Using %d us as run timeout (default)",
355
+ PW_RUN_TIMEOUT);
356
+ return PW_RUN_TIMEOUT; // 5000 us as of this time
357
+ }
341
358
 
342
- result = rb_ary_new();
343
- rb_ary_push(result, ret_code_to_sym(pw_ret->action));
344
- rb_ary_push(result, pw_ret->data == NULL ? Qnil : rb_str_new2(pw_ret->data));
359
+ if (!FIXNUM_P(arg_max_run_budg)) {
360
+ RUBY_LOG(PWL_ERROR, "Max run budget is not a fixnum; "
361
+ "using default %d us", PW_RUN_TIMEOUT);
362
+ return PW_RUN_TIMEOUT; // 5000 us as of this time
363
+ }
345
364
 
346
- powerwaf_freeInput(&pw_args, false);
347
- powerwaf_freeReturn(pw_ret);
365
+ long res = FIX2LONG(arg_max_run_budg);
366
+ if (res <= 0) {
367
+ RUBY_LOG(PWL_DEBUG, "Max run budget is not positive; "
368
+ "using default %d us", PW_RUN_TIMEOUT);
369
+ return PW_RUN_TIMEOUT;
370
+ }
348
371
 
349
- return result;
372
+ RUBY_LOG(PWL_DEBUG, "Using %ld us as max run timeout override", res);
373
+ return (int64_t)res;
350
374
  }
351
375
 
352
- VALUE log_level_to_sym(PW_LOG_LEVEL level) {
353
- VALUE sym;
354
-
355
- switch (level) {
356
- case PWL_TRACE:
357
- sym = ID2SYM(rb_intern("trace"));
358
- break;
359
- case PWL_DEBUG:
360
- sym = ID2SYM(rb_intern("debug"));
361
- break;
362
- case PWL_INFO:
363
- sym = ID2SYM(rb_intern("info"));
364
- break;
365
- case PWL_WARN:
366
- sym = ID2SYM(rb_intern("warn"));
367
- break;
368
- case PWL_ERROR:
369
- sym = ID2SYM(rb_intern("error"));
370
- break;
371
- default:
372
- sym = Qnil;
373
- rb_raise(rb_eArgError, "not valid value");
374
- break;
376
+ static inline struct timespec
377
+ get_time_mono() {
378
+ struct timespec out;
379
+ int res = clock_gettime(CLOCK_MONOTONIC, &out);
380
+ if (res) {
381
+ // this should never fail, but...
382
+ RUBY_LOG(PWL_INFO, "clock_gettime failed");
383
+ return (struct timespec) {0};
375
384
  }
376
-
377
- return sym;
385
+ return out;
378
386
  }
379
387
 
380
- VALUE log_level_to_fixnum(PW_LOG_LEVEL level) {
381
- VALUE sym;
388
+ static inline int64_t
389
+ timespec_diff_us(struct timespec a, struct timespec b)
390
+ {
391
+ return (((int64_t)a.tv_sec - (int64_t)b.tv_sec) * 1000000000L +
392
+ ((int64_t)a.tv_nsec - (int64_t)b.tv_nsec)) / 1000;
393
+ }
382
394
 
383
- switch (level) {
384
- case PWL_TRACE:
385
- sym = INT2FIX(0); // Logger::DEBUG
386
- break;
387
- case PWL_DEBUG:
388
- sym = INT2FIX(0); // Logger::DEBUG
389
- break;
390
- case PWL_INFO:
391
- sym = INT2FIX(1); // Logger::INFO
392
- break;
393
- case PWL_WARN:
394
- sym = INT2FIX(2); // Logger::WARN
395
- break;
396
- case PWL_ERROR:
397
- sym = INT2FIX(3); // Logger::ERROR
398
- break;
399
- default:
400
- sym = Qnil;
401
- rb_raise(rb_eArgError, "not valid value");
402
- break;
395
+ static inline size_t
396
+ calc_run_budget(struct timespec start,
397
+ int64_t gen_budget,
398
+ VALUE arg_max_run_budg) {
399
+ struct timespec after_conv = get_time_mono();
400
+ int64_t spent_conv = timespec_diff_us(after_conv, start);
401
+ int64_t rem_gen_budget = gen_budget - spent_conv;
402
+ if (rem_gen_budget < 0) {
403
+ rem_gen_budget = 0;
404
+ }
405
+ RUBY_LOG(PWL_DEBUG, "Conversion of WAF arguments took %" PRId64
406
+ " us; remaining general budget is %" PRId64 " us",
407
+ spent_conv, rem_gen_budget);
408
+ if (rem_gen_budget == 0) {
409
+ RUBY_LOG(PWL_INFO, "General budget of %" PRId64 " us exhausted after "
410
+ "native conversion (spent %" PRId64 " us)",
411
+ gen_budget, spent_conv);
412
+ return 0;
403
413
  }
404
414
 
405
- return sym;
406
- }
407
-
408
- PW_LOG_LEVEL sym_to_log_level(VALUE sym) {
409
- PW_LOG_LEVEL level;
410
-
411
- Check_Type(sym, T_SYMBOL);
412
-
413
- if (SYM2ID(sym) == rb_intern("trace")) {
414
- level = PWL_TRACE;
415
- } else if (SYM2ID(sym) == rb_intern("debug")) {
416
- level = PWL_DEBUG;
417
- } else if (SYM2ID(sym) == rb_intern("info")) {
418
- level = PWL_INFO;
419
- } else if (SYM2ID(sym) == rb_intern("warn")) {
420
- level = PWL_WARN;
421
- } else if (SYM2ID(sym) == rb_intern("error")) {
422
- level = PWL_ERROR;
415
+ size_t run_budget;
416
+ int64_t max_run_budget = get_max_run_budget(arg_max_run_budg);
417
+ if (rem_gen_budget > max_run_budget) {
418
+ run_budget = (size_t)max_run_budget;
419
+ RUBY_LOG(PWL_DEBUG, "Using run budget of % " PRId64 " us instead of "
420
+ "remaining general budget of %" PRId64 " us",
421
+ run_budget, rem_gen_budget);
423
422
  } else {
424
- rb_raise(rb_eArgError, "not valid value");
425
- level = _PWL_AFTER_LAST;
423
+ run_budget = (size_t)rem_gen_budget;
426
424
  }
427
-
428
- return level;
425
+ return run_budget;
429
426
  }
430
427
 
431
- void on_log(PW_LOG_LEVEL level, const char *function, const char *file, int line, const char *message, size_t message_len);
432
-
433
428
  static VALUE
434
- libsqreen_waf_log_enable(VALUE self, VALUE severity) {
435
- PW_LOG_LEVEL level;
429
+ libsqreen_waf_run(int argc, const VALUE *argv, VALUE self) {
430
+ (void)self;
436
431
 
437
- Check_Type(severity, T_SYMBOL);
432
+ VALUE name, args, budget, max_run_budget;
438
433
 
439
- level = sym_to_log_level(severity);
440
- powerwaf_setupLogging((powerwaf_logging_cb_t)on_log, level);
434
+ rb_scan_args(argc, argv, "31", &name, &args, &budget, &max_run_budget);
441
435
 
442
- return Qnil;
443
- }
444
-
445
- static VALUE
446
- libsqreen_waf_log_disable(VALUE self) {
447
- PW_LOG_LEVEL level = PWL_ERROR;
448
-
449
- powerwaf_setupLogging(NULL, level);
450
-
451
- return Qnil;
452
- }
436
+ PWRet *pw_ret;
437
+ VALUE result = rb_ary_new();
453
438
 
454
- static VALUE
455
- libsqreen_waf_set_logger(VALUE self, VALUE logger) {
456
- ID i_logger;
439
+ Check_Type(name, T_STRING);
440
+ Check_Type(args, T_HASH);
441
+ Check_Type(budget, T_FIXNUM);
442
+ if (!NIL_P(max_run_budget)) {
443
+ Check_Type(max_run_budget, T_FIXNUM);
444
+ }
457
445
 
458
- if (logger == Qnil) {
459
- libsqreen_waf_log_disable(self);
446
+ int64_t gen_budget = (int64_t)FIX2LONG(budget); // in us
447
+ struct timespec start = get_time_mono();
448
+
449
+ VALUE waf_args = libsqreen_waf_args_new_from_hash(args);
450
+ PWArgs *pw_args = libsqreen_waf_args_get_boxed(waf_args);
451
+ const char *pw_name = StringValueCStr(name);
452
+ size_t run_budget = calc_run_budget(start, gen_budget, max_run_budget);
453
+ if (run_budget == 0) {
454
+ rb_ary_push(result, ID2SYM(rb_intern("timeout")));
455
+ rb_ary_push(result, Qnil);
456
+ return result;
460
457
  }
461
458
 
462
- i_logger = rb_intern("@logger");
463
- rb_ivar_set(self, i_logger, logger);
459
+ pw_ret = powerwaf_run(pw_name, pw_args, run_budget);
464
460
 
465
- if (logger != Qnil) {
466
- libsqreen_waf_log_enable(self, ID2SYM(rb_intern("error")));
467
- }
461
+ rb_ary_push(result, ret_code_to_sym(pw_ret->action));
462
+ rb_ary_push(result, pw_ret->data == NULL ? Qnil : rb_str_new2(pw_ret->data));
463
+
464
+ libsqreen_waf_args_invalidate(waf_args);
465
+ powerwaf_freeReturn(pw_ret);
468
466
 
469
- return logger;
467
+ return result;
470
468
  }
471
469
 
472
470
  static VALUE
473
- libsqreen_waf_get_logger(VALUE self) {
474
- VALUE logger;
475
- ID i_logger;
476
-
477
- i_logger = rb_intern("@logger");
478
- logger = rb_ivar_get(self, i_logger);
471
+ encode_as_utf8(VALUE string) {
472
+ return rb_funcall(string, rb_intern("encode"), 1, utf8_str);
473
+ }
479
474
 
480
- return logger;
475
+ static VALUE
476
+ fall_back_encode_as_utf8(VALUE string) {
477
+ VALUE dup = rb_funcall(string, rb_intern("dup"), 0);
478
+ VALUE latin1_str = rb_funcall(
479
+ dup, rb_intern("force_encoding"), 1, iso8859_1_str);
480
+ return encode_as_utf8(latin1_str);
481
481
  }
482
482
 
483
- void
484
- on_log(PW_LOG_LEVEL level, const char *function, const char *file, int line, const char *message, size_t message_len) {
485
- VALUE severity;
486
- VALUE logger;
487
- VALUE log_msg;
488
- VALUE mWAF;
489
-
490
- mWAF = libsqreen_waf_module();
491
- logger = libsqreen_waf_get_logger(mWAF);
492
- if (logger == Qnil) {
493
- return;
483
+ static VALUE
484
+ cvt_ruby_str_to_utf8(VALUE value) {
485
+ rb_encoding *const enc = rb_enc_get(value);
486
+
487
+ bool is_utf8 = enc == utf8_enc || enc == usascii_enc;
488
+ if (!is_utf8 && (enc == ascii8bit_enc || enc == latin1_enc)) {
489
+ char *str = RSTRING_PTR(value);
490
+ long len = RSTRING_LEN(value);
491
+ // still some hope left
492
+ is_utf8 = true;
493
+ for (long i = 0; i < len; i++) {
494
+ if ((unsigned char) str[i] >= 0x80) {
495
+ is_utf8 = false;
496
+ break;
497
+ }
498
+ }
494
499
  }
495
500
 
496
- severity = log_level_to_fixnum(level);
497
- if (severity == Qnil) {
498
- return;
501
+ if (is_utf8) {
502
+ // may not be valid_encoding?, but the WAF tolerates this
503
+ return value;
499
504
  }
500
505
 
501
- log_msg = rb_sprintf("from %s:%d:in `%s': %s", file, line, function, message);
502
- //rb_funcall(logger, SYM2ID(sym_level), 1, log_msg);
503
- rb_funcall(logger, rb_intern("add"), 2, severity, log_msg);
506
+ // attempt conversion
507
+ int excepted;
508
+ VALUE result = rb_protect(encode_as_utf8, value, &excepted);
509
+ if (excepted) {
510
+ RUBY_LOG(PWL_DEBUG, "Error converting string to UTF-8. Encoding: %s",
511
+ ONIGENC_NAME(enc));
512
+ // conversion failed, try fallback
513
+ result = rb_protect(fall_back_encode_as_utf8, value, &excepted);
514
+ if (excepted) {
515
+ RUBY_LOG(PWL_INFO, "Fallback conversion -> latin1 -> utf8 also "
516
+ "failed!");
517
+ // fallback also failed; stop attempting conversion
518
+ return value;
519
+ }
520
+ }
521
+ return result;
504
522
  }
505
523
 
506
- #endif
507
-
508
524
  // extension initializer
509
525
  void
510
526
  Init_libsqreen_extension(void) {
@@ -512,48 +528,37 @@ Init_libsqreen_extension(void) {
512
528
 
513
529
  mLibSqreen = rb_define_module("LibSqreen");
514
530
 
515
- // record load order
516
- {
517
- ID i_load_order;
518
- VALUE load_order;
519
-
520
- i_load_order = rb_intern("@load_order");
521
- load_order = rb_ivar_get(mLibSqreen, i_load_order);
522
- if (load_order == Qnil) {
523
- load_order = rb_ary_new();
524
- rb_ivar_set(mLibSqreen, i_load_order, load_order);
525
- }
526
-
527
- rb_ary_push(load_order, ID2SYM(rb_intern("libsqreen_extension.c")));
528
- }
529
-
530
- #ifndef LIBSQREEN_STUB
531
-
532
531
  // define C API bindings
533
532
  {
534
533
  rb_define_module_function(mLibSqreen, "version", libsqreen_version, 0);
535
534
 
536
- {
537
- VALUE mWAF;
538
- mWAF = rb_define_module_under(mLibSqreen, "WAF");
539
- rb_define_module_function(mWAF, "[]=", libsqreen_waf_set, 2);
540
- rb_define_module_function(mWAF, "delete", libsqreen_waf_delete, 1);
541
- rb_define_module_function(mWAF, "clear", libsqreen_waf_clear, 0);
542
- rb_define_module_function(mWAF, "run", libsqreen_waf_run, 3);
543
- rb_define_module_function(mWAF, "logger", libsqreen_waf_get_logger, 0);
544
- rb_define_module_function(mWAF, "logger=", libsqreen_waf_set_logger, 1);
545
- rb_define_module_function(mWAF, "log_level=", libsqreen_waf_log_enable, 1);
546
- rb_define_module_function(mWAF, "log_disable", libsqreen_waf_log_disable, 0);
547
- rb_define_const(mWAF, "BUDGET_MAX", ULL2NUM((size_t)-1));
548
-
549
- {
550
- VALUE cArgs;
551
- cArgs = rb_define_class_under(mWAF, "Args", rb_cData);
552
- rb_define_alloc_func(cArgs, libsqreen_waf_args_alloc);
553
- rb_define_method(cArgs, "initialize", libsqreen_waf_args_initialize, -2);
554
- }
555
- }
535
+ waf_mod = rb_define_module_under(mLibSqreen, "WAF");
536
+ rb_gc_register_mark_object(waf_mod);
537
+ rb_define_module_function(waf_mod, "[]=", libsqreen_waf_set, 2);
538
+ rb_define_module_function(waf_mod, "set", libsqreen_waf_set, 2);
539
+ rb_define_module_function(waf_mod, "delete", libsqreen_waf_delete, 1);
540
+ rb_define_module_function(waf_mod, "clear", libsqreen_waf_clear, 0);
541
+ rb_define_module_function(waf_mod, "run", libsqreen_waf_run, -1);
542
+ rb_define_module_function(waf_mod, "logger", libsqreen_waf_get_logger, 0);
543
+ rb_define_module_function(waf_mod, "logger=", libsqreen_waf_set_logger, 1);
544
+ rb_define_module_function(waf_mod, "log_level=", libsqreen_waf_log_enable, 1);
545
+ rb_define_module_function(waf_mod, "log_disable", libsqreen_waf_log_disable, 0);
546
+
547
+ waf_args_cls = rb_define_class_under(waf_mod, "Args", rb_cData);
548
+ rb_define_alloc_func(waf_args_cls, libsqreen_waf_args_alloc);
549
+ rb_define_method(waf_args_cls, "initialize", libsqreen_waf_args_initialize, -2);
550
+ rb_gc_register_mark_object(waf_args_cls);
556
551
  }
557
552
 
558
- #endif
553
+ utf8_enc = rb_enc_find("utf-8");
554
+ latin1_enc = rb_enc_find("iso-8859-1");
555
+ ascii8bit_enc = rb_ascii8bit_encoding();
556
+ usascii_enc = rb_usascii_encoding();
557
+
558
+ utf8_str = rb_str_new2("utf-8");
559
+ rb_gc_register_mark_object(utf8_str);
560
+ iso8859_1_str = rb_str_new2("iso-8859-1");
561
+ rb_gc_register_mark_object(iso8859_1_str);
562
+
563
+ log_init();
559
564
  }