2 * This file is part of buxton.
4 * Copyright (C) 2013 Intel Corporation
6 * buxton is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1
9 * of the License, or (at your option) any later version.
23 #include "buxtonclient.h"
24 #include "buxtonkey.h"
25 #include "buxtonresponse.h"
26 #include "buxtonstring.h"
34 static pthread_mutex_t callback_guard = PTHREAD_MUTEX_INITIALIZER;
35 static Hashmap *callbacks = NULL;
36 static Hashmap *notify_callbacks = NULL;
37 static volatile uint32_t _msgid = 0;
43 BuxtonControlMessage type;
47 static uint32_t get_msgid(void)
49 return __sync_fetch_and_add(&_msgid, 1);
52 bool setup_callbacks(void)
57 s = pthread_mutex_lock(&callback_guard);
62 if (callbacks && notify_callbacks) {
66 callbacks = hashmap_new(trivial_hash_func, trivial_compare_func);
71 notify_callbacks = hashmap_new(trivial_hash_func, trivial_compare_func);
72 if (!notify_callbacks) {
73 hashmap_free(callbacks);
80 s = pthread_mutex_unlock(&callback_guard);
88 void cleanup_callbacks(void)
90 struct notify_value *nvi;
92 #if UINTPTR_MAX == 0xffffffffffffffff
98 (void)pthread_mutex_lock(&callback_guard);
101 HASHMAP_FOREACH_KEY(nvi, hkey, callbacks, it) {
102 (void)hashmap_remove(callbacks, (void *)hkey);
106 hashmap_free(callbacks);
110 if (notify_callbacks) {
111 HASHMAP_FOREACH_KEY(nvi, hkey, notify_callbacks, it) {
112 (void)hashmap_remove(notify_callbacks, (void *)hkey);
116 hashmap_free(notify_callbacks);
118 notify_callbacks = NULL;
120 (void)pthread_mutex_unlock(&callback_guard);
123 void run_callback(BuxtonCallback callback, void *data, size_t count,
124 BuxtonData *list, BuxtonControlMessage type, _BuxtonKey *key)
126 BuxtonArray *array = NULL;
127 _BuxtonResponse response;
133 array = buxton_array_new();
138 for (int i = 0; i < count; i++)
139 if (!buxton_array_add(array, &list[i])) {
143 response.type = type;
144 response.data = array;
146 callback(&response, data);
149 buxton_array_free(&array, NULL);
152 void reap_callbacks(void)
154 struct notify_value *nvi;
157 #if UINTPTR_MAX == 0xffffffffffffffff
163 (void)gettimeofday(&tv, NULL);
165 /* remove timed out callbacks */
166 HASHMAP_FOREACH_KEY(nvi, hkey, callbacks, it) {
167 if (tv.tv_sec - nvi->tv.tv_sec > TIMEOUT) {
168 (void)hashmap_remove(callbacks, (void *)hkey);
175 bool send_message(_BuxtonClient *client, uint8_t *send, size_t send_len,
176 BuxtonCallback callback, void *data, uint32_t msgid,
177 BuxtonControlMessage type, _BuxtonKey *key)
179 struct notify_value *nv;
180 _BuxtonKey *k = NULL;
184 nv = malloc0(sizeof(struct notify_value));
190 k = malloc0(sizeof(_BuxtonKey));
194 if (!buxton_key_copy(key, k)) {
199 (void)gettimeofday(&nv->tv, NULL);
205 s = pthread_mutex_lock(&callback_guard);
212 #if UINTPTR_MAX == 0xffffffffffffffff
213 s = hashmap_put(callbacks, (void *)((uint64_t)msgid), nv);
215 s = hashmap_put(callbacks, (void *)msgid, nv);
217 (void)pthread_mutex_unlock(&callback_guard);
220 buxton_debug("Error adding callback for msgid: %llu\n", msgid);
224 /* Now write it off */
225 if (!_write(client->fd, send, send_len)) {
226 buxton_debug("Write failed for msgid: %llu\n", msgid);
240 void handle_callback_response(BuxtonControlMessage msg, uint32_t msgid,
241 BuxtonData *list, size_t count)
243 struct notify_value *nv;
245 /* use notification callbacks for notification messages */
246 if (msg == BUXTON_CONTROL_CHANGED) {
247 #if UINTPTR_MAX == 0xffffffffffffffff
248 nv = hashmap_get(notify_callbacks, (void *)((uint64_t)msgid));
250 nv = hashmap_get(notify_callbacks, (void *)msgid);
256 run_callback((BuxtonCallback)(nv->cb), nv->data, count, list,
257 BUXTON_CONTROL_CHANGED, nv->key);
261 #if UINTPTR_MAX == 0xffffffffffffffff
262 nv = hashmap_remove(callbacks, (void *)((uint64_t)msgid));
264 nv = hashmap_remove(callbacks, (void *)msgid);
270 if (nv->type == BUXTON_CONTROL_NOTIFY) {
271 if (list[0].type == INT32 &&
272 list[0].store.d_int32 == 0) {
273 #if UINTPTR_MAX == 0xffffffffffffffff
274 if (hashmap_put(notify_callbacks, (void *)((uint64_t)msgid), nv)
276 if (hashmap_put(notify_callbacks, (void *)msgid, nv)
282 } else if (nv->type == BUXTON_CONTROL_UNNOTIFY) {
283 if (list[0].type == INT32 &&
284 list[0].store.d_int32 == 0) {
285 (void)hashmap_remove(notify_callbacks,
286 #if UINTPTR_MAX == 0xffffffffffffffff
287 (void *)((uint64_t)list[2].store.d_uint32));
289 (void *)list[2].store.d_uint32);
296 /* callback should be run on notfiy or unnotify failure */
297 /* and on any other server message we are waiting for */
298 run_callback((BuxtonCallback)(nv->cb), nv->data, count, list, nv->type,
305 ssize_t buxton_wire_handle_response(_BuxtonClient *client)
308 _cleanup_free_ uint8_t *response = NULL;
309 BuxtonData *r_list = NULL;
310 BuxtonControlMessage r_msg = BUXTON_CONTROL_MIN;
313 size_t size = BUXTON_MESSAGE_HEADER_LENGTH;
318 s = pthread_mutex_lock(&callback_guard);
323 (void)pthread_mutex_unlock(&callback_guard);
325 response = malloc0(BUXTON_MESSAGE_HEADER_LENGTH);
331 l = read(client->fd, response + offset, size - offset);
336 if (offset < BUXTON_MESSAGE_HEADER_LENGTH) {
339 if (size == BUXTON_MESSAGE_HEADER_LENGTH) {
340 size = buxton_get_message_size(response, offset);
341 if (size == 0 || size > BUXTON_MESSAGE_MAX_LENGTH) {
345 if (size != BUXTON_MESSAGE_HEADER_LENGTH) {
346 response = realloc(response, size);
351 if (size != offset) {
355 count = buxton_deserialize_message(response, &r_msg, size, &r_msgid, &r_list);
360 if (!(r_msg == BUXTON_CONTROL_STATUS && r_list && r_list[0].type == INT32)
361 && !(r_msg == BUXTON_CONTROL_CHANGED)) {
363 buxton_log("Critical error: Invalid response\n");
367 s = pthread_mutex_lock(&callback_guard);
372 handle_callback_response(r_msg, r_msgid, r_list, (size_t)count);
374 (void)pthread_mutex_unlock(&callback_guard);
379 for (int i = 0; i < count; i++) {
380 if (r_list[i].type == STRING) {
381 free(r_list[i].store.d_string.value);
387 /* reset for next possible message */
388 size = BUXTON_MESSAGE_HEADER_LENGTH;
393 int buxton_wire_get_response(_BuxtonClient *client)
395 struct pollfd pfd[1];
399 pfd[0].fd = client->fd;
400 pfd[0].events = POLLIN;
402 r = poll(pfd, 1, 5000);
410 processed = buxton_wire_handle_response(client);
412 return (int)processed;
415 bool buxton_wire_set_value(_BuxtonClient *client, _BuxtonKey *key, void *value,
416 BuxtonCallback callback, void *data)
418 _cleanup_free_ uint8_t *send = NULL;
421 BuxtonArray *list = NULL;
426 uint32_t msgid = get_msgid();
428 buxton_string_to_data(&key->layer, &d_layer);
429 buxton_string_to_data(&key->group, &d_group);
430 buxton_string_to_data(&key->name, &d_name);
431 d_value.type = key->type;
434 d_value.store.d_string.value = (char *)value;
435 d_value.store.d_string.length = (uint32_t)strlen((char *)value) + 1;
438 d_value.store.d_int32 = *(int32_t *)value;
441 d_value.store.d_int64 = *(int64_t *)value;
444 d_value.store.d_uint32 = *(uint32_t *)value;
447 d_value.store.d_uint64 = *(uint64_t *)value;
450 d_value.store.d_float = *(float *)value;
453 memcpy(&d_value.store.d_double, value, sizeof(double));
456 d_value.store.d_boolean = *(bool *)value;
462 list = buxton_array_new();
463 if (!buxton_array_add(list, &d_layer)) {
464 buxton_log("Failed to add layer to set_value array\n");
467 if (!buxton_array_add(list, &d_group)) {
468 buxton_log("Failed to add group to set_value array\n");
471 if (!buxton_array_add(list, &d_name)) {
472 buxton_log("Failed to add name to set_value array\n");
475 if (!buxton_array_add(list, &d_value)) {
476 buxton_log("Failed to add value to set_value array\n");
480 send_len = buxton_serialize_message(&send, BUXTON_CONTROL_SET, msgid, list);
487 if (!send_message(client, send, send_len, callback, data, msgid,
488 BUXTON_CONTROL_SET, key)) {
495 buxton_array_free(&list, NULL);
499 bool buxton_wire_set_label(_BuxtonClient *client,
500 _BuxtonKey *key, BuxtonString *value,
501 BuxtonCallback callback, void *data)
507 _cleanup_free_ uint8_t *send = NULL;
510 BuxtonArray *list = NULL;
515 uint32_t msgid = get_msgid();
517 buxton_string_to_data(&key->layer, &d_layer);
518 buxton_string_to_data(&key->group, &d_group);
519 buxton_string_to_data(value, &d_value);
521 list = buxton_array_new();
522 if (!buxton_array_add(list, &d_layer)) {
523 buxton_log("Failed to add layer to set_label array\n");
526 if (!buxton_array_add(list, &d_group)) {
527 buxton_log("Failed to add group to set_label array\n");
530 if (key->name.value) {
531 buxton_string_to_data(&key->name, &d_name);
532 if (!buxton_array_add(list, &d_name)) {
533 buxton_log("Failed to add name to set_label array\n");
537 if (!buxton_array_add(list, &d_value)) {
538 buxton_log("Failed to add value to set_label array\n");
542 send_len = buxton_serialize_message(&send, BUXTON_CONTROL_SET_LABEL, msgid, list);
548 if (!send_message(client, send, send_len, callback, data, msgid,
549 BUXTON_CONTROL_SET_LABEL, key)) {
556 buxton_array_free(&list, NULL);
560 bool buxton_wire_create_group(_BuxtonClient *client, _BuxtonKey *key,
561 BuxtonCallback callback, void *data)
566 _cleanup_free_ uint8_t *send = NULL;
569 BuxtonArray *list = NULL;
572 uint32_t msgid = get_msgid();
574 buxton_string_to_data(&key->layer, &d_layer);
575 buxton_string_to_data(&key->group, &d_group);
577 list = buxton_array_new();
578 if (!buxton_array_add(list, &d_layer)) {
579 buxton_log("Failed to add layer to create_group array\n");
582 if (!buxton_array_add(list, &d_group)) {
583 buxton_log("Failed to add group to create_group array\n");
587 send_len = buxton_serialize_message(&send, BUXTON_CONTROL_CREATE_GROUP, msgid, list);
593 if (!send_message(client, send, send_len, callback, data, msgid,
594 BUXTON_CONTROL_CREATE_GROUP, key)) {
601 buxton_array_free(&list, NULL);
605 bool buxton_wire_remove_group(_BuxtonClient *client, _BuxtonKey *key,
606 BuxtonCallback callback, void *data)
611 _cleanup_free_ uint8_t *send = NULL;
614 BuxtonArray *list = NULL;
617 uint32_t msgid = get_msgid();
619 buxton_string_to_data(&key->layer, &d_layer);
620 buxton_string_to_data(&key->group, &d_group);
622 list = buxton_array_new();
623 if (!buxton_array_add(list, &d_layer)) {
624 buxton_log("Failed to add layer to remove_group array\n");
627 if (!buxton_array_add(list, &d_group)) {
628 buxton_log("Failed to add group to remove_group array\n");
632 send_len = buxton_serialize_message(&send, BUXTON_CONTROL_REMOVE_GROUP, msgid, list);
638 if (!send_message(client, send, send_len, callback, data, msgid,
639 BUXTON_CONTROL_REMOVE_GROUP, key)) {
646 buxton_array_free(&list, NULL);
650 bool buxton_wire_get_value(_BuxtonClient *client, _BuxtonKey *key,
651 BuxtonCallback callback, void *data)
655 _cleanup_free_ uint8_t *send = NULL;
656 BuxtonArray *list = NULL;
661 uint32_t msgid = get_msgid();
663 buxton_string_to_data(&key->group, &d_group);
664 buxton_string_to_data(&key->name, &d_name);
665 d_type.type = UINT32;
666 d_type.store.d_int32 = key->type;
668 list = buxton_array_new();
669 if (key->layer.value) {
670 buxton_string_to_data(&key->layer, &d_layer);
671 if (!buxton_array_add(list, &d_layer)) {
672 buxton_log("Unable to prepare get_value message\n");
676 if (!buxton_array_add(list, &d_group)) {
677 buxton_log("Failed to add group to set_value array\n");
680 if (!buxton_array_add(list, &d_name)) {
681 buxton_log("Failed to add name to set_value array\n");
684 if (!buxton_array_add(list, &d_type)) {
685 buxton_log("Failed to add type to set_value array\n");
689 send_len = buxton_serialize_message(&send, BUXTON_CONTROL_GET, msgid, list);
695 if (!send_message(client, send, send_len, callback, data, msgid,
696 BUXTON_CONTROL_GET, key)) {
703 buxton_array_free(&list, NULL);
707 bool buxton_wire_unset_value(_BuxtonClient *client,
709 BuxtonCallback callback,
715 _cleanup_free_ uint8_t *send = NULL;
717 BuxtonArray *list = NULL;
723 uint32_t msgid = get_msgid();
725 buxton_string_to_data(&key->group, &d_group);
726 buxton_string_to_data(&key->name, &d_name);
727 buxton_string_to_data(&key->layer, &d_layer);
728 d_type.type = UINT32;
729 d_type.store.d_int32 = key->type;
731 list = buxton_array_new();
732 if (!buxton_array_add(list, &d_layer)) {
733 buxton_log("Failed to add layer to set_value array\n");
736 if (!buxton_array_add(list, &d_group)) {
737 buxton_log("Failed to add group to set_value array\n");
740 if (!buxton_array_add(list, &d_name)) {
741 buxton_log("Failed to add name to set_value array\n");
744 if (!buxton_array_add(list, &d_type)) {
745 buxton_log("Failed to add type to set_value array\n");
749 send_len = buxton_serialize_message(&send, BUXTON_CONTROL_UNSET,
756 if (!send_message(client, send, send_len, callback, data, msgid,
757 BUXTON_CONTROL_UNSET, key)) {
764 buxton_array_free(&list, NULL);
768 bool buxton_wire_list_keys(_BuxtonClient *client,
770 BuxtonCallback callback,
776 _cleanup_free_ uint8_t *send = NULL;
778 BuxtonArray *list = NULL;
781 uint32_t msgid = get_msgid();
783 buxton_string_to_data(layer, &d_layer);
785 list = buxton_array_new();
786 if (!buxton_array_add(list, &d_layer)) {
787 buxton_log("Unable to add layer to list_keys array\n");
791 send_len = buxton_serialize_message(&send, BUXTON_CONTROL_LIST, msgid,
798 if (!send_message(client, send, send_len, callback, data, msgid,
799 BUXTON_CONTROL_LIST, NULL)) {
806 buxton_array_free(&list, NULL);
811 bool buxton_wire_register_notification(_BuxtonClient *client,
813 BuxtonCallback callback,
819 _cleanup_free_ uint8_t *send = NULL;
821 BuxtonArray *list = NULL;
826 uint32_t msgid = get_msgid();
828 buxton_string_to_data(&key->group, &d_group);
829 buxton_string_to_data(&key->name, &d_name);
830 d_type.type = UINT32;
831 d_type.store.d_int32 = key->type;
833 list = buxton_array_new();
834 if (!buxton_array_add(list, &d_group)) {
835 buxton_log("Failed to add group to set_value array\n");
838 if (!buxton_array_add(list, &d_name)) {
839 buxton_log("Failed to add name to set_value array\n");
842 if (!buxton_array_add(list, &d_type)) {
843 buxton_log("Failed to add type to set_value array\n");
847 send_len = buxton_serialize_message(&send, BUXTON_CONTROL_NOTIFY, msgid,
854 if (!send_message(client, send, send_len, callback, data, msgid,
855 BUXTON_CONTROL_NOTIFY, key)) {
862 buxton_array_free(&list, NULL);
866 bool buxton_wire_unregister_notification(_BuxtonClient *client,
868 BuxtonCallback callback,
874 _cleanup_free_ uint8_t *send = NULL;
876 BuxtonArray *list = NULL;
881 uint32_t msgid = get_msgid();
883 buxton_string_to_data(&key->group, &d_group);
884 buxton_string_to_data(&key->name, &d_name);
885 d_type.type = UINT32;
886 d_type.store.d_int32 = key->type;
888 list = buxton_array_new();
889 if (!buxton_array_add(list, &d_group)) {
890 buxton_log("Failed to add group to set_value array\n");
893 if (!buxton_array_add(list, &d_name)) {
894 buxton_log("Failed to add name to set_value array\n");
897 if (!buxton_array_add(list, &d_type)) {
898 buxton_log("Failed to add type to set_value array\n");
902 send_len = buxton_serialize_message(&send, BUXTON_CONTROL_UNNOTIFY,
909 if (!send_message(client, send, send_len, callback, data, msgid,
910 BUXTON_CONTROL_UNNOTIFY, key)) {
917 buxton_array_free(&list, NULL);
921 void include_protocol(void)
927 * Editor modelines - http://www.wireshark.org/tools/modelines.html
932 * indent-tabs-mode: t
935 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
936 * :indentSize=8:tabSize=8:noTabs=false: