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) {
446 __attribute__((unused)) bool unused;
450 if (nitem->old_data && value) {
451 switch (value->type) {
453 c = memcmp((const void *)
454 (nitem->old_data->store.d_string.value),
455 (const void *)(value->store.d_string.value),
456 value->store.d_string.length);
459 c = memcmp((const void *)&(nitem->old_data->store.d_int32),
460 (const void *)&(value->store.d_int32),
464 c = memcmp((const void *)&(nitem->old_data->store.d_uint32),
465 (const void *)&(value->store.d_uint32),
469 c = memcmp((const void *)&(nitem->old_data->store.d_int64),
470 (const void *)&(value->store.d_int64),
474 c = memcmp((const void *)&(nitem->old_data->store.d_uint64),
475 (const void *)&(value->store.d_uint64),
479 c = memcmp((const void *)&(nitem->old_data->store.d_float),
480 (const void *)&(value->store.d_float),
484 c = memcmp((const void *)&(nitem->old_data->store.d_double),
485 (const void *)&(value->store.d_double),
489 c = memcmp((const void *)&(nitem->old_data->store.d_boolean),
490 (const void *)&(value->store.d_boolean),
494 buxton_log("Internal state corruption: Notification data type invalid\n");
502 if (nitem->old_data && (nitem->old_data->type == STRING)) {
503 free(nitem->old_data->store.d_string.value);
505 free(nitem->old_data);
507 nitem->old_data = malloc0(sizeof(BuxtonData));
508 if (!nitem->old_data) {
512 if (!buxton_data_copy(value, nitem->old_data)) {
517 out_list = buxton_array_new();
522 if (!buxton_array_add(out_list, value)) {
527 response_len = buxton_serialize_message(&response,
528 BUXTON_CONTROL_CHANGED,
529 nitem->msgid, out_list);
530 buxton_array_free(&out_list, NULL);
531 if (response_len == 0) {
532 if (errno == ENOMEM) {
535 buxton_log("Failed to serialize notification\n");
538 buxton_debug("Notification to %d of key change (%s)\n", nitem->client->fd,
541 unused = _write(nitem->client->fd, response, response_len);
545 void set_value(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
546 BuxtonData *value, int32_t *status)
557 buxton_debug("Daemon setting [%s][%s][%s]\n",
562 self->buxton.client.uid = client->cred.uid;
564 if (!buxton_direct_set_value(&self->buxton, key, value, client->smack_label)) {
569 buxton_debug("Daemon set value completed\n");
572 void set_label(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
573 BuxtonData *value, int32_t *status)
584 buxton_debug("Daemon setting label on [%s][%s][%s]\n",
589 self->buxton.client.uid = client->cred.uid;
591 /* Use internal library to set label */
592 if (!buxton_direct_set_label(&self->buxton, key, &value->store.d_string)) {
597 buxton_debug("Daemon set label completed\n");
600 void create_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
610 buxton_debug("Daemon creating group [%s][%s]\n",
614 self->buxton.client.uid = client->cred.uid;
616 /* Use internal library to create group */
617 if (!buxton_direct_create_group(&self->buxton, key, client->smack_label)) {
622 buxton_debug("Daemon create group completed\n");
625 void remove_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
635 buxton_debug("Daemon removing group [%s][%s]\n",
639 self->buxton.client.uid = client->cred.uid;
641 /* Use internal library to create group */
642 if (!buxton_direct_remove_group(&self->buxton, key, client->smack_label)) {
647 buxton_debug("Daemon remove group completed\n");
650 void unset_value(BuxtonDaemon *self, client_list_item *client,
651 _BuxtonKey *key, int32_t *status)
660 buxton_debug("Daemon unsetting [%s][%s][%s]\n",
665 /* Use internal library to unset value */
666 self->buxton.client.uid = client->cred.uid;
667 if (!buxton_direct_unset_value(&self->buxton, key, client->smack_label)) {
671 buxton_debug("unset value returned successfully from db\n");
674 buxton_debug("Daemon unset value completed\n");
677 BuxtonData *get_value(BuxtonDaemon *self, client_list_item *client,
678 _BuxtonKey *key, int32_t *status)
680 BuxtonData *data = NULL;
691 data = malloc0(sizeof(BuxtonData));
696 if (key->layer.value) {
697 buxton_debug("Daemon getting [%s][%s][%s]\n", key->layer.value,
698 key->group.value, key->name.value);
700 buxton_debug("Daemon getting [%s][%s]\n", key->group.value,
703 self->buxton.client.uid = client->cred.uid;
704 ret = buxton_direct_get_value(&self->buxton, key, data, &label,
705 client->smack_label);
711 buxton_debug("get value returned successfully from db\n");
716 buxton_debug("get value failed\n");
724 BuxtonArray *list_keys(BuxtonDaemon *self, client_list_item *client,
725 BuxtonString *layer, int32_t *status)
727 BuxtonArray *ret_list = NULL;
734 if (buxton_direct_list_keys(&self->buxton, layer, &ret_list)) {
740 void register_notification(BuxtonDaemon *self, client_list_item *client,
741 _BuxtonKey *key, uint32_t msgid,
744 BuxtonList *n_list = NULL;
745 BuxtonNotification *nitem;
746 BuxtonData *old_data = NULL;
758 nitem = malloc0(sizeof(BuxtonNotification));
762 nitem->client = client;
764 /* Store data now, cheap */
765 old_data = get_value(self, client, key, &key_status);
766 if (key_status != 0) {
770 nitem->old_data = old_data;
771 nitem->msgid = msgid;
773 /* May be null, but will append regardless */
774 r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
778 n_list = hashmap_get(self->notify_mapping, key_name);
781 if (!buxton_list_append(&n_list, nitem)) {
785 if (hashmap_put(self->notify_mapping, key_name, n_list) < 0) {
790 if (!buxton_list_append(&n_list, nitem)) {
797 uint32_t unregister_notification(BuxtonDaemon *self, client_list_item *client,
798 _BuxtonKey *key, int32_t *status)
800 BuxtonList *n_list = NULL;
801 BuxtonList *elem = NULL;
802 BuxtonNotification *nitem, *citem = NULL;
804 _cleanup_free_ char *key_name = NULL;
814 r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
818 n_list = hashmap_get2(self->notify_mapping, key_name, &old_key_name);
819 /* This key isn't actually registered for notifications */
824 BUXTON_LIST_FOREACH(n_list, elem) {
829 /* Find the list item for this client */
830 if (nitem->client == client) {
836 /* Client hasn't registered for notifications on this key */
841 msgid = citem->msgid;
842 /* Remove client from notifications */
843 free_buxton_data(&(citem->old_data));
844 buxton_list_remove(&n_list, citem, true);
846 /* If we removed the last item, remove the mapping too */
849 (void)hashmap_remove(self->notify_mapping, key_name);
857 bool identify_client(client_list_item *cl)
859 /* Identity handling */
864 __attribute__((unused)) struct ucred *ucredp;
865 struct cmsghdr *cmhp;
866 socklen_t len = sizeof(struct ucred);
873 char control[CMSG_SPACE(sizeof(struct ucred))];
876 setsockopt(cl->fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
878 control_un.cmh.cmsg_len = CMSG_LEN(sizeof(struct ucred));
879 control_un.cmh.cmsg_level = SOL_SOCKET;
880 control_un.cmh.cmsg_type = SCM_CREDENTIALS;
882 msgh.msg_control = control_un.control;
883 msgh.msg_controllen = sizeof(control_un.control);
887 iov.iov_base = &data;
888 iov.iov_len = sizeof(int);
890 msgh.msg_name = NULL;
891 msgh.msg_namelen = 0;
893 nr = recvmsg(cl->fd, &msgh, MSG_PEEK | MSG_DONTWAIT);
898 cmhp = CMSG_FIRSTHDR(&msgh);
900 if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
904 if (cmhp->cmsg_level != SOL_SOCKET || cmhp->cmsg_type != SCM_CREDENTIALS) {
908 ucredp = (struct ucred *) CMSG_DATA(cmhp);
910 if (getsockopt(cl->fd, SOL_SOCKET, SO_PEERCRED, &cl->cred, &len) == -1) {
917 void add_pollfd(BuxtonDaemon *self, int fd, short events, bool a)
922 if (!greedy_realloc((void **) &(self->pollfds), &(self->nfds_alloc),
923 (size_t)((self->nfds + 1) * (sizeof(struct pollfd))))) {
926 if (!greedy_realloc((void **) &(self->accepting), &(self->accepting_alloc),
927 (size_t)((self->nfds + 1) * (sizeof(self->accepting))))) {
930 self->pollfds[self->nfds].fd = fd;
931 self->pollfds[self->nfds].events = events;
932 self->pollfds[self->nfds].revents = 0;
933 self->accepting[self->nfds] = a;
936 buxton_debug("Added fd %d to our poll list (accepting=%d)\n", fd, a);
939 void del_pollfd(BuxtonDaemon *self, nfds_t i)
942 assert(i < self->nfds);
944 buxton_debug("Removing fd %d from our list\n", self->pollfds[i].fd);
946 if (i != (self->nfds - 1)) {
947 memmove(&(self->pollfds[i]),
948 &(self->pollfds[i + 1]),
950 * nfds < max int because of kernel limit of
951 * fds. i + 1 < nfds because of if and assert
952 * so the casts below are always >= 0 and less
953 * than long unsigned max int so no loss of
956 (size_t)(self->nfds - i - 1) * sizeof(struct pollfd));
957 memmove(&(self->accepting[i]),
958 &(self->accepting[i + 1]),
959 (size_t)(self->nfds - i - 1) * sizeof(self->accepting));
964 static void handle_smack_label(client_list_item *cl)
966 socklen_t slabel_len = 1;
968 BuxtonString *slabel = NULL;
971 ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, NULL, &slabel_len);
972 /* libsmack ignores ERANGE here, so we ignore it too */
973 if (ret < 0 && errno != ERANGE) {
976 /* If Smack is not enabled, do not set the client label */
977 cl->smack_label = NULL;
980 buxton_log("getsockopt(): %m\n");
985 slabel = malloc0(sizeof(BuxtonString));
990 /* already checked slabel_len positive above */
991 buf = malloc0((size_t)slabel_len + 1);
996 ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, buf, &slabel_len);
998 buxton_log("getsockopt(): %m\n");
1002 slabel->value = buf;
1003 slabel->length = (uint32_t)slabel_len;
1005 buxton_debug("getsockopt(): label=\"%s\"\n", slabel->value);
1007 cl->smack_label = slabel;
1010 bool handle_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
1014 bool more_data = false;
1015 int message_limit = 32;
1021 cl->data = malloc0(BUXTON_MESSAGE_HEADER_LENGTH);
1023 cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1028 /* client closed the connection, or some error occurred? */
1029 if (recv(cl->fd, cl->data, cl->size, MSG_PEEK | MSG_DONTWAIT) <= 0) {
1033 /* need to authenticate the client? */
1034 if ((cl->cred.uid == 0) || (cl->cred.pid == 0)) {
1035 if (!identify_client(cl)) {
1039 handle_smack_label(cl);
1042 buxton_debug("New packet from UID %ld, PID %ld\n", cl->cred.uid, cl->cred.pid);
1044 /* Hand off any read data */
1046 l = read(self->pollfds[i].fd, (cl->data) + cl->offset, cl->size - cl->offset);
1049 * Close clients with read errors. If there isn't more
1050 * data and we don't have a complete message just
1051 * cleanup and let the client resend their request.
1054 if (errno != EAGAIN) {
1059 } else if (l == 0) {
1063 cl->offset += (size_t)l;
1064 if (cl->offset < BUXTON_MESSAGE_HEADER_LENGTH) {
1067 if (cl->size == BUXTON_MESSAGE_HEADER_LENGTH) {
1068 cl->size = buxton_get_message_size(cl->data, cl->offset);
1069 if (cl->size == 0 || cl->size > BUXTON_MESSAGE_MAX_LENGTH) {
1073 if (cl->size != BUXTON_MESSAGE_HEADER_LENGTH) {
1074 cl->data = realloc(cl->data, cl->size);
1079 if (cl->size != cl->offset) {
1082 if (!buxtond_handle_message(self, cl, cl->size)) {
1083 buxton_log("Communication failed with client %d\n", cl->fd);
1088 if (message_limit) {
1091 if (recv(cl->fd, &peek, sizeof(uint16_t), MSG_PEEK | MSG_DONTWAIT) > 0) {
1100 cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1105 terminate_client(self, cl, i);
1109 void terminate_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
1111 del_pollfd(self, i);
1113 if (cl->smack_label) {
1114 free(cl->smack_label->value);
1116 free(cl->smack_label);
1118 buxton_debug("Closed connection from fd %d\n", cl->fd);
1119 LIST_REMOVE(client_list_item, item, self->client_list, cl);
1125 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1130 * indent-tabs-mode: t
1133 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1134 * :indentSize=8:tabSize=8:noTabs=false: