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.
21 #include <attr/xattr.h>
27 #include "buxtonlist.h"
29 bool parse_list(BuxtonControlMessage msg, size_t count, BuxtonData *list,
30 _BuxtonKey *key, BuxtonData **value)
33 case BUXTON_CONTROL_SET:
37 if (list[0].type != STRING || list[1].type != STRING ||
38 list[2].type != STRING || list[3].type == BUXTON_TYPE_MIN ||
39 list[3].type == BUXTON_TYPE_MAX) {
42 key->layer = list[0].store.d_string;
43 key->group = list[1].store.d_string;
44 key->name = list[2].store.d_string;
45 key->type = list[3].type;
48 case BUXTON_CONTROL_SET_LABEL:
50 if (list[0].type != STRING || list[1].type != STRING ||
51 list[2].type != STRING) {
55 key->layer = list[0].store.d_string;
56 key->group = list[1].store.d_string;
58 } else if (count == 4) {
59 if (list[0].type != STRING || list[1].type != STRING ||
60 list[2].type != STRING || list[3].type != STRING) {
64 key->layer = list[0].store.d_string;
65 key->group = list[1].store.d_string;
66 key->name = list[2].store.d_string;
72 case BUXTON_CONTROL_CREATE_GROUP:
76 if (list[0].type != STRING || list[1].type != STRING) {
80 key->layer = list[0].store.d_string;
81 key->group = list[1].store.d_string;
83 case BUXTON_CONTROL_REMOVE_GROUP:
87 if (list[0].type != STRING || list[1].type != STRING) {
91 key->layer = list[0].store.d_string;
92 key->group = list[1].store.d_string;
94 case BUXTON_CONTROL_GET:
96 if (list[0].type != STRING || list[1].type != STRING ||
97 list[2].type != STRING || list[3].type != UINT32) {
100 key->layer = list[0].store.d_string;
101 key->group = list[1].store.d_string;
102 key->name = list[2].store.d_string;
103 key->type = list[3].store.d_uint32;
104 } else if (count == 3) {
105 if (list[0].type != STRING || list[1].type != STRING ||
106 list[2].type != UINT32) {
109 key->group = list[0].store.d_string;
110 key->name = list[1].store.d_string;
111 key->type = list[2].store.d_uint32;
116 case BUXTON_CONTROL_LIST:
121 if (list[0].type != STRING) {
126 case BUXTON_CONTROL_UNSET:
130 if (list[0].type != STRING || list[1].type != STRING ||
131 list[2].type != STRING || list[3].type != UINT32) {
134 key->layer = list[0].store.d_string;
135 key->group = list[1].store.d_string;
136 key->name = list[2].store.d_string;
137 key->type = list[3].store.d_uint32;
139 case BUXTON_CONTROL_NOTIFY:
143 if (list[0].type != STRING || list[1].type != STRING ||
144 list[2].type != UINT32) {
147 key->group = list[0].store.d_string;
148 key->name = list[1].store.d_string;
149 key->type = list[2].store.d_uint32;
151 case BUXTON_CONTROL_UNNOTIFY:
155 if (list[0].type != STRING || list[1].type != STRING ||
156 list[2].type != UINT32) {
159 key->group = list[0].store.d_string;
160 key->name = list[1].store.d_string;
161 key->type = list[2].store.d_uint32;
170 bool buxtond_handle_message(BuxtonDaemon *self, client_list_item *client, size_t size)
172 BuxtonControlMessage msg;
174 BuxtonData *list = NULL;
175 _cleanup_buxton_data_ BuxtonData *data = NULL;
179 BuxtonData response_data, mdata;
180 BuxtonData *value = NULL;
181 _BuxtonKey key = {{0}, {0}, {0}, 0};
182 BuxtonArray *out_list = NULL, *key_list = NULL;
183 _cleanup_free_ uint8_t *response_store = NULL;
187 uint32_t n_msgid = 0;
192 uid = self->buxton.client.uid;
193 p_count = buxton_deserialize_message((uint8_t*)client->data, &msg, size,
196 if (errno == ENOMEM) {
199 /* Todo: terminate the client due to invalid message */
200 buxton_debug("Failed to deserialize message\n");
204 /* Check valid range */
205 if (msg <= BUXTON_CONTROL_MIN || msg >= BUXTON_CONTROL_MAX) {
209 if (!parse_list(msg, (size_t)p_count, list, &key, &value)) {
213 /* use internal function from buxtond */
215 case BUXTON_CONTROL_SET:
216 set_value(self, client, &key, value, &response);
218 case BUXTON_CONTROL_SET_LABEL:
219 set_label(self, client, &key, value, &response);
221 case BUXTON_CONTROL_CREATE_GROUP:
222 create_group(self, client, &key, &response);
224 case BUXTON_CONTROL_REMOVE_GROUP:
225 remove_group(self, client, &key, &response);
227 case BUXTON_CONTROL_GET:
228 data = get_value(self, client, &key, &response);
230 case BUXTON_CONTROL_UNSET:
231 unset_value(self, client, &key, &response);
233 case BUXTON_CONTROL_LIST:
234 key_list = list_keys(self, client, &value->store.d_string,
237 case BUXTON_CONTROL_NOTIFY:
238 register_notification(self, client, &key, msgid, &response);
240 case BUXTON_CONTROL_UNNOTIFY:
241 n_msgid = unregister_notification(self, client, &key, &response);
246 /* Set a response code */
247 response_data.type = INT32;
248 response_data.store.d_int32 = response;
249 out_list = buxton_array_new();
253 if (!buxton_array_add(out_list, &response_data)) {
259 /* TODO: Use cascading switch */
260 case BUXTON_CONTROL_SET:
261 response_len = buxton_serialize_message(&response_store,
262 BUXTON_CONTROL_STATUS,
264 if (response_len == 0) {
265 if (errno == ENOMEM) {
268 buxton_log("Failed to serialize set response message\n");
269 //FIXME abort here because serialization
270 //failed for a message generated by the daemon
274 case BUXTON_CONTROL_SET_LABEL:
275 response_len = buxton_serialize_message(&response_store,
276 BUXTON_CONTROL_STATUS,
278 if (response_len == 0) {
279 if (errno == ENOMEM) {
282 buxton_log("Failed to serialize set_label response message\n");
286 case BUXTON_CONTROL_CREATE_GROUP:
287 response_len = buxton_serialize_message(&response_store,
288 BUXTON_CONTROL_STATUS,
290 if (response_len == 0) {
291 if (errno == ENOMEM) {
294 buxton_log("Failed to serialize create_group response message\n");
298 case BUXTON_CONTROL_REMOVE_GROUP:
299 response_len = buxton_serialize_message(&response_store,
300 BUXTON_CONTROL_STATUS,
302 if (response_len == 0) {
303 if (errno == ENOMEM) {
306 buxton_log("Failed to serialize remove_group response message\n");
310 case BUXTON_CONTROL_GET:
311 if (data && !buxton_array_add(out_list, data)) {
314 response_len = buxton_serialize_message(&response_store,
315 BUXTON_CONTROL_STATUS,
317 if (response_len == 0) {
318 if (errno == ENOMEM) {
321 buxton_log("Failed to serialize get response message\n");
325 case BUXTON_CONTROL_UNSET:
326 response_len = buxton_serialize_message(&response_store,
327 BUXTON_CONTROL_STATUS,
329 if (response_len == 0) {
330 if (errno == ENOMEM) {
333 buxton_log("Failed to serialize unset response message\n");
337 case BUXTON_CONTROL_LIST:
339 for (i = 0; i < key_list->len; i++) {
340 if (!buxton_array_add(out_list, buxton_array_get(key_list, i))) {
344 buxton_array_free(&key_list, NULL);
346 response_len = buxton_serialize_message(&response_store,
347 BUXTON_CONTROL_STATUS,
349 if (response_len == 0) {
350 if (errno == ENOMEM) {
353 buxton_log("Failed to serialize list response message\n");
357 case BUXTON_CONTROL_NOTIFY:
358 response_len = buxton_serialize_message(&response_store,
359 BUXTON_CONTROL_STATUS,
361 if (response_len == 0) {
362 if (errno == ENOMEM) {
365 buxton_log("Failed to serialize notify response message\n");
369 case BUXTON_CONTROL_UNNOTIFY:
371 mdata.store.d_uint32 = n_msgid;
372 if (!buxton_array_add(out_list, &mdata)) {
375 response_len = buxton_serialize_message(&response_store,
376 BUXTON_CONTROL_STATUS,
378 if (response_len == 0) {
379 if (errno == ENOMEM) {
382 buxton_log("Failed to serialize unnotify response message\n");
390 /* Now write the response */
391 ret = _write(client->fd, response_store, response_len);
393 if (msg == BUXTON_CONTROL_SET && response == 0) {
394 buxtond_notify_clients(self, client, &key, value);
395 } else if (msg == BUXTON_CONTROL_UNSET && response == 0) {
396 buxtond_notify_clients(self, client, &key, NULL);
401 /* Restore our own UID */
402 self->buxton.client.uid = uid;
404 buxton_array_free(&out_list, NULL);
407 for (i=0; i < p_count; i++) {
408 if (list[i].type == STRING) {
409 free(list[i].store.d_string.value);
417 void buxtond_notify_clients(BuxtonDaemon *self, client_list_item *client,
418 _BuxtonKey *key, BuxtonData *value)
420 BuxtonList *list = NULL;
421 BuxtonList *elem = NULL;
422 BuxtonNotification *nitem;
423 _cleanup_free_ uint8_t* response = NULL;
425 BuxtonArray *out_list = NULL;
426 _cleanup_free_ char *key_name;
433 r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
437 list = hashmap_get(self->notify_mapping, key_name);
442 BUXTON_LIST_FOREACH(list, elem) {
444 //FIXME abort here since NULL elements
445 //shouldn't be added to the list
450 __attribute__((unused)) bool unused;
454 if (nitem->old_data && value) {
455 switch (value->type) {
457 c = memcmp((const void *)
458 (nitem->old_data->store.d_string.value),
459 (const void *)(value->store.d_string.value),
460 value->store.d_string.length);
463 c = memcmp((const void *)&(nitem->old_data->store.d_int32),
464 (const void *)&(value->store.d_int32),
468 c = memcmp((const void *)&(nitem->old_data->store.d_uint32),
469 (const void *)&(value->store.d_uint32),
473 c = memcmp((const void *)&(nitem->old_data->store.d_int64),
474 (const void *)&(value->store.d_int64),
478 c = memcmp((const void *)&(nitem->old_data->store.d_uint64),
479 (const void *)&(value->store.d_uint64),
483 c = memcmp((const void *)&(nitem->old_data->store.d_float),
484 (const void *)&(value->store.d_float),
488 c = memcmp((const void *)&(nitem->old_data->store.d_double),
489 (const void *)&(value->store.d_double),
493 c = memcmp((const void *)&(nitem->old_data->store.d_boolean),
494 (const void *)&(value->store.d_boolean),
498 buxton_log("Internal state corruption: Notification data type invalid\n");
506 if (nitem->old_data && (nitem->old_data->type == STRING)) {
507 free(nitem->old_data->store.d_string.value);
509 free(nitem->old_data);
511 nitem->old_data = malloc0(sizeof(BuxtonData));
512 if (!nitem->old_data) {
516 if (!buxton_data_copy(value, nitem->old_data)) {
521 out_list = buxton_array_new();
526 if (!buxton_array_add(out_list, value)) {
531 response_len = buxton_serialize_message(&response,
532 BUXTON_CONTROL_CHANGED,
533 nitem->msgid, out_list);
534 buxton_array_free(&out_list, NULL);
535 if (response_len == 0) {
536 if (errno == ENOMEM) {
539 buxton_log("Failed to serialize notification\n");
540 //FIXME abort here serialize message error
543 buxton_debug("Notification to %d of key change (%s)\n", nitem->client->fd,
546 unused = _write(nitem->client->fd, response, response_len);
550 void set_value(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
551 BuxtonData *value, int32_t *status)
562 buxton_debug("Daemon setting [%s][%s][%s]\n",
567 self->buxton.client.uid = client->cred.uid;
569 if (!buxton_direct_set_value(&self->buxton, key, value, client->smack_label)) {
574 buxton_debug("Daemon set value completed\n");
577 void set_label(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
578 BuxtonData *value, int32_t *status)
589 buxton_debug("Daemon setting label on [%s][%s][%s]\n",
594 self->buxton.client.uid = client->cred.uid;
596 /* Use internal library to set label */
597 if (!buxton_direct_set_label(&self->buxton, key, &value->store.d_string)) {
602 buxton_debug("Daemon set label completed\n");
605 void create_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
615 buxton_debug("Daemon creating group [%s][%s]\n",
619 self->buxton.client.uid = client->cred.uid;
621 /* Use internal library to create group */
622 if (!buxton_direct_create_group(&self->buxton, key, client->smack_label)) {
627 buxton_debug("Daemon create group completed\n");
630 void remove_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
640 buxton_debug("Daemon removing group [%s][%s]\n",
644 self->buxton.client.uid = client->cred.uid;
646 /* Use internal library to create group */
647 if (!buxton_direct_remove_group(&self->buxton, key, client->smack_label)) {
652 buxton_debug("Daemon remove group completed\n");
655 void unset_value(BuxtonDaemon *self, client_list_item *client,
656 _BuxtonKey *key, int32_t *status)
665 buxton_debug("Daemon unsetting [%s][%s][%s]\n",
670 /* Use internal library to unset value */
671 self->buxton.client.uid = client->cred.uid;
672 if (!buxton_direct_unset_value(&self->buxton, key, client->smack_label)) {
676 buxton_debug("unset value returned successfully from db\n");
679 buxton_debug("Daemon unset value completed\n");
682 BuxtonData *get_value(BuxtonDaemon *self, client_list_item *client,
683 _BuxtonKey *key, int32_t *status)
685 BuxtonData *data = NULL;
696 data = malloc0(sizeof(BuxtonData));
701 if (key->layer.value) {
702 buxton_debug("Daemon getting [%s][%s][%s]\n", key->layer.value,
703 key->group.value, key->name.value);
705 buxton_debug("Daemon getting [%s][%s]\n", key->group.value,
708 self->buxton.client.uid = client->cred.uid;
709 ret = buxton_direct_get_value(&self->buxton, key, data, &label,
710 client->smack_label);
716 buxton_debug("get value returned successfully from db\n");
721 buxton_debug("get value failed\n");
729 BuxtonArray *list_keys(BuxtonDaemon *self, client_list_item *client,
730 BuxtonString *layer, int32_t *status)
732 BuxtonArray *ret_list = NULL;
739 if (buxton_direct_list_keys(&self->buxton, layer, &ret_list)) {
745 void register_notification(BuxtonDaemon *self, client_list_item *client,
746 _BuxtonKey *key, uint32_t msgid,
749 BuxtonList *n_list = NULL;
750 BuxtonNotification *nitem;
751 BuxtonData *old_data = NULL;
763 nitem = malloc0(sizeof(BuxtonNotification));
767 nitem->client = client;
769 /* Store data now, cheap */
770 old_data = get_value(self, client, key, &key_status);
771 if (key_status != 0) {
775 nitem->old_data = old_data;
776 nitem->msgid = msgid;
778 /* May be null, but will append regardless */
779 r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
783 n_list = hashmap_get(self->notify_mapping, key_name);
786 if (!buxton_list_append(&n_list, nitem)) {
790 if (hashmap_put(self->notify_mapping, key_name, n_list) < 0) {
795 if (!buxton_list_append(&n_list, nitem)) {
802 uint32_t unregister_notification(BuxtonDaemon *self, client_list_item *client,
803 _BuxtonKey *key, int32_t *status)
805 BuxtonList *n_list = NULL;
806 BuxtonList *elem = NULL;
807 BuxtonNotification *nitem, *citem = NULL;
809 _cleanup_free_ char *key_name = NULL;
819 r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
823 n_list = hashmap_get2(self->notify_mapping, key_name, &old_key_name);
824 /* This key isn't actually registered for notifications */
829 BUXTON_LIST_FOREACH(n_list, elem) {
831 /* Find the list item for this client */
832 if (nitem->client == client) {
838 /* Client hasn't registered for notifications on this key */
843 msgid = citem->msgid;
844 /* Remove client from notifications */
845 free_buxton_data(&(citem->old_data));
846 buxton_list_remove(&n_list, citem, true);
848 /* If we removed the last item, remove the mapping too */
850 (void)hashmap_remove(self->notify_mapping, key_name);
859 bool identify_client(client_list_item *cl)
861 /* Identity handling */
866 __attribute__((unused)) struct ucred *ucredp;
867 struct cmsghdr *cmhp;
868 socklen_t len = sizeof(struct ucred);
875 char control[CMSG_SPACE(sizeof(struct ucred))];
878 setsockopt(cl->fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
880 control_un.cmh.cmsg_len = CMSG_LEN(sizeof(struct ucred));
881 control_un.cmh.cmsg_level = SOL_SOCKET;
882 control_un.cmh.cmsg_type = SCM_CREDENTIALS;
884 msgh.msg_control = control_un.control;
885 msgh.msg_controllen = sizeof(control_un.control);
889 iov.iov_base = &data;
890 iov.iov_len = sizeof(int);
892 msgh.msg_name = NULL;
893 msgh.msg_namelen = 0;
895 nr = recvmsg(cl->fd, &msgh, MSG_PEEK | MSG_DONTWAIT);
900 cmhp = CMSG_FIRSTHDR(&msgh);
902 if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
903 //FIXME figure out if abort here since socket setup
904 //didn't work quite right
908 if (cmhp->cmsg_level != SOL_SOCKET || cmhp->cmsg_type != SCM_CREDENTIALS) {
912 ucredp = (struct ucred *) CMSG_DATA(cmhp);
914 if (getsockopt(cl->fd, SOL_SOCKET, SO_PEERCRED, &cl->cred, &len) == -1) {
921 void add_pollfd(BuxtonDaemon *self, int fd, short events, bool a)
926 if (!greedy_realloc((void **) &(self->pollfds), &(self->nfds_alloc),
927 (size_t)((self->nfds + 1) * (sizeof(struct pollfd))))) {
930 if (!greedy_realloc((void **) &(self->accepting), &(self->accepting_alloc),
931 (size_t)((self->nfds + 1) * (sizeof(self->accepting))))) {
934 self->pollfds[self->nfds].fd = fd;
935 self->pollfds[self->nfds].events = events;
936 self->pollfds[self->nfds].revents = 0;
937 self->accepting[self->nfds] = a;
940 buxton_debug("Added fd %d to our poll list (accepting=%d)\n", fd, a);
943 void del_pollfd(BuxtonDaemon *self, nfds_t i)
946 assert(i < self->nfds);
948 buxton_debug("Removing fd %d from our list\n", self->pollfds[i].fd);
950 if (i != (self->nfds - 1)) {
951 memmove(&(self->pollfds[i]),
952 &(self->pollfds[i + 1]),
954 * nfds < max int because of kernel limit of
955 * fds. i + 1 < nfds because of if and assert
956 * so the casts below are always >= 0 and less
957 * than long unsigned max int so no loss of
960 (size_t)(self->nfds - i - 1) * sizeof(struct pollfd));
961 memmove(&(self->accepting[i]),
962 &(self->accepting[i + 1]),
963 (size_t)(self->nfds - i - 1) * sizeof(bool));
968 static void handle_smack_label(client_list_item *cl)
970 socklen_t slabel_len = 1;
972 BuxtonString *slabel = NULL;
975 ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, NULL, &slabel_len);
976 /* libsmack ignores ERANGE here, so we ignore it too */
977 if (ret < 0 && errno != ERANGE) {
980 /* If Smack is not enabled, do not set the client label */
981 cl->smack_label = NULL;
984 buxton_log("getsockopt(): %m\n");
989 slabel = malloc0(sizeof(BuxtonString));
994 /* already checked slabel_len positive above */
995 buf = malloc0((size_t)slabel_len + 1);
1000 ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, buf, &slabel_len);
1002 buxton_log("getsockopt(): %m\n");
1006 slabel->value = buf;
1007 slabel->length = (uint32_t)slabel_len;
1009 buxton_debug("getsockopt(): label=\"%s\"\n", slabel->value);
1011 cl->smack_label = slabel;
1014 bool handle_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
1018 bool more_data = false;
1019 int message_limit = 32;
1025 cl->data = malloc0(BUXTON_MESSAGE_HEADER_LENGTH);
1027 cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1032 /* client closed the connection, or some error occurred? */
1033 if (recv(cl->fd, cl->data, cl->size, MSG_PEEK | MSG_DONTWAIT) <= 0) {
1037 /* need to authenticate the client? */
1038 if ((cl->cred.uid == 0) || (cl->cred.pid == 0)) {
1039 if (!identify_client(cl)) {
1043 handle_smack_label(cl);
1046 buxton_debug("New packet from UID %ld, PID %ld\n", cl->cred.uid, cl->cred.pid);
1048 /* Hand off any read data */
1050 l = read(self->pollfds[i].fd, (cl->data) + cl->offset, cl->size - cl->offset);
1053 * Close clients with read errors. If there isn't more
1054 * data and we don't have a complete message just
1055 * cleanup and let the client resend their request.
1058 if (errno != EAGAIN) {
1063 } else if (l == 0) {
1067 cl->offset += (size_t)l;
1068 if (cl->offset < BUXTON_MESSAGE_HEADER_LENGTH) {
1071 if (cl->size == BUXTON_MESSAGE_HEADER_LENGTH) {
1072 cl->size = buxton_get_message_size(cl->data, cl->offset);
1073 if (cl->size == 0 || cl->size > BUXTON_MESSAGE_MAX_LENGTH) {
1077 if (cl->size != BUXTON_MESSAGE_HEADER_LENGTH) {
1078 cl->data = realloc(cl->data, cl->size);
1083 if (cl->size != cl->offset) {
1086 if (!buxtond_handle_message(self, cl, cl->size)) {
1087 buxton_log("Communication failed with client %d\n", cl->fd);
1092 if (message_limit) {
1095 if (recv(cl->fd, &peek, sizeof(uint16_t), MSG_PEEK | MSG_DONTWAIT) > 0) {
1104 cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1109 terminate_client(self, cl, i);
1113 void terminate_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
1115 del_pollfd(self, i);
1117 if (cl->smack_label) {
1118 free(cl->smack_label->value);
1120 free(cl->smack_label);
1122 buxton_debug("Closed connection from fd %d\n", cl->fd);
1123 LIST_REMOVE(client_list_item, item, self->client_list, cl);
1129 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1134 * indent-tabs-mode: t
1137 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1138 * :indentSize=8:tabSize=8:noTabs=false: