mosquitto 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.travis.yml +29 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +25 -0
  6. data/LICENSE.md +13 -0
  7. data/README.md +244 -0
  8. data/Rakefile +42 -0
  9. data/TODO.md +19 -0
  10. data/examples/pub_sub.rb +41 -0
  11. data/ext/mosquitto/client.c +2163 -0
  12. data/ext/mosquitto/client.h +145 -0
  13. data/ext/mosquitto/extconf.rb +31 -0
  14. data/ext/mosquitto/message.c +168 -0
  15. data/ext/mosquitto/message.h +16 -0
  16. data/ext/mosquitto/mosquitto_ext.c +88 -0
  17. data/ext/mosquitto/mosquitto_ext.h +35 -0
  18. data/ext/mosquitto/mosquitto_prelude.h +26 -0
  19. data/ext/mosquitto/rubinius.h +6 -0
  20. data/ext/mosquitto/ruby18.h +6 -0
  21. data/ext/mosquitto/ruby19.h +9 -0
  22. data/ext/mosquitto/ruby2.h +6 -0
  23. data/lib/mosquitto.rb +11 -0
  24. data/lib/mosquitto/client.rb +8 -0
  25. data/lib/mosquitto/logging.rb +32 -0
  26. data/lib/mosquitto/version.rb +5 -0
  27. data/mosquitto.gemspec +23 -0
  28. data/test/helper.rb +59 -0
  29. data/test/ssl/all-ca.crt +75 -0
  30. data/test/ssl/client-expired.crt +61 -0
  31. data/test/ssl/client-revoked.crt +61 -0
  32. data/test/ssl/client-revoked.csr +12 -0
  33. data/test/ssl/client-revoked.key +15 -0
  34. data/test/ssl/client.crt +61 -0
  35. data/test/ssl/client.csr +12 -0
  36. data/test/ssl/client.key +15 -0
  37. data/test/ssl/crl.pem +10 -0
  38. data/test/ssl/demoCA/crlnumber +1 -0
  39. data/test/ssl/demoCA/index.txt +1 -0
  40. data/test/ssl/demoCA/index.txt.attr +1 -0
  41. data/test/ssl/demoCA/serial +1 -0
  42. data/test/ssl/gen.sh +70 -0
  43. data/test/ssl/mosquitto.org.crt +18 -0
  44. data/test/ssl/openssl.cnf +406 -0
  45. data/test/ssl/readme.txt +2 -0
  46. data/test/ssl/rootCA/crlnumber +1 -0
  47. data/test/ssl/rootCA/index.txt +2 -0
  48. data/test/ssl/rootCA/index.txt.attr +1 -0
  49. data/test/ssl/rootCA/serial +1 -0
  50. data/test/ssl/server-expired.crt +0 -0
  51. data/test/ssl/server.crt +60 -0
  52. data/test/ssl/server.csr +12 -0
  53. data/test/ssl/server.key +15 -0
  54. data/test/ssl/signingCA/crlnumber +1 -0
  55. data/test/ssl/signingCA/index.txt +4 -0
  56. data/test/ssl/signingCA/index.txt.attr +1 -0
  57. data/test/ssl/signingCA/serial +1 -0
  58. data/test/ssl/test-alt-ca.crt +58 -0
  59. data/test/ssl/test-alt-ca.key +15 -0
  60. data/test/ssl/test-bad-root-ca.crt +17 -0
  61. data/test/ssl/test-bad-root-ca.key +15 -0
  62. data/test/ssl/test-ca.srl +1 -0
  63. data/test/ssl/test-fake-root-ca.crt +17 -0
  64. data/test/ssl/test-fake-root-ca.key +15 -0
  65. data/test/ssl/test-root-ca.crt +17 -0
  66. data/test/ssl/test-root-ca.key +15 -0
  67. data/test/ssl/test-signing-ca.crt +58 -0
  68. data/test/ssl/test-signing-ca.key +15 -0
  69. data/test/test_callbacks.rb +93 -0
  70. data/test/test_client.rb +141 -0
  71. data/test/test_custom_logger.rb +30 -0
  72. data/test/test_integration.rb +572 -0
  73. data/test/test_loops.rb +56 -0
  74. data/test/test_mosquitto.rb +28 -0
  75. data/test/test_pub_sub.rb +51 -0
  76. data/test/test_threads.rb +69 -0
  77. data/test/test_tls.rb +67 -0
  78. metadata +203 -0
