libsqreen 0.3.0.0.3 → 0.6.1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }