tizen 2.3 release
[external/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                         abort();
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                         abort();
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                         abort();
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                         abort();
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                         abort();
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                         abort();
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                         abort();
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                         abort();
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                         abort();
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                 nitem = elem->data;
442                 int c = 1;
443                 __attribute__((unused)) bool unused;
444                 free(response);
445                 response = NULL;
446
447                 if (nitem->old_data && value) {
448                         switch (value->type) {
449                         case STRING:
450                                 c = memcmp((const void *)
451                                            (nitem->old_data->store.d_string.value),
452                                            (const void *)(value->store.d_string.value),
453                                            value->store.d_string.length);
454                                 break;
455                         case INT32:
456                                 c = memcmp((const void *)&(nitem->old_data->store.d_int32),
457                                            (const void *)&(value->store.d_int32),
458                                            sizeof(int32_t));
459                                 break;
460                         case UINT32:
461                                 c = memcmp((const void *)&(nitem->old_data->store.d_uint32),
462                                            (const void *)&(value->store.d_uint32),
463                                            sizeof(uint32_t));
464                                 break;
465                         case INT64:
466                                 c = memcmp((const void *)&(nitem->old_data->store.d_int64),
467                                            (const void *)&(value->store.d_int64),
468                                            sizeof(int64_t));
469                                 break;
470                         case UINT64:
471                                 c = memcmp((const void *)&(nitem->old_data->store.d_uint64),
472                                            (const void *)&(value->store.d_uint64),
473                                            sizeof(uint64_t));
474                                 break;
475                         case FLOAT:
476                                 c = memcmp((const void *)&(nitem->old_data->store.d_float),
477                                            (const void *)&(value->store.d_float),
478                                            sizeof(float));
479                                 break;
480                         case DOUBLE:
481                                 c = memcmp((const void *)&(nitem->old_data->store.d_double),
482                                            (const void *)&(value->store.d_double),
483                                            sizeof(double));
484                                 break;
485                         case BOOLEAN:
486                                 c = memcmp((const void *)&(nitem->old_data->store.d_boolean),
487                                            (const void *)&(value->store.d_boolean),
488                                            sizeof(bool));
489                                 break;
490                         default:
491                                 buxton_log("Internal state corruption: Notification data type invalid\n");
492                                 abort();
493                         }
494                 }
495
496                 if (!c) {
497                         continue;
498                 }
499                 if (nitem->old_data && (nitem->old_data->type == STRING)) {
500                         free(nitem->old_data->store.d_string.value);
501                 }
502                 free(nitem->old_data);
503
504                 nitem->old_data = malloc0(sizeof(BuxtonData));
505                 if (!nitem->old_data) {
506                         abort();
507                 }
508                 if (value) {
509                         if (!buxton_data_copy(value, nitem->old_data)) {
510                                 abort();
511                         }
512                 }
513
514                 out_list = buxton_array_new();
515                 if (!out_list) {
516                         abort();
517                 }
518                 if (value) {
519                         if (!buxton_array_add(out_list, value)) {
520                                 abort();
521                         }
522                 }
523
524                 response_len = buxton_serialize_message(&response,
525                                                         BUXTON_CONTROL_CHANGED,
526                                                         nitem->msgid, out_list);
527                 buxton_array_free(&out_list, NULL);
528                 if (response_len == 0) {
529                         if (errno == ENOMEM) {
530                                 abort();
531                         }
532                         buxton_log("Failed to serialize notification\n");
533                         abort();
534                 }
535                 buxton_debug("Notification to %d of key change (%s)\n", nitem->client->fd,
536                              key_name);
537
538                 unused = _write(nitem->client->fd, response, response_len);
539         }
540 }
541
542 void set_value(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
543                BuxtonData *value, int32_t *status)
544 {
545
546         assert(self);
547         assert(client);
548         assert(key);
549         assert(value);
550         assert(status);
551
552         *status = -1;
553
554         buxton_debug("Daemon setting [%s][%s][%s]\n",
555                      key->layer.value,
556                      key->group.value,
557                      key->name.value);
558
559         self->buxton.client.uid = client->cred.uid;
560
561         if (!buxton_direct_set_value(&self->buxton, key, value, client->smack_label)) {
562                 return;
563         }
564
565         *status = 0;
566         buxton_debug("Daemon set value completed\n");
567 }
568
569 void set_label(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
570                BuxtonData *value, int32_t *status)
571 {
572
573         assert(self);
574         assert(client);
575         assert(key);
576         assert(value);
577         assert(status);
578
579         *status = -1;
580
581         buxton_debug("Daemon setting label on [%s][%s][%s]\n",
582                      key->layer.value,
583                      key->group.value,
584                      key->name.value);
585
586         self->buxton.client.uid = client->cred.uid;
587
588         /* Use internal library to set label */
589         if (!buxton_direct_set_label(&self->buxton, key, &value->store.d_string)) {
590                 return;
591         }
592
593         *status = 0;
594         buxton_debug("Daemon set label completed\n");
595 }
596
597 void create_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
598                   int32_t *status)
599 {
600         assert(self);
601         assert(client);
602         assert(key);
603         assert(status);
604
605         *status = -1;
606
607         buxton_debug("Daemon creating group [%s][%s]\n",
608                      key->layer.value,
609                      key->group.value);
610
611         self->buxton.client.uid = client->cred.uid;
612
613         /* Use internal library to create group */
614         if (!buxton_direct_create_group(&self->buxton, key, client->smack_label)) {
615                 return;
616         }
617
618         *status = 0;
619         buxton_debug("Daemon create group completed\n");
620 }
621
622 void remove_group(BuxtonDaemon *self, client_list_item *client, _BuxtonKey *key,
623                   int32_t *status)
624 {
625         assert(self);
626         assert(client);
627         assert(key);
628         assert(status);
629
630         *status = -1;
631
632         buxton_debug("Daemon removing group [%s][%s]\n",
633                      key->layer.value,
634                      key->group.value);
635
636         self->buxton.client.uid = client->cred.uid;
637
638         /* Use internal library to create group */
639         if (!buxton_direct_remove_group(&self->buxton, key, client->smack_label)) {
640                 return;
641         }
642
643         *status = 0;
644         buxton_debug("Daemon remove group completed\n");
645 }
646
647 void unset_value(BuxtonDaemon *self, client_list_item *client,
648                  _BuxtonKey *key, int32_t *status)
649 {
650         assert(self);
651         assert(client);
652         assert(key);
653         assert(status);
654
655         *status = -1;
656
657         buxton_debug("Daemon unsetting [%s][%s][%s]\n",
658                      key->layer.value,
659                      key->group.value,
660                      key->name.value);
661
662         /* Use internal library to unset value */
663         self->buxton.client.uid = client->cred.uid;
664         if (!buxton_direct_unset_value(&self->buxton, key, client->smack_label)) {
665                 return;
666         }
667
668         buxton_debug("unset value returned successfully from db\n");
669
670         *status = 0;
671         buxton_debug("Daemon unset value completed\n");
672 }
673
674 BuxtonData *get_value(BuxtonDaemon *self, client_list_item *client,
675                       _BuxtonKey *key, int32_t *status)
676 {
677         BuxtonData *data = NULL;
678         BuxtonString label;
679         int32_t ret;
680
681         assert(self);
682         assert(client);
683         assert(key);
684         assert(status);
685
686         *status = -1;
687
688         data = malloc0(sizeof(BuxtonData));
689         if (!data) {
690                 abort();
691         }
692
693         if (key->layer.value) {
694                 buxton_debug("Daemon getting [%s][%s][%s]\n", key->layer.value,
695                              key->group.value, key->name.value);
696         } else {
697                 buxton_debug("Daemon getting [%s][%s]\n", key->group.value,
698                              key->name.value);
699         }
700         self->buxton.client.uid = client->cred.uid;
701         ret = buxton_direct_get_value(&self->buxton, key, data, &label,
702                                       client->smack_label);
703         if (ret) {
704                 goto fail;
705         }
706
707         free(label.value);
708         buxton_debug("get value returned successfully from db\n");
709
710         *status = 0;
711         goto end;
712 fail:
713         buxton_debug("get value failed\n");
714         free(data);
715         data = NULL;
716 end:
717
718         return data;
719 }
720
721 BuxtonArray *list_keys(BuxtonDaemon *self, client_list_item *client,
722                        BuxtonString *layer, int32_t *status)
723 {
724         BuxtonArray *ret_list = NULL;
725         assert(self);
726         assert(client);
727         assert(layer);
728         assert(status);
729
730         *status = -1;
731         if (buxton_direct_list_keys(&self->buxton, layer, &ret_list)) {
732                 *status = 0;
733         }
734         return ret_list;
735 }
736
737 void register_notification(BuxtonDaemon *self, client_list_item *client,
738                            _BuxtonKey *key, uint32_t msgid,
739                            int32_t *status)
740 {
741         BuxtonList *n_list = NULL;
742         BuxtonNotification *nitem;
743         BuxtonData *old_data = NULL;
744         int32_t key_status;
745         char *key_name;
746         int r;
747
748         assert(self);
749         assert(client);
750         assert(key);
751         assert(status);
752
753         *status = -1;
754
755         nitem = malloc0(sizeof(BuxtonNotification));
756         if (!nitem) {
757                 abort();
758         }
759         nitem->client = client;
760
761         /* Store data now, cheap */
762         old_data = get_value(self, client, key, &key_status);
763         if (key_status != 0) {
764                 free(nitem);
765                 return;
766         }
767         nitem->old_data = old_data;
768         nitem->msgid = msgid;
769
770         /* May be null, but will append regardless */
771         r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
772         if (r == -1) {
773                 abort();
774         }
775         n_list = hashmap_get(self->notify_mapping, key_name);
776
777         if (!n_list) {
778                 if (!buxton_list_append(&n_list, nitem)) {
779                         abort();
780                 }
781
782                 if (hashmap_put(self->notify_mapping, key_name, n_list) < 0) {
783                         abort();
784                 }
785         } else {
786                 free(key_name);
787                 if (!buxton_list_append(&n_list, nitem)) {
788                         abort();
789                 }
790         }
791         *status = 0;
792 }
793
794 uint32_t unregister_notification(BuxtonDaemon *self, client_list_item *client,
795                                  _BuxtonKey *key, int32_t *status)
796 {
797         BuxtonList *n_list = NULL;
798         BuxtonList *elem = NULL;
799         BuxtonNotification *nitem, *citem = NULL;
800         uint32_t msgid = 0;
801         _cleanup_free_ char *key_name = NULL;
802         void *old_key_name;
803         int r;
804
805         assert(self);
806         assert(client);
807         assert(key);
808         assert(status);
809
810         *status = -1;
811         r = asprintf(&key_name, "%s%s", key->group.value, key->name.value);
812         if (r == -1) {
813                 abort();
814         }
815         n_list = hashmap_get2(self->notify_mapping, key_name, &old_key_name);
816         /* This key isn't actually registered for notifications */
817         if (!n_list) {
818                 return 0;
819         }
820
821         BUXTON_LIST_FOREACH(n_list, elem) {
822                 nitem = elem->data;
823                 /* Find the list item for this client */
824                 if (nitem->client == client) {
825                         citem = nitem;
826                 }
827                 break;
828         };
829
830         /* Client hasn't registered for notifications on this key */
831         if (!citem) {
832                 return 0;
833         }
834
835         msgid = citem->msgid;
836         /* Remove client from notifications */
837         free_buxton_data(&(citem->old_data));
838         buxton_list_remove(&n_list, citem, true);
839
840         /* If we removed the last item, remove the mapping too */
841         if (!n_list) {
842                 (void)hashmap_remove(self->notify_mapping, key_name);
843                 free(old_key_name);
844         }
845
846         *status = 0;
847
848         return msgid;
849 }
850
851 bool identify_client(client_list_item *cl)
852 {
853         /* Identity handling */
854         ssize_t nr;
855         int data;
856         struct msghdr msgh;
857         struct iovec iov;
858         __attribute__((unused)) struct ucred *ucredp;
859         struct cmsghdr *cmhp;
860         socklen_t len = sizeof(struct ucred);
861         int on = 1;
862
863         assert(cl);
864
865         union {
866                 struct cmsghdr cmh;
867                 char control[CMSG_SPACE(sizeof(struct ucred))];
868         } control_un;
869
870         setsockopt(cl->fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
871
872         control_un.cmh.cmsg_len = CMSG_LEN(sizeof(struct ucred));
873         control_un.cmh.cmsg_level = SOL_SOCKET;
874         control_un.cmh.cmsg_type = SCM_CREDENTIALS;
875
876         msgh.msg_control = control_un.control;
877         msgh.msg_controllen = sizeof(control_un.control);
878
879         msgh.msg_iov = &iov;
880         msgh.msg_iovlen = 1;
881         iov.iov_base = &data;
882         iov.iov_len = sizeof(int);
883
884         msgh.msg_name = NULL;
885         msgh.msg_namelen = 0;
886
887         nr = recvmsg(cl->fd, &msgh, MSG_PEEK | MSG_DONTWAIT);
888         if (nr == -1) {
889                 return false;
890         }
891
892         cmhp = CMSG_FIRSTHDR(&msgh);
893
894         if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
895                 buxton_log("Invalid cmessage header from kernel\n");
896                 abort();
897         }
898
899         if (cmhp->cmsg_level != SOL_SOCKET || cmhp->cmsg_type != SCM_CREDENTIALS) {
900                 buxton_log("Missing credentials on socket\n");
901                 abort();
902         }
903
904         ucredp = (struct ucred *) CMSG_DATA(cmhp);
905
906         if (getsockopt(cl->fd, SOL_SOCKET, SO_PEERCRED, &cl->cred, &len) == -1) {
907                 buxton_log("Missing label on socket\n");
908                 abort();
909         }
910
911         return true;
912 }
913
914 void add_pollfd(BuxtonDaemon *self, int fd, short events, bool a)
915 {
916         assert(self);
917         assert(fd >= 0);
918
919         if (!greedy_realloc((void **) &(self->pollfds), &(self->nfds_alloc),
920                             (size_t)((self->nfds + 1) * (sizeof(struct pollfd))))) {
921                 abort();
922         }
923         if (!greedy_realloc((void **) &(self->accepting), &(self->accepting_alloc),
924                             (size_t)((self->nfds + 1) * (sizeof(self->accepting))))) {
925                 abort();
926         }
927         self->pollfds[self->nfds].fd = fd;
928         self->pollfds[self->nfds].events = events;
929         self->pollfds[self->nfds].revents = 0;
930         self->accepting[self->nfds] = a;
931         self->nfds++;
932
933         buxton_debug("Added fd %d to our poll list (accepting=%d)\n", fd, a);
934 }
935
936 void del_pollfd(BuxtonDaemon *self, nfds_t i)
937 {
938         assert(self);
939         assert(i < self->nfds);
940
941         buxton_debug("Removing fd %d from our list\n", self->pollfds[i].fd);
942
943         if (i != (self->nfds - 1)) {
944                 memmove(&(self->pollfds[i]),
945                         &(self->pollfds[i + 1]),
946                         /*
947                          * nfds < max int because of kernel limit of
948                          * fds. i + 1 < nfds because of if and assert
949                          * so the casts below are always >= 0 and less
950                          * than long unsigned max int so no loss of
951                          * precision.
952                          */
953                         (size_t)(self->nfds - i - 1) * sizeof(struct pollfd));
954                 memmove(&(self->accepting[i]),
955                         &(self->accepting[i + 1]),
956                         (size_t)(self->nfds - i - 1) * sizeof(bool));
957         }
958         self->nfds--;
959 }
960
961 void handle_smack_label(client_list_item *cl)
962 {
963         socklen_t slabel_len = 1;
964         char *buf = NULL;
965         BuxtonString *slabel = NULL;
966         int ret;
967
968         ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, NULL, &slabel_len);
969         /* libsmack ignores ERANGE here, so we ignore it too */
970         if (ret < 0 && errno != ERANGE) {
971                 switch (errno) {
972                 case ENOPROTOOPT:
973                         /* If Smack is not enabled, do not set the client label */
974                         cl->smack_label = NULL;
975                         return;
976                 default:
977                         buxton_log("getsockopt(): %m\n");
978                         exit(EXIT_FAILURE);
979                 }
980         }
981
982         slabel = malloc0(sizeof(BuxtonString));
983         if (!slabel) {
984                 abort();
985         }
986
987         /* already checked slabel_len positive above */
988         buf = malloc0((size_t)slabel_len + 1);
989         if (!buf) {
990                 abort();
991         }
992
993         ret = getsockopt(cl->fd, SOL_SOCKET, SO_PEERSEC, buf, &slabel_len);
994         if (ret < 0) {
995                 buxton_log("getsockopt(): %m\n");
996                 exit(EXIT_FAILURE);
997         }
998
999         slabel->value = buf;
1000         slabel->length = (uint32_t)slabel_len;
1001
1002         buxton_debug("getsockopt(): label=\"%s\"\n", slabel->value);
1003
1004         cl->smack_label = slabel;
1005 }
1006
1007 bool handle_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
1008 {
1009         ssize_t l;
1010         uint16_t peek;
1011         bool more_data = false;
1012         int message_limit = 32;
1013
1014         assert(self);
1015         assert(cl);
1016
1017         if (!cl->data) {
1018                 cl->data = malloc0(BUXTON_MESSAGE_HEADER_LENGTH);
1019                 cl->offset = 0;
1020                 cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1021         }
1022         if (!cl->data) {
1023                 abort();
1024         }
1025         /* client closed the connection, or some error occurred? */
1026         if (recv(cl->fd, cl->data, cl->size, MSG_PEEK | MSG_DONTWAIT) <= 0) {
1027                 goto terminate;
1028         }
1029
1030         /* need to authenticate the client? */
1031         if ((cl->cred.uid == 0) || (cl->cred.pid == 0)) {
1032                 if (!identify_client(cl)) {
1033                         goto terminate;
1034                 }
1035
1036                 handle_smack_label(cl);
1037         }
1038
1039         buxton_debug("New packet from UID %ld, PID %ld\n", cl->cred.uid, cl->cred.pid);
1040
1041         /* Hand off any read data */
1042         do {
1043                 l = read(self->pollfds[i].fd, (cl->data) + cl->offset, cl->size - cl->offset);
1044
1045                 /*
1046                  * Close clients with read errors. If there isn't more
1047                  * data and we don't have a complete message just
1048                  * cleanup and let the client resend their request.
1049                  */
1050                 if (l < 0) {
1051                         if (errno != EAGAIN) {
1052                                 goto terminate;
1053                         } else {
1054                                 goto cleanup;
1055                         }
1056                 } else if (l == 0) {
1057                         goto cleanup;
1058                 }
1059
1060                 cl->offset += (size_t)l;
1061                 if (cl->offset < BUXTON_MESSAGE_HEADER_LENGTH) {
1062                         continue;
1063                 }
1064                 if (cl->size == BUXTON_MESSAGE_HEADER_LENGTH) {
1065                         cl->size = buxton_get_message_size(cl->data, cl->offset);
1066                         if (cl->size == 0 || cl->size > BUXTON_MESSAGE_MAX_LENGTH) {
1067                                 goto terminate;
1068                         }
1069                 }
1070                 if (cl->size != BUXTON_MESSAGE_HEADER_LENGTH) {
1071                         cl->data = realloc(cl->data, cl->size);
1072                         if (!cl->data) {
1073                                 abort();
1074                         }
1075                 }
1076                 if (cl->size != cl->offset) {
1077                         continue;
1078                 }
1079                 if (!buxtond_handle_message(self, cl, cl->size)) {
1080                         buxton_log("Communication failed with client %d\n", cl->fd);
1081                         goto terminate;
1082                 }
1083
1084                 message_limit--;
1085                 if (message_limit) {
1086                         cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1087                         cl->offset = 0;
1088                         continue;
1089                 }
1090                 if (recv(cl->fd, &peek, sizeof(uint16_t), MSG_PEEK | MSG_DONTWAIT) > 0) {
1091                         more_data = true;
1092                 }
1093                 goto cleanup;
1094         } while (l > 0);
1095
1096 cleanup:
1097         free(cl->data);
1098         cl->data = NULL;
1099         cl->size = BUXTON_MESSAGE_HEADER_LENGTH;
1100         cl->offset = 0;
1101         return more_data;
1102
1103 terminate:
1104         terminate_client(self, cl, i);
1105         return more_data;
1106 }
1107
1108 void terminate_client(BuxtonDaemon *self, client_list_item *cl, nfds_t i)
1109 {
1110         del_pollfd(self, i);
1111         close(cl->fd);
1112         if (cl->smack_label) {
1113                 free(cl->smack_label->value);
1114         }
1115         free(cl->smack_label);
1116         free(cl->data);
1117         buxton_debug("Closed connection from fd %d\n", cl->fd);
1118         LIST_REMOVE(client_list_item, item, self->client_list, cl);
1119         free(cl);
1120         cl = NULL;
1121 }
1122
1123 /*
1124  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
1125  *
1126  * Local variables:
1127  * c-basic-offset: 8
1128  * tab-width: 8
1129  * indent-tabs-mode: t
1130  * End:
1131  *
1132  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1133  * :indentSize=8:tabSize=8:noTabs=false:
1134  */