@@ -0,0 +1,2163 @@
1
+ #include "mosquitto_ext.h"
2
+
3
+ static void rb_mosquitto_run_callback(mosquitto_callback_t *callback);
4
+
5
+ VALUE mosquitto_tls_password;
6
+
7
+ static int rb_mosquitto_tls_password_callback(char *buf, int size, int rwflag, void *obj)
8
+ {
9
+ strncpy(buf, StringValueCStr(mosquitto_tls_password), size);
10
+ return RSTRING_LEN(mosquitto_tls_password);
11
+ rb_gc_unregister_address(&mosquitto_tls_password);
12
+ }
13
+
14
+ /*
15
+ * :nodoc:
16
+ * Pushes a callback onto the client's callback queue. The callback runs within the context of an event thread.
17
+ *
18
+ */
19
+ static void mosquitto_callback_queue_push(mosquitto_callback_t *cb)
20
+ {
21
+ mosquitto_client_wrapper *client = cb->client;
22
+ cb->next = client->callback_queue;
23
+ client->callback_queue = cb;
24
+ }
25
+
26
+ /*
27
+ * :nodoc:
28
+ * Pops a callback off the client's callback queue. The callback runs within the context of an event thread.
29
+ *
30
+ */
31
+ static mosquitto_callback_t *mosquitto_callback_queue_pop(mosquitto_client_wrapper *client)
32
+ {
33
+ mosquitto_callback_t *cb = client->callback_queue;
34
+ if(cb)
35
+ {
36
+ client->callback_queue = cb->next;
37
+ }
38
+
39
+ return cb;
40
+ }
41
+
42
+ /*
43
+ * :nodoc:
44
+ * Runs without the GIL (Global Interpreter Lock) and polls the client's callback queue for any callbacks
45
+ * to handle.
46
+ *
47
+ * Only applicable to clients that run with the threaded Mosquitto::Client#loop_start event loop
48
+ *
49
+ */
50
+ static void *mosquitto_wait_for_callbacks(void *w)
51
+ {
52
+ mosquitto_callback_waiting_t *waiter = (mosquitto_callback_waiting_t *)w;
53
+ mosquitto_client_wrapper *client = waiter->client;
54
+
55
+ pthread_mutex_lock(&client->callback_mutex);
56
+ while (!waiter->abort && (waiter->callback = mosquitto_callback_queue_pop(client)) == NULL)
57
+ {
58
+ pthread_cond_wait(&client->callback_cond, &client->callback_mutex);
59
+ }
60
+ pthread_mutex_unlock(&client->callback_mutex);
61
+
62
+ return (void *)Qnil;
63
+ }
64
+
65
+ /*
66
+ * :nodoc:
67
+ * Unblocking function for the callback poller - invoked when the event thread should exit.
68
+ *
69
+ * Only applicable to clients that run with the threaded Mosquitto::Client#loop_start event loop
70
+ *
71
+ */
72
+ static void mosquitto_stop_waiting_for_callbacks(void *w)
73
+ {
74
+ mosquitto_callback_waiting_t *waiter = (mosquitto_callback_waiting_t *)w;
75
+ mosquitto_client_wrapper *client = waiter->client;
76
+
77
+ pthread_mutex_lock(&client->callback_mutex);
78
+ waiter->abort = 1;
79
+ pthread_mutex_unlock(&client->callback_mutex);
80
+ pthread_cond_signal(&client->callback_cond);
81
+ }
82
+
83
+ /*
84
+ * :nodoc:
85
+ * Enqueues a callback to be handled by the event thread for a given client.
86
+ *
87
+ * Callbacks for clients that don't use the threaded Mosquitto::Client#loop_start event loop are invoked
88
+ * directly within context of the current Ruby thread. Thus there's no locking overhead and associated
89
+ * cruft.
90
+ *
91
+ */
92
+ static void rb_mosquitto_queue_callback(mosquitto_callback_t *callback)
93
+ {
94
+ mosquitto_client_wrapper *client = callback->client;
95
+ if (!NIL_P(client->callback_thread)) {
96
+ pthread_mutex_lock(&client->callback_mutex);
97
+ mosquitto_callback_queue_push(callback);
98
+ pthread_mutex_unlock(&client->callback_mutex);
99
+ pthread_cond_signal(&client->callback_cond);
100
+ } else {
101
+ rb_mosquitto_run_callback(callback);
102
+ }
103
+ }
104
+
105
+ /*
106
+ * :nodoc:
107
+ * Main callback dispatch method. Invokes callback procedures with variable argument counts. It's expected
108
+ * to raise exceptions - they're handled by a wrapper function.
109
+ *
110
+ */
111
+ static VALUE rb_mosquitto_funcall_protected0(VALUE *args)
112
+ {
113
+ int argc = args[1];
114
+ VALUE proc = args[0];
115
+ if (NIL_P(proc)) MosquittoError("invalid callback");
116
+ if (argc == 1) {
117
+ rb_funcall(proc, intern_call, 1, args[2]);
118
+ } else if (argc == 2) {
119
+ rb_funcall(proc, intern_call, 2, args[2], args[3]);
120
+ } else if (argc == 3) {
121
+ rb_funcall(proc, intern_call, 3, args[2], args[3], args[4]);
122
+ }
123
+ return Qnil;
124
+ }
125
+
126
+ /*
127
+ * :nodoc:
128
+ * Invokes the main callback dispatch method, but tracks error state and allows us to take appropriate action
129
+ * on exception.
130
+ *
131
+ */
132
+ static VALUE rb_mosquitto_funcall_protected(int *error_tag, VALUE *args)
133
+ {
134
+ rb_protect((VALUE(*)(VALUE))rb_mosquitto_funcall_protected0, (VALUE)args, error_tag);
135
+ return Qnil;
136
+ }
137
+
138
+ /*
139
+ * :nodoc:
140
+ * Releases resources allocated for a given callback. Mostly callback specific arguments copied from libmosquitto
141
+ * callback arguments for later processing.
142
+ *
143
+ */
144
+ static void rb_mosquitto_free_callback(mosquitto_callback_t *callback)
145
+ {
146
+ if (callback->type == ON_LOG_CALLBACK) {
147
+ on_log_callback_args_t *args = (on_log_callback_args_t *)callback->data;
148
+ xfree(args->str);
149
+ }
150
+
151
+ xfree(callback->data);
152
+ xfree(callback);
153
+ }
154
+
155
+ /*
156
+ * :nodoc:
157
+ * Our main callback handler within the Ruby VM. I sets up arguments, it's safe to raise exceptions here and coerces
158
+ * C specific callback arguments to Ruby counterparts prior to method dispatch.
159
+ *
160
+ */
161
+ static void rb_mosquitto_handle_callback(int *error_tag, mosquitto_callback_t *callback)
162
+ {
163
+ VALUE args[5];
164
+ mosquitto_client_wrapper *client = callback->client;
165
+ switch (callback->type) {
166
+ case ON_CONNECT_CALLBACK: {
167
+ on_connect_callback_args_t *cb = (on_connect_callback_args_t *)callback->data;
168
+ args[0] = client->connect_cb;
169
+ args[1] = (VALUE)1;
170
+ args[2] = INT2NUM(cb->rc);
171
+ switch (cb->rc) {
172
+ case 1:
173
+ MosquittoError("connection refused (unacceptable protocol version)");
174
+ break;
175
+ case 2:
176
+ MosquittoError("connection refused (identifier rejected)");
177
+ break;
178
+ case 3:
179
+ MosquittoError("connection refused (broker unavailable)");
180
+ break;
181
+ default:
182
+ rb_mosquitto_funcall_protected(error_tag, args);
183
+ }
184
+ }
185
+ break;
186
+
187
+ case ON_DISCONNECT_CALLBACK: {
188
+ on_disconnect_callback_args_t *cb = (on_disconnect_callback_args_t *)callback->data;
189
+ args[0] = client->disconnect_cb;
190
+ args[1] = (VALUE)1;
191
+ args[2] = INT2NUM(cb->rc);
192
+ rb_mosquitto_funcall_protected(error_tag, args);
193
+ }
194
+ break;
195
+
196
+ case ON_PUBLISH_CALLBACK: {
197
+ on_publish_callback_args_t *cb = (on_publish_callback_args_t *)callback->data;
198
+ args[0] = client->publish_cb;
199
+ args[1] = (VALUE)1;
200
+ args[2] = INT2NUM(cb->mid);
201
+ rb_mosquitto_funcall_protected(error_tag, args);
202
+ }
203
+ break;
204
+
205
+ case ON_MESSAGE_CALLBACK: {
206
+ on_message_callback_args_t *cb = (on_message_callback_args_t *)callback->data;
207
+ args[0] = client->message_cb;
208
+ args[1] = (VALUE)1;
209
+ args[2] = rb_mosquitto_message_alloc(cb->msg);
210
+ rb_mosquitto_funcall_protected(error_tag, args);
211
+ }
212
+ break;
213
+
214
+ case ON_SUBSCRIBE_CALLBACK: {
215
+ int i;
216
+ on_subscribe_callback_args_t *cb = (on_subscribe_callback_args_t *)callback->data;
217
+ VALUE granted_qos = rb_ary_new2(cb->qos_count);
218
+ for (i = 0; i < cb->qos_count; i++) {
219
+ rb_ary_push(granted_qos, INT2NUM(cb->granted_qos[i]));
220
+ }
221
+ args[0] = client->subscribe_cb;
222
+ args[1] = (VALUE)2;
223
+ args[2] = INT2NUM(cb->mid);
224
+ args[3] = granted_qos;
225
+ rb_mosquitto_funcall_protected(error_tag, args);
226
+ }
227
+ break;
228
+
229
+ case ON_UNSUBSCRIBE_CALLBACK: {
230
+ on_unsubscribe_callback_args_t *cb = (on_unsubscribe_callback_args_t *)callback->data;
231
+ args[0] = client->unsubscribe_cb;
232
+ args[1] = (VALUE)1;
233
+ args[2] = INT2NUM(cb->mid);
234
+ rb_mosquitto_funcall_protected(error_tag, args);
235
+ }
236
+ break;
237
+
238
+ case ON_LOG_CALLBACK: {
239
+ on_log_callback_args_t *cb = (on_log_callback_args_t *)callback->data;
240
+ args[0] = client->log_cb;
241
+ args[1] = (VALUE)2;
242
+ args[2] = INT2NUM(cb->level);
243
+ args[3] = MosquittoEncode(rb_str_new2(cb->str));
244
+ rb_mosquitto_funcall_protected(error_tag, args);
245
+ }
246
+ break;
247
+ }
248
+ }
249
+
250
+ /*
251
+ * :nodoc:
252
+ * Wrapper function for running callbacks. It respects error / exception status as well as frees any resources
253
+ * allocated specific to callback processing.
254
+ *
255
+ */
256
+ static void rb_mosquitto_run_callback(mosquitto_callback_t *callback)
257
+ {
258
+ int error_tag;
259
+ rb_mosquitto_handle_callback(&error_tag, callback);
260
+ rb_mosquitto_free_callback(callback);
261
+ if (error_tag) rb_jump_tag(error_tag);
262
+ }
263
+
264
+ /*
265
+ * :nodoc:
266
+ * The callback thread - the main workhorse for the threaded Mosquitto::Client#loop_start event loop.
267
+ *
268
+ */
269
+ static VALUE rb_mosquitto_callback_thread(void *obj)
270
+ {
271
+ mosquitto_client_wrapper *client = (mosquitto_client_wrapper *)obj;
272
+ mosquitto_callback_waiting_t waiter;
273
+ waiter.callback = NULL;
274
+ waiter.abort = 0;
275
+ waiter.client = client;
276
+ while (!waiter.abort)
277
+ {
278
+ rb_thread_call_without_gvl(mosquitto_wait_for_callbacks, (void *)&waiter, mosquitto_stop_waiting_for_callbacks, (void *)&waiter);
279
+ if (waiter.callback)
280
+ {
281
+ rb_mosquitto_run_callback(waiter.callback);
282
+ }
283
+ }
284
+
285
+ return Qnil;
286
+ }
287
+
288
+ /*
289
+ * :nodoc:
290
+ * On connect callback - invoked by libmosquitto.
291
+ *
292
+ */
293
+ static void rb_mosquitto_client_on_connect_cb(MOSQ_UNUSED struct mosquitto *mosq, void *obj, int rc)
294
+ {
295
+ mosquitto_callback_t *callback = MOSQ_ALLOC(mosquitto_callback_t);
296
+ callback->type = ON_CONNECT_CALLBACK;
297
+ callback->client = (mosquitto_client_wrapper *)obj;
298
+
299
+ on_connect_callback_args_t *args = MOSQ_ALLOC(on_connect_callback_args_t);
300
+ args->rc = rc;
301
+
302
+ callback->data = (void *)args;
303
+ rb_mosquitto_queue_callback(callback);
304
+ }
305
+
306
+ /*
307
+ * :nodoc:
308
+ * On disconnect callback - invoked by libmosquitto.
309
+ *
310
+ */
311
+ static void rb_mosquitto_client_on_disconnect_cb(MOSQ_UNUSED struct mosquitto *mosq, void *obj, int rc)
312
+ {
313
+ mosquitto_callback_t *callback = MOSQ_ALLOC(mosquitto_callback_t);
314
+ callback->type = ON_DISCONNECT_CALLBACK;
315
+ callback->client = (mosquitto_client_wrapper *)obj;
316
+
317
+ on_disconnect_callback_args_t *args = MOSQ_ALLOC(on_disconnect_callback_args_t);
318
+ args->rc = rc;
319
+
320
+ callback->data = (void *)args;
321
+ rb_mosquitto_queue_callback(callback);
322
+ }
323
+
324
+ /*
325
+ * :nodoc:
326
+ * On publish callback - invoked by libmosquitto.
327
+ *
328
+ */
329
+ static void rb_mosquitto_client_on_publish_cb(MOSQ_UNUSED struct mosquitto *mosq, void *obj, int mid)
330
+ {
331
+ mosquitto_callback_t *callback = MOSQ_ALLOC(mosquitto_callback_t);
332
+ callback->type = ON_PUBLISH_CALLBACK;
333
+ callback->client = (mosquitto_client_wrapper *)obj;
334
+
335
+ on_publish_callback_args_t *args = MOSQ_ALLOC(on_publish_callback_args_t);
336
+ args->mid = mid;
337
+
338
+ callback->data = (void *)args;
339
+ rb_mosquitto_queue_callback(callback);
340
+ }
341
+
342
+ /*
343
+ * :nodoc:
344
+ * On message callback - invoked by libmosquitto.
345
+ *
346
+ */
347
+ static void rb_mosquitto_client_on_message_cb(MOSQ_UNUSED struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)
348
+ {
349
+ mosquitto_callback_t *callback = MOSQ_ALLOC(mosquitto_callback_t);
350
+ callback->type = ON_MESSAGE_CALLBACK;
351
+ callback->client = (mosquitto_client_wrapper *)obj;
352
+
353
+ on_message_callback_args_t *args = MOSQ_ALLOC(on_message_callback_args_t);
354
+ args->msg = MOSQ_ALLOC(struct mosquitto_message);
355
+ mosquitto_message_copy(args->msg, msg);
356
+
357
+ callback->data = (void *)args;
358
+ rb_mosquitto_queue_callback(callback);
359
+ }
360
+
361
+ /*
362
+ * :nodoc:
363
+ * On subscribe callback - invoked by libmosquitto.
364
+ *
365
+ */
366
+ static void rb_mosquitto_client_on_subscribe_cb(MOSQ_UNUSED struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)
367
+ {
368
+ mosquitto_callback_t *callback = MOSQ_ALLOC(mosquitto_callback_t);
369
+ callback->type = ON_SUBSCRIBE_CALLBACK;
370
+ callback->client = (mosquitto_client_wrapper *)obj;
371
+
372
+ on_subscribe_callback_args_t *args = MOSQ_ALLOC(on_subscribe_callback_args_t);
373
+ args->mid = mid;
374
+ args->qos_count = qos_count;
375
+ args->granted_qos = granted_qos;
376
+
377
+ callback->data = (void *)args;
378
+ rb_mosquitto_queue_callback(callback);
379
+ }
380
+
381
+ /*
382
+ * :nodoc:
383
+ * On unsubscribe callback - invoked by libmosquitto.
384
+ *
385
+ */
386
+ static void rb_mosquitto_client_on_unsubscribe_cb(MOSQ_UNUSED struct mosquitto *mosq, void *obj, int mid)
387
+ {
388
+ mosquitto_callback_t *callback = MOSQ_ALLOC(mosquitto_callback_t);
389
+ callback->type = ON_UNSUBSCRIBE_CALLBACK;
390
+ callback->client = (mosquitto_client_wrapper *)obj;
391
+
392
+ on_unsubscribe_callback_args_t *args = MOSQ_ALLOC(on_unsubscribe_callback_args_t);
393
+ args->mid = mid;
394
+
395
+ callback->data = (void *)args;
396
+ rb_mosquitto_queue_callback(callback);
397
+ }
398
+
399
+ /*
400
+ * :nodoc:
401
+ * On log callback - invoked by libmosquitto.
402
+ *
403
+ */
404
+ static void rb_mosquitto_client_on_log_cb(MOSQ_UNUSED struct mosquitto *mosq, void *obj, int level, const char *str)
405
+ {
406
+ mosquitto_callback_t *callback = MOSQ_ALLOC(mosquitto_callback_t);
407
+ callback->type = ON_LOG_CALLBACK;
408
+ callback->client = (mosquitto_client_wrapper *)obj;
409
+
410
+ on_log_callback_args_t *args = MOSQ_ALLOC(on_log_callback_args_t);
411
+ args->level = level;
412
+ args->str = strdup(str);
413
+
414
+ callback->data = (void *)args;
415
+ rb_mosquitto_queue_callback(callback);
416
+ }
417
+
418
+ /*
419
+ * :nodoc:
420
+ * GC callback for Mosquitto::Client objects - invoked during the GC mark phase.
421
+ *
422
+ */
423
+ static void rb_mosquitto_mark_client(void *ptr)
424
+ {
425
+ mosquitto_client_wrapper *client = (mosquitto_client_wrapper *)ptr;
426
+ if (client) {
427
+ rb_gc_mark(client->connect_cb);
428
+ rb_gc_mark(client->disconnect_cb);
429
+ rb_gc_mark(client->publish_cb);
430
+ rb_gc_mark(client->message_cb);
431
+ rb_gc_mark(client->subscribe_cb);
432
+ rb_gc_mark(client->unsubscribe_cb);
433
+ rb_gc_mark(client->log_cb);
434
+ rb_gc_mark(client->callback_thread);
435
+ }
436
+ }
437
+
438
+ /*
439
+ * :nodoc:
440
+ * GC callback for releasing an out of scope Mosquitto::Client object
441
+ *
442
+ */
443
+ static void rb_mosquitto_free_client(void *ptr)
444
+ {
445
+ mosquitto_client_wrapper *client = (mosquitto_client_wrapper *)ptr;
446
+ if (client) {
447
+ mosquitto_destroy(client->mosq);
448
+ xfree(client);
449
+ }
450
+ }
451
+
452
+ /*
453
+ * call-seq:
454
+ * Mosquitto::Client.new("some-id") -> Mosquitto::Client
455
+ *
456
+ * Create a new mosquitto client instance.
457
+ *
458
+ * @param identifier [String] the client identifier. Set to nil to have a random one generated.
459
+ * clean_session must be true if the identifier is nil.
460
+ * @param clean_session [true, false] set to true to instruct the broker to clean all messages
461
+ * and subscriptions on disconnect, false to instruct it to
462
+ * keep them
463
+ * @return [Mosquitto::Client] mosquitto client instance
464
+ * @raise [Mosquitto::Error] on invalid input params
465
+ * @note As per the MQTT spec, client identifiers cannot exceed 23 characters
466
+ * @example
467
+ * Mosquitto::Client.new("session_id") -> Mosquitto::Client
468
+ * Mosquitto::Client.new(nil, true) -> Mosquitto::Client
469
+ *
470
+ */
471
+ static VALUE rb_mosquitto_client_s_new(int argc, VALUE *argv, VALUE client)
472
+ {
473
+ VALUE client_id;
474
+ VALUE cl_session;
475
+ char *cl_id = NULL;
476
+ mosquitto_client_wrapper *cl = NULL;
477
+ bool clean_session;
478
+ rb_scan_args(argc, argv, "02", &client_id, &cl_session);
479
+ if (NIL_P(client_id)) {
480
+ clean_session = true;
481
+ } else {
482
+ clean_session = false;
483
+ Check_Type(client_id, T_STRING);
484
+ MosquittoEncode(client_id);
485
+ cl_id = StringValueCStr(client_id);
486
+ }
487
+ client = Data_Make_Struct(rb_cMosquittoClient, mosquitto_client_wrapper, rb_mosquitto_mark_client, rb_mosquitto_free_client, cl);
488
+ cl->mosq = mosquitto_new(cl_id, clean_session, (void *)cl);
489
+ if (cl->mosq == NULL) {
490
+ xfree(cl);
491
+ switch (errno) {
492
+ case EINVAL:
493
+ MosquittoError("invalid input params");
494
+ break;
495
+ case ENOMEM:
496
+ rb_memerror();
497
+ break;
498
+ default:
499
+ return Qfalse;
500
+ }
501
+ }
502
+ cl->connect_cb = Qnil;
503
+ cl->disconnect_cb = Qnil;
504
+ cl->publish_cb = Qnil;
505
+ cl->message_cb = Qnil;
506
+ cl->subscribe_cb = Qnil;
507
+ cl->unsubscribe_cb = Qnil;
508
+ cl->log_cb = Qnil;
509
+ cl->callback_thread = Qnil;
510
+ cl->callback_queue = NULL;
511
+ rb_obj_call_init(client, 0, NULL);
512
+ return client;
513
+ }
514
+
515
+ static void *rb_mosquitto_client_reinitialise_nogvl(void *ptr)
516
+ {
517
+ struct nogvl_reinitialise_args *args = ptr;
518
+ return (void *)mosquitto_reinitialise(args->mosq, args->client_id, args->clean_session, args->obj);
519
+ }
520
+
521
+ /*
522
+ * call-seq:
523
+ * client.reinitialise("some-id") -> Mosquitto::Client
524
+ *
525
+ * Allows an existing mosquitto client to be reused. Call on a mosquitto instance to close any
526
+ * open network connections, free memory and reinitialise the client with the new parameters.
527
+ *
528
+ * @param identifier [String] the client identifier. Set to nil to have a random one generated.
529
+ * clean_session must be true if the identifier is nil.
530
+ * @param clean_session [true, false] set to true to instruct the broker to clean all messages
531
+ * and subscriptions on disconnect, false to instruct it to
532
+ * keep them
533
+ * @return [Mosquitto::Client] mosquitto client instance
534
+ * @raise [Mosquitto::Error] on invalid input params
535
+ * @note As per the MQTT spec, client identifiers cannot exceed 23 characters
536
+ * @example
537
+ * client.reinitialise("session_id") -> Mosquitto::Client
538
+ * client.reinitialise(nil, true) -> Mosquitto::Client
539
+ *
540
+ */
541
+ static VALUE rb_mosquitto_client_reinitialise(int argc, VALUE *argv, VALUE obj)
542
+ {
543
+ struct nogvl_reinitialise_args args;
544
+ VALUE client_id;
545
+ int ret;
546
+ bool clean_session;
547
+ char *cl_id = NULL;
548
+ MosquittoGetClient(obj);
549
+ rb_scan_args(argc, argv, "01", &client_id);
550
+ if (NIL_P(client_id)) {
551
+ clean_session = true;
552
+ } else {
553
+ clean_session = false;
554
+ Check_Type(client_id, T_STRING);
555
+ MosquittoEncode(client_id);
556
+ cl_id = StringValueCStr(client_id);
557
+ }
558
+ args.mosq = client->mosq;
559
+ args.client_id = cl_id;
560
+ args.clean_session = clean_session;
561
+ args.obj = (void *)obj;
562
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_reinitialise_nogvl, (void *)&args, RUBY_UBF_IO, 0);
563
+ switch (ret) {
564
+ case MOSQ_ERR_INVAL:
565
+ MosquittoError("invalid input params");
566
+ break;
567
+ case MOSQ_ERR_NOMEM:
568
+ rb_memerror();
569
+ break;
570
+ default:
571
+ return Qtrue;
572
+ }
573
+ }
574
+
575
+ /*
576
+ * call-seq:
577
+ * client.will_set("topic", "died", Mosquitto::AT_MOST_ONCE, false) -> Mosquitto::Client
578
+ *
579
+ * Configure will information for a mosquitto instance. By default, clients do not have a will.
580
+ *
581
+ * @param topic [String] the topic on which to publish the will
582
+ * @param payload [String] the message payload. Max 256MB
583
+ * @param qos [Mosquitto::AT_MOST_ONCE, Mosquitto::AT_LEAST_ONCE, Mosquitto::EXACTLY_ONCE] quality
584
+ * of service used for the will
585
+ * @param retain [true, false] set to true to make the will a retained message
586
+ * @return [true] on success
587
+ * @raise [Mosquitto::Error] on invalid input params or a too large payload size
588
+ * @note This must be called before calling Mosquitto::Client#connect
589
+ * @example
590
+ * client.will_set("will_set", "test", Mosquitto::AT_MOST_ONCE, true)
591
+ *
592
+ */
593
+ static VALUE rb_mosquitto_client_will_set(VALUE obj, VALUE topic, VALUE payload, VALUE qos, VALUE retain)
594
+ {
595
+ int ret;
596
+ int payload_len;
597
+ MosquittoGetClient(obj);
598
+ Check_Type(topic, T_STRING);
599
+ MosquittoEncode(topic);
600
+ Check_Type(payload, T_STRING);
601
+ MosquittoEncode(payload);
602
+ Check_Type(qos, T_FIXNUM);
603
+ payload_len = (int)RSTRING_LEN(payload);
604
+ ret = mosquitto_will_set(client->mosq, StringValueCStr(topic), payload_len, (payload_len == 0 ? NULL : StringValueCStr(payload)), NUM2INT(qos), ((retain == Qtrue) ? true : false));
605
+ switch (ret) {
606
+ case MOSQ_ERR_INVAL:
607
+ MosquittoError("invalid input params");
608
+ break;
609
+ case MOSQ_ERR_NOMEM:
610
+ rb_memerror();
611
+ break;
612
+ case MOSQ_ERR_PAYLOAD_SIZE:
613
+ MosquittoError("payload too large");
614
+ break;
615
+ default:
616
+ return Qtrue;
617
+ }
618
+ }
619
+
620
+ /*
621
+ * call-seq:
622
+ * client.will_clear -> Boolean
623
+ *
624
+ * Remove a previously configured will.
625
+ *
626
+ * @return [true] on success
627
+ * @raise [Mosquitto::Error] on invalid input params
628
+ * @note This must be called before calling Mosquitto::Client#connect
629
+ * @example
630
+ * client.will_clear
631
+ *
632
+ */
633
+ static VALUE rb_mosquitto_client_will_clear(VALUE obj)
634
+ {
635
+ int ret;
636
+ MosquittoGetClient(obj);
637
+ ret = mosquitto_will_clear(client->mosq);
638
+ switch (ret) {
639
+ case MOSQ_ERR_INVAL:
640
+ MosquittoError("invalid input params");
641
+ break;
642
+ default:
643
+ return Qtrue;
644
+ }
645
+ }
646
+
647
+ /*
648
+ * call-seq:
649
+ * client.auth("username", "password") -> Boolean
650
+ *
651
+ * Configure username and password for a mosquitto instance. This is only supported by brokers that
652
+ * implement the MQTT spec v3.1. By default, no username or password will be sent.
653
+ *
654
+ * @param username [String] the username to send, or nil to disable authentication.
655
+ * @param password [String] the password to send. Set to nil when username is valid in order to send
656
+ * just a username.
657
+ * @return [true] on success
658
+ * @raise [Mosquitto::Error] on invalid input params
659
+ * @note This must be called before calling Mosquitto::Client#connect
660
+ * @example
661
+ * client.auth("username", "password")
662
+ *
663
+ */
664
+ static VALUE rb_mosquitto_client_auth(VALUE obj, VALUE username, VALUE password)
665
+ {
666
+ int ret;
667
+ MosquittoGetClient(obj);
668
+ if (!NIL_P(username)) {
669
+ Check_Type(username, T_STRING);
670
+ MosquittoEncode(username);
671
+ }
672
+ if (!NIL_P(password)) {
673
+ Check_Type(password, T_STRING);
674
+ MosquittoEncode(password);
675
+ }
676
+ ret = mosquitto_username_pw_set(client->mosq, (NIL_P(username) ? NULL : StringValueCStr(username)), (NIL_P(password) ? NULL : StringValueCStr(password)));
677
+ switch (ret) {
678
+ case MOSQ_ERR_INVAL:
679
+ MosquittoError("invalid input params");
680
+ break;
681
+ case MOSQ_ERR_NOMEM:
682
+ rb_memerror();
683
+ break;
684
+ default:
685
+ return Qtrue;
686
+ }
687
+ }
688
+
689
+ /*
690
+ * call-seq:
691
+ * client.tls_set('/certs/all-ca.crt'), '/certs', '/certs/client.crt'), '/certs/client.key') -> Boolean
692
+ *
693
+ * Configure the client for certificate based SSL/TLS support.
694
+ *
695
+ * Cannot be used in conjunction with Mosquitto::Client#tls_psk_set.
696
+ *
697
+ * Define the Certificate Authority certificates to be trusted (ie. the server
698
+ * certificate must be signed with one of these certificates) using cafile.
699
+ *
700
+ * If the server you are connecting to requires clients to provide a
701
+ * certificate, define certfile and keyfile with your client certificate and
702
+ * private key
703
+ *
704
+ * @param cafile [String] path to a file containing the PEM encoded trusted CA certificate files.
705
+ * Either cafile or capath must not be nil.
706
+ * @param capath [String] path to a directory containing the PEM encoded trusted CA certificate files.
707
+ * Either cafile or capath must not be nil.
708
+ * @param certfile [String] path to a file containing the PEM encoded certificate file for this client.
709
+ * If nil, keyfile must also be nil and no client certificate will be used.
710
+ * @param keyfile [String] path to a file containing the PEM encoded private key for this client. If nil,
711
+ * certfile must also be NULL and no client certificate will be used.
712
+ * @param password [String] password for encrypted keyfile
713
+ * @return [true] on success
714
+ * @raise [Mosquitto::Error] on invalid input params or when TLS is not supported
715
+ * @note This must be called before calling Mosquitto::Client#connect
716
+ * @example
717
+ * client.tls_set('/certs/all-ca.crt'), '/certs', '/certs/client.crt'), '/certs/client.key')
718
+ *
719
+ */
720
+ static VALUE rb_mosquitto_client_tls_set(VALUE obj, VALUE cafile, VALUE capath, VALUE certfile, VALUE keyfile, VALUE password)
721
+ {
722
+ int ret;
723
+ int (*pw_callback)(char *, int, int, void *) = NULL;
724
+ MosquittoGetClient(obj);
725
+ if (!NIL_P(cafile)) {
726
+ Check_Type(cafile, T_STRING);
727
+ MosquittoEncode(cafile);
728
+ }
729
+ if (!NIL_P(capath)) {
730
+ Check_Type(capath, T_STRING);
731
+ MosquittoEncode(capath);
732
+ }
733
+ if (!NIL_P(certfile)) {
734
+ Check_Type(certfile, T_STRING);
735
+ MosquittoEncode(certfile);
736
+ }
737
+ if (!NIL_P(keyfile)) {
738
+ Check_Type(keyfile, T_STRING);
739
+ MosquittoEncode(keyfile);
740
+ }
741
+
742
+ if (!NIL_P(password)) {
743
+ Check_Type(password, T_STRING);
744
+ mosquitto_tls_password = password;
745
+ rb_gc_register_address(&mosquitto_tls_password);
746
+ pw_callback = rb_mosquitto_tls_password_callback;
747
+ }
748
+
749
+ if (NIL_P(cafile) && NIL_P(capath)) MosquittoError("Either CA path or CA file is required!");
750
+ if (NIL_P(certfile) && !NIL_P(keyfile)) MosquittoError("Key file can only be used with a certificate file!");
751
+ if (NIL_P(keyfile) && !NIL_P(certfile)) MosquittoError("Certificate file also requires a key file!");
752
+
753
+ ret = mosquitto_tls_set(client->mosq, (NIL_P(cafile) ? NULL : StringValueCStr(cafile)), (NIL_P(capath) ? NULL : StringValueCStr(capath)), (NIL_P(certfile) ? NULL : StringValueCStr(certfile)), (NIL_P(keyfile) ? NULL : StringValueCStr(keyfile)), pw_callback);
754
+ switch (ret) {
755
+ case MOSQ_ERR_INVAL:
756
+ MosquittoError("invalid input params");
757
+ break;
758
+ case MOSQ_ERR_NOMEM:
759
+ rb_memerror();
760
+ break;
761
+ case MOSQ_ERR_NOT_SUPPORTED:
762
+ MosquittoError("TLS support is not available");
763
+ default:
764
+ return Qtrue;
765
+ }
766
+ }
767
+
768
+ /*
769
+ * call-seq:
770
+ * client.insecure = true -> Boolean
771
+ *
772
+ * Configure verification of the server hostname in the server certificate. If
773
+ * value is set to true, it is impossible to guarantee that the host you are
774
+ * connecting to is not impersonating your server. This can be useful in
775
+ * initial server testing, but makes it possible for a malicious third party to
776
+ * impersonate your server through DNS spoofing, for example.
777
+ * Do not use this function in a real system. Setting value to true makes the
778
+ * connection encryption pointless.
779
+ *
780
+ * @param insecure [true, false] if set to false, the default, certificate hostname checking is
781
+ * performed. If set to true, no hostname checking is performed and
782
+ * the connection is insecure.
783
+ * @return [true] on success
784
+ * @raise [Mosquitto::Error] on invalid input params or when TLS is not supported
785
+ * @note This must be called before calling Mosquitto::Client#connect
786
+ * @example
787
+ * client.insecure = true
788
+ *
789
+ */
790
+ static VALUE rb_mosquitto_client_tls_insecure_set(VALUE obj, VALUE insecure)
791
+ {
792
+ int ret;
793
+ MosquittoGetClient(obj);
794
+ if (insecure != Qtrue && insecure != Qfalse) {
795
+ rb_raise(rb_eTypeError, "changing TLS verification semantics requires a boolean value");
796
+ }
797
+
798
+ ret = mosquitto_tls_insecure_set(client->mosq, ((insecure == Qtrue) ? true : false));
799
+ switch (ret) {
800
+ case MOSQ_ERR_INVAL:
801
+ MosquittoError("invalid input params");
802
+ break;
803
+ case MOSQ_ERR_NOT_SUPPORTED:
804
+ MosquittoError("TLS support is not available");
805
+ default:
806
+ return Qtrue;
807
+ }
808
+ }
809
+
810
+ /*
811
+ * call-seq:
812
+ * client.tls_opts_set(Mosquitto::SSL_VERIFY_PEER, "tlsv1.2", nil) -> Boolean
813
+ *
814
+ * Set advanced SSL/TLS options.
815
+ *
816
+ * @param cert_reqs [Mosquitto::SSL_VERIFY_NONE, Mosquitto::SSL_VERIFY_NONE] an integer defining the verification
817
+ * requirements the client will impose on the server. The default and recommended value is
818
+ * Mosquitto::SSL_VERIFY_PEER. Using Mosquitto::SSL_VERIFY_NONE provides no security.
819
+ * @param tls_version ["tlsv1.2", "tlsv1.1", "tlsv1"] the version of the SSL/TLS protocol to use as a string. If
820
+ nil, the default value is used.
821
+ * @param ciphers [String] a string describing the ciphers available for use. See the `openssl ciphers` tool for
822
+ more information. If nil, the default ciphers will be used.
823
+ * @return [true] on success
824
+ * @raise [Mosquitto::Error] on invalid input params or when TLS is not supported
825
+ * @note This must be called before calling Mosquitto::Client#connect
826
+ * @see `openssl ciphers`
827
+ * @example
828
+ * client.tls_opts_set(Mosquitto::SSL_VERIFY_PEER, "tlsv1.2", nil)
829
+ *
830
+ */
831
+ static VALUE rb_mosquitto_client_tls_opts_set(VALUE obj, VALUE cert_reqs, VALUE tls_version, VALUE ciphers)
832
+ {
833
+ int ret;
834
+ MosquittoGetClient(obj);
835
+ Check_Type(cert_reqs, T_FIXNUM);
836
+ if (!NIL_P(tls_version)) {
837
+ Check_Type(tls_version, T_STRING);
838
+ MosquittoEncode(tls_version);
839
+ }
840
+ if (!NIL_P(ciphers)) {
841
+ Check_Type(ciphers, T_STRING);
842
+ MosquittoEncode(ciphers);
843
+ }
844
+
845
+ if (NUM2INT(cert_reqs) != 0 && NUM2INT(cert_reqs) != 1) {
846
+ MosquittoError("TLS verification requirement should be one of Mosquitto::SSL_VERIFY_NONE or Mosquitto::SSL_VERIFY_PEER");
847
+ }
848
+
849
+ ret = mosquitto_tls_opts_set(client->mosq, NUM2INT(cert_reqs), (NIL_P(tls_version) ? NULL : StringValueCStr(tls_version)), (NIL_P(ciphers) ? NULL : StringValueCStr(ciphers)));
850
+ switch (ret) {
851
+ case MOSQ_ERR_INVAL:
852
+ MosquittoError("invalid input params");
853
+ break;
854
+ case MOSQ_ERR_NOMEM:
855
+ rb_memerror();
856
+ break;
857
+ case MOSQ_ERR_NOT_SUPPORTED:
858
+ MosquittoError("TLS support is not available");
859
+ default:
860
+ return Qtrue;
861
+ }
862
+ }
863
+
864
+ /*
865
+ * call-seq:
866
+ * client.tls_psk_set("deadbeef", "psk-id", nil) -> Boolean
867
+ *
868
+ * Configure the client for pre-shared-key based TLS support.
869
+ *
870
+ * @param psk [String] the pre-shared-key in hex format with no leading "0x".
871
+ * @param identity [String] the identity of this client. May be used as the username depending on the server
872
+ * settings.
873
+ * @param ciphers [String] a string describing the ciphers available for use. See the `openssl ciphers` tool for
874
+ more information. If nil, the default ciphers will be used.
875
+ * @return [true] on success
876
+ * @raise [Mosquitto::Error] on invalid input params or when TLS is not supported
877
+ * @note This must be called before calling Mosquitto::Client#connect
878
+ * @see `openssl ciphers`
879
+ * @example
880
+ * client.tls_psk_set("deadbeef", "psk-id", nil)
881
+ *
882
+ */
883
+ static VALUE rb_mosquitto_client_tls_psk_set(VALUE obj, VALUE psk, VALUE identity, VALUE ciphers)
884
+ {
885
+ int ret;
886
+ MosquittoGetClient(obj);
887
+ Check_Type(psk, T_STRING);
888
+ Check_Type(identity, T_STRING);
889
+ if (!NIL_P(ciphers)) {
890
+ Check_Type(ciphers, T_STRING);
891
+ MosquittoEncode(ciphers);
892
+ }
893
+
894
+ ret = mosquitto_tls_psk_set(client->mosq, StringValueCStr(psk), StringValueCStr(identity), (NIL_P(ciphers) ? NULL : StringValueCStr(ciphers)));
895
+ switch (ret) {
896
+ case MOSQ_ERR_INVAL:
897
+ MosquittoError("invalid input params");
898
+ break;
899
+ case MOSQ_ERR_NOMEM:
900
+ rb_memerror();
901
+ break;
902
+ case MOSQ_ERR_NOT_SUPPORTED:
903
+ MosquittoError("TLS support is not available");
904
+ default:
905
+ return Qtrue;
906
+ }
907
+ }
908
+
909
+ static void *rb_mosquitto_client_connect_nogvl(void *ptr)
910
+ {
911
+ struct nogvl_connect_args *args = ptr;
912
+ return (void *)mosquitto_connect(args->mosq, args->host, args->port, args->keepalive);
913
+ }
914
+
915
+ /*
916
+ * call-seq:
917
+ * client.connect("localhost", 1883, 10) -> Boolean
918
+ *
919
+ * Connect to an MQTT broker.
920
+ *
921
+ * @param host [String] the hostname or ip address of the broker to connect to.
922
+ * @param port [Integer] the network port to connect to. Usually 1883 (or 8883 for TLS)
923
+ * @param keepalive [Integer] the number of seconds after which the broker should send a PING message
924
+ * to the client if no other messages have been exchanged in that time.
925
+ * @return [true] on success
926
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or system call errors
927
+ * @example
928
+ * client.connect("localhost", 1883, 10)
929
+ *
930
+ */
931
+ static VALUE rb_mosquitto_client_connect(VALUE obj, VALUE host, VALUE port, VALUE keepalive)
932
+ {
933
+ struct nogvl_connect_args args;
934
+ int ret;
935
+ MosquittoGetClient(obj);
936
+ Check_Type(host, T_STRING);
937
+ MosquittoEncode(host);
938
+ Check_Type(port, T_FIXNUM);
939
+ Check_Type(keepalive, T_FIXNUM);
940
+ args.mosq = client->mosq;
941
+ args.host = StringValueCStr(host);
942
+ args.port = NUM2INT(port);
943
+ args.keepalive = NUM2INT(keepalive);
944
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_connect_nogvl, (void *)&args, RUBY_UBF_IO, 0);
945
+ switch (ret) {
946
+ case MOSQ_ERR_INVAL:
947
+ MosquittoError("invalid input params");
948
+ break;
949
+ case MOSQ_ERR_ERRNO:
950
+ rb_sys_fail("mosquitto_connect");
951
+ break;
952
+ default:
953
+ return Qtrue;
954
+ }
955
+ }
956
+
957
+ static void *rb_mosquitto_client_connect_bind_nogvl(void *ptr)
958
+ {
959
+ struct nogvl_connect_args *args = ptr;
960
+ return (void *)mosquitto_connect_bind(args->mosq, args->host, args->port, args->keepalive, args->bind_address);
961
+ }
962
+
963
+ /*
964
+ * call-seq:
965
+ * client.connect_bind("localhost", 1883, 10, "10.0.0.3") -> Boolean
966
+ *
967
+ * Connect to an MQTT broker. This extends the functionality of Mosquitto::Client#connect by adding the bind_address
968
+ * parameter. Use this function if you need to restrict network communication over a particular interface.
969
+ *
970
+ * @param host [String] the hostname or ip address of the broker to connect to.
971
+ * @param port [Integer] the network port to connect to. Usually 1883 (or 8883 for TLS)
972
+ * @param keepalive [Integer] the number of seconds after which the broker should send a PING message
973
+ * to the client if no other messages have been exchanged in that time.
974
+ * @param bind_address [String] the hostname or ip address of the local network interface to bind to
975
+ * @return [true] on success
976
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or system call errors
977
+ * @example
978
+ * client.connect_bind("localhost", 1883, 10, "10.0.0.3")
979
+ *
980
+ */
981
+ static VALUE rb_mosquitto_client_connect_bind(VALUE obj, VALUE host, VALUE port, VALUE keepalive, VALUE bind_address)
982
+ {
983
+ struct nogvl_connect_args args;
984
+ int ret;
985
+ MosquittoGetClient(obj);
986
+ Check_Type(host, T_STRING);
987
+ MosquittoEncode(host);
988
+ Check_Type(port, T_FIXNUM);
989
+ Check_Type(keepalive, T_FIXNUM);
990
+ Check_Type(bind_address, T_STRING);
991
+ MosquittoEncode(bind_address);
992
+ args.mosq = client->mosq;
993
+ args.host = StringValueCStr(host);
994
+ args.port = NUM2INT(port);
995
+ args.keepalive = NUM2INT(keepalive);
996
+ args.bind_address = StringValueCStr(bind_address);
997
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_connect_bind_nogvl, (void *)&args, RUBY_UBF_IO, 0);
998
+ switch (ret) {
999
+ case MOSQ_ERR_INVAL:
1000
+ MosquittoError("invalid input params");
1001
+ break;
1002
+ case MOSQ_ERR_ERRNO:
1003
+ rb_sys_fail("mosquitto_connect_bind");
1004
+ break;
1005
+ default:
1006
+ return Qtrue;
1007
+ }
1008
+ }
1009
+
1010
+ static void *rb_mosquitto_client_connect_async_nogvl(void *ptr)
1011
+ {
1012
+ struct nogvl_connect_args *args = ptr;
1013
+ return mosquitto_connect_async(args->mosq, args->host, args->port, args->keepalive);
1014
+ }
1015
+
1016
+ /*
1017
+ * call-seq:
1018
+ * client.connect_async("localhost", 1883, 10) -> Boolean
1019
+ *
1020
+ * Connect to an MQTT broker. This is a non-blocking call. If you use
1021
+ * Mosquitto::Client#connect_async your client must use the threaded interface
1022
+ * Mosquitto::Client#loop_start. If you need to use Mosquitto::Client#loop, you must use
1023
+ * Mosquitto::Client#connect to connect the client.
1024
+ *
1025
+ * @param host [String] the hostname or ip address of the broker to connect to.
1026
+ * @param port [Integer] the network port to connect to. Usually 1883 (or 8883 for TLS)
1027
+ * @param keepalive [Integer] the number of seconds after which the broker should send a PING message
1028
+ * to the client if no other messages have been exchanged in that time.
1029
+ * @return [true] on success
1030
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or system call errors
1031
+ * @example
1032
+ * client.connect_async("localhost", 1883, 10)
1033
+ *
1034
+ */
1035
+ static VALUE rb_mosquitto_client_connect_async(VALUE obj, VALUE host, VALUE port, VALUE keepalive)
1036
+ {
1037
+ struct nogvl_connect_args args;
1038
+ int ret;
1039
+ MosquittoGetClient(obj);
1040
+ Check_Type(host, T_STRING);
1041
+ MosquittoEncode(host);
1042
+ Check_Type(port, T_FIXNUM);
1043
+ Check_Type(keepalive, T_FIXNUM);
1044
+ args.mosq = client->mosq;
1045
+ args.host = StringValueCStr(host);
1046
+ args.port = NUM2INT(port);
1047
+ args.keepalive = NUM2INT(keepalive);
1048
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_connect_async_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1049
+ switch (ret) {
1050
+ case MOSQ_ERR_INVAL:
1051
+ MosquittoError("invalid input params");
1052
+ break;
1053
+ case MOSQ_ERR_ERRNO:
1054
+ rb_sys_fail("mosquitto_connect_async");
1055
+ break;
1056
+ default:
1057
+ return Qtrue;
1058
+ }
1059
+ }
1060
+
1061
+ static void *rb_mosquitto_client_connect_bind_async_nogvl(void *ptr)
1062
+ {
1063
+ struct nogvl_connect_args *args = ptr;
1064
+ return mosquitto_connect_bind_async(args->mosq, args->host, args->port, args->keepalive, args->bind_address);
1065
+ }
1066
+
1067
+ /*
1068
+ * call-seq:
1069
+ * client.connect_bind_async("localhost", 1883, 10, "10.0.0.3") -> Boolean
1070
+ *
1071
+ * Connect to an MQTT broker. This is a non-blocking call. If you use
1072
+ * Mosquitto::Client#connect_async your client must use the threaded interface
1073
+ * Mosquitto::Client#loop_start. If you need to use Mosquitto::Client#loop, you must use
1074
+ * Mosquitto::Client#connect to connect the client.
1075
+ *
1076
+ * This extends the functionality of Mosquitto::Client#connect_async by adding the
1077
+ * bind_address parameter. Use this function if you need to restrict network
1078
+ * communication over a particular interface.
1079
+ *
1080
+ * @param host [String] the hostname or ip address of the broker to connect to.
1081
+ * @param port [Integer] the network port to connect to. Usually 1883 (or 8883 for TLS)
1082
+ * @param keepalive [Integer] the number of seconds after which the broker should send a PING message
1083
+ * to the client if no other messages have been exchanged in that time.
1084
+ * @param bind_address [String] the hostname or ip address of the local network interface to bind to
1085
+ * @return [true] on success
1086
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or system call errors
1087
+ * @example
1088
+ * client.connect_bind_async("localhost", 1883, 10, "10.0.0.3")
1089
+ *
1090
+ */
1091
+ static VALUE rb_mosquitto_client_connect_bind_async(VALUE obj, VALUE host, VALUE port, VALUE keepalive, VALUE bind_address)
1092
+ {
1093
+ struct nogvl_connect_args args;
1094
+ int ret;
1095
+ MosquittoGetClient(obj);
1096
+ Check_Type(host, T_STRING);
1097
+ MosquittoEncode(host);
1098
+ Check_Type(port, T_FIXNUM);
1099
+ Check_Type(keepalive, T_FIXNUM);
1100
+ Check_Type(bind_address, T_STRING);
1101
+ MosquittoEncode(bind_address);
1102
+ args.mosq = client->mosq;
1103
+ args.host = StringValueCStr(host);
1104
+ args.port = NUM2INT(port);
1105
+ args.keepalive = NUM2INT(keepalive);
1106
+ args.bind_address = StringValueCStr(bind_address);
1107
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_connect_bind_async_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1108
+ switch (ret) {
1109
+ case MOSQ_ERR_INVAL:
1110
+ MosquittoError("invalid input params");
1111
+ break;
1112
+ case MOSQ_ERR_ERRNO:
1113
+ rb_sys_fail("mosquitto_connect_bind_async");
1114
+ break;
1115
+ default:
1116
+ return Qtrue;
1117
+ }
1118
+ }
1119
+
1120
+ static void *rb_mosquitto_client_reconnect_nogvl(void *ptr)
1121
+ {
1122
+ return mosquitto_reconnect((struct mosquitto *)ptr);
1123
+ }
1124
+
1125
+ /*
1126
+ * call-seq:
1127
+ * client.reconnect -> Boolean
1128
+ *
1129
+ * Reconnect to a broker.
1130
+ *
1131
+ * This function provides an easy way of reconnecting to a broker after a connection has been lost.
1132
+ * It uses the values that were provided in the Mosquitto::Client#connect call.
1133
+ *
1134
+ * @return [true] on success
1135
+ * @note It must not be called before Mosquitto::Client#connect
1136
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or system call errors
1137
+ * @example
1138
+ * client.reconnect
1139
+ *
1140
+ */
1141
+ static VALUE rb_mosquitto_client_reconnect(VALUE obj)
1142
+ {
1143
+ int ret;
1144
+ MosquittoGetClient(obj);
1145
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_reconnect_nogvl, (void *)client->mosq, RUBY_UBF_IO, 0);
1146
+ switch (ret) {
1147
+ case MOSQ_ERR_INVAL:
1148
+ MosquittoError("invalid input params");
1149
+ break;
1150
+ case MOSQ_ERR_ERRNO:
1151
+ rb_sys_fail("mosquitto_reconnect");
1152
+ break;
1153
+ default:
1154
+ return Qtrue;
1155
+ }
1156
+ }
1157
+
1158
+ static void *rb_mosquitto_client_disconnect_nogvl(void *ptr)
1159
+ {
1160
+ return (VALUE)mosquitto_disconnect((struct mosquitto *)ptr);
1161
+ }
1162
+
1163
+ /*
1164
+ * call-seq:
1165
+ * client.disconnect-> Boolean
1166
+ *
1167
+ * Disconnect from the broker.
1168
+ *
1169
+ * @return [true] on success
1170
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or if the client is not connected
1171
+ * @example
1172
+ * client.disconnect
1173
+ *
1174
+ */
1175
+ static VALUE rb_mosquitto_client_disconnect(VALUE obj)
1176
+ {
1177
+ int ret;
1178
+ MosquittoGetClient(obj);
1179
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_disconnect_nogvl, (void *)client->mosq, RUBY_UBF_IO, 0);
1180
+ switch (ret) {
1181
+ case MOSQ_ERR_INVAL:
1182
+ MosquittoError("invalid input params");
1183
+ break;
1184
+ case MOSQ_ERR_NO_CONN:
1185
+ MosquittoError("client not connected to broker");
1186
+ break;
1187
+ default:
1188
+ return Qtrue;
1189
+ }
1190
+ }
1191
+
1192
+ static void *rb_mosquitto_client_publish_nogvl(void *ptr)
1193
+ {
1194
+ struct nogvl_publish_args *args = ptr;
1195
+ return (VALUE)mosquitto_publish(args->mosq, args->mid, args->topic, args->payloadlen, args->payload, args->qos, args->retain);
1196
+ }
1197
+
1198
+ /*
1199
+ * call-seq:
1200
+ * client.publish(3, "publish", "test", Mosquitto::AT_MOST_ONCE, true) -> Boolean
1201
+ *
1202
+ * Publish a message on a given topic.
1203
+ *
1204
+ * @param mid [Integer, nil] If not nil, the function will set this to the message id of this particular message.
1205
+ * This can be then used with the publish callback to determine when the message has been
1206
+ * sent. Note that although the MQTT protocol doesn't use message ids for messages with
1207
+ * QoS=0, libmosquitto assigns them message ids so they can be tracked with this parameter.
1208
+ * @param payload [String] Message payload to send. Max 256MB
1209
+ * @param qos [Mosquitto::AT_MOST_ONCE, Mosquitto::AT_LEAST_ONCE, Mosquitto::EXACTLY_ONCE] Quality of Service to be
1210
+ * used for the message.
1211
+ * @param retain [true, false] set to true to make the message retained
1212
+ * @return [true] on success
1213
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or system call errors
1214
+ * @example
1215
+ * client.publish(3, "publish", "test", Mosquitto::AT_MOST_ONCE, true)
1216
+ *
1217
+ */
1218
+ static VALUE rb_mosquitto_client_publish(VALUE obj, VALUE mid, VALUE topic, VALUE payload, VALUE qos, VALUE retain)
1219
+ {
1220
+ struct nogvl_publish_args args;
1221
+ int ret, msg_id;
1222
+ MosquittoGetClient(obj);
1223
+ Check_Type(topic, T_STRING);
1224
+ MosquittoEncode(topic);
1225
+ Check_Type(payload, T_STRING);
1226
+ MosquittoEncode(payload);
1227
+ Check_Type(qos, T_FIXNUM);
1228
+ if (!NIL_P(mid)) {
1229
+ Check_Type(mid, T_FIXNUM);
1230
+ msg_id = NUM2INT(mid);
1231
+ }
1232
+ args.mosq = client->mosq;
1233
+ args.mid = NIL_P(mid) ? NULL : &msg_id;
1234
+ args.topic = StringValueCStr(topic);
1235
+ args.payloadlen = (int)RSTRING_LEN(payload);
1236
+ args.payload = (const char *)(args.payloadlen == 0 ? NULL : StringValueCStr(payload));
1237
+ args.qos = NUM2INT(qos);
1238
+ args.retain = (retain == Qtrue) ? true : false;
1239
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_publish_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1240
+ switch (ret) {
1241
+ case MOSQ_ERR_INVAL:
1242
+ MosquittoError("invalid input params");
1243
+ break;
1244
+ case MOSQ_ERR_NOMEM:
1245
+ rb_memerror();
1246
+ break;
1247
+ case MOSQ_ERR_NO_CONN:
1248
+ MosquittoError("client not connected to broker");
1249
+ break;
1250
+ case MOSQ_ERR_PROTOCOL:
1251
+ MosquittoError("protocol error communicating with broker");
1252
+ break;
1253
+ case MOSQ_ERR_PAYLOAD_SIZE:
1254
+ MosquittoError("payload too large");
1255
+ break;
1256
+ default:
1257
+ return Qtrue;
1258
+ }
1259
+ }
1260
+
1261
+ static void *rb_mosquitto_client_subscribe_nogvl(void *ptr)
1262
+ {
1263
+ struct nogvl_subscribe_args *args = ptr;
1264
+ return (VALUE)mosquitto_subscribe(args->mosq, args->mid, args->subscription, args->qos);
1265
+ }
1266
+
1267
+ /*
1268
+ * call-seq:
1269
+ * client.subscribe(3, "subscribe", Mosquitto::AT_MOST_ONCE) -> Boolean
1270
+ *
1271
+ * Subscribe to a topic.
1272
+ *
1273
+ * @param mid [Integer, nil] If not nil, the function will set this to the message id of this particular message.
1274
+ * This can be then used with the subscribe callback to determine when the message has been
1275
+ * sent.
1276
+ * @param subscription [String] The subscription pattern
1277
+ * @param qos [Mosquitto::AT_MOST_ONCE, Mosquitto::AT_LEAST_ONCE, Mosquitto::EXACTLY_ONCE] Quality of Service to be
1278
+ * used for the subscription
1279
+ * @return [true] on success
1280
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or system call errors
1281
+ * @example
1282
+ * client.subscribe(3, "subscribe", Mosquitto::AT_MOST_ONCE)
1283
+ *
1284
+ */
1285
+ static VALUE rb_mosquitto_client_subscribe(VALUE obj, VALUE mid, VALUE subscription, VALUE qos)
1286
+ {
1287
+ struct nogvl_subscribe_args args;
1288
+ int ret, msg_id;
1289
+ MosquittoGetClient(obj);
1290
+ Check_Type(subscription, T_STRING);
1291
+ MosquittoEncode(subscription);
1292
+ Check_Type(qos, T_FIXNUM);
1293
+ if (!NIL_P(mid)) {
1294
+ Check_Type(mid, T_FIXNUM);
1295
+ msg_id = NUM2INT(mid);
1296
+ }
1297
+ args.mosq = client->mosq;
1298
+ args.mid = NIL_P(mid) ? NULL : &msg_id;
1299
+ args.subscription = StringValueCStr(subscription);
1300
+ args.qos = NUM2INT(qos);
1301
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_subscribe_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1302
+ switch (ret) {
1303
+ case MOSQ_ERR_INVAL:
1304
+ MosquittoError("invalid input params");
1305
+ break;
1306
+ case MOSQ_ERR_NOMEM:
1307
+ rb_memerror();
1308
+ break;
1309
+ case MOSQ_ERR_NO_CONN:
1310
+ MosquittoError("client not connected to broker");
1311
+ break;
1312
+ default:
1313
+ return Qtrue;
1314
+ }
1315
+ }
1316
+
1317
+ static void *rb_mosquitto_client_unsubscribe_nogvl(void *ptr)
1318
+ {
1319
+ struct nogvl_subscribe_args *args = ptr;
1320
+ return (VALUE)mosquitto_unsubscribe(args->mosq, args->mid, args->subscription);
1321
+ }
1322
+
1323
+ /*
1324
+ * call-seq:
1325
+ * client.unsubscribe(3, "unsubscribe") -> Boolean
1326
+ *
1327
+ * Unsubscribe from a topic.
1328
+ *
1329
+ * @param mid [Integer, nil] If not nil, the function will set this to the message id of this particular message.
1330
+ * This can be then used with the unsubscribe callback to determine when the message has been
1331
+ * sent.
1332
+ * @param subscription [String] the unsubscription pattern.
1333
+ * @return [true] on success
1334
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or system call errors
1335
+ * @example
1336
+ * client.unsubscribe(3, "unsubscribe")
1337
+ *
1338
+ */
1339
+ static VALUE rb_mosquitto_client_unsubscribe(VALUE obj, VALUE mid, VALUE subscription)
1340
+ {
1341
+ struct nogvl_subscribe_args args;
1342
+ int ret, msg_id;
1343
+ MosquittoGetClient(obj);
1344
+ Check_Type(subscription, T_STRING);
1345
+ MosquittoEncode(subscription);
1346
+ if (!NIL_P(mid)) {
1347
+ Check_Type(mid, T_FIXNUM);
1348
+ msg_id = NUM2INT(mid);
1349
+ }
1350
+ args.mosq = client->mosq;
1351
+ args.mid = NIL_P(mid) ? NULL : &msg_id;
1352
+ args.subscription = StringValueCStr(subscription);
1353
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_unsubscribe_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1354
+ switch (ret) {
1355
+ case MOSQ_ERR_INVAL:
1356
+ MosquittoError("invalid input params");
1357
+ break;
1358
+ case MOSQ_ERR_NOMEM:
1359
+ rb_memerror();
1360
+ break;
1361
+ case MOSQ_ERR_NO_CONN:
1362
+ MosquittoError("client not connected to broker");
1363
+ break;
1364
+ default:
1365
+ return Qtrue;
1366
+ }
1367
+ }
1368
+
1369
+ /*
1370
+ * call-seq:
1371
+ * client.socket -> Integer
1372
+ *
1373
+ * Return the socket handle for a mosquitto instance. Useful if you want to include a mosquitto client in your own
1374
+ * select() calls.
1375
+ *
1376
+ * @return [Integer] socket identifier, or -1 on failure
1377
+ * @example
1378
+ * client.socket
1379
+ *
1380
+ */
1381
+ static VALUE rb_mosquitto_client_socket(VALUE obj)
1382
+ {
1383
+ int socket;
1384
+ MosquittoGetClient(obj);
1385
+ socket = mosquitto_socket(client->mosq);
1386
+ return INT2NUM(socket);
1387
+ }
1388
+
1389
+ static void *rb_mosquitto_client_loop_nogvl(void *ptr)
1390
+ {
1391
+ struct nogvl_loop_args *args = ptr;
1392
+ return (VALUE)mosquitto_loop(args->mosq, args->timeout, args->max_packets);
1393
+ }
1394
+
1395
+ /*
1396
+ * call-seq:
1397
+ * client.loop(10, 10) -> Boolean
1398
+ *
1399
+ * The main network loop for the client. You must call this frequently in order
1400
+ * to keep communications between the client and broker working. If incoming
1401
+ * data is present it will then be processed. Outgoing commands, from e.g.
1402
+ * Mosquitto::Client#publish, are normally sent immediately that their function is
1403
+ * called, but this is not always possible. Mosquitto::Client#loop will also attempt
1404
+ * to send any remaining outgoing messages, which also includes commands that
1405
+ * are part of the flow for messages with QoS>0.
1406
+ *
1407
+ * An alternative approach is to use Mosquitto::Client#loop_start to run the client
1408
+ * loop in its own thread.
1409
+ *
1410
+ * This calls select() to monitor the client network socket. If you want to
1411
+ * integrate mosquitto client operation with your own select() call, use
1412
+ * Mosquitto::Client#socket, Mosquitto::Client#loop_read, Mosquitto::Client#loop_write and
1413
+ * Mosquitto::Client#loop_misc.
1414
+ *
1415
+ * @param timeout [Integer] Maximum number of milliseconds to wait for network activity in the select()
1416
+ * call before timing out. Set to 0 for instant return. Set negative to use the
1417
+ * default of 1000ms
1418
+ * @param max_packets [Integer] this parameter is currently unused and should be set to 1 for future compatibility.
1419
+ * @return [true] on success
1420
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or system call errors
1421
+ * @example
1422
+ * client.loop(10, 10)
1423
+ *
1424
+ */
1425
+ static VALUE rb_mosquitto_client_loop(VALUE obj, VALUE timeout, VALUE max_packets)
1426
+ {
1427
+ struct nogvl_loop_args args;
1428
+ int ret;
1429
+ MosquittoGetClient(obj);
1430
+ Check_Type(timeout, T_FIXNUM);
1431
+ Check_Type(max_packets, T_FIXNUM);
1432
+ args.mosq = client->mosq;
1433
+ args.timeout = NUM2INT(timeout);
1434
+ args.max_packets = NUM2INT(max_packets);
1435
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_loop_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1436
+ switch (ret) {
1437
+ case MOSQ_ERR_INVAL:
1438
+ MosquittoError("invalid input params");
1439
+ break;
1440
+ case MOSQ_ERR_NOMEM:
1441
+ rb_memerror();
1442
+ break;
1443
+ case MOSQ_ERR_NO_CONN:
1444
+ MosquittoError("client not connected to broker");
1445
+ break;
1446
+ case MOSQ_ERR_CONN_LOST:
1447
+ MosquittoError("connection to the broker was lost");
1448
+ break;
1449
+ case MOSQ_ERR_PROTOCOL:
1450
+ MosquittoError("protocol error communicating with the broker");
1451
+ break;
1452
+ case MOSQ_ERR_ERRNO:
1453
+ rb_sys_fail("mosquitto_loop");
1454
+ break;
1455
+ default:
1456
+ return Qtrue;
1457
+ }
1458
+ }
1459
+
1460
+ static void *rb_mosquitto_client_loop_forever_nogvl(void *ptr)
1461
+ {
1462
+ struct nogvl_loop_args *args = ptr;
1463
+ return (VALUE)mosquitto_loop_forever(args->mosq, args->timeout, args->max_packets);
1464
+ }
1465
+
1466
+ static void rb_mosquitto_client_loop_forever_ubf(void *ptr)
1467
+ {
1468
+ mosquitto_client_wrapper *client = (mosquitto_client_wrapper *)ptr;
1469
+ mosquitto_disconnect(client->mosq);
1470
+ }
1471
+
1472
+ /*
1473
+ * call-seq:
1474
+ * client.loop_forever(10, 1) -> Boolean
1475
+ *
1476
+ * This function calls Mosquitto::Client#loop for you in an infinite blocking loop. It is useful
1477
+ * for the case where you only want to run the MQTT client loop in your program.
1478
+ *
1479
+ * It handles reconnecting in case server connection is lost. If you call Mosquitto::Client#disconnect in
1480
+ * a callback it will return.
1481
+ *
1482
+ * @param timeout [Integer] Maximum number of milliseconds to wait for network activity in the select()
1483
+ * call before timing out. Set to 0 for instant return. Set negative to use the
1484
+ * default of 1000ms
1485
+ * @param max_packets [Integer] this parameter is currently unused and should be set to 1 for future compatibility.
1486
+ * @return [true] on success
1487
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or system call errors
1488
+ * @example
1489
+ * client.loop_forever(10, 1)
1490
+ *
1491
+ */
1492
+ static VALUE rb_mosquitto_client_loop_forever(VALUE obj, VALUE timeout, VALUE max_packets)
1493
+ {
1494
+ struct nogvl_loop_args args;
1495
+ int ret;
1496
+ MosquittoGetClient(obj);
1497
+ Check_Type(timeout, T_FIXNUM);
1498
+ Check_Type(max_packets, T_FIXNUM);
1499
+ args.mosq = client->mosq;
1500
+ args.timeout = NUM2INT(timeout);
1501
+ args.max_packets = NUM2INT(max_packets);
1502
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_loop_forever_nogvl, (void *)&args, rb_mosquitto_client_loop_forever_ubf, client);
1503
+ switch (ret) {
1504
+ case MOSQ_ERR_INVAL:
1505
+ MosquittoError("invalid input params");
1506
+ break;
1507
+ case MOSQ_ERR_NOMEM:
1508
+ rb_memerror();
1509
+ break;
1510
+ case MOSQ_ERR_NO_CONN:
1511
+ MosquittoError("client not connected to broker");
1512
+ break;
1513
+ case MOSQ_ERR_CONN_LOST:
1514
+ MosquittoError("connection to the broker was lost");
1515
+ break;
1516
+ case MOSQ_ERR_PROTOCOL:
1517
+ MosquittoError("protocol error communicating with the broker");
1518
+ break;
1519
+ case MOSQ_ERR_ERRNO:
1520
+ rb_sys_fail("mosquitto_loop");
1521
+ break;
1522
+ default:
1523
+ return Qtrue;
1524
+ }
1525
+ }
1526
+
1527
+ static void *rb_mosquitto_client_loop_start_nogvl(void *ptr)
1528
+ {
1529
+ return (VALUE)mosquitto_loop_start((struct mosquitto *)ptr);
1530
+ }
1531
+
1532
+ /*
1533
+ * call-seq:
1534
+ * client.loop_start -> Boolean
1535
+ *
1536
+ * This is part of the threaded client interface. Call this once to start a new
1537
+ * thread to process network traffic. This provides an alternative to repeatedly calling
1538
+ * Mosquitto::Client#loop yourself.
1539
+ *
1540
+ * @return [true] on success
1541
+ * @raise [Mosquitto::Error] on invalid input params or if thread support is not available
1542
+ * @example
1543
+ * client.loop_start
1544
+ *
1545
+ */
1546
+ static VALUE rb_mosquitto_client_loop_start(VALUE obj)
1547
+ {
1548
+ int ret;
1549
+ struct timeval time;
1550
+ MosquittoGetClient(obj);
1551
+ /* Let's not spawn duplicate threaded loops */
1552
+ if (!NIL_P(client->callback_thread)) return Qtrue;
1553
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_loop_start_nogvl, (void *)client->mosq, RUBY_UBF_IO, 0);
1554
+ switch (ret) {
1555
+ case MOSQ_ERR_INVAL:
1556
+ MosquittoError("invalid input params");
1557
+ break;
1558
+ case MOSQ_ERR_NOT_SUPPORTED :
1559
+ MosquittoError("thread support is not available");
1560
+ break;
1561
+ default:
1562
+ pthread_mutex_init(&client->callback_mutex, NULL);
1563
+ pthread_cond_init(&client->callback_cond, NULL);
1564
+ client->callback_thread = rb_thread_create(rb_mosquitto_callback_thread, client);
1565
+ /* Allow the callback thread some startup time */
1566
+ time.tv_sec = 0;
1567
+ time.tv_usec = 100 * 1000; /* 0.1 sec */
1568
+ rb_thread_wait_for(time);
1569
+ return Qtrue;
1570
+ }
1571
+ }
1572
+
1573
+ static void *rb_mosquitto_client_loop_stop_nogvl(void *ptr)
1574
+ {
1575
+ struct nogvl_loop_stop_args *args = ptr;
1576
+ return (VALUE)mosquitto_loop_stop(args->mosq, args->force);
1577
+ }
1578
+
1579
+ /*
1580
+ * call-seq:
1581
+ * client.loop_start -> Boolean
1582
+ *
1583
+ * This is part of the threaded client interface. Call this once to stop the
1584
+ * network thread previously created with Mosquitto::Client#loop_start. This call
1585
+ * will block until the network thread finishes. For the network thread to end,
1586
+ * you must have previously called Mosquitto::Client#disconnect or have set the force
1587
+ * parameter to true.
1588
+ *
1589
+ * @param force [Boolean] set to true to force thread cancellation. If false, Mosquitto::Client#disconnect
1590
+ * must have already been called.
1591
+ * @return [true] on success
1592
+ * @raise [Mosquitto::Error] on invalid input params or if thread support is not available
1593
+ * @example
1594
+ * client.loop_start
1595
+ *
1596
+ */
1597
+ static VALUE rb_mosquitto_client_loop_stop(VALUE obj, VALUE force)
1598
+ {
1599
+ struct nogvl_loop_stop_args args;
1600
+ int ret;
1601
+ MosquittoGetClient(obj);
1602
+ args.mosq = client->mosq;
1603
+ args.force = ((force == Qtrue) ? true : false);
1604
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_loop_stop_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1605
+ switch (ret) {
1606
+ case MOSQ_ERR_INVAL:
1607
+ MosquittoError("invalid input params");
1608
+ break;
1609
+ case MOSQ_ERR_NOT_SUPPORTED :
1610
+ MosquittoError("thread support is not available");
1611
+ break;
1612
+ default:
1613
+ pthread_mutex_destroy(&client->callback_mutex);
1614
+ pthread_cond_destroy(&client->callback_cond);
1615
+ rb_thread_kill(client->callback_thread);
1616
+ client->callback_thread = Qnil;
1617
+ return Qtrue;
1618
+ }
1619
+ }
1620
+
1621
+ static void *rb_mosquitto_client_loop_read_nogvl(void *ptr)
1622
+ {
1623
+ struct nogvl_loop_args *args = ptr;
1624
+ return (VALUE)mosquitto_loop_read(args->mosq, args->max_packets);
1625
+ }
1626
+
1627
+ /*
1628
+ * call-seq:
1629
+ * client.loop_read(10) -> Boolean
1630
+ *
1631
+ * Carry out network read operations. This should only be used if you are not using Mosquitto::Client#loop and
1632
+ * are monitoring the client network socket for activity yourself.
1633
+ *
1634
+ * @param max_packets [Integer] this parameter is currently unused and should be set to 1 for
1635
+ * future compatibility.
1636
+ * @return [true] on success
1637
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or system call errors
1638
+ * @example
1639
+ * client.loop_read(10)
1640
+ *
1641
+ */
1642
+ static VALUE rb_mosquitto_client_loop_read(VALUE obj, VALUE max_packets)
1643
+ {
1644
+ struct nogvl_loop_args args;
1645
+ int ret;
1646
+ MosquittoGetClient(obj);
1647
+ Check_Type(max_packets, T_FIXNUM);
1648
+ args.mosq = client->mosq;
1649
+ args.max_packets = NUM2INT(max_packets);
1650
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_loop_read_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1651
+ switch (ret) {
1652
+ case MOSQ_ERR_INVAL:
1653
+ MosquittoError("invalid input params");
1654
+ break;
1655
+ case MOSQ_ERR_NOMEM:
1656
+ rb_memerror();
1657
+ break;
1658
+ case MOSQ_ERR_NO_CONN:
1659
+ MosquittoError("client not connected to broker");
1660
+ break;
1661
+ case MOSQ_ERR_CONN_LOST:
1662
+ MosquittoError("connection to the broker was lost");
1663
+ break;
1664
+ case MOSQ_ERR_PROTOCOL:
1665
+ MosquittoError("protocol error communicating with the broker");
1666
+ break;
1667
+ case MOSQ_ERR_ERRNO:
1668
+ rb_sys_fail("mosquitto_loop");
1669
+ break;
1670
+ default:
1671
+ return Qtrue;
1672
+ }
1673
+ }
1674
+
1675
+ static void *rb_mosquitto_client_loop_write_nogvl(void *ptr)
1676
+ {
1677
+ struct nogvl_loop_args *args = ptr;
1678
+ return (VALUE)mosquitto_loop_write(args->mosq, args->max_packets);
1679
+ }
1680
+
1681
+ /*
1682
+ * call-seq:
1683
+ * client.loop_write(1) -> Boolean
1684
+ *
1685
+ * Carry out network write operations. This should only be used if you are not using Mosquitto::Client#loop and
1686
+ * are monitoring the client network socket for activity yourself.
1687
+ *
1688
+ * @param max_packets [Integer] this parameter is currently unused and should be set to 1 for
1689
+ * future compatibility.
1690
+ * @return [true] on success
1691
+ * @raise [Mosquitto::Error, SystemCallError] on invalid input params or system call errors
1692
+ * @example
1693
+ * client.loop_write(1)
1694
+ *
1695
+ */
1696
+ static VALUE rb_mosquitto_client_loop_write(VALUE obj, VALUE max_packets)
1697
+ {
1698
+ struct nogvl_loop_args args;
1699
+ int ret;
1700
+ MosquittoGetClient(obj);
1701
+ Check_Type(max_packets, T_FIXNUM);
1702
+ args.mosq = client->mosq;
1703
+ args.max_packets = NUM2INT(max_packets);
1704
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_loop_write_nogvl, (void *)&args, RUBY_UBF_IO, 0);
1705
+ switch (ret) {
1706
+ case MOSQ_ERR_INVAL:
1707
+ MosquittoError("invalid input params");
1708
+ break;
1709
+ case MOSQ_ERR_NOMEM:
1710
+ rb_memerror();
1711
+ break;
1712
+ case MOSQ_ERR_NO_CONN:
1713
+ MosquittoError("client not connected to broker");
1714
+ break;
1715
+ case MOSQ_ERR_CONN_LOST:
1716
+ MosquittoError("connection to the broker was lost");
1717
+ break;
1718
+ case MOSQ_ERR_PROTOCOL:
1719
+ MosquittoError("protocol error communicating with the broker");
1720
+ break;
1721
+ case MOSQ_ERR_ERRNO:
1722
+ rb_sys_fail("mosquitto_loop");
1723
+ break;
1724
+ default:
1725
+ return Qtrue;
1726
+ }
1727
+ }
1728
+
1729
+ static void *rb_mosquitto_client_loop_misc_nogvl(void *ptr)
1730
+ {
1731
+ return (VALUE)mosquitto_loop_misc((struct mosquitto *)ptr);
1732
+ }
1733
+
1734
+ /*
1735
+ * call-seq:
1736
+ * client.loop_misc -> Boolean
1737
+ *
1738
+ * Carry out miscellaneous operations required as part of the network loop.
1739
+ * This should only be used if you are not using Mosquitto::Client#loop and are
1740
+ * monitoring the client network socket for activity yourself.
1741
+ *
1742
+ * This function deals with handling PINGs and checking whether messages need
1743
+ * to be retried, so should be called fairly frequently.
1744
+ *
1745
+ * @return [true] on success
1746
+ * @raise [Mosquitto::Error] on invalid input params or when not connected to the broker
1747
+ * @example
1748
+ * client.loop_misc
1749
+ *
1750
+ */
1751
+ static VALUE rb_mosquitto_client_loop_misc(VALUE obj)
1752
+ {
1753
+ int ret;
1754
+ MosquittoGetClient(obj);
1755
+ ret = (int)rb_thread_call_without_gvl(rb_mosquitto_client_loop_misc_nogvl, (void *)client->mosq, RUBY_UBF_IO, 0);
1756
+ switch (ret) {
1757
+ case MOSQ_ERR_INVAL:
1758
+ MosquittoError("invalid input params");
1759
+ break;
1760
+ case MOSQ_ERR_NO_CONN:
1761
+ MosquittoError("client not connected to broker");
1762
+ break;
1763
+ default:
1764
+ return Qtrue;
1765
+ }
1766
+ }
1767
+
1768
+ /*
1769
+ * call-seq:
1770
+ * client.want_write? -> Boolean
1771
+ *
1772
+ * Returns true if there is data ready to be written on the socket.
1773
+ *
1774
+ * @return [true, false] true if there is data ready to be written on the socket
1775
+ * @example
1776
+ * client.want_write
1777
+ *
1778
+ */
1779
+ static VALUE rb_mosquitto_client_want_write(VALUE obj)
1780
+ {
1781
+ bool ret;
1782
+ MosquittoGetClient(obj);
1783
+ ret = mosquitto_want_write(client->mosq);
1784
+ return (ret == true) ? Qtrue : Qfalse;
1785
+ }
1786
+
1787
+ /*
1788
+ * call-seq:
1789
+ * client.reconnect_delay_set(2, 10, true) -> Boolean
1790
+ *
1791
+ * Control the behaviour of the client when it has unexpectedly disconnected in
1792
+ * Mosquitto::Client#loop_forever or after Mosquitto::Client#loop_start. The default
1793
+ * behaviour if this function is not used is to repeatedly attempt to reconnect
1794
+ * with a delay of 1 second until the connection succeeds.
1795
+ *
1796
+ * Use reconnect_delay parameter to change the delay between successive
1797
+ * reconnection attempts. You may also enable exponential backoff of the time
1798
+ * between reconnections by setting reconnect_exponential_backoff to true and
1799
+ * set an upper bound on the delay with reconnect_delay_max.
1800
+ *
1801
+ * Example 1:
1802
+ * delay=2, delay_max=10, exponential_backoff=False
1803
+ * Delays would be: 2, 4, 6, 8, 10, 10, ...
1804
+ *
1805
+ * Example 2:
1806
+ * delay=3, delay_max=30, exponential_backoff=True
1807
+ * Delays would be: 3, 6, 12, 24, 30, 30, ...
1808
+ *
1809
+ * @param delay [Integer] the number of seconds to wait between reconnects
1810
+ * @param delay_max [Integer] the maximum number of seconds to wait between reconnects
1811
+ * @param exponential_backoff [true, false] use exponential backoff between reconnect attempts.
1812
+ Set to true to enable exponential backoff.
1813
+ * @return [true] on success
1814
+ * @raise [Mosquitto::Error] on invalid input params
1815
+ * @example
1816
+ * client.reconnect_delay_set(2, 10, true)
1817
+ *
1818
+ */
1819
+ static VALUE rb_mosquitto_client_reconnect_delay_set(VALUE obj, VALUE delay, VALUE delay_max, VALUE exp_backoff)
1820
+ {
1821
+ int ret;
1822
+ MosquittoGetClient(obj);
1823
+ Check_Type(delay, T_FIXNUM);
1824
+ Check_Type(delay_max, T_FIXNUM);
1825
+ ret = mosquitto_reconnect_delay_set(client->mosq, INT2NUM(delay), INT2NUM(delay_max), ((exp_backoff == Qtrue) ? true : false));
1826
+ switch (ret) {
1827
+ case MOSQ_ERR_INVAL:
1828
+ MosquittoError("invalid input params");
1829
+ break;
1830
+ default:
1831
+ return Qtrue;
1832
+ }
1833
+ }
1834
+
1835
+ /*
1836
+ * call-seq:
1837
+ * client.max_inflight_messages = 10 -> Boolean
1838
+ *
1839
+ * Set the number of QoS 1 and 2 messages that can be "in flight" at one time.
1840
+ * An in flight message is part way through its delivery flow. Attempts to send
1841
+ * further messages with mosquitto::Client#publish will result in the messages being
1842
+ * queued until the number of in flight messages reduces.
1843
+ *
1844
+ * A higher number here results in greater message throughput, but if set
1845
+ * higher than the maximum in flight messages on the broker may lead to
1846
+ * delays in the messages being acknowledged.
1847
+ *
1848
+ * Set to 0 for no maximum.
1849
+ *
1850
+ * @param max_messages [Integer] the maximum number of inflight messages. Defaults to 20.
1851
+ * @return [true] on success
1852
+ * @raise [Mosquitto::Error] on invalid input params
1853
+ * @example
1854
+ * client.max_inflight_messages = 10
1855
+ *
1856
+ */
1857
+ static VALUE rb_mosquitto_client_max_inflight_messages_equals(VALUE obj, VALUE max_messages)
1858
+ {
1859
+ int ret;
1860
+ MosquittoGetClient(obj);
1861
+ Check_Type(max_messages, T_FIXNUM);
1862
+ ret = mosquitto_max_inflight_messages_set(client->mosq, INT2NUM(max_messages));
1863
+ switch (ret) {
1864
+ case MOSQ_ERR_INVAL:
1865
+ MosquittoError("invalid input params");
1866
+ break;
1867
+ default:
1868
+ return Qtrue;
1869
+ }
1870
+ }
1871
+
1872
+ /*
1873
+ * call-seq:
1874
+ * client.message_retry = 10 -> Boolean
1875
+ *
1876
+ * Set the number of seconds to wait before retrying messages. This applies to
1877
+ * publish messages with QoS>0. May be called at any time.
1878
+ *
1879
+ * @param message_retry [Integer] the number of seconds to wait for a response before retrying. Defaults to 20.
1880
+ * @return [true] on success
1881
+ * @raise [Mosquitto::Error] on invalid input params
1882
+ * @example
1883
+ * client.message_retry = 10
1884
+ *
1885
+ */
1886
+ static VALUE rb_mosquitto_client_message_retry_equals(VALUE obj, VALUE seconds)
1887
+ {
1888
+ MosquittoGetClient(obj);
1889
+ Check_Type(seconds, T_FIXNUM);
1890
+ mosquitto_message_retry_set(client->mosq, INT2NUM(seconds));
1891
+ return Qtrue;
1892
+ }
1893
+
1894
+ /*
1895
+ * call-seq:
1896
+ * client.on_connect{|rc| p :connected } -> Boolean
1897
+ *
1898
+ * Set the connect callback. This is called when the broker sends a CONNACK
1899
+ * message in response to a connection.
1900
+ *
1901
+ * @yield connect callback
1902
+ * @yieldparam rc [Integer] the return code of the connection response, one of: 0 - success,
1903
+ * 1 - connection refused (unacceptable protocol version),
1904
+ * 2 - connection refused (identifier rejected)
1905
+ * 3 - connection refused (broker unavailable)
1906
+ * @return [true] on success
1907
+ * @raise [TypeError, ArgumentError] if callback is not a Proc or if the method arity is wrong
1908
+ * @example
1909
+ * client.on_connect{|rc| p :connected }
1910
+ *
1911
+ */
1912
+ static VALUE rb_mosquitto_client_on_connect(int argc, VALUE *argv, VALUE obj)
1913
+ {
1914
+ VALUE proc, cb;
1915
+ MosquittoGetClient(obj);
1916
+ rb_scan_args(argc, argv, "01&", &proc, &cb);
1917
+ MosquittoAssertCallback(cb, 1);
1918
+ if (!NIL_P(client->connect_cb)) rb_gc_unregister_address(&client->connect_cb);
1919
+ mosquitto_connect_callback_set(client->mosq, rb_mosquitto_client_on_connect_cb);
1920
+ client->connect_cb = cb;
1921
+ rb_gc_register_address(&client->connect_cb);
1922
+ return Qtrue;
1923
+ }
1924
+
1925
+ /*
1926
+ * call-seq:
1927
+ * client.on_disconnect{|rc| p :disconnected } -> Boolean
1928
+ *
1929
+ * Set the disconnect callback. This is called when the broker has received the
1930
+ * DISCONNECT command and has disconnected the client.
1931
+ *
1932
+ * @yield disconnect callback
1933
+ * @yieldparam rc [Integer] integer value indicating the reason for the disconnect. A value of 0
1934
+ * means the client has called Mosquitto::Client#disconnect. Any other value indicates that
1935
+ * the disconnect is unexpected.
1936
+ * @return [true] on success
1937
+ * @raise [TypeError, ArgumentError] if callback is not a Proc or if the method arity is wrong
1938
+ * @example
1939
+ * client.on_disconnect{|rc| p :disconnected }
1940
+ *
1941
+ */
1942
+ static VALUE rb_mosquitto_client_on_disconnect(int argc, VALUE *argv, VALUE obj)
1943
+ {
1944
+ VALUE proc, cb;
1945
+ MosquittoGetClient(obj);
1946
+ rb_scan_args(argc, argv, "01&", &proc, &cb);
1947
+ MosquittoAssertCallback(cb, 1);
1948
+ if (!NIL_P(client->disconnect_cb)) rb_gc_unregister_address(&client->disconnect_cb);
1949
+ mosquitto_disconnect_callback_set(client->mosq, rb_mosquitto_client_on_disconnect_cb);
1950
+ client->disconnect_cb = cb;
1951
+ rb_gc_register_address(&client->disconnect_cb);
1952
+ return Qtrue;
1953
+ }
1954
+
1955
+ /*
1956
+ * call-seq:
1957
+ * client.on_publish{|mid| p :published } -> Boolean
1958
+ *
1959
+ * Set the publish callback. This is called when a message initiated with
1960
+ * Mosquitto::Client#publish has been sent to the broker successfully.
1961
+ *
1962
+ * @yield publish callback
1963
+ * @yieldparam mid [Integer] the message id of the sent message
1964
+ * @return [true] on success
1965
+ * @raise [TypeError, ArgumentError] if callback is not a Proc or if the method arity is wrong
1966
+ * @example
1967
+ * client.on_publish{|mid| p :published }
1968
+ *
1969
+ */
1970
+ static VALUE rb_mosquitto_client_on_publish(int argc, VALUE *argv, VALUE obj)
1971
+ {
1972
+ VALUE proc, cb;
1973
+ MosquittoGetClient(obj);
1974
+ rb_scan_args(argc, argv, "01&", &proc, &cb);
1975
+ MosquittoAssertCallback(cb, 1);
1976
+ if (!NIL_P(client->publish_cb)) rb_gc_unregister_address(&client->publish_cb);
1977
+ mosquitto_publish_callback_set(client->mosq, rb_mosquitto_client_on_publish_cb);
1978
+ client->publish_cb = cb;
1979
+ rb_gc_register_address(&client->publish_cb);
1980
+ return Qtrue;
1981
+ }
1982
+
1983
+ /*
1984
+ * call-seq:
1985
+ * client.on_message{|msg| p msg } -> Boolean
1986
+ *
1987
+ * Set the message callback. This is called when a message is received from the
1988
+ * broker.
1989
+ *
1990
+ * @yield message callback
1991
+ * @yieldparam msg [Mosquitto::Message] the message data
1992
+ * @return [true] on success
1993
+ * @raise [TypeError, ArgumentError] if callback is not a Proc or if the method arity is wrong
1994
+ * @example
1995
+ * client.on_message{|msg| p msg }
1996
+ *
1997
+ */
1998
+ static VALUE rb_mosquitto_client_on_message(int argc, VALUE *argv, VALUE obj)
1999
+ {
2000
+ VALUE proc, cb;
2001
+ MosquittoGetClient(obj);
2002
+ rb_scan_args(argc, argv, "01&", &proc, &cb);
2003
+ MosquittoAssertCallback(cb, 1);
2004
+ if (!NIL_P(client->message_cb)) rb_gc_unregister_address(&client->message_cb);
2005
+ mosquitto_message_callback_set(client->mosq, rb_mosquitto_client_on_message_cb);
2006
+ client->message_cb = cb;
2007
+ rb_gc_register_address(&client->message_cb);
2008
+ return Qtrue;
2009
+ }
2010
+
2011
+ /*
2012
+ * call-seq:
2013
+ * client.on_subscribe{|mid, granted_qos| p :subscribed } -> Boolean
2014
+ *
2015
+ * Set the subscribe callback. This is called when the broker responds to a
2016
+ * subscription request.
2017
+ *
2018
+ * @yield subscription callback
2019
+ * @yieldparam mid [Integer] the message id of the subscribe message.
2020
+ * @yieldparam granted_qos [Array] an array of integers indicating the granted QoS for each of
2021
+ * the subscriptions.
2022
+ * @return [true] on success
2023
+ * @raise [TypeError, ArgumentError] if callback is not a Proc or if the method arity is wrong
2024
+ * @example
2025
+ * client.on_subscribe{|mid, granted_qos| p :subscribed }
2026
+ *
2027
+ */
2028
+ static VALUE rb_mosquitto_client_on_subscribe(int argc, VALUE *argv, VALUE obj)
2029
+ {
2030
+ VALUE proc, cb;
2031
+ MosquittoGetClient(obj);
2032
+ rb_scan_args(argc, argv, "01&", &proc, &cb);
2033
+ MosquittoAssertCallback(cb, 2);
2034
+ if (!NIL_P(client->subscribe_cb)) rb_gc_unregister_address(&client->subscribe_cb);
2035
+ mosquitto_subscribe_callback_set(client->mosq, rb_mosquitto_client_on_subscribe_cb);
2036
+ client->subscribe_cb = cb;
2037
+ rb_gc_register_address(&client->subscribe_cb);
2038
+ return Qtrue;
2039
+ }
2040
+
2041
+ /*
2042
+ * call-seq:
2043
+ * client.on_unsubscribe{|mid| p :unsubscribed } -> Boolean
2044
+ *
2045
+ * Set the unsubscribe callback. This is called when the broker responds to a
2046
+ * unsubscription request.
2047
+ *
2048
+ * @yield unsubscribe callback
2049
+ * @yieldparam mid [Integer] the message id of the unsubscribe message.
2050
+
2051
+ * @return [true] on success
2052
+ * @raise [TypeError, ArgumentError] if callback is not a Proc or if the method arity is wrong
2053
+ * @example
2054
+ * client.on_unsubscribe{|mid| p :unsubscribed }
2055
+ *
2056
+ */
2057
+ static VALUE rb_mosquitto_client_on_unsubscribe(int argc, VALUE *argv, VALUE obj)
2058
+ {
2059
+ VALUE proc, cb;
2060
+ MosquittoGetClient(obj);
2061
+ rb_scan_args(argc, argv, "01&", &proc, &cb);
2062
+ MosquittoAssertCallback(cb, 1);
2063
+ if (!NIL_P(client->unsubscribe_cb)) rb_gc_unregister_address(&client->unsubscribe_cb);
2064
+ mosquitto_unsubscribe_callback_set(client->mosq, rb_mosquitto_client_on_unsubscribe_cb);
2065
+ client->unsubscribe_cb = cb;
2066
+ rb_gc_register_address(&client->unsubscribe_cb);
2067
+ return Qtrue;
2068
+ }
2069
+
2070
+ /*
2071
+ * call-seq:
2072
+ * client.on_log{|level, msg| p msg } -> Boolean
2073
+ *
2074
+ * Set the logging callback. This should be used if you want event logging
2075
+ * information from the client library.
2076
+ *
2077
+ * @yield unsubscribe callback
2078
+ * @yieldparam level [Mosquitto::LOG_INFO, Mosquitto::LOG_NOTICE, Mosquitto::LOG_WARNING,
2079
+ * Mosquitto::LOG_ERR, Mosquitto::LOG_DEBUG] the log message level
2080
+ * @yieldparam msg [String] log message
2081
+ * @return [true] on success
2082
+ * @raise [TypeError, ArgumentError] if callback is not a Proc or if the method arity is wrong
2083
+ * @example
2084
+ * client.on_log{|level, msg| p msg }
2085
+ *
2086
+ */
2087
+ static VALUE rb_mosquitto_client_on_log(int argc, VALUE *argv, VALUE obj)
2088
+ {
2089
+ VALUE proc, cb;
2090
+ MosquittoGetClient(obj);
2091
+ rb_scan_args(argc, argv, "01&", &proc, &cb);
2092
+ MosquittoAssertCallback(cb, 2);
2093
+ if (!NIL_P(client->log_cb)) rb_gc_unregister_address(&client->log_cb);
2094
+ mosquitto_log_callback_set(client->mosq, rb_mosquitto_client_on_log_cb);
2095
+ client->log_cb = cb;
2096
+ rb_gc_register_address(&client->log_cb);
2097
+ return Qtrue;
2098
+ }
2099
+
2100
+ void _init_rb_mosquitto_client()
2101
+ {
2102
+ mosquitto_tls_password = Qnil;
2103
+
2104
+ rb_cMosquittoClient = rb_define_class_under(rb_mMosquitto, "Client", rb_cObject);
2105
+
2106
+ /* Init / setup specific methods */
2107
+
2108
+ rb_define_singleton_method(rb_cMosquittoClient, "new", rb_mosquitto_client_s_new, -1);
2109
+ rb_define_method(rb_cMosquittoClient, "reinitialise", rb_mosquitto_client_reinitialise, -1);
2110
+ rb_define_method(rb_cMosquittoClient, "will_set", rb_mosquitto_client_will_set, 4);
2111
+ rb_define_method(rb_cMosquittoClient, "will_clear", rb_mosquitto_client_will_clear, 0);
2112
+ rb_define_method(rb_cMosquittoClient, "auth", rb_mosquitto_client_auth, 2);
2113
+
2114
+ /* Network specific methods */
2115
+
2116
+ rb_define_method(rb_cMosquittoClient, "connect", rb_mosquitto_client_connect, 3);
2117
+ rb_define_method(rb_cMosquittoClient, "connect_bind", rb_mosquitto_client_connect_bind, 4);
2118
+ rb_define_method(rb_cMosquittoClient, "connect_async", rb_mosquitto_client_connect_async, 3);
2119
+ rb_define_method(rb_cMosquittoClient, "connect_bind_async", rb_mosquitto_client_connect_bind_async, 4);
2120
+ rb_define_method(rb_cMosquittoClient, "reconnect", rb_mosquitto_client_reconnect, 0);
2121
+ rb_define_method(rb_cMosquittoClient, "disconnect", rb_mosquitto_client_disconnect, 0);
2122
+
2123
+ /* Messaging specific methods */
2124
+
2125
+ rb_define_method(rb_cMosquittoClient, "publish", rb_mosquitto_client_publish, 5);
2126
+ rb_define_method(rb_cMosquittoClient, "subscribe", rb_mosquitto_client_subscribe, 3);
2127
+ rb_define_method(rb_cMosquittoClient, "unsubscribe", rb_mosquitto_client_unsubscribe, 2);
2128
+
2129
+ /* Main / event loop specific methods */
2130
+
2131
+ rb_define_method(rb_cMosquittoClient, "socket", rb_mosquitto_client_socket, 0);
2132
+ rb_define_method(rb_cMosquittoClient, "loop", rb_mosquitto_client_loop, 2);
2133
+ rb_define_method(rb_cMosquittoClient, "loop_start", rb_mosquitto_client_loop_start, 0);
2134
+ rb_define_method(rb_cMosquittoClient, "loop_forever", rb_mosquitto_client_loop_forever, 2);
2135
+ rb_define_method(rb_cMosquittoClient, "loop_stop", rb_mosquitto_client_loop_stop, 1);
2136
+ rb_define_method(rb_cMosquittoClient, "loop_read", rb_mosquitto_client_loop_read, 1);
2137
+ rb_define_method(rb_cMosquittoClient, "loop_write", rb_mosquitto_client_loop_write, 1);
2138
+ rb_define_method(rb_cMosquittoClient, "loop_misc", rb_mosquitto_client_loop_misc, 0);
2139
+ rb_define_method(rb_cMosquittoClient, "want_write?", rb_mosquitto_client_want_write, 0);
2140
+
2141
+ /* Tuning specific methods */
2142
+
2143
+ rb_define_method(rb_cMosquittoClient, "reconnect_delay_set", rb_mosquitto_client_reconnect_delay_set, 3);
2144
+ rb_define_method(rb_cMosquittoClient, "max_inflight_messages=", rb_mosquitto_client_max_inflight_messages_equals, 1);
2145
+ rb_define_method(rb_cMosquittoClient, "message_retry=", rb_mosquitto_client_message_retry_equals, 1);
2146
+
2147
+ /* TLS specific methods */
2148
+
2149
+ rb_define_method(rb_cMosquittoClient, "tls_set", rb_mosquitto_client_tls_set, 5);
2150
+ rb_define_method(rb_cMosquittoClient, "tls_insecure=", rb_mosquitto_client_tls_insecure_set, 1);
2151
+ rb_define_method(rb_cMosquittoClient, "tls_opts_set", rb_mosquitto_client_tls_opts_set, 3);
2152
+ rb_define_method(rb_cMosquittoClient, "tls_psk_set", rb_mosquitto_client_tls_psk_set, 3);
2153
+
2154
+ /* Callback specific methods */
2155
+
2156
+ rb_define_method(rb_cMosquittoClient, "on_connect", rb_mosquitto_client_on_connect, -1);
2157
+ rb_define_method(rb_cMosquittoClient, "on_disconnect", rb_mosquitto_client_on_disconnect, -1);
2158
+ rb_define_method(rb_cMosquittoClient, "on_publish", rb_mosquitto_client_on_publish, -1);
2159
+ rb_define_method(rb_cMosquittoClient, "on_message", rb_mosquitto_client_on_message, -1);
2160
+ rb_define_method(rb_cMosquittoClient, "on_subscribe", rb_mosquitto_client_on_subscribe, -1);
2161
+ rb_define_method(rb_cMosquittoClient, "on_unsubscribe", rb_mosquitto_client_on_unsubscribe, -1);
2162
+ rb_define_method(rb_cMosquittoClient, "on_log", rb_mosquitto_client_on_log, -1);
2163
+ }