mosquitto 0.2

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