endpoint_security 0.1.0-universal-darwin
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/.rubocop.yml +44 -0
- data/LICENSE.txt +21 -0
- data/README.md +114 -0
- data/Rakefile +170 -0
- data/codegen/ast.rb +67 -0
- data/codegen/emit_c.rb +68 -0
- data/codegen/emit_ruby.rb +128 -0
- data/codegen/ir.rb +193 -0
- data/codegen/overlay/availability.yml +158 -0
- data/codegen/overlay/version_map.yml +92 -0
- data/codegen/run.rb +80 -0
- data/codegen/snapshots/26.5.json +5828 -0
- data/examples/eslogger_clone.rb +5 -0
- data/examples/execblock.rb +16 -0
- data/examples/filemon.rb +10 -0
- data/examples/procmon.rb +13 -0
- data/ext/endpoint_security/client.c +795 -0
- data/ext/endpoint_security/client.h +88 -0
- data/ext/endpoint_security/endpoint_security.c +17 -0
- data/ext/endpoint_security/extconf.rb +37 -0
- data/ext/endpoint_security/field.c +719 -0
- data/ext/endpoint_security/field.h +42 -0
- data/ext/endpoint_security/generated/es_schema.c +1233 -0
- data/ext/endpoint_security/message.c +426 -0
- data/ext/endpoint_security/message.h +13 -0
- data/ext/endpoint_security/mute.c +291 -0
- data/ext/endpoint_security/mute.h +8 -0
- data/ext/endpoint_security/queue.c +124 -0
- data/ext/endpoint_security/queue.h +46 -0
- data/ext/endpoint_security/watchdog.c +243 -0
- data/lib/endpoint_security/availability.rb +30 -0
- data/lib/endpoint_security/client.rb +280 -0
- data/lib/endpoint_security/diagnostics.rb +26 -0
- data/lib/endpoint_security/doctor.rb +35 -0
- data/lib/endpoint_security/errors.rb +42 -0
- data/lib/endpoint_security/generated/availability.rb +169 -0
- data/lib/endpoint_security/generated/enums.rb +409 -0
- data/lib/endpoint_security/generated/event_types.rb +520 -0
- data/lib/endpoint_security/message.rb +29 -0
- data/lib/endpoint_security/object_model.rb +170 -0
- data/lib/endpoint_security/recorder.rb +26 -0
- data/lib/endpoint_security/version.rb +6 -0
- data/lib/endpoint_security.rb +38 -0
- data/support/entitlements.plist +8 -0
- data/support/esmock/esmock.c +482 -0
- data/support/esmock/esmock.h +21 -0
- data/support/sign_ruby.sh +13 -0
- metadata +90 -0
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
/* client.c
|
|
2
|
+
* Calling threads: lifecycle/control = Ruby threads with the GVL; native lifecycle/watchdog and ES handler = no GVL.
|
|
3
|
+
*/
|
|
4
|
+
#include "client.h"
|
|
5
|
+
|
|
6
|
+
#include <errno.h>
|
|
7
|
+
#include <fcntl.h>
|
|
8
|
+
#include <limits.h>
|
|
9
|
+
#include <mach/mach_time.h>
|
|
10
|
+
#include <math.h>
|
|
11
|
+
#include <sched.h>
|
|
12
|
+
#include <stdint.h>
|
|
13
|
+
#include <stdlib.h>
|
|
14
|
+
#include <unistd.h>
|
|
15
|
+
|
|
16
|
+
#include "message.h"
|
|
17
|
+
#include "mute.h"
|
|
18
|
+
#ifdef ESRB_MOCK
|
|
19
|
+
#include "esmock.h"
|
|
20
|
+
#include "field.h"
|
|
21
|
+
#endif
|
|
22
|
+
|
|
23
|
+
static VALUE c_client;
|
|
24
|
+
|
|
25
|
+
static void handle_message(esrb_client_t *client, es_client_t *native_client, const es_message_t *message);
|
|
26
|
+
|
|
27
|
+
es_respond_result_t
|
|
28
|
+
esrb_send_response(esrb_client_t *client, es_client_t *native_client, const es_message_t *message,
|
|
29
|
+
es_auth_result_t result, uint32_t flags, bool cache)
|
|
30
|
+
{
|
|
31
|
+
es_respond_result_t response = message->event_type == ES_EVENT_TYPE_AUTH_OPEN
|
|
32
|
+
? es_respond_flags_result(native_client, message, flags, cache)
|
|
33
|
+
: es_respond_auth_result(native_client, message, result, cache);
|
|
34
|
+
if (response != ES_RESPOND_RESULT_SUCCESS) {
|
|
35
|
+
atomic_fetch_add_explicit(&client->response_errors, 1, memory_order_relaxed);
|
|
36
|
+
}
|
|
37
|
+
return response;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static void *
|
|
41
|
+
client_native_main(void *argument)
|
|
42
|
+
{
|
|
43
|
+
esrb_client_t *client = argument;
|
|
44
|
+
es_new_client_result_t result = es_new_client(&client->client, ^(es_client_t *native, const es_message_t *message) {
|
|
45
|
+
handle_message(client, native, message);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
pthread_mutex_lock(&client->watchdog.mutex);
|
|
49
|
+
atomic_store_explicit(&client->creation_result, result, memory_order_relaxed);
|
|
50
|
+
atomic_store_explicit(&client->client_ready, true, memory_order_release);
|
|
51
|
+
pthread_cond_broadcast(&client->watchdog.condition);
|
|
52
|
+
pthread_mutex_unlock(&client->watchdog.mutex);
|
|
53
|
+
if (result != ES_NEW_CLIENT_RESULT_SUCCESS) {
|
|
54
|
+
return NULL;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
esrb_watchdog_main(client);
|
|
58
|
+
atomic_store_explicit(&client->watchdog_stopped, true, memory_order_release);
|
|
59
|
+
|
|
60
|
+
pthread_mutex_lock(&client->watchdog.mutex);
|
|
61
|
+
while (!atomic_load_explicit(&client->delete_ready, memory_order_acquire)) {
|
|
62
|
+
pthread_cond_wait(&client->watchdog.condition, &client->watchdog.mutex);
|
|
63
|
+
}
|
|
64
|
+
pthread_mutex_unlock(&client->watchdog.mutex);
|
|
65
|
+
if (es_delete_client(client->client) != ES_RETURN_SUCCESS) {
|
|
66
|
+
atomic_fetch_add_explicit(&client->delete_errors, 1, memory_order_relaxed);
|
|
67
|
+
}
|
|
68
|
+
return NULL;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static void
|
|
72
|
+
client_answer_slot(esrb_client_t *client, esrb_slot_t *slot)
|
|
73
|
+
{
|
|
74
|
+
uint32_t expected = ESRB_ANSWER_PENDING;
|
|
75
|
+
if (!atomic_compare_exchange_strong_explicit(
|
|
76
|
+
&slot->answer_state, &expected, ESRB_ANSWER_ANSWERED, memory_order_acq_rel, memory_order_acquire)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
esrb_send_response(client, slot->client, slot->message, client->default_auth,
|
|
80
|
+
client->default_auth == ES_AUTH_RESULT_ALLOW ? UINT32_MAX : 0, client->default_cache);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static void
|
|
84
|
+
client_answer_occupied(esrb_client_t *client)
|
|
85
|
+
{
|
|
86
|
+
for (size_t index = 0; index < client->queue.capacity; index++) {
|
|
87
|
+
esrb_slot_t *slot = &client->queue.slots[index];
|
|
88
|
+
if (atomic_load_explicit(&slot->occupied, memory_order_acquire)) {
|
|
89
|
+
client_answer_slot(client, slot);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static void
|
|
95
|
+
client_drain_and_release(esrb_client_t *client)
|
|
96
|
+
{
|
|
97
|
+
esrb_slot_t *slot;
|
|
98
|
+
while ((slot = esrb_queue_dequeue(&client->queue)) != NULL) {
|
|
99
|
+
client_answer_slot(client, slot);
|
|
100
|
+
es_release_message(slot->message);
|
|
101
|
+
esrb_queue_release(&client->queue, slot);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
static void
|
|
106
|
+
client_close_native(esrb_client_t *client)
|
|
107
|
+
{
|
|
108
|
+
if (atomic_exchange_explicit(&client->closed, true, memory_order_acq_rel)) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (client->client != NULL) {
|
|
113
|
+
es_unsubscribe_all(client->client);
|
|
114
|
+
}
|
|
115
|
+
while (atomic_load_explicit(&client->active_callbacks, memory_order_acquire) != 0) {
|
|
116
|
+
sched_yield();
|
|
117
|
+
}
|
|
118
|
+
if (client->watchdog_thread_started) {
|
|
119
|
+
atomic_store_explicit(&client->watchdog_running, false, memory_order_release);
|
|
120
|
+
pthread_cond_broadcast(&client->watchdog.condition);
|
|
121
|
+
while (!atomic_load_explicit(&client->watchdog_stopped, memory_order_acquire)) {
|
|
122
|
+
sched_yield();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
client_answer_occupied(client);
|
|
126
|
+
client_drain_and_release(client);
|
|
127
|
+
if (client->watchdog_thread_started) {
|
|
128
|
+
pthread_mutex_lock(&client->watchdog.mutex);
|
|
129
|
+
atomic_store_explicit(&client->delete_ready, true, memory_order_release);
|
|
130
|
+
pthread_cond_broadcast(&client->watchdog.condition);
|
|
131
|
+
pthread_mutex_unlock(&client->watchdog.mutex);
|
|
132
|
+
pthread_join(client->watchdog_thread, NULL);
|
|
133
|
+
client->watchdog_thread_started = false;
|
|
134
|
+
client->client = NULL;
|
|
135
|
+
}
|
|
136
|
+
esrb_notify(client);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static void
|
|
140
|
+
client_free(void *pointer)
|
|
141
|
+
{
|
|
142
|
+
esrb_client_t *client = pointer;
|
|
143
|
+
bool owned_here = client->owner_pid == 0 || client->owner_pid == getpid();
|
|
144
|
+
if (owned_here) {
|
|
145
|
+
client_close_native(client);
|
|
146
|
+
} else {
|
|
147
|
+
client->client = NULL;
|
|
148
|
+
atomic_store_explicit(&client->watchdog_running, false, memory_order_release);
|
|
149
|
+
atomic_store_explicit(&client->closed, true, memory_order_release);
|
|
150
|
+
}
|
|
151
|
+
esrb_queue_destroy(&client->queue);
|
|
152
|
+
if (owned_here) {
|
|
153
|
+
esrb_watchdog_destroy(&client->watchdog);
|
|
154
|
+
} else {
|
|
155
|
+
esrb_watchdog_abandon(&client->watchdog);
|
|
156
|
+
}
|
|
157
|
+
if (client->wakeup_fd[0] >= 0) {
|
|
158
|
+
close(client->wakeup_fd[0]);
|
|
159
|
+
}
|
|
160
|
+
if (client->wakeup_fd[1] >= 0) {
|
|
161
|
+
close(client->wakeup_fd[1]);
|
|
162
|
+
}
|
|
163
|
+
xfree(client);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
static size_t
|
|
167
|
+
client_size(const void *pointer)
|
|
168
|
+
{
|
|
169
|
+
esrb_client_t const *client = pointer;
|
|
170
|
+
return client == NULL ? 0
|
|
171
|
+
: sizeof(*client) + client->queue.capacity *
|
|
172
|
+
(sizeof(esrb_slot_t) + sizeof(esrb_watchdog_request_t) + sizeof(esrb_watchdog_entry_t));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const rb_data_type_t esrb_client_type = {
|
|
176
|
+
.wrap_struct_name = "EndpointSecurity::Client",
|
|
177
|
+
.function = {.dfree = client_free, .dsize = client_size},
|
|
178
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
static VALUE
|
|
182
|
+
client_allocate(VALUE klass)
|
|
183
|
+
{
|
|
184
|
+
esrb_client_t *client;
|
|
185
|
+
VALUE object = TypedData_Make_Struct(klass, esrb_client_t, &esrb_client_type, client);
|
|
186
|
+
client->wakeup_fd[0] = -1;
|
|
187
|
+
client->wakeup_fd[1] = -1;
|
|
188
|
+
client->watchdog_thread_started = false;
|
|
189
|
+
atomic_init(&client->closed, true);
|
|
190
|
+
return object;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
static esrb_client_t *
|
|
194
|
+
get_client(VALUE self)
|
|
195
|
+
{
|
|
196
|
+
esrb_client_t *client;
|
|
197
|
+
TypedData_Get_Struct(self, esrb_client_t, &esrb_client_type, client);
|
|
198
|
+
if (client->owner_pid != getpid()) {
|
|
199
|
+
rb_raise(rb_path2class("EndpointSecurity::ForkedClientError"), "Endpoint Security clients cannot be used after fork");
|
|
200
|
+
}
|
|
201
|
+
return client;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static esrb_client_t *
|
|
205
|
+
get_open_client(VALUE self)
|
|
206
|
+
{
|
|
207
|
+
esrb_client_t *client = get_client(self);
|
|
208
|
+
if (atomic_load_explicit(&client->closed, memory_order_acquire)) {
|
|
209
|
+
rb_raise(rb_path2class("EndpointSecurity::ClientError"), "client is closed");
|
|
210
|
+
}
|
|
211
|
+
return client;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
static void __attribute__((noreturn))
|
|
215
|
+
raise_new_client_error(es_new_client_result_t result)
|
|
216
|
+
{
|
|
217
|
+
const char *klass;
|
|
218
|
+
const char *name;
|
|
219
|
+
switch (result) {
|
|
220
|
+
case ES_NEW_CLIENT_RESULT_ERR_INVALID_ARGUMENT:
|
|
221
|
+
klass = "EndpointSecurity::InvalidArgumentError";
|
|
222
|
+
name = "err_invalid_argument";
|
|
223
|
+
break;
|
|
224
|
+
case ES_NEW_CLIENT_RESULT_ERR_NOT_ENTITLED:
|
|
225
|
+
klass = "EndpointSecurity::NotEntitledError";
|
|
226
|
+
name = "err_not_entitled";
|
|
227
|
+
break;
|
|
228
|
+
case ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED:
|
|
229
|
+
klass = "EndpointSecurity::NotPermittedError";
|
|
230
|
+
name = "err_not_permitted";
|
|
231
|
+
break;
|
|
232
|
+
case ES_NEW_CLIENT_RESULT_ERR_NOT_PRIVILEGED:
|
|
233
|
+
klass = "EndpointSecurity::NotPrivilegedError";
|
|
234
|
+
name = "err_not_privileged";
|
|
235
|
+
break;
|
|
236
|
+
case ES_NEW_CLIENT_RESULT_ERR_TOO_MANY_CLIENTS:
|
|
237
|
+
klass = "EndpointSecurity::TooManyClientsError";
|
|
238
|
+
name = "err_too_many_clients";
|
|
239
|
+
break;
|
|
240
|
+
default:
|
|
241
|
+
klass = "EndpointSecurity::InternalError";
|
|
242
|
+
name = "err_internal";
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
VALUE diagnostics = rb_path2class("EndpointSecurity::Diagnostics");
|
|
246
|
+
VALUE message = rb_funcall(diagnostics, rb_intern("explain"), 1, ID2SYM(rb_intern(name)));
|
|
247
|
+
rb_exc_raise(rb_exc_new_str(rb_path2class(klass), message));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
void
|
|
251
|
+
esrb_notify(esrb_client_t *client)
|
|
252
|
+
{
|
|
253
|
+
bool expected = false;
|
|
254
|
+
if (!atomic_compare_exchange_strong_explicit(
|
|
255
|
+
&client->notified, &expected, true, memory_order_acq_rel, memory_order_relaxed)) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
unsigned char byte = 1;
|
|
260
|
+
ssize_t ignored = write(client->wakeup_fd[1], &byte, sizeof(byte));
|
|
261
|
+
(void)ignored;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
static uint64_t
|
|
265
|
+
deadline_fire_time(esrb_client_t *client, uint64_t deadline)
|
|
266
|
+
{
|
|
267
|
+
uint64_t now = mach_absolute_time();
|
|
268
|
+
if (deadline <= now) {
|
|
269
|
+
return now;
|
|
270
|
+
}
|
|
271
|
+
uint64_t remaining = deadline - now;
|
|
272
|
+
uint64_t advance = client->deadline_margin == 1.0
|
|
273
|
+
? remaining
|
|
274
|
+
: (uint64_t)((double)remaining * client->deadline_margin);
|
|
275
|
+
if (advance < client->min_margin_ticks) {
|
|
276
|
+
advance = client->min_margin_ticks;
|
|
277
|
+
}
|
|
278
|
+
return advance >= remaining ? now : deadline - advance;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
static void
|
|
282
|
+
record_sequence_gaps(esrb_client_t *client, const es_message_t *message)
|
|
283
|
+
{
|
|
284
|
+
if (message->version >= 4) {
|
|
285
|
+
uint64_t previous = atomic_exchange_explicit(&client->last_global_seq, message->global_seq_num, memory_order_relaxed);
|
|
286
|
+
bool seen = atomic_exchange_explicit(&client->seen_global_seq, true, memory_order_relaxed);
|
|
287
|
+
if (seen && message->global_seq_num > previous + 1) {
|
|
288
|
+
atomic_fetch_add_explicit(&client->seq_gaps, message->global_seq_num - previous - 1, memory_order_relaxed);
|
|
289
|
+
}
|
|
290
|
+
} else if (message->version >= 2 && message->event_type >= 0 && message->event_type < ES_EVENT_TYPE_LAST) {
|
|
291
|
+
size_t index = (size_t)message->event_type;
|
|
292
|
+
uint64_t previous = atomic_exchange_explicit(&client->last_event_seq[index], message->seq_num, memory_order_relaxed);
|
|
293
|
+
bool seen = atomic_exchange_explicit(&client->seen_event_seq[index], true, memory_order_relaxed);
|
|
294
|
+
if (seen && message->seq_num > previous + 1) {
|
|
295
|
+
atomic_fetch_add_explicit(&client->seq_gaps, message->seq_num - previous - 1, memory_order_relaxed);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
static void
|
|
301
|
+
handle_message(esrb_client_t *client, es_client_t *native_client, const es_message_t *message)
|
|
302
|
+
{
|
|
303
|
+
atomic_fetch_add_explicit(&client->active_callbacks, 1, memory_order_acquire);
|
|
304
|
+
if (atomic_load_explicit(&client->closed, memory_order_acquire)) {
|
|
305
|
+
if (message->action_type == ES_ACTION_TYPE_AUTH) {
|
|
306
|
+
esrb_send_response(client, native_client, message, client->default_auth,
|
|
307
|
+
client->default_auth == ES_AUTH_RESULT_ALLOW ? UINT32_MAX : 0, client->default_cache);
|
|
308
|
+
}
|
|
309
|
+
atomic_fetch_sub_explicit(&client->active_callbacks, 1, memory_order_release);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
record_sequence_gaps(client, message);
|
|
313
|
+
es_retain_message(message);
|
|
314
|
+
esrb_slot_t *slot = esrb_queue_enqueue(&client->queue);
|
|
315
|
+
if (slot == NULL) {
|
|
316
|
+
if (message->action_type == ES_ACTION_TYPE_AUTH) {
|
|
317
|
+
esrb_send_response(client, native_client, message, client->default_auth,
|
|
318
|
+
client->default_auth == ES_AUTH_RESULT_ALLOW ? UINT32_MAX : 0, client->default_cache);
|
|
319
|
+
}
|
|
320
|
+
atomic_fetch_add_explicit(&client->dropped, 1, memory_order_relaxed);
|
|
321
|
+
es_release_message(message);
|
|
322
|
+
atomic_fetch_sub_explicit(&client->active_callbacks, 1, memory_order_release);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
slot->message = message;
|
|
327
|
+
slot->client = native_client;
|
|
328
|
+
slot->fire_at = message->action_type == ES_ACTION_TYPE_AUTH ? deadline_fire_time(client, message->deadline) : UINT64_MAX;
|
|
329
|
+
atomic_store_explicit(&slot->answer_state,
|
|
330
|
+
message->action_type == ES_ACTION_TYPE_AUTH ? ESRB_ANSWER_PENDING : ESRB_ANSWER_NOT_AUTH, memory_order_relaxed);
|
|
331
|
+
atomic_store_explicit(&slot->occupied, true, memory_order_release);
|
|
332
|
+
if (message->action_type == ES_ACTION_TYPE_AUTH && !esrb_watchdog_arm(&client->watchdog, slot)) {
|
|
333
|
+
client_answer_slot(client, slot);
|
|
334
|
+
}
|
|
335
|
+
esrb_queue_publish(&client->queue, slot);
|
|
336
|
+
esrb_notify(client);
|
|
337
|
+
atomic_fetch_sub_explicit(&client->active_callbacks, 1, memory_order_release);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
static VALUE
|
|
341
|
+
client_initialize(int argc, VALUE *argv, VALUE self)
|
|
342
|
+
{
|
|
343
|
+
VALUE options;
|
|
344
|
+
rb_scan_args(argc, argv, "0:", &options);
|
|
345
|
+
esrb_client_t *client;
|
|
346
|
+
TypedData_Get_Struct(self, esrb_client_t, &esrb_client_type, client);
|
|
347
|
+
|
|
348
|
+
size_t queue_depth = 8192;
|
|
349
|
+
client->default_auth = ES_AUTH_RESULT_ALLOW;
|
|
350
|
+
client->default_cache = false;
|
|
351
|
+
client->strict_cache = false;
|
|
352
|
+
client->strict_version = false;
|
|
353
|
+
client->warn_on_truncated_path = false;
|
|
354
|
+
client->deadline_margin = 0.2;
|
|
355
|
+
uint64_t min_margin_ns = 5000000ULL;
|
|
356
|
+
if (!NIL_P(options)) {
|
|
357
|
+
VALUE depth = rb_hash_aref(options, ID2SYM(rb_intern("queue_depth")));
|
|
358
|
+
VALUE auth = rb_hash_aref(options, ID2SYM(rb_intern("auth_default")));
|
|
359
|
+
VALUE margin = rb_hash_aref(options, ID2SYM(rb_intern("deadline_margin")));
|
|
360
|
+
VALUE minimum = rb_hash_aref(options, ID2SYM(rb_intern("min_margin_ns")));
|
|
361
|
+
VALUE strict_cache = rb_hash_aref(options, ID2SYM(rb_intern("strict_cache")));
|
|
362
|
+
VALUE strict_version = rb_hash_aref(options, ID2SYM(rb_intern("strict_version")));
|
|
363
|
+
VALUE default_cache = rb_hash_aref(options, ID2SYM(rb_intern("default_cache")));
|
|
364
|
+
VALUE on_full = rb_hash_aref(options, ID2SYM(rb_intern("on_full")));
|
|
365
|
+
VALUE warn_on_truncated_path = rb_hash_aref(options, ID2SYM(rb_intern("warn_on_truncated_path")));
|
|
366
|
+
queue_depth = NIL_P(depth) ? queue_depth : NUM2SIZET(depth);
|
|
367
|
+
client->deadline_margin = NIL_P(margin) ? client->deadline_margin : NUM2DBL(margin);
|
|
368
|
+
min_margin_ns = NIL_P(minimum) ? min_margin_ns : NUM2ULL(minimum);
|
|
369
|
+
client->strict_cache = RTEST(strict_cache);
|
|
370
|
+
client->strict_version = RTEST(strict_version);
|
|
371
|
+
client->default_cache = RTEST(default_cache);
|
|
372
|
+
client->warn_on_truncated_path = RTEST(warn_on_truncated_path);
|
|
373
|
+
if (!NIL_P(on_full)) {
|
|
374
|
+
Check_Type(on_full, T_SYMBOL);
|
|
375
|
+
if (SYM2ID(on_full) != rb_intern("drop") && SYM2ID(on_full) != rb_intern("respond_default")) {
|
|
376
|
+
rb_raise(rb_eArgError, "on_full must be :drop or :respond_default");
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
if (!NIL_P(auth)) {
|
|
380
|
+
Check_Type(auth, T_SYMBOL);
|
|
381
|
+
if (SYM2ID(auth) == rb_intern("deny")) {
|
|
382
|
+
client->default_auth = ES_AUTH_RESULT_DENY;
|
|
383
|
+
} else if (SYM2ID(auth) != rb_intern("allow")) {
|
|
384
|
+
rb_raise(rb_eArgError, "auth_default must be :allow or :deny");
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (!isfinite(client->deadline_margin) || client->deadline_margin < 0.0 || client->deadline_margin > 1.0) {
|
|
389
|
+
rb_raise(rb_eArgError, "deadline_margin must be between 0.0 and 1.0");
|
|
390
|
+
}
|
|
391
|
+
if (!esrb_queue_init(&client->queue, queue_depth)) {
|
|
392
|
+
rb_raise(rb_eArgError, "queue_depth must be a power of two and at least 2");
|
|
393
|
+
}
|
|
394
|
+
if (!esrb_watchdog_init(&client->watchdog, queue_depth)) {
|
|
395
|
+
esrb_queue_destroy(&client->queue);
|
|
396
|
+
rb_raise(rb_eNoMemError, "failed to allocate Endpoint Security watchdog");
|
|
397
|
+
}
|
|
398
|
+
if (pipe(client->wakeup_fd) != 0) {
|
|
399
|
+
esrb_watchdog_destroy(&client->watchdog);
|
|
400
|
+
esrb_queue_destroy(&client->queue);
|
|
401
|
+
rb_sys_fail("pipe");
|
|
402
|
+
}
|
|
403
|
+
if (fcntl(client->wakeup_fd[0], F_SETFL, O_NONBLOCK) == -1 ||
|
|
404
|
+
fcntl(client->wakeup_fd[1], F_SETFL, O_NONBLOCK) == -1) {
|
|
405
|
+
int error = errno;
|
|
406
|
+
close(client->wakeup_fd[0]);
|
|
407
|
+
close(client->wakeup_fd[1]);
|
|
408
|
+
client->wakeup_fd[0] = -1;
|
|
409
|
+
client->wakeup_fd[1] = -1;
|
|
410
|
+
esrb_watchdog_destroy(&client->watchdog);
|
|
411
|
+
esrb_queue_destroy(&client->queue);
|
|
412
|
+
errno = error;
|
|
413
|
+
rb_sys_fail("fcntl");
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
mach_timebase_info_data_t info;
|
|
417
|
+
mach_timebase_info(&info);
|
|
418
|
+
client->min_margin_ticks = min_margin_ns > UINT64_MAX / info.denom
|
|
419
|
+
? UINT64_MAX
|
|
420
|
+
: min_margin_ns * info.denom / info.numer;
|
|
421
|
+
client->owner_pid = getpid();
|
|
422
|
+
atomic_init(&client->notified, false);
|
|
423
|
+
atomic_init(&client->active_callbacks, 0);
|
|
424
|
+
atomic_init(&client->dropped, 0);
|
|
425
|
+
atomic_init(&client->delivered, 0);
|
|
426
|
+
atomic_init(&client->timeouts, 0);
|
|
427
|
+
atomic_init(&client->response_errors, 0);
|
|
428
|
+
atomic_init(&client->delete_errors, 0);
|
|
429
|
+
atomic_init(&client->errors, 0);
|
|
430
|
+
atomic_init(&client->seq_gaps, 0);
|
|
431
|
+
atomic_init(&client->leaked_messages, 0);
|
|
432
|
+
atomic_init(&client->last_global_seq, 0);
|
|
433
|
+
atomic_init(&client->seen_global_seq, false);
|
|
434
|
+
for (size_t index = 0; index < ES_EVENT_TYPE_LAST; index++) {
|
|
435
|
+
atomic_init(&client->last_event_seq[index], 0);
|
|
436
|
+
atomic_init(&client->seen_event_seq[index], false);
|
|
437
|
+
}
|
|
438
|
+
atomic_init(&client->watchdog_running, true);
|
|
439
|
+
atomic_init(&client->watchdog_stopped, false);
|
|
440
|
+
atomic_init(&client->delete_ready, false);
|
|
441
|
+
atomic_init(&client->client_ready, false);
|
|
442
|
+
atomic_init(&client->creation_result, ES_NEW_CLIENT_RESULT_ERR_INTERNAL);
|
|
443
|
+
atomic_store_explicit(&client->closed, false, memory_order_release);
|
|
444
|
+
|
|
445
|
+
if (pthread_create(&client->watchdog_thread, NULL, client_native_main, client) != 0) {
|
|
446
|
+
atomic_store_explicit(&client->watchdog_running, false, memory_order_release);
|
|
447
|
+
close(client->wakeup_fd[0]);
|
|
448
|
+
close(client->wakeup_fd[1]);
|
|
449
|
+
client->wakeup_fd[0] = -1;
|
|
450
|
+
client->wakeup_fd[1] = -1;
|
|
451
|
+
esrb_watchdog_destroy(&client->watchdog);
|
|
452
|
+
esrb_queue_destroy(&client->queue);
|
|
453
|
+
atomic_store_explicit(&client->closed, true, memory_order_release);
|
|
454
|
+
rb_raise(rb_eRuntimeError, "failed to create Endpoint Security watchdog");
|
|
455
|
+
}
|
|
456
|
+
client->watchdog_thread_started = true;
|
|
457
|
+
|
|
458
|
+
pthread_mutex_lock(&client->watchdog.mutex);
|
|
459
|
+
while (!atomic_load_explicit(&client->client_ready, memory_order_acquire)) {
|
|
460
|
+
pthread_cond_wait(&client->watchdog.condition, &client->watchdog.mutex);
|
|
461
|
+
}
|
|
462
|
+
pthread_mutex_unlock(&client->watchdog.mutex);
|
|
463
|
+
es_new_client_result_t result = atomic_load_explicit(&client->creation_result, memory_order_relaxed);
|
|
464
|
+
if (result != ES_NEW_CLIENT_RESULT_SUCCESS) {
|
|
465
|
+
pthread_join(client->watchdog_thread, NULL);
|
|
466
|
+
client->watchdog_thread_started = false;
|
|
467
|
+
close(client->wakeup_fd[0]);
|
|
468
|
+
close(client->wakeup_fd[1]);
|
|
469
|
+
client->wakeup_fd[0] = -1;
|
|
470
|
+
client->wakeup_fd[1] = -1;
|
|
471
|
+
esrb_watchdog_destroy(&client->watchdog);
|
|
472
|
+
esrb_queue_destroy(&client->queue);
|
|
473
|
+
atomic_store_explicit(&client->closed, true, memory_order_release);
|
|
474
|
+
raise_new_client_error(result);
|
|
475
|
+
}
|
|
476
|
+
return self;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
static VALUE
|
|
480
|
+
client_close(VALUE self)
|
|
481
|
+
{
|
|
482
|
+
client_close_native(get_client(self));
|
|
483
|
+
return Qnil;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
static VALUE
|
|
487
|
+
client_closed_p(VALUE self)
|
|
488
|
+
{
|
|
489
|
+
return atomic_load_explicit(&get_client(self)->closed, memory_order_acquire) ? Qtrue : Qfalse;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
static VALUE
|
|
493
|
+
client_wakeup_fd(VALUE self)
|
|
494
|
+
{
|
|
495
|
+
return INT2NUM(get_open_client(self)->wakeup_fd[0]);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
static VALUE
|
|
499
|
+
client_wake(VALUE self)
|
|
500
|
+
{
|
|
501
|
+
esrb_notify(get_client(self));
|
|
502
|
+
return Qnil;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
static VALUE
|
|
506
|
+
client_drain(int argc, VALUE *argv, VALUE self)
|
|
507
|
+
{
|
|
508
|
+
VALUE maximum_value;
|
|
509
|
+
rb_scan_args(argc, argv, "01", &maximum_value);
|
|
510
|
+
long maximum = NIL_P(maximum_value) ? 256 : NUM2LONG(maximum_value);
|
|
511
|
+
esrb_client_t *client = get_open_client(self);
|
|
512
|
+
unsigned char buffer[256];
|
|
513
|
+
while (read(client->wakeup_fd[0], buffer, sizeof(buffer)) > 0) {
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
VALUE messages = rb_ary_new_capa(maximum);
|
|
517
|
+
for (long index = 0; index < maximum; index++) {
|
|
518
|
+
esrb_slot_t *slot = esrb_queue_dequeue(&client->queue);
|
|
519
|
+
if (slot == NULL) {
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
rb_ary_push(messages, esrb_message_wrap(self, client, slot));
|
|
523
|
+
atomic_fetch_add_explicit(&client->delivered, 1, memory_order_relaxed);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
atomic_store_explicit(&client->notified, false, memory_order_release);
|
|
527
|
+
if (esrb_queue_depth(&client->queue) > 0) {
|
|
528
|
+
esrb_notify(client);
|
|
529
|
+
}
|
|
530
|
+
return messages;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
static VALUE
|
|
534
|
+
client_subscribe(VALUE self, VALUE values)
|
|
535
|
+
{
|
|
536
|
+
esrb_client_t *client = get_open_client(self);
|
|
537
|
+
Check_Type(values, T_ARRAY);
|
|
538
|
+
long count = RARRAY_LEN(values);
|
|
539
|
+
if (count <= 0 || count > UINT32_MAX) {
|
|
540
|
+
rb_raise(rb_eArgError, "at least one event is required");
|
|
541
|
+
}
|
|
542
|
+
VALUE storage = rb_str_new(NULL, count * (long)sizeof(es_event_type_t));
|
|
543
|
+
es_event_type_t *events = (es_event_type_t *)RSTRING_PTR(storage);
|
|
544
|
+
for (long index = 0; index < count; index++) {
|
|
545
|
+
events[index] = (es_event_type_t)NUM2INT(rb_ary_entry(values, index));
|
|
546
|
+
}
|
|
547
|
+
es_return_t result = es_subscribe(client->client, events, (uint32_t)count);
|
|
548
|
+
RB_GC_GUARD(storage);
|
|
549
|
+
if (result != ES_RETURN_SUCCESS) {
|
|
550
|
+
rb_raise(rb_path2class("EndpointSecurity::SubscriptionError"), "es_subscribe failed");
|
|
551
|
+
}
|
|
552
|
+
return Qtrue;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
static VALUE
|
|
556
|
+
client_unsubscribe(VALUE self, VALUE values)
|
|
557
|
+
{
|
|
558
|
+
esrb_client_t *client = get_open_client(self);
|
|
559
|
+
if (NIL_P(values)) {
|
|
560
|
+
if (es_unsubscribe_all(client->client) != ES_RETURN_SUCCESS) {
|
|
561
|
+
rb_raise(rb_path2class("EndpointSecurity::SubscriptionError"), "es_unsubscribe_all failed");
|
|
562
|
+
}
|
|
563
|
+
return Qtrue;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
Check_Type(values, T_ARRAY);
|
|
567
|
+
long count = RARRAY_LEN(values);
|
|
568
|
+
if (count > UINT32_MAX) {
|
|
569
|
+
rb_raise(rb_eArgError, "too many events");
|
|
570
|
+
}
|
|
571
|
+
VALUE storage = rb_str_new(NULL, count * (long)sizeof(es_event_type_t));
|
|
572
|
+
es_event_type_t *events = (es_event_type_t *)RSTRING_PTR(storage);
|
|
573
|
+
for (long index = 0; index < count; index++) {
|
|
574
|
+
events[index] = (es_event_type_t)NUM2INT(rb_ary_entry(values, index));
|
|
575
|
+
}
|
|
576
|
+
es_return_t result = es_unsubscribe(client->client, events, (uint32_t)count);
|
|
577
|
+
RB_GC_GUARD(storage);
|
|
578
|
+
if (result != ES_RETURN_SUCCESS) {
|
|
579
|
+
rb_raise(rb_path2class("EndpointSecurity::SubscriptionError"), "es_unsubscribe failed");
|
|
580
|
+
}
|
|
581
|
+
return Qtrue;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
typedef struct {
|
|
585
|
+
es_event_type_t *values;
|
|
586
|
+
size_t count;
|
|
587
|
+
} esrb_subscriptions_t;
|
|
588
|
+
|
|
589
|
+
static VALUE
|
|
590
|
+
copy_subscriptions(VALUE context_value)
|
|
591
|
+
{
|
|
592
|
+
esrb_subscriptions_t *context = (esrb_subscriptions_t *)(uintptr_t)context_value;
|
|
593
|
+
VALUE values = rb_ary_new_capa((long)context->count);
|
|
594
|
+
VALUE event_type = rb_path2class("EndpointSecurity::EventType");
|
|
595
|
+
for (size_t index = 0; index < context->count; index++) {
|
|
596
|
+
rb_ary_push(values, rb_funcall(event_type, rb_intern("symbol"), 1, INT2NUM(context->values[index])));
|
|
597
|
+
}
|
|
598
|
+
return values;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
static VALUE
|
|
602
|
+
release_subscriptions(VALUE context_value)
|
|
603
|
+
{
|
|
604
|
+
esrb_subscriptions_t *context = (esrb_subscriptions_t *)(uintptr_t)context_value;
|
|
605
|
+
free(context->values);
|
|
606
|
+
return Qnil;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
static VALUE
|
|
610
|
+
client_subscriptions(VALUE self)
|
|
611
|
+
{
|
|
612
|
+
esrb_client_t *client = get_open_client(self);
|
|
613
|
+
esrb_subscriptions_t context = {0};
|
|
614
|
+
if (es_subscriptions(client->client, &context.count, &context.values) != ES_RETURN_SUCCESS) {
|
|
615
|
+
rb_raise(rb_path2class("EndpointSecurity::SubscriptionError"), "es_subscriptions failed");
|
|
616
|
+
}
|
|
617
|
+
if (context.count > LONG_MAX) {
|
|
618
|
+
free(context.values);
|
|
619
|
+
rb_raise(rb_eRangeError, "subscription count is too large");
|
|
620
|
+
}
|
|
621
|
+
return rb_ensure(copy_subscriptions, (VALUE)(uintptr_t)&context,
|
|
622
|
+
release_subscriptions, (VALUE)(uintptr_t)&context);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
static VALUE
|
|
626
|
+
client_stats(VALUE self)
|
|
627
|
+
{
|
|
628
|
+
esrb_client_t *client = get_client(self);
|
|
629
|
+
VALUE stats = rb_hash_new();
|
|
630
|
+
#define SET_STAT(name, value) rb_hash_aset(stats, ID2SYM(rb_intern(name)), ULL2NUM(value))
|
|
631
|
+
SET_STAT("delivered", atomic_load_explicit(&client->delivered, memory_order_relaxed));
|
|
632
|
+
SET_STAT("dropped", atomic_load_explicit(&client->dropped, memory_order_relaxed));
|
|
633
|
+
SET_STAT("timeouts", atomic_load_explicit(&client->timeouts, memory_order_relaxed));
|
|
634
|
+
SET_STAT("response_errors", atomic_load_explicit(&client->response_errors, memory_order_relaxed));
|
|
635
|
+
SET_STAT("delete_errors", atomic_load_explicit(&client->delete_errors, memory_order_relaxed));
|
|
636
|
+
SET_STAT("errors", atomic_load_explicit(&client->errors, memory_order_relaxed));
|
|
637
|
+
SET_STAT("queue_depth_max", atomic_load_explicit(&client->queue.depth_max, memory_order_relaxed));
|
|
638
|
+
SET_STAT("queue_depth", esrb_queue_depth(&client->queue));
|
|
639
|
+
SET_STAT("seq_gaps", atomic_load_explicit(&client->seq_gaps, memory_order_relaxed));
|
|
640
|
+
SET_STAT("leaked_messages", atomic_load_explicit(&client->leaked_messages, memory_order_relaxed));
|
|
641
|
+
#undef SET_STAT
|
|
642
|
+
return stats;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
bool
|
|
646
|
+
esrb_respond_slot(esrb_client_t *client, esrb_slot_t *slot, es_auth_result_t result, uint32_t flags, bool cache,
|
|
647
|
+
es_respond_result_t *response)
|
|
648
|
+
{
|
|
649
|
+
if (atomic_load_explicit(&client->closed, memory_order_acquire)) {
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
uint32_t expected = ESRB_ANSWER_PENDING;
|
|
653
|
+
if (!atomic_compare_exchange_strong_explicit(
|
|
654
|
+
&slot->answer_state, &expected, ESRB_ANSWER_ANSWERED, memory_order_acq_rel, memory_order_acquire)) {
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
es_respond_result_t result_code = esrb_send_response(client, slot->client, slot->message, result, flags, cache);
|
|
659
|
+
if (response != NULL) {
|
|
660
|
+
*response = result_code;
|
|
661
|
+
}
|
|
662
|
+
return result_code == ES_RESPOND_RESULT_SUCCESS;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
#ifdef ESRB_MOCK
|
|
666
|
+
static VALUE
|
|
667
|
+
mock_inject(int argc, VALUE *argv, VALUE module)
|
|
668
|
+
{
|
|
669
|
+
(void)module;
|
|
670
|
+
VALUE client_value;
|
|
671
|
+
VALUE options;
|
|
672
|
+
rb_scan_args(argc, argv, "1:", &client_value, &options);
|
|
673
|
+
VALUE event = rb_hash_fetch(options, ID2SYM(rb_intern("event")));
|
|
674
|
+
VALUE auth_value = rb_hash_aref(options, ID2SYM(rb_intern("auth")));
|
|
675
|
+
VALUE action_value = rb_hash_aref(options, ID2SYM(rb_intern("action_type")));
|
|
676
|
+
VALUE deadline_ms = rb_hash_aref(options, ID2SYM(rb_intern("deadline_ms")));
|
|
677
|
+
VALUE version_value = rb_hash_aref(options, ID2SYM(rb_intern("version")));
|
|
678
|
+
VALUE result_type_value = rb_hash_aref(options, ID2SYM(rb_intern("result_type")));
|
|
679
|
+
VALUE result_value = rb_hash_aref(options, ID2SYM(rb_intern("result")));
|
|
680
|
+
VALUE source_es_client = rb_hash_aref(options, ID2SYM(rb_intern("source_es_client")));
|
|
681
|
+
esrb_client_t *client = get_open_client(client_value);
|
|
682
|
+
mach_timebase_info_data_t info;
|
|
683
|
+
mach_timebase_info(&info);
|
|
684
|
+
uint64_t delay = NIL_P(deadline_ms) ? 1000 : NUM2ULL(deadline_ms);
|
|
685
|
+
uint64_t deadline = mach_absolute_time() + delay * 1000000ULL * info.denom / info.numer;
|
|
686
|
+
es_event_type_t event_type = (es_event_type_t)NUM2INT(event);
|
|
687
|
+
size_t event_size = 0;
|
|
688
|
+
for (size_t index = 0; index < esrb_event_schema_count; index++) {
|
|
689
|
+
if (esrb_event_schemas[index].event_type == event_type && esrb_event_schemas[index].indirect) {
|
|
690
|
+
event_size = esrb_event_schemas[index].size;
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
uint32_t version = NIL_P(version_value) ? 4 : NUM2UINT(version_value);
|
|
695
|
+
es_action_type_t action_type = NIL_P(action_value)
|
|
696
|
+
? (RTEST(auth_value) ? ES_ACTION_TYPE_AUTH : ES_ACTION_TYPE_NOTIFY)
|
|
697
|
+
: (es_action_type_t)NUM2INT(action_value);
|
|
698
|
+
es_result_type_t result_type = NIL_P(result_type_value) ? ES_RESULT_TYPE_AUTH : (es_result_type_t)NUM2INT(result_type_value);
|
|
699
|
+
uint32_t result = NIL_P(result_value) ? ES_AUTH_RESULT_ALLOW : NUM2UINT(result_value);
|
|
700
|
+
esmock_inject(
|
|
701
|
+
client->client, event_type, action_type, deadline, version, event_size, result_type, result, RTEST(source_es_client));
|
|
702
|
+
return Qnil;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
static VALUE
|
|
706
|
+
mock_response_count(VALUE module)
|
|
707
|
+
{
|
|
708
|
+
(void)module;
|
|
709
|
+
return SIZET2NUM(esmock_response_count());
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
static VALUE
|
|
713
|
+
mock_last_response(VALUE module)
|
|
714
|
+
{
|
|
715
|
+
(void)module;
|
|
716
|
+
return UINT2NUM(esmock_last_response());
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
static VALUE
|
|
720
|
+
mock_client_count(VALUE module)
|
|
721
|
+
{
|
|
722
|
+
(void)module;
|
|
723
|
+
return SIZET2NUM(esmock_client_count());
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
static VALUE
|
|
727
|
+
mock_delete_on_creator_thread(VALUE module)
|
|
728
|
+
{
|
|
729
|
+
(void)module;
|
|
730
|
+
return esmock_delete_on_creator_thread() ? Qtrue : Qfalse;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
static VALUE
|
|
734
|
+
mock_reset(VALUE module)
|
|
735
|
+
{
|
|
736
|
+
(void)module;
|
|
737
|
+
esmock_reset();
|
|
738
|
+
return Qnil;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
static VALUE
|
|
742
|
+
mock_set_new_client_result(VALUE module, VALUE result)
|
|
743
|
+
{
|
|
744
|
+
(void)module;
|
|
745
|
+
esmock_set_new_client_result((es_new_client_result_t)NUM2INT(result));
|
|
746
|
+
return result;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
static VALUE
|
|
750
|
+
mock_set_respond_result(VALUE module, VALUE result)
|
|
751
|
+
{
|
|
752
|
+
(void)module;
|
|
753
|
+
esmock_set_respond_result((es_respond_result_t)NUM2INT(result));
|
|
754
|
+
return result;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
static VALUE
|
|
758
|
+
mock_set_delete_result(VALUE module, VALUE result)
|
|
759
|
+
{
|
|
760
|
+
(void)module;
|
|
761
|
+
esmock_set_delete_result((es_return_t)NUM2INT(result));
|
|
762
|
+
return result;
|
|
763
|
+
}
|
|
764
|
+
#endif
|
|
765
|
+
|
|
766
|
+
void
|
|
767
|
+
esrb_init_client(VALUE endpoint_security)
|
|
768
|
+
{
|
|
769
|
+
c_client = rb_define_class_under(endpoint_security, "Client", rb_cObject);
|
|
770
|
+
rb_define_alloc_func(c_client, client_allocate);
|
|
771
|
+
rb_define_method(c_client, "initialize", client_initialize, -1);
|
|
772
|
+
rb_define_method(c_client, "close", client_close, 0);
|
|
773
|
+
rb_define_method(c_client, "closed?", client_closed_p, 0);
|
|
774
|
+
rb_define_method(c_client, "__wakeup_fd", client_wakeup_fd, 0);
|
|
775
|
+
rb_define_method(c_client, "__wake", client_wake, 0);
|
|
776
|
+
rb_define_method(c_client, "__drain", client_drain, -1);
|
|
777
|
+
rb_define_method(c_client, "__subscribe", client_subscribe, 1);
|
|
778
|
+
rb_define_method(c_client, "__unsubscribe", client_unsubscribe, 1);
|
|
779
|
+
rb_define_method(c_client, "__subscriptions", client_subscriptions, 0);
|
|
780
|
+
rb_define_method(c_client, "stats", client_stats, 0);
|
|
781
|
+
esrb_init_mute(c_client);
|
|
782
|
+
|
|
783
|
+
#ifdef ESRB_MOCK
|
|
784
|
+
VALUE mock = rb_define_module_under(endpoint_security, "Mock");
|
|
785
|
+
rb_define_singleton_method(mock, "inject", mock_inject, -1);
|
|
786
|
+
rb_define_singleton_method(mock, "response_count", mock_response_count, 0);
|
|
787
|
+
rb_define_singleton_method(mock, "last_response", mock_last_response, 0);
|
|
788
|
+
rb_define_singleton_method(mock, "client_count", mock_client_count, 0);
|
|
789
|
+
rb_define_singleton_method(mock, "delete_on_creator_thread?", mock_delete_on_creator_thread, 0);
|
|
790
|
+
rb_define_singleton_method(mock, "new_client_result=", mock_set_new_client_result, 1);
|
|
791
|
+
rb_define_singleton_method(mock, "respond_result=", mock_set_respond_result, 1);
|
|
792
|
+
rb_define_singleton_method(mock, "delete_result=", mock_set_delete_result, 1);
|
|
793
|
+
rb_define_singleton_method(mock, "reset", mock_reset, 0);
|
|
794
|
+
#endif
|
|
795
|
+
}
|