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");
272 case BUXTON_CONTROL_SET_LABEL:
273 response_len = buxton_serialize_message(&response_store,
274 BUXTON_CONTROL_STATUS,
276 if (response_len == 0) {
277 if (errno == ENOMEM) {
280 buxton_log("Failed to serialize set_label response message\n");
284 case BUXTON_CONTROL_CREATE_GROUP:
285 response_len = buxton_serialize_message(&response_store,
286 BUXTON_CONTROL_STATUS,
288 if (response_len == 0) {
289 if (errno == ENOMEM) {
292 buxton_log("Failed to serialize create_group response message\n");
296 case BUXTON_CONTROL_REMOVE_GROUP:
297 response_len = buxton_serialize_message(&response_store,
298 BUXTON_CONTROL_STATUS,
300 if (response_len == 0) {
301 if (errno == ENOMEM) {
304 buxton_log("Failed to serialize remove_group response message\n");
308 case BUXTON_CONTROL_GET:
309 if (data && !buxton_array_add(out_list, data)) {
312 response_len = buxton_serialize_message(&response_store,
313 BUXTON_CONTROL_STATUS,
315 if (response_len == 0) {
316 if (errno == ENOMEM) {
319 buxton_log("Failed to serialize get response message\n");
323 case BUXTON_CONTROL_UNSET:
324 response_len = buxton_serialize_message(&response_store,
325 BUXTON_CONTROL_STATUS,
327 if (response_len == 0) {
328 if (errno == ENOMEM) {
331 buxton_log("Failed to serialize unset response message\n");
335 case BUXTON_CONTROL_LIST:
337 for (i = 0; i < key_list->len; i++) {
338 if (!buxton_array_add(out_list, buxton_array_get(key_list, i))) {
342 buxton_array_free(&key_list, NULL);
344 response_len = buxton_serialize_message(&response_store,
345 BUXTON_CONTROL_STATUS,
347 if (response_len == 0) {
348 if (errno == ENOMEM) {
351 buxton_log("Failed to serialize list response message\n");
355 case BUXTON_CONTROL_NOTIFY:
356 response_len = buxton_serialize_message(&response_store,
357 BUXTON_CONTROL_STATUS,
359 if (response_len == 0) {
360 if (errno == ENOMEM) {
363 buxton_log("Failed to serialize notify response message\n");
367 case BUXTON_CONTROL_UNNOTIFY:
369 mdata.store.d_uint32 = n_msgid;
370 if (!buxton_array_add(out_list, &mdata)) {
373 response_len = buxton_serialize_message(&response_store,
374 BUXTON_CONTROL_STATUS,
376 if (response_len == 0) {
377 if (errno == ENOMEM) {
380 buxton_log("Failed to serialize unnotify response message\n");
388 /* Now write the response */
389 ret = _write(client->fd, response_store, response_len);
391 if (msg == BUXTON_CONTROL_SET && response == 0) {
392 buxtond_notify_clients(self, client, &key, value);
393 } else if (msg == BUXTON_CONTROL_UNSET && response == 0) {
394 buxtond_notify_clients(self, client, &key, NULL);
399 /* Restore our own UID */
400 self->buxton.client.uid = uid;
402 buxton_array_free(&out_list, NULL);
405 for (i=0; i < p_count; i++) {
406 if (list[i].type == STRING) {
407 free(list[i].store.d_string.value);
415 void buxtond_notify_clients(BuxtonDaemon *self, client_list_item *client,
416 _BuxtonKey *key, BuxtonData *value)
418 BuxtonList *list = NULL;
419 BuxtonList *elem = NULL;
420 BuxtonNotification *nitem;
421 _cleanup_free_ uint8_t* response = NULL;
423 BuxtonArray *out_list = NULL;
424 _cleanup_free_ char *key_name;
431 r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
435 list = hashmap_get(self->notify_mapping, key_name);
440 BUXTON_LIST_FOREACH(list, elem) {
443 __attribute__((unused)) bool unused;
447 if (nitem->old_data && value) {
448 switch (value->type) {
450 c = memcmp((const void *)
451 (nitem->old_data->store.d_string.value),
452 (const void *)(value->store.d_string.value),
453 value->store.d_string.length);
456 c = memcmp((const void *)&(nitem->old_data->store.d_int32),
457 (const void *)&(value->store.d_int32),
461 c = memcmp((const void *)&(nitem->old_data->store.d_uint32),
462 (const void *)&(value->store.d_uint32),
466 c = memcmp((const void *)&(nitem->old_data->store.d_int64),
467 (const void *)&(value->store.d_int64),
471 c = memcmp((const void *)&(nitem->old_data->store.d_uint64),
472 (const void *)&(value->store.d_uint64),
476 c = memcmp((const void *)&(nitem->old_data->store.d_float),
477 (const void *)&(value->store.d_float),
481 c = memcmp((const void *)&(nitem->old_data->store.d_double),
482 (const void *)&(value->store.d_double),
486 c = memcmp((const void *)&(nitem->old_data->store.d_boolean),
487 (const void *)&(value->store.d_boolean),
491 buxton_log("Internal state corruption: Notification data type invalid\n");
499 if (nitem->old_data && (nitem->old_data->type == STRING)) {
500 free(nitem->old_data->store.d_string.value);
502 free(nitem->old_data);
504 nitem->old_data = malloc0(sizeof(BuxtonData));
505 if (!nitem->old_data) {
509 if (!buxton_data_copy(value, nitem->old_data)) {
514 out_list = buxton_array_new();
519 if (!buxton_array_add(out_list, value)) {
524 response_len = buxton_serialize_message(&response,
525 BUXTON_CONTROL_CHANGED,
526 nitem->msgid, out_list);
527 buxton_array_free(&out_list, NULL);
528 if (response_len == 0) {
529 if (errno == ENOMEM) {
532 buxton_log("Failed to serialize notification\n");
535 buxton_debug("Notification to %d of key change (%s)\n", nitem->client->fd,
538 unused = _write(nitem->client->fd, response, response_len);
542 void set_value(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
543 BuxtonData *value, int32_t *status)
554 buxton_debug("Daemon setting [%s][%s][%s]\n",
559 self->buxton.client.uid = client->cred.uid;
561 if (!buxton_direct_set_value(&self->buxton, key, value, client->smack_label)) {
566 buxton_debug("Daemon set value completed\n");
569 void set_label(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
570 BuxtonData *value, int32_t *status)
581 buxton_debug("Daemon setting label on [%s][%s][%s]\n",
586 self->buxton.client.uid = client->cred.uid;
588 /* Use internal library to set label */
589 if (!buxton_direct_set_label(&self->buxton, key, &value->store.d_string)) {
594 buxton_debug("Daemon set label completed\n");
597 void create_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
607 buxton_debug("Daemon creating group [%s][%s]\n",
611 self->buxton.client.uid = client->cred.uid;
613 /* Use internal library to create group */
614 if (!buxton_direct_create_group(&self->buxton, key, client->smack_label)) {
619 buxton_debug("Daemon create group completed\n");
622 void remove_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
632 buxton_debug("Daemon removing group [%s][%s]\n",
636 self->buxton.client.uid = client->cred.uid;
638 /* Use internal library to create group */
639 if (!buxton_direct_remove_group(&self->buxton, key, client->smack_label)) {
644 buxton_debug("Daemon remove group completed\n");
647 void unset_value(BuxtonDaemon *self, client_list_item *client,
648 _BuxtonKey *key, int32_t *status)
657 buxton_debug("Daemon unsetting [%s][%s][%s]\n",
662 /* Use internal library to unset value */
663 self->buxton.client.uid = client->cred.uid;
664 if (!buxton_direct_unset_value(&self->buxton, key, client->smack_label)) {
668 buxton_debug("unset value returned successfully from db\n");
671 buxton_debug("Daemon unset value completed\n");
674 BuxtonData *get_value(BuxtonDaemon *self, client_list_item *client,
675 _BuxtonKey *key, int32_t *status)
677 BuxtonData *data = NULL;
688 data = malloc0(sizeof(BuxtonData));
693 if (key->layer.value) {
694 buxton_debug("Daemon getting [%s][%s][%s]\n", key->layer.value,
695 key->group.value, key->name.value);
697 buxton_debug("Daemon getting [%s][%s]\n", key->group.value,
700 self->buxton.client.uid = client->cred.uid;
701 ret = buxton_direct_get_value(&self->buxton, key, data, &label,
702 client->smack_label);
708 buxton_debug("get value returned successfully from db\n");
713 buxton_debug("get value failed\n");
721 BuxtonArray *list_keys(BuxtonDaemon *self, client_list_item *client,
722 BuxtonString *layer, int32_t *status)
724 BuxtonArray *ret_list = NULL;
731 if (buxton_direct_list_keys(&self->buxton, layer, &ret_list)) {
737 void register_notification(BuxtonDaemon *self, client_list_item *client,
738 _BuxtonKey *key, uint32_t msgid,
741 BuxtonList *n_list = NULL;
742 BuxtonNotification *nitem;
743 BuxtonData *old_data = NULL;
755 nitem = malloc0(sizeof(BuxtonNotification));
759 nitem->client = client;
761 /* Store data now, cheap */
762 old_data = get_value(self, client, key, &key_status);
763 if (key_status != 0) {
767 nitem->old_data = old_data;
768 nitem->msgid = msgid;
770 /* May be null, but will append regardless */
771 r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
775 n_list = hashmap_get(self->notify_mapping, key_name);
778 if (!buxton_list_append(&n_list, nitem)) {
782 if (hashmap_put(self->notify_mapping, key_name, n_list) < 0) {
787 if (!buxton_list_append(&n_list, nitem)) {
794 uint32_t unregister_notification(BuxtonDaemon *self, client_list_item *client,
795 _BuxtonKey *key, int32_t *status)
797 BuxtonList *n_list = NULL;
798 BuxtonList *elem = NULL;
799 BuxtonNotification *nitem, *citem = NULL;
801 _cleanup_free_ char *key_name = NULL;
811 r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
815 n_list = hashmap_get2(self->notify_mapping, key_name, &old_key_name);
816 /* This key isn't actually registered for notifications */
821 BUXTON_LIST_FOREACH(n_list, elem) {
823 /* Find the list item for this client */
824 if (nitem->client == client) {
830 /* Client hasn't registered for notifications on this key */
835 msgid = citem->msgid;
836 /* Remove client from notifications */
837 free_buxton_data(&(citem->old_data));
838 buxton_list_remove(&n_list, citem, true);
840 /* If we removed the last item, remove the mapping too */
842 (void)hashmap_remove(self->notify_mapping, key_name);
851 bool identify_client(client_list_item *cl)
853 /* Identity handling */
858 __attribute__((unused)) struct ucred *ucredp;
859 struct cmsghdr *cmhp;
860 socklen_t len = sizeof(struct ucred);
867 char control[CMSG_SPACE(sizeof(struct ucred))];
870 setsockopt(cl->fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
872 control_un.cmh.cmsg_len = CMSG_LEN(sizeof(struct ucred));
873 control_un.cmh.cmsg_level = SOL_SOCKET;
874 control_un.cmh.cmsg_type = SCM_CREDENTIALS;
876 msgh.msg_control = control_un.control;
877 msgh.msg_controllen = sizeof(control_un.control);
881 iov.iov_base = &data;
882 iov.iov_len = sizeof(int);
884 msgh.msg_name = NULL;
885 msgh.msg_namelen = 0;
887 nr = recvmsg(cl->fd, &msgh, MSG_PEEK | MSG_DONTWAIT);
892 cmhp = CMSG_FIRSTHDR(&msgh);
894 if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
895 buxton_log("Invalid cmessage header from kernel\n");
899 if (cmhp->cmsg_level != SOL_SOCKET || cmhp->cmsg_type != SCM_CREDENTIALS) {
900 buxton_log("Missing credentials on socket\n");
904 ucredp = (struct ucred *) CMSG_DATA(cmhp);
906 if (getsockopt(cl->fd, SOL_SOCKET, SO_PEERCRED, &cl->cred, &len) == -1) {
907 buxton_log("Missing label on socket\n");
914 void add_pollfd(BuxtonDaemon *self, int fd, short events, bool a)
919 if (!greedy_realloc((void **) &(self->pollfds), &(self->nfds_alloc),
920 (size_t)((self->nfds + 1) * (sizeof(struct pollfd))))) {
923 if (!greedy_realloc((void **) &(self->accepting), &(self->accepting_alloc),
924 (size_t)((self->nfds + 1) * (sizeof(self->accepting))))) {
927 self->pollfds[self->nfds].fd = fd;
928 self->pollfds[self->nfds].events = events;
929 self->pollfds[self->nfds].revents = 0;
930 self->accepting[self->nfds] = a;
933 buxton_debug("Added fd %d to our poll list (accepting=%d)\n", fd, a);
936 void del_pollfd(BuxtonDaemon *self, nfds_t i)
939 assert(i < self->nfds);
941 buxton_debug("Removing fd %d from our list\n", self->pollfds[i].fd);
943 if (i != (self->nfds - 1)) {
944 memmove(&(self->pollfds[i]),
945 &(self->pollfds[i + 1]),
947 * nfds < max int because of kernel limit of
948 * fds. i + 1 < nfds because of if and assert
949 * so the casts below are always >= 0 and less
950 * than long unsigned max int so no loss of
953 (size_t)(self->nfds - i - 1) * sizeof(struct pollfd));
954 memmove(&(self->accepting[i]),
955 &(self->accepting[i + 1]),
956 (size_t)(self->nfds - i - 1) * sizeof(bool));
961 void handle_smack_label(client_list_item *cl)
963 socklen_t slabel_len = 1;
965 BuxtonString *slabel = NULL;
968 ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, NULL, &slabel_len);
969 /* libsmack ignores ERANGE here, so we ignore it too */
970 if (ret < 0 && errno != ERANGE) {
973 /* If Smack is not enabled, do not set the client label */
974 cl->smack_label = NULL;
977 buxton_log("getsockopt(): %m\n");
982 slabel = malloc0(sizeof(BuxtonString));
987 /* already checked slabel_len positive above */
988 buf = malloc0((size_t)slabel_len + 1);
993 ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, buf, &slabel_len);
995 buxton_log("getsockopt(): %m\n");
1000 slabel->length = (uint32_t)slabel_len;
1002 buxton_debug("getsockopt(): label=\"%s\"\n", slabel->value);
1004 cl->smack_label = slabel;
1007 bool handle_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
1011 bool more_data = false;
1012 int message_limit = 32;
1018 cl->data = malloc0(BUXTON_MESSAGE_HEADER_LENGTH);
1020 cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1025 /* client closed the connection, or some error occurred? */
1026 if (recv(cl->fd, cl->data, cl->size, MSG_PEEK | MSG_DONTWAIT) <= 0) {
1030 /* need to authenticate the client? */
1031 if ((cl->cred.uid == 0) || (cl->cred.pid == 0)) {
1032 if (!identify_client(cl)) {
1036 handle_smack_label(cl);
1039 buxton_debug("New packet from UID %ld, PID %ld\n", cl->cred.uid, cl->cred.pid);
1041 /* Hand off any read data */
1043 l = read(self->pollfds[i].fd, (cl->data) + cl->offset, cl->size - cl->offset);
1046 * Close clients with read errors. If there isn't more
1047 * data and we don't have a complete message just
1048 * cleanup and let the client resend their request.
1051 if (errno != EAGAIN) {
1056 } else if (l == 0) {
1060 cl->offset += (size_t)l;
1061 if (cl->offset < BUXTON_MESSAGE_HEADER_LENGTH) {
1064 if (cl->size == BUXTON_MESSAGE_HEADER_LENGTH) {
1065 cl->size = buxton_get_message_size(cl->data, cl->offset);
1066 if (cl->size == 0 || cl->size > BUXTON_MESSAGE_MAX_LENGTH) {
1070 if (cl->size != BUXTON_MESSAGE_HEADER_LENGTH) {
1071 cl->data = realloc(cl->data, cl->size);
1076 if (cl->size != cl->offset) {
1079 if (!buxtond_handle_message(self, cl, cl->size)) {
1080 buxton_log("Communication failed with client %d\n", cl->fd);
1085 if (message_limit) {
1086 cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1090 if (recv(cl->fd, &peek, sizeof(uint16_t), MSG_PEEK | MSG_DONTWAIT) > 0) {
1099 cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1104 terminate_client(self, cl, i);
1108 void terminate_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
1110 del_pollfd(self, i);
1112 if (cl->smack_label) {
1113 free(cl->smack_label->value);
1115 free(cl->smack_label);
1117 buxton_debug("Closed connection from fd %d\n", cl->fd);
1118 LIST_REMOVE(client_list_item, item, self->client_list, cl);
1124 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1129 * indent-tabs-mode: t
1132 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1133 * :indentSize=8:tabSize=8:noTabs=false: