Imported Upstream version 2
[platform/upstream/buxton.git] / src / core / daemon.c
1 /*
2  * This file is part of buxton.
3  *
4  * Copyright (C) 2013 Intel Corporation
5  *
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.
10  */
11
12 #ifdef HAVE_CONFIG_H
13         #include "config.h"
14 #endif
15
16 #include <assert.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <stdint.h>
20 #include <string.h>
21 #include <attr/xattr.h>
22
23 #include "daemon.h"
24 #include "direct.h"
25 #include "log.h"
26 #include "util.h"
27 #include "buxtonlist.h"
28
29 bool parse_list(BuxtonControlMessage msg, size_t count, BuxtonData *list,
30                 _BuxtonKey *key, BuxtonData **value)
31 {
32         switch (msg) {
33         case BUXTON_CONTROL_SET:
34                 if (count != 4) {
35                         return false;
36                 }
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) {
40                         return false;
41                 }
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;
46                 *value = &(list[3]);
47                 break;
48         case BUXTON_CONTROL_SET_LABEL:
49                 if (count == 3) {
50                         if (list[0].type != STRING || list[1].type != STRING ||
51                             list[2].type != STRING) {
52                                 return false;
53                         }
54                         key->type = STRING;
55                         key->layer = list[0].store.d_string;
56                         key->group = list[1].store.d_string;
57                         *value = &list[2];
58                 } else if (count == 4) {
59                         if (list[0].type != STRING || list[1].type != STRING ||
60                             list[2].type != STRING || list[3].type != STRING) {
61                                 return false;
62                         }
63                         key->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;
67                         *value = &list[3];
68                 } else {
69                         return false;
70                 }
71                 break;
72         case BUXTON_CONTROL_CREATE_GROUP:
73                 if (count != 2) {
74                         return false;
75                 }
76                 if (list[0].type != STRING || list[1].type != STRING) {
77                         return false;
78                 }
79                 key->type = STRING;
80                 key->layer = list[0].store.d_string;
81                 key->group = list[1].store.d_string;
82                 break;
83         case BUXTON_CONTROL_REMOVE_GROUP:
84                 if (count != 2) {
85                         return false;
86                 }
87                 if (list[0].type != STRING || list[1].type != STRING) {
88                         return false;
89                 }
90                 key->type = STRING;
91                 key->layer = list[0].store.d_string;
92                 key->group = list[1].store.d_string;
93                 break;
94         case BUXTON_CONTROL_GET:
95                 if (count == 4) {
96                         if (list[0].type != STRING || list[1].type != STRING ||
97                             list[2].type != STRING || list[3].type != UINT32) {
98                                 return false;
99                         }
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) {
107                                 return false;
108                         }
109                         key->group = list[0].store.d_string;
110                         key->name = list[1].store.d_string;
111                         key->type = list[2].store.d_uint32;
112                 } else {
113                         return false;
114                 }
115                 break;
116         case BUXTON_CONTROL_LIST:
117                 return false;
118                 if (count != 1) {
119                         return false;
120                 }
121                 if (list[0].type != STRING) {
122                         return false;
123                 }
124                 *value = &list[0];
125                 break;
126         case BUXTON_CONTROL_UNSET:
127                 if (count != 4) {
128                         return false;
129                 }
130                 if (list[0].type != STRING || list[1].type != STRING ||
131                     list[2].type != STRING || list[3].type != UINT32) {
132                         return false;
133                 }
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;
138                 break;
139         case BUXTON_CONTROL_NOTIFY:
140                 if (count != 3) {
141                         return false;
142                 }
143                 if (list[0].type != STRING || list[1].type != STRING ||
144                     list[2].type != UINT32) {
145                         return false;
146                 }
147                 key->group = list[0].store.d_string;
148                 key->name = list[1].store.d_string;
149                 key->type = list[2].store.d_uint32;
150                 break;
151         case BUXTON_CONTROL_UNNOTIFY:
152                 if (count != 3) {
153                         return false;
154                 }
155                 if (list[0].type != STRING || list[1].type != STRING ||
156                     list[2].type != UINT32) {
157                         return false;
158                 }
159                 key->group = list[0].store.d_string;
160                 key->name = list[1].store.d_string;
161                 key->type = list[2].store.d_uint32;
162                 break;
163         default:
164                 return false;
165         }
166
167         return true;
168 }
169
170 bool buxtond_handle_message(BuxtonDaemon *self, client_list_item *client, size_t size)
171 {
172         BuxtonControlMessage msg;
173         int32_t response;
174         BuxtonData *list = NULL;
175         _cleanup_buxton_data_ BuxtonData *data = NULL;
176         uint16_t i;
177         ssize_t p_count;
178         size_t response_len;
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;
184         uid_t uid;
185         bool ret = false;
186         uint32_t msgid = 0;
187         uint32_t n_msgid = 0;
188
189         assert(self);
190         assert(client);
191
192         uid = self->buxton.client.uid;
193         p_count = buxton_deserialize_message((uint8_t*)client->data, &msg, size,
194                                              &msgid, &list);
195         if (p_count < 0) {
196                 if (errno == ENOMEM) {
197                         abort();
198                 }
199                 /* Todo: terminate the client due to invalid message */
200                 buxton_debug("Failed to deserialize message\n");
201                 goto end;
202         }
203
204         /* Check valid range */
205         if (msg <= BUXTON_CONTROL_MIN || msg >= BUXTON_CONTROL_MAX) {
206                 goto end;
207         }
208
209         if (!parse_list(msg, (size_t)p_count, list, &key, &value)) {
210                 goto end;
211         }
212
213         /* use internal function from buxtond */
214         switch (msg) {
215         case BUXTON_CONTROL_SET:
216                 set_value(self, client, &key, value, &response);
217                 break;
218         case BUXTON_CONTROL_SET_LABEL:
219                 set_label(self, client, &key, value, &response);
220                 break;
221         case BUXTON_CONTROL_CREATE_GROUP:
222                 create_group(self, client, &key, &response);
223                 break;
224         case BUXTON_CONTROL_REMOVE_GROUP:
225                 remove_group(self, client, &key, &response);
226                 break;
227         case BUXTON_CONTROL_GET:
228                 data = get_value(self, client, &key, &response);
229                 break;
230         case BUXTON_CONTROL_UNSET:
231                 unset_value(self, client, &key, &response);
232                 break;
233         case BUXTON_CONTROL_LIST:
234                 key_list = list_keys(self, client, &value->store.d_string,
235                                      &response);
236                 break;
237         case BUXTON_CONTROL_NOTIFY:
238                 register_notification(self, client, &key, msgid, &response);
239                 break;
240         case BUXTON_CONTROL_UNNOTIFY:
241                 n_msgid = unregister_notification(self, client, &key, &response);
242                 break;
243         default:
244                 goto end;
245         }
246         /* Set a response code */
247         response_data.type = INT32;
248         response_data.store.d_int32 = response;
249         out_list = buxton_array_new();
250         if (!out_list) {
251                 abort();
252         }
253         if (!buxton_array_add(out_list, &response_data)) {
254                 abort();
255         }
256
257
258         switch (msg) {
259                 /* TODO: Use cascading switch */
260         case BUXTON_CONTROL_SET:
261                 response_len = buxton_serialize_message(&response_store,
262                                                         BUXTON_CONTROL_STATUS,
263                                                         msgid, out_list);
264                 if (response_len == 0) {
265                         if (errno == ENOMEM) {
266                                 abort();
267                         }
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
271                         goto end;
272                 }
273                 break;
274         case BUXTON_CONTROL_SET_LABEL:
275                 response_len = buxton_serialize_message(&response_store,
276                                                         BUXTON_CONTROL_STATUS,
277                                                         msgid, out_list);
278                 if (response_len == 0) {
279                         if (errno == ENOMEM) {
280                                 abort();
281                         }
282                         buxton_log("Failed to serialize set_label response message\n");
283                         goto end;
284                 }
285                 break;
286         case BUXTON_CONTROL_CREATE_GROUP:
287                 response_len = buxton_serialize_message(&response_store,
288                                                         BUXTON_CONTROL_STATUS,
289                                                         msgid, out_list);
290                 if (response_len == 0) {
291                         if (errno == ENOMEM) {
292                                 abort();
293                         }
294                         buxton_log("Failed to serialize create_group response message\n");
295                         goto end;
296                 }
297                 break;
298         case BUXTON_CONTROL_REMOVE_GROUP:
299                 response_len = buxton_serialize_message(&response_store,
300                                                         BUXTON_CONTROL_STATUS,
301                                                         msgid, out_list);
302                 if (response_len == 0) {
303                         if (errno == ENOMEM) {
304                                 abort();
305                         }
306                         buxton_log("Failed to serialize remove_group response message\n");
307                         goto end;
308                 }
309                 break;
310         case BUXTON_CONTROL_GET:
311                 if (data && !buxton_array_add(out_list, data)) {
312                         abort();
313                 }
314                 response_len = buxton_serialize_message(&response_store,
315                                                         BUXTON_CONTROL_STATUS,
316                                                         msgid, out_list);
317                 if (response_len == 0) {
318                         if (errno == ENOMEM) {
319                                 abort();
320                         }
321                         buxton_log("Failed to serialize get response message\n");
322                         goto end;
323                 }
324                 break;
325         case BUXTON_CONTROL_UNSET:
326                 response_len = buxton_serialize_message(&response_store,
327                                                         BUXTON_CONTROL_STATUS,
328                                                         msgid, out_list);
329                 if (response_len == 0) {
330                         if (errno == ENOMEM) {
331                                 abort();
332                         }
333                         buxton_log("Failed to serialize unset response message\n");
334                         goto end;
335                 }
336                 break;
337         case BUXTON_CONTROL_LIST:
338                 if (key_list) {
339                         for (i = 0; i < key_list->len; i++) {
340                                 if (!buxton_array_add(out_list, buxton_array_get(key_list, i))) {
341                                         abort();
342                                 }
343                         }
344                         buxton_array_free(&key_list, NULL);
345                 }
346                 response_len = buxton_serialize_message(&response_store,
347                                                         BUXTON_CONTROL_STATUS,
348                                                         msgid, out_list);
349                 if (response_len == 0) {
350                         if (errno == ENOMEM) {
351                                 abort();
352                         }
353                         buxton_log("Failed to serialize list response message\n");
354                         goto end;
355                 }
356                 break;
357         case BUXTON_CONTROL_NOTIFY:
358                 response_len = buxton_serialize_message(&response_store,
359                                                         BUXTON_CONTROL_STATUS,
360                                                         msgid, out_list);
361                 if (response_len == 0) {
362                         if (errno == ENOMEM) {
363                                 abort();
364                         }
365                         buxton_log("Failed to serialize notify response message\n");
366                         goto end;
367                 }
368                 break;
369         case BUXTON_CONTROL_UNNOTIFY:
370                 mdata.type = UINT32;
371                 mdata.store.d_uint32 = n_msgid;
372                 if (!buxton_array_add(out_list, &mdata)) {
373                         abort();
374                 }
375                 response_len = buxton_serialize_message(&response_store,
376                                                         BUXTON_CONTROL_STATUS,
377                                                         msgid, out_list);
378                 if (response_len == 0) {
379                         if (errno == ENOMEM) {
380                                 abort();
381                         }
382                         buxton_log("Failed to serialize unnotify response message\n");
383                         goto end;
384                 }
385                 break;
386         default:
387                 goto end;
388         }
389
390         /* Now write the response */
391         ret = _write(client->fd, response_store, response_len);
392         if (ret) {
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);
397                 }
398         }
399
400 end:
401         /* Restore our own UID */
402         self->buxton.client.uid = uid;
403         if (out_list) {
404                 buxton_array_free(&out_list, NULL);
405         }
406         if (list) {
407                 for (i=0; i < p_count; i++) {
408                         if (list[i].type == STRING) {
409                                 free(list[i].store.d_string.value);
410                         }
411                 }
412                 free(list);
413         }
414         return ret;
415 }
416
417 void buxtond_notify_clients(BuxtonDaemon *self, client_list_item *client,
418                               _BuxtonKey *key, BuxtonData *value)
419 {
420         BuxtonList *list = NULL;
421         BuxtonList *elem = NULL;
422         BuxtonNotification *nitem;
423         _cleanup_free_ uint8_t* response = NULL;
424         size_t response_len;
425         BuxtonArray *out_list = NULL;
426         _cleanup_free_ char *key_name;
427         int r;
428
429         assert(self);
430         assert(client);
431         assert(key);
432
433         r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
434         if (r == -1) {
435                 abort();
436         }
437         list = hashmap_get(self->notify_mapping, key_name);
438         if (!list) {
439                 return;
440         }
441
442         BUXTON_LIST_FOREACH(list, elem) {
443                 if (!elem) {
444                         //FIXME abort here since NULL elements
445                         //shouldn't be added to the list
446                         break;
447                 }
448                 nitem = elem->data;
449                 int c = 1;
450                 __attribute__((unused)) bool unused;
451                 free(response);
452                 response = NULL;
453
454                 if (nitem->old_data && value) {
455                         switch (value->type) {
456                         case STRING:
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);
461                                 break;
462                         case INT32:
463                                 c = memcmp((const void *)&(nitem->old_data->store.d_int32),
464                                            (const void *)&(value->store.d_int32),
465                                            sizeof(int32_t));
466                                 break;
467                         case UINT32:
468                                 c = memcmp((const void *)&(nitem->old_data->store.d_uint32),
469                                            (const void *)&(value->store.d_uint32),
470                                            sizeof(uint32_t));
471                                 break;
472                         case INT64:
473                                 c = memcmp((const void *)&(nitem->old_data->store.d_int64),
474                                            (const void *)&(value->store.d_int64),
475                                            sizeof(int64_t));
476                                 break;
477                         case UINT64:
478                                 c = memcmp((const void *)&(nitem->old_data->store.d_uint64),
479                                            (const void *)&(value->store.d_uint64),
480                                            sizeof(uint64_t));
481                                 break;
482                         case FLOAT:
483                                 c = memcmp((const void *)&(nitem->old_data->store.d_float),
484                                            (const void *)&(value->store.d_float),
485                                            sizeof(float));
486                                 break;
487                         case DOUBLE:
488                                 c = memcmp((const void *)&(nitem->old_data->store.d_double),
489                                            (const void *)&(value->store.d_double),
490                                            sizeof(double));
491                                 break;
492                         case BOOLEAN:
493                                 c = memcmp((const void *)&(nitem->old_data->store.d_boolean),
494                                            (const void *)&(value->store.d_boolean),
495                                            sizeof(bool));
496                                 break;
497                         default:
498                                 buxton_log("Internal state corruption: Notification data type invalid\n");
499                                 return;
500                         }
501                 }
502
503                 if (!c) {
504                         continue;
505                 }
506                 if (nitem->old_data && (nitem->old_data->type == STRING)) {
507                         free(nitem->old_data->store.d_string.value);
508                 }
509                 free(nitem->old_data);
510
511                 nitem->old_data = malloc0(sizeof(BuxtonData));
512                 if (!nitem->old_data) {
513                         abort();
514                 }
515                 if (value) {
516                         if (!buxton_data_copy(value, nitem->old_data)) {
517                                 abort();
518                         }
519                 }
520
521                 out_list = buxton_array_new();
522                 if (!out_list) {
523                         abort();
524                 }
525                 if (value) {
526                         if (!buxton_array_add(out_list, value)) {
527                                 abort();
528                         }
529                 }
530
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) {
537                                 abort();
538                         }
539                         buxton_log("Failed to serialize notification\n");
540                         //FIXME abort here serialize message error
541                         return;
542                 }
543                 buxton_debug("Notification to %d of key change (%s)\n", nitem->client->fd,
544                              key_name);
545
546                 unused = _write(nitem->client->fd, response, response_len);
547         }
548 }
549
550 void set_value(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
551                BuxtonData *value, int32_t *status)
552 {
553
554         assert(self);
555         assert(client);
556         assert(key);
557         assert(value);
558         assert(status);
559
560         *status = -1;
561
562         buxton_debug("Daemon setting [%s][%s][%s]\n",
563                      key->layer.value,
564                      key->group.value,
565                      key->name.value);
566
567         self->buxton.client.uid = client->cred.uid;
568
569         if (!buxton_direct_set_value(&self->buxton, key, value, client->smack_label)) {
570                 return;
571         }
572
573         *status = 0;
574         buxton_debug("Daemon set value completed\n");
575 }
576
577 void set_label(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
578                BuxtonData *value, int32_t *status)
579 {
580
581         assert(self);
582         assert(client);
583         assert(key);
584         assert(value);
585         assert(status);
586
587         *status = -1;
588
589         buxton_debug("Daemon setting label on [%s][%s][%s]\n",
590                      key->layer.value,
591                      key->group.value,
592                      key->name.value);
593
594         self->buxton.client.uid = client->cred.uid;
595
596         /* Use internal library to set label */
597         if (!buxton_direct_set_label(&self->buxton, key, &value->store.d_string)) {
598                 return;
599         }
600
601         *status = 0;
602         buxton_debug("Daemon set label completed\n");
603 }
604
605 void create_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
606                   int32_t *status)
607 {
608         assert(self);
609         assert(client);
610         assert(key);
611         assert(status);
612
613         *status = -1;
614
615         buxton_debug("Daemon creating group [%s][%s]\n",
616                      key->layer.value,
617                      key->group.value);
618
619         self->buxton.client.uid = client->cred.uid;
620
621         /* Use internal library to create group */
622         if (!buxton_direct_create_group(&self->buxton, key, client->smack_label)) {
623                 return;
624         }
625
626         *status = 0;
627         buxton_debug("Daemon create group completed\n");
628 }
629
630 void remove_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
631                   int32_t *status)
632 {
633         assert(self);
634         assert(client);
635         assert(key);
636         assert(status);
637
638         *status = -1;
639
640         buxton_debug("Daemon removing group [%s][%s]\n",
641                      key->layer.value,
642                      key->group.value);
643
644         self->buxton.client.uid = client->cred.uid;
645
646         /* Use internal library to create group */
647         if (!buxton_direct_remove_group(&self->buxton, key, client->smack_label)) {
648                 return;
649         }
650
651         *status = 0;
652         buxton_debug("Daemon remove group completed\n");
653 }
654
655 void unset_value(BuxtonDaemon *self, client_list_item *client,
656                  _BuxtonKey *key, int32_t *status)
657 {
658         assert(self);
659         assert(client);
660         assert(key);
661         assert(status);
662
663         *status = -1;
664
665         buxton_debug("Daemon unsetting [%s][%s][%s]\n",
666                      key->layer.value,
667                      key->group.value,
668                      key->name.value);
669
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)) {
673                 return;
674         }
675
676         buxton_debug("unset value returned successfully from db\n");
677
678         *status = 0;
679         buxton_debug("Daemon unset value completed\n");
680 }
681
682 BuxtonData *get_value(BuxtonDaemon *self, client_list_item *client,
683                       _BuxtonKey *key, int32_t *status)
684 {
685         BuxtonData *data = NULL;
686         BuxtonString label;
687         int32_t ret;
688
689         assert(self);
690         assert(client);
691         assert(key);
692         assert(status);
693
694         *status = -1;
695
696         data = malloc0(sizeof(BuxtonData));
697         if (!data) {
698                 abort();
699         }
700
701         if (key->layer.value) {
702                 buxton_debug("Daemon getting [%s][%s][%s]\n", key->layer.value,
703                              key->group.value, key->name.value);
704         } else {
705                 buxton_debug("Daemon getting [%s][%s]\n", key->group.value,
706                              key->name.value);
707         }
708         self->buxton.client.uid = client->cred.uid;
709         ret = buxton_direct_get_value(&self->buxton, key, data, &label,
710                                       client->smack_label);
711         if (ret) {
712                 goto fail;
713         }
714
715         free(label.value);
716         buxton_debug("get value returned successfully from db\n");
717
718         *status = 0;
719         goto end;
720 fail:
721         buxton_debug("get value failed\n");
722         free(data);
723         data = NULL;
724 end:
725
726         return data;
727 }
728
729 BuxtonArray *list_keys(BuxtonDaemon *self, client_list_item *client,
730                        BuxtonString *layer, int32_t *status)
731 {
732         BuxtonArray *ret_list = NULL;
733         assert(self);
734         assert(client);
735         assert(layer);
736         assert(status);
737
738         *status = -1;
739         if (buxton_direct_list_keys(&self->buxton, layer, &ret_list)) {
740                 *status = 0;
741         }
742         return ret_list;
743 }
744
745 void register_notification(BuxtonDaemon *self, client_list_item *client,
746                            _BuxtonKey *key, uint32_t msgid,
747                            int32_t *status)
748 {
749         BuxtonList *n_list = NULL;
750         BuxtonNotification *nitem;
751         BuxtonData *old_data = NULL;
752         int32_t key_status;
753         char *key_name;
754         int r;
755
756         assert(self);
757         assert(client);
758         assert(key);
759         assert(status);
760
761         *status = -1;
762
763         nitem = malloc0(sizeof(BuxtonNotification));
764         if (!nitem) {
765                 abort();
766         }
767         nitem->client = client;
768
769         /* Store data now, cheap */
770         old_data = get_value(self, client, key, &key_status);
771         if (key_status != 0) {
772                 free(nitem);
773                 return;
774         }
775         nitem->old_data = old_data;
776         nitem->msgid = msgid;
777
778         /* May be null, but will append regardless */
779         r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
780         if (r == -1) {
781                 abort();
782         }
783         n_list = hashmap_get(self->notify_mapping, key_name);
784
785         if (!n_list) {
786                 if (!buxton_list_append(&n_list, nitem)) {
787                         abort();
788                 }
789
790                 if (hashmap_put(self->notify_mapping, key_name, n_list) < 0) {
791                         abort();
792                 }
793         } else {
794                 free(key_name);
795                 if (!buxton_list_append(&n_list, nitem)) {
796                         abort();
797                 }
798         }
799         *status = 0;
800 }
801
802 uint32_t unregister_notification(BuxtonDaemon *self, client_list_item *client,
803                                  _BuxtonKey *key, int32_t *status)
804 {
805         BuxtonList *n_list = NULL;
806         BuxtonList *elem = NULL;
807         BuxtonNotification *nitem, *citem = NULL;
808         uint32_t msgid = 0;
809         _cleanup_free_ char *key_name = NULL;
810         void *old_key_name;
811         int r;
812
813         assert(self);
814         assert(client);
815         assert(key);
816         assert(status);
817
818         *status = -1;
819         r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
820         if (r == -1) {
821                 abort();
822         }
823         n_list = hashmap_get2(self->notify_mapping, key_name, &old_key_name);
824         /* This key isn't actually registered for notifications */
825         if (!n_list) {
826                 return 0;
827         }
828
829         BUXTON_LIST_FOREACH(n_list, elem) {
830                 nitem = elem->data;
831                 /* Find the list item for this client */
832                 if (nitem->client == client) {
833                         citem = nitem;
834                 }
835                 break;
836         };
837
838         /* Client hasn't registered for notifications on this key */
839         if (!citem) {
840                 return 0;
841         }
842
843         msgid = citem->msgid;
844         /* Remove client from notifications */
845         free_buxton_data(&(citem->old_data));
846         buxton_list_remove(&n_list, citem, true);
847
848         /* If we removed the last item, remove the mapping too */
849         if (!n_list) {
850                 (void)hashmap_remove(self->notify_mapping, key_name);
851                 free(old_key_name);
852         }
853
854         *status = 0;
855
856         return msgid;
857 }
858
859 bool identify_client(client_list_item *cl)
860 {
861         /* Identity handling */
862         ssize_t nr;
863         int data;
864         struct msghdr msgh;
865         struct iovec iov;
866         __attribute__((unused)) struct ucred *ucredp;
867         struct cmsghdr *cmhp;
868         socklen_t len = sizeof(struct ucred);
869         int on = 1;
870
871         assert(cl);
872
873         union {
874                 struct cmsghdr cmh;
875                 char control[CMSG_SPACE(sizeof(struct ucred))];
876         } control_un;
877
878         setsockopt(cl->fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
879
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;
883
884         msgh.msg_control = control_un.control;
885         msgh.msg_controllen = sizeof(control_un.control);
886
887         msgh.msg_iov = &iov;
888         msgh.msg_iovlen = 1;
889         iov.iov_base = &data;
890         iov.iov_len = sizeof(int);
891
892         msgh.msg_name = NULL;
893         msgh.msg_namelen = 0;
894
895         nr = recvmsg(cl->fd, &msgh, MSG_PEEK | MSG_DONTWAIT);
896         if (nr == -1) {
897                 return false;
898         }
899
900         cmhp = CMSG_FIRSTHDR(&msgh);
901
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
905                 return false;
906         }
907
908         if (cmhp->cmsg_level != SOL_SOCKET || cmhp->cmsg_type != SCM_CREDENTIALS) {
909                 return false;
910         }
911
912         ucredp = (struct ucred *) CMSG_DATA(cmhp);
913
914         if (getsockopt(cl->fd, SOL_SOCKET, SO_PEERCRED, &cl->cred, &len) == -1) {
915                 return false;
916         }
917
918         return true;
919 }
920
921 void add_pollfd(BuxtonDaemon *self, int fd, short events, bool a)
922 {
923         assert(self);
924         assert(fd >= 0);
925
926         if (!greedy_realloc((void **) &(self->pollfds), &(self->nfds_alloc),
927                             (size_t)((self->nfds + 1) * (sizeof(struct pollfd))))) {
928                 abort();
929         }
930         if (!greedy_realloc((void **) &(self->accepting), &(self->accepting_alloc),
931                             (size_t)((self->nfds + 1) * (sizeof(self->accepting))))) {
932                 abort();
933         }
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;
938         self->nfds++;
939
940         buxton_debug("Added fd %d to our poll list (accepting=%d)\n", fd, a);
941 }
942
943 void del_pollfd(BuxtonDaemon *self, nfds_t i)
944 {
945         assert(self);
946         assert(i < self->nfds);
947
948         buxton_debug("Removing fd %d from our list\n", self->pollfds[i].fd);
949
950         if (i != (self->nfds - 1)) {
951                 memmove(&(self->pollfds[i]),
952                         &(self->pollfds[i + 1]),
953                         /*
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
958                          * precision.
959                          */
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));
964         }
965         self->nfds--;
966 }
967
968 static void handle_smack_label(client_list_item *cl)
969 {
970         socklen_t slabel_len = 1;
971         char *buf = NULL;
972         BuxtonString *slabel = NULL;
973         int ret;
974
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) {
978                 switch (errno) {
979                 case ENOPROTOOPT:
980                         /* If Smack is not enabled, do not set the client label */
981                         cl->smack_label = NULL;
982                         return;
983                 default:
984                         buxton_log("getsockopt(): %m\n");
985                         exit(EXIT_FAILURE);
986                 }
987         }
988
989         slabel = malloc0(sizeof(BuxtonString));
990         if (!slabel) {
991                 abort();
992         }
993
994         /* already checked slabel_len positive above */
995         buf = malloc0((size_t)slabel_len + 1);
996         if (!buf) {
997                 abort();
998         }
999
1000         ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, buf, &slabel_len);
1001         if (ret < 0) {
1002                 buxton_log("getsockopt(): %m\n");
1003                 exit(EXIT_FAILURE);
1004         }
1005
1006         slabel->value = buf;
1007         slabel->length = (uint32_t)slabel_len;
1008
1009         buxton_debug("getsockopt(): label=\"%s\"\n", slabel->value);
1010
1011         cl->smack_label = slabel;
1012 }
1013
1014 bool handle_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
1015 {
1016         ssize_t l;
1017         uint16_t peek;
1018         bool more_data = false;
1019         int message_limit = 32;
1020
1021         assert(self);
1022         assert(cl);
1023
1024         if (!cl->data) {
1025                 cl->data = malloc0(BUXTON_MESSAGE_HEADER_LENGTH);
1026                 cl->offset = 0;
1027                 cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1028         }
1029         if (!cl->data) {
1030                 abort();
1031         }
1032         /* client closed the connection, or some error occurred? */
1033         if (recv(cl->fd, cl->data, cl->size, MSG_PEEK | MSG_DONTWAIT) <= 0) {
1034                 goto terminate;
1035         }
1036
1037         /* need to authenticate the client? */
1038         if ((cl->cred.uid == 0) || (cl->cred.pid == 0)) {
1039                 if (!identify_client(cl)) {
1040                         goto terminate;
1041                 }
1042
1043                 handle_smack_label(cl);
1044         }
1045
1046         buxton_debug("New packet from UID %ld, PID %ld\n", cl->cred.uid, cl->cred.pid);
1047
1048         /* Hand off any read data */
1049         do {
1050                 l = read(self->pollfds[i].fd, (cl->data) + cl->offset, cl->size - cl->offset);
1051
1052                 /*
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.
1056                  */
1057                 if (l < 0) {
1058                         if (errno != EAGAIN) {
1059                                 goto terminate;
1060                         } else {
1061                                 goto cleanup;
1062                         }
1063                 } else if (l == 0) {
1064                         goto cleanup;
1065                 }
1066
1067                 cl->offset += (size_t)l;
1068                 if (cl->offset < BUXTON_MESSAGE_HEADER_LENGTH) {
1069                         continue;
1070                 }
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) {
1074                                 goto terminate;
1075                         }
1076                 }
1077                 if (cl->size != BUXTON_MESSAGE_HEADER_LENGTH) {
1078                         cl->data = realloc(cl->data, cl->size);
1079                         if (!cl->data) {
1080                                 abort();
1081                         }
1082                 }
1083                 if (cl->size != cl->offset) {
1084                         continue;
1085                 }
1086                 if (!buxtond_handle_message(self, cl, cl->size)) {
1087                         buxton_log("Communication failed with client %d\n", cl->fd);
1088                         goto terminate;
1089                 }
1090
1091                 message_limit--;
1092                 if (message_limit) {
1093                         continue;
1094                 }
1095                 if (recv(cl->fd, &peek, sizeof(uint16_t), MSG_PEEK | MSG_DONTWAIT) > 0) {
1096                         more_data = true;
1097                 }
1098                 goto cleanup;
1099         } while (l > 0);
1100
1101 cleanup:
1102         free(cl->data);
1103         cl->data = NULL;
1104         cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1105         cl->offset = 0;
1106         return more_data;
1107
1108 terminate:
1109         terminate_client(self, cl, i);
1110         return more_data;
1111 }
1112
1113 void terminate_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
1114 {
1115         del_pollfd(self, i);
1116         close(cl->fd);
1117         if (cl->smack_label) {
1118                 free(cl->smack_label->value);
1119         }
1120         free(cl->smack_label);
1121         free(cl->data);
1122         buxton_debug("Closed connection from fd %d\n", cl->fd);
1123         LIST_REMOVE(client_list_item, item, self->client_list, cl);
1124         free(cl);
1125         cl = NULL;
1126 }
1127
1128 /*
1129  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
1130  *
1131  * Local variables:
1132  * c-basic-offset: 8
1133  * tab-width: 8
1134  * indent-tabs-mode: t
1135  * End:
1136  *
1137  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1138  * :indentSize=8:tabSize=8:noTabs=false:
1139  */