Imported Upstream version 1
[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                         goto end;
270                 }
271                 break;
272         case BUXTON_CONTROL_SET_LABEL:
273                 response_len = buxton_serialize_message(&response_store,
274                                                         BUXTON_CONTROL_STATUS,
275                                                         msgid, out_list);
276                 if (response_len == 0) {
277                         if (errno == ENOMEM) {
278                                 abort();
279                         }
280                         buxton_log("Failed to serialize set_label response message\n");
281                         goto end;
282                 }
283                 break;
284         case BUXTON_CONTROL_CREATE_GROUP:
285                 response_len = buxton_serialize_message(&response_store,
286                                                         BUXTON_CONTROL_STATUS,
287                                                         msgid, out_list);
288                 if (response_len == 0) {
289                         if (errno == ENOMEM) {
290                                 abort();
291                         }
292                         buxton_log("Failed to serialize create_group response message\n");
293                         goto end;
294                 }
295                 break;
296         case BUXTON_CONTROL_REMOVE_GROUP:
297                 response_len = buxton_serialize_message(&response_store,
298                                                         BUXTON_CONTROL_STATUS,
299                                                         msgid, out_list);
300                 if (response_len == 0) {
301                         if (errno == ENOMEM) {
302                                 abort();
303                         }
304                         buxton_log("Failed to serialize remove_group response message\n");
305                         goto end;
306                 }
307                 break;
308         case BUXTON_CONTROL_GET:
309                 if (data && !buxton_array_add(out_list, data)) {
310                         abort();
311                 }
312                 response_len = buxton_serialize_message(&response_store,
313                                                         BUXTON_CONTROL_STATUS,
314                                                         msgid, out_list);
315                 if (response_len == 0) {
316                         if (errno == ENOMEM) {
317                                 abort();
318                         }
319                         buxton_log("Failed to serialize get response message\n");
320                         goto end;
321                 }
322                 break;
323         case BUXTON_CONTROL_UNSET:
324                 response_len = buxton_serialize_message(&response_store,
325                                                         BUXTON_CONTROL_STATUS,
326                                                         msgid, out_list);
327                 if (response_len == 0) {
328                         if (errno == ENOMEM) {
329                                 abort();
330                         }
331                         buxton_log("Failed to serialize unset response message\n");
332                         goto end;
333                 }
334                 break;
335         case BUXTON_CONTROL_LIST:
336                 if (key_list) {
337                         for (i = 0; i < key_list->len; i++) {
338                                 if (!buxton_array_add(out_list, buxton_array_get(key_list, i))) {
339                                         abort();
340                                 }
341                         }
342                         buxton_array_free(&key_list, NULL);
343                 }
344                 response_len = buxton_serialize_message(&response_store,
345                                                         BUXTON_CONTROL_STATUS,
346                                                         msgid, out_list);
347                 if (response_len == 0) {
348                         if (errno == ENOMEM) {
349                                 abort();
350                         }
351                         buxton_log("Failed to serialize list response message\n");
352                         goto end;
353                 }
354                 break;
355         case BUXTON_CONTROL_NOTIFY:
356                 response_len = buxton_serialize_message(&response_store,
357                                                         BUXTON_CONTROL_STATUS,
358                                                         msgid, out_list);
359                 if (response_len == 0) {
360                         if (errno == ENOMEM) {
361                                 abort();
362                         }
363                         buxton_log("Failed to serialize notify response message\n");
364                         goto end;
365                 }
366                 break;
367         case BUXTON_CONTROL_UNNOTIFY:
368                 mdata.type = UINT32;
369                 mdata.store.d_uint32 = n_msgid;
370                 if (!buxton_array_add(out_list, &mdata)) {
371                         abort();
372                 }
373                 response_len = buxton_serialize_message(&response_store,
374                                                         BUXTON_CONTROL_STATUS,
375                                                         msgid, out_list);
376                 if (response_len == 0) {
377                         if (errno == ENOMEM) {
378                                 abort();
379                         }
380                         buxton_log("Failed to serialize unnotify response message\n");
381                         goto end;
382                 }
383                 break;
384         default:
385                 goto end;
386         }
387
388         /* Now write the response */
389         ret = _write(client->fd, response_store, response_len);
390         if (ret) {
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);
395                 }
396         }
397
398 end:
399         /* Restore our own UID */
400         self->buxton.client.uid = uid;
401         if (out_list) {
402                 buxton_array_free(&out_list, NULL);
403         }
404         if (list) {
405                 for (i=0; i < p_count; i++) {
406                         if (list[i].type == STRING) {
407                                 free(list[i].store.d_string.value);
408                         }
409                 }
410                 free(list);
411         }
412         return ret;
413 }
414
415 void buxtond_notify_clients(BuxtonDaemon *self, client_list_item *client,
416                               _BuxtonKey *key, BuxtonData *value)
417 {
418         BuxtonList *list = NULL;
419         BuxtonList *elem = NULL;
420         BuxtonNotification *nitem;
421         _cleanup_free_ uint8_t* response = NULL;
422         size_t response_len;
423         BuxtonArray *out_list = NULL;
424         _cleanup_free_ char *key_name;
425         int r;
426
427         assert(self);
428         assert(client);
429         assert(key);
430
431         r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
432         if (r == -1) {
433                 abort();
434         }
435         list = hashmap_get(self->notify_mapping, key_name);
436         if (!list) {
437                 return;
438         }
439
440         BUXTON_LIST_FOREACH(list, elem) {
441                 if (!elem) {
442                         break;
443                 }
444                 nitem = elem->data;
445                 int c = 1;
446                 __attribute__((unused)) bool unused;
447                 free(response);
448                 response = NULL;
449
450                 if (nitem->old_data && value) {
451                         switch (value->type) {
452                         case STRING:
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);
457                                 break;
458                         case INT32:
459                                 c = memcmp((const void *)&(nitem->old_data->store.d_int32),
460                                            (const void *)&(value->store.d_int32),
461                                            sizeof(int32_t));
462                                 break;
463                         case UINT32:
464                                 c = memcmp((const void *)&(nitem->old_data->store.d_uint32),
465                                            (const void *)&(value->store.d_uint32),
466                                            sizeof(uint32_t));
467                                 break;
468                         case INT64:
469                                 c = memcmp((const void *)&(nitem->old_data->store.d_int64),
470                                            (const void *)&(value->store.d_int64),
471                                            sizeof(int64_t));
472                                 break;
473                         case UINT64:
474                                 c = memcmp((const void *)&(nitem->old_data->store.d_uint64),
475                                            (const void *)&(value->store.d_uint64),
476                                            sizeof(uint64_t));
477                                 break;
478                         case FLOAT:
479                                 c = memcmp((const void *)&(nitem->old_data->store.d_float),
480                                            (const void *)&(value->store.d_float),
481                                            sizeof(float));
482                                 break;
483                         case DOUBLE:
484                                 c = memcmp((const void *)&(nitem->old_data->store.d_double),
485                                            (const void *)&(value->store.d_double),
486                                            sizeof(double));
487                                 break;
488                         case BOOLEAN:
489                                 c = memcmp((const void *)&(nitem->old_data->store.d_boolean),
490                                            (const void *)&(value->store.d_boolean),
491                                            sizeof(bool));
492                                 break;
493                         default:
494                                 buxton_log("Internal state corruption: Notification data type invalid\n");
495                                 return;
496                         }
497                 }
498
499                 if (!c) {
500                         continue;
501                 }
502                 if (nitem->old_data && (nitem->old_data->type == STRING)) {
503                         free(nitem->old_data->store.d_string.value);
504                 }
505                 free(nitem->old_data);
506
507                 nitem->old_data = malloc0(sizeof(BuxtonData));
508                 if (!nitem->old_data) {
509                         abort();
510                 }
511                 if (value) {
512                         if (!buxton_data_copy(value, nitem->old_data)) {
513                                 abort();
514                         }
515                 }
516
517                 out_list = buxton_array_new();
518                 if (!out_list) {
519                         abort();
520                 }
521                 if (value) {
522                         if (!buxton_array_add(out_list, value)) {
523                                 abort();
524                         }
525                 }
526
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) {
533                                 abort();
534                         }
535                         buxton_log("Failed to serialize notification\n");
536                         return;
537                 }
538                 buxton_debug("Notification to %d of key change (%s)\n", nitem->client->fd,
539                              key_name);
540
541                 unused = _write(nitem->client->fd, response, response_len);
542         }
543 }
544
545 void set_value(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
546                BuxtonData *value, int32_t *status)
547 {
548
549         assert(self);
550         assert(client);
551         assert(key);
552         assert(value);
553         assert(status);
554
555         *status = -1;
556
557         buxton_debug("Daemon setting [%s][%s][%s]\n",
558                      key->layer.value,
559                      key->group.value,
560                      key->name.value);
561
562         self->buxton.client.uid = client->cred.uid;
563
564         if (!buxton_direct_set_value(&self->buxton, key, value, client->smack_label)) {
565                 return;
566         }
567
568         *status = 0;
569         buxton_debug("Daemon set value completed\n");
570 }
571
572 void set_label(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
573                BuxtonData *value, int32_t *status)
574 {
575
576         assert(self);
577         assert(client);
578         assert(key);
579         assert(value);
580         assert(status);
581
582         *status = -1;
583
584         buxton_debug("Daemon setting label on [%s][%s][%s]\n",
585                      key->layer.value,
586                      key->group.value,
587                      key->name.value);
588
589         self->buxton.client.uid = client->cred.uid;
590
591         /* Use internal library to set label */
592         if (!buxton_direct_set_label(&self->buxton, key, &value->store.d_string)) {
593                 return;
594         }
595
596         *status = 0;
597         buxton_debug("Daemon set label completed\n");
598 }
599
600 void create_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
601                   int32_t *status)
602 {
603         assert(self);
604         assert(client);
605         assert(key);
606         assert(status);
607
608         *status = -1;
609
610         buxton_debug("Daemon creating group [%s][%s]\n",
611                      key->layer.value,
612                      key->group.value);
613
614         self->buxton.client.uid = client->cred.uid;
615
616         /* Use internal library to create group */
617         if (!buxton_direct_create_group(&self->buxton, key, client->smack_label)) {
618                 return;
619         }
620
621         *status = 0;
622         buxton_debug("Daemon create group completed\n");
623 }
624
625 void remove_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
626                   int32_t *status)
627 {
628         assert(self);
629         assert(client);
630         assert(key);
631         assert(status);
632
633         *status = -1;
634
635         buxton_debug("Daemon removing group [%s][%s]\n",
636                      key->layer.value,
637                      key->group.value);
638
639         self->buxton.client.uid = client->cred.uid;
640
641         /* Use internal library to create group */
642         if (!buxton_direct_remove_group(&self->buxton, key, client->smack_label)) {
643                 return;
644         }
645
646         *status = 0;
647         buxton_debug("Daemon remove group completed\n");
648 }
649
650 void unset_value(BuxtonDaemon *self, client_list_item *client,
651                  _BuxtonKey *key, int32_t *status)
652 {
653         assert(self);
654         assert(client);
655         assert(key);
656         assert(status);
657
658         *status = -1;
659
660         buxton_debug("Daemon unsetting [%s][%s][%s]\n",
661                      key->layer.value,
662                      key->group.value,
663                      key->name.value);
664
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)) {
668                 return;
669         }
670
671         buxton_debug("unset value returned successfully from db\n");
672
673         *status = 0;
674         buxton_debug("Daemon unset value completed\n");
675 }
676
677 BuxtonData *get_value(BuxtonDaemon *self, client_list_item *client,
678                       _BuxtonKey *key, int32_t *status)
679 {
680         BuxtonData *data = NULL;
681         BuxtonString label;
682         int32_t ret;
683
684         assert(self);
685         assert(client);
686         assert(key);
687         assert(status);
688
689         *status = -1;
690
691         data = malloc0(sizeof(BuxtonData));
692         if (!data) {
693                 abort();
694         }
695
696         if (key->layer.value) {
697                 buxton_debug("Daemon getting [%s][%s][%s]\n", key->layer.value,
698                              key->group.value, key->name.value);
699         } else {
700                 buxton_debug("Daemon getting [%s][%s]\n", key->group.value,
701                              key->name.value);
702         }
703         self->buxton.client.uid = client->cred.uid;
704         ret = buxton_direct_get_value(&self->buxton, key, data, &label,
705                                       client->smack_label);
706         if (ret) {
707                 goto fail;
708         }
709
710         free(label.value);
711         buxton_debug("get value returned successfully from db\n");
712
713         *status = 0;
714         goto end;
715 fail:
716         buxton_debug("get value failed\n");
717         free(data);
718         data = NULL;
719 end:
720
721         return data;
722 }
723
724 BuxtonArray *list_keys(BuxtonDaemon *self, client_list_item *client,
725                        BuxtonString *layer, int32_t *status)
726 {
727         BuxtonArray *ret_list = NULL;
728         assert(self);
729         assert(client);
730         assert(layer);
731         assert(status);
732
733         *status = -1;
734         if (buxton_direct_list_keys(&self->buxton, layer, &ret_list)) {
735                 *status = 0;
736         }
737         return ret_list;
738 }
739
740 void register_notification(BuxtonDaemon *self, client_list_item *client,
741                            _BuxtonKey *key, uint32_t msgid,
742                            int32_t *status)
743 {
744         BuxtonList *n_list = NULL;
745         BuxtonNotification *nitem;
746         BuxtonData *old_data = NULL;
747         int32_t key_status;
748         char *key_name;
749         int r;
750
751         assert(self);
752         assert(client);
753         assert(key);
754         assert(status);
755
756         *status = -1;
757
758         nitem = malloc0(sizeof(BuxtonNotification));
759         if (!nitem) {
760                 abort();
761         }
762         nitem->client = client;
763
764         /* Store data now, cheap */
765         old_data = get_value(self, client, key, &key_status);
766         if (key_status != 0) {
767                 free(nitem);
768                 return;
769         }
770         nitem->old_data = old_data;
771         nitem->msgid = msgid;
772
773         /* May be null, but will append regardless */
774         r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
775         if (r == -1) {
776                 abort();
777         }
778         n_list = hashmap_get(self->notify_mapping, key_name);
779
780         if (!n_list) {
781                 if (!buxton_list_append(&n_list, nitem)) {
782                         abort();
783                 }
784
785                 if (hashmap_put(self->notify_mapping, key_name, n_list) < 0) {
786                         abort();
787                 }
788         } else {
789                 free(key_name);
790                 if (!buxton_list_append(&n_list, nitem)) {
791                         abort();
792                 }
793         }
794         *status = 0;
795 }
796
797 uint32_t unregister_notification(BuxtonDaemon *self, client_list_item *client,
798                                  _BuxtonKey *key, int32_t *status)
799 {
800         BuxtonList *n_list = NULL;
801         BuxtonList *elem = NULL;
802         BuxtonNotification *nitem, *citem = NULL;
803         uint32_t msgid = 0;
804         _cleanup_free_ char *key_name = NULL;
805         void *old_key_name;
806         int r;
807
808         assert(self);
809         assert(client);
810         assert(key);
811         assert(status);
812
813         *status = -1;
814         r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
815         if (r == -1) {
816                 abort();
817         }
818         n_list = hashmap_get2(self->notify_mapping, key_name, &old_key_name);
819         /* This key isn't actually registered for notifications */
820         if (!n_list) {
821                 return 0;
822         }
823
824         BUXTON_LIST_FOREACH(n_list, elem) {
825                 if (!elem) {
826                         break;
827                 }
828                 nitem = elem->data;
829                 /* Find the list item for this client */
830                 if (nitem->client == client) {
831                         citem = nitem;
832                 }
833                 break;
834         };
835
836         /* Client hasn't registered for notifications on this key */
837         if (!citem) {
838                 return 0;
839         }
840
841         msgid = citem->msgid;
842         /* Remove client from notifications */
843         free_buxton_data(&(citem->old_data));
844         buxton_list_remove(&n_list, citem, true);
845
846         /* If we removed the last item, remove the mapping too */
847         if (!n_list) {
848                 free(old_key_name);
849                 (void)hashmap_remove(self->notify_mapping, key_name);
850         }
851
852         *status = 0;
853
854         return msgid;
855 }
856
857 bool identify_client(client_list_item *cl)
858 {
859         /* Identity handling */
860         ssize_t nr;
861         int data;
862         struct msghdr msgh;
863         struct iovec iov;
864         __attribute__((unused)) struct ucred *ucredp;
865         struct cmsghdr *cmhp;
866         socklen_t len = sizeof(struct ucred);
867         int on = 1;
868
869         assert(cl);
870
871         union {
872                 struct cmsghdr cmh;
873                 char control[CMSG_SPACE(sizeof(struct ucred))];
874         } control_un;
875
876         setsockopt(cl->fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
877
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;
881
882         msgh.msg_control = control_un.control;
883         msgh.msg_controllen = sizeof(control_un.control);
884
885         msgh.msg_iov = &iov;
886         msgh.msg_iovlen = 1;
887         iov.iov_base = &data;
888         iov.iov_len = sizeof(int);
889
890         msgh.msg_name = NULL;
891         msgh.msg_namelen = 0;
892
893         nr = recvmsg(cl->fd, &msgh, MSG_PEEK | MSG_DONTWAIT);
894         if (nr == -1) {
895                 return false;
896         }
897
898         cmhp = CMSG_FIRSTHDR(&msgh);
899
900         if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
901                 return false;
902         }
903
904         if (cmhp->cmsg_level != SOL_SOCKET || cmhp->cmsg_type != SCM_CREDENTIALS) {
905                 return false;
906         }
907
908         ucredp = (struct ucred *) CMSG_DATA(cmhp);
909
910         if (getsockopt(cl->fd, SOL_SOCKET, SO_PEERCRED, &cl->cred, &len) == -1) {
911                 return false;
912         }
913
914         return true;
915 }
916
917 void add_pollfd(BuxtonDaemon *self, int fd, short events, bool a)
918 {
919         assert(self);
920         assert(fd >= 0);
921
922         if (!greedy_realloc((void **) &(self->pollfds), &(self->nfds_alloc),
923                             (size_t)((self->nfds + 1) * (sizeof(struct pollfd))))) {
924                 abort();
925         }
926         if (!greedy_realloc((void **) &(self->accepting), &(self->accepting_alloc),
927                             (size_t)((self->nfds + 1) * (sizeof(self->accepting))))) {
928                 abort();
929         }
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;
934         self->nfds++;
935
936         buxton_debug("Added fd %d to our poll list (accepting=%d)\n", fd, a);
937 }
938
939 void del_pollfd(BuxtonDaemon *self, nfds_t i)
940 {
941         assert(self);
942         assert(i < self->nfds);
943
944         buxton_debug("Removing fd %d from our list\n", self->pollfds[i].fd);
945
946         if (i != (self->nfds - 1)) {
947                 memmove(&(self->pollfds[i]),
948                         &(self->pollfds[i + 1]),
949                         /*
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
954                          * precision.
955                          */
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));
960         }
961         self->nfds--;
962 }
963
964 static void handle_smack_label(client_list_item *cl)
965 {
966         socklen_t slabel_len = 1;
967         char *buf = NULL;
968         BuxtonString *slabel = NULL;
969         int ret;
970
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) {
974                 switch (errno) {
975                 case ENOPROTOOPT:
976                         /* If Smack is not enabled, do not set the client label */
977                         cl->smack_label = NULL;
978                         return;
979                 default:
980                         buxton_log("getsockopt(): %m\n");
981                         exit(EXIT_FAILURE);
982                 }
983         }
984
985         slabel = malloc0(sizeof(BuxtonString));
986         if (!slabel) {
987                 abort();
988         }
989
990         /* already checked slabel_len positive above */
991         buf = malloc0((size_t)slabel_len + 1);
992         if (!buf) {
993                 abort();
994         }
995
996         ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, buf, &slabel_len);
997         if (ret < 0) {
998                 buxton_log("getsockopt(): %m\n");
999                 exit(EXIT_FAILURE);
1000         }
1001
1002         slabel->value = buf;
1003         slabel->length = (uint32_t)slabel_len;
1004
1005         buxton_debug("getsockopt(): label=\"%s\"\n", slabel->value);
1006
1007         cl->smack_label = slabel;
1008 }
1009
1010 bool handle_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
1011 {
1012         ssize_t l;
1013         uint16_t peek;
1014         bool more_data = false;
1015         int message_limit = 32;
1016
1017         assert(self);
1018         assert(cl);
1019
1020         if (!cl->data) {
1021                 cl->data = malloc0(BUXTON_MESSAGE_HEADER_LENGTH);
1022                 cl->offset = 0;
1023                 cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1024         }
1025         if (!cl->data) {
1026                 abort();
1027         }
1028         /* client closed the connection, or some error occurred? */
1029         if (recv(cl->fd, cl->data, cl->size, MSG_PEEK | MSG_DONTWAIT) <= 0) {
1030                 goto terminate;
1031         }
1032
1033         /* need to authenticate the client? */
1034         if ((cl->cred.uid == 0) || (cl->cred.pid == 0)) {
1035                 if (!identify_client(cl)) {
1036                         goto terminate;
1037                 }
1038
1039                 handle_smack_label(cl);
1040         }
1041
1042         buxton_debug("New packet from UID %ld, PID %ld\n", cl->cred.uid, cl->cred.pid);
1043
1044         /* Hand off any read data */
1045         do {
1046                 l = read(self->pollfds[i].fd, (cl->data) + cl->offset, cl->size - cl->offset);
1047
1048                 /*
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.
1052                  */
1053                 if (l < 0) {
1054                         if (errno != EAGAIN) {
1055                                 goto terminate;
1056                         } else {
1057                                 goto cleanup;
1058                         }
1059                 } else if (l == 0) {
1060                         goto cleanup;
1061                 }
1062
1063                 cl->offset += (size_t)l;
1064                 if (cl->offset < BUXTON_MESSAGE_HEADER_LENGTH) {
1065                         continue;
1066                 }
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) {
1070                                 goto terminate;
1071                         }
1072                 }
1073                 if (cl->size != BUXTON_MESSAGE_HEADER_LENGTH) {
1074                         cl->data = realloc(cl->data, cl->size);
1075                         if (!cl->data) {
1076                                 abort();
1077                         }
1078                 }
1079                 if (cl->size != cl->offset) {
1080                         continue;
1081                 }
1082                 if (!buxtond_handle_message(self, cl, cl->size)) {
1083                         buxton_log("Communication failed with client %d\n", cl->fd);
1084                         goto terminate;
1085                 }
1086
1087                 message_limit--;
1088                 if (message_limit) {
1089                         continue;
1090                 }
1091                 if (recv(cl->fd, &peek, sizeof(uint16_t), MSG_PEEK | MSG_DONTWAIT) > 0) {
1092                         more_data = true;
1093                 }
1094                 goto cleanup;
1095         } while (l > 0);
1096
1097 cleanup:
1098         free(cl->data);
1099         cl->data = NULL;
1100         cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1101         cl->offset = 0;
1102         return more_data;
1103
1104 terminate:
1105         terminate_client(self, cl, i);
1106         return more_data;
1107 }
1108
1109 void terminate_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
1110 {
1111         del_pollfd(self, i);
1112         close(cl->fd);
1113         if (cl->smack_label) {
1114                 free(cl->smack_label->value);
1115         }
1116         free(cl->smack_label);
1117         free(cl->data);
1118         buxton_debug("Closed connection from fd %d\n", cl->fd);
1119         LIST_REMOVE(client_list_item, item, self->client_list, cl);
1120         free(cl);
1121         cl = NULL;
1122 }
1123
1124 /*
1125  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
1126  *
1127  * Local variables:
1128  * c-basic-offset: 8
1129  * tab-width: 8
1130  * indent-tabs-mode: t
1131  * End:
1132  *
1133  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1134  * :indentSize=8:tabSize=8:noTabs=false:
1135  */