Try to use gettimeofday if clock_gettime is failed
[framework/appfw/shortcut.git] / lib / src / icon.c
1 /*
2  * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16 */
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <sys/socket.h>
24 #include <sys/ioctl.h>
25 #include <libgen.h>
26 #include <sys/time.h>
27 #include <time.h>
28
29 #include <dlog.h>
30 #include <glib.h>
31 #include <db-util.h>
32 #include <vconf.h>
33 #include <vconf-keys.h>
34
35 #include <packet.h>
36 #include <com-core.h>
37 #include <com-core_packet.h>
38
39 #include "shortcut_internal.h"
40 #include "shortcut.h"
41 #include "dlist.h"
42
43
44
45 #define CREATED 0x00BEEF00
46 #define DESTROYED 0x00DEAD00
47
48 static struct info {
49         int fd;
50         int (*init_cb)(int status, void *data);
51         void *cbdata;
52         int initialized;
53
54         const char *utility_socket;
55
56         struct dlist *pending_list;
57 #if defined(_USE_ECORE_TIME_GET)
58         clockid_t type;
59 #endif
60 } s_info = {
61         .fd = -1,
62         .init_cb = NULL,
63         .cbdata = NULL,
64         .initialized = 0,
65
66         .utility_socket = "/tmp/.utility.service",
67         .pending_list = NULL,
68 #if defined(_USE_ECORE_TIME_GET)
69         .type = CLOCK_MONOTONIC,
70 #endif
71 };
72
73
74
75 struct request_item {
76         struct shortcut_icon *handle;
77         icon_request_cb_t result_cb;
78         void *data;
79 };
80
81
82
83 struct pending_item {
84         struct request_item *item;
85         struct packet *packet;
86 };
87
88
89
90 struct block {
91         unsigned int idx;
92
93         char *type;
94         char *part;
95         char *data;
96         char *option;
97         char *id;
98         char *target_id;
99 };
100
101
102
103 struct shortcut_icon {
104         unsigned int state;
105         struct shortcut_desc *desc;
106         int refcnt;
107         void *data;
108 };
109
110
111
112 struct shortcut_desc {
113         int for_pd;
114
115         unsigned int last_idx;
116
117         struct dlist *block_list;
118 };
119
120
121
122 static inline void delete_block(struct block *block)
123 {
124         DbgPrint("Release block: %p\n", block);
125         free(block->type);
126         free(block->part);
127         free(block->data);
128         free(block->option);
129         free(block->id);
130         free(block->target_id);
131         free(block);
132 }
133
134
135
136 static inline int shortcut_icon_desc_close(struct shortcut_desc *handle)
137 {
138         struct dlist *l;
139         struct dlist *n;
140         struct block *block;
141
142         dlist_foreach_safe(handle->block_list, l, n, block) {
143                 handle->block_list = dlist_remove(handle->block_list, l);
144                 delete_block(block);
145         }
146
147         free(handle);
148         return 0;
149 }
150
151
152
153 static inline struct shortcut_icon *shortcut_icon_request_unref(struct shortcut_icon *handle)
154 {
155         handle->refcnt--;
156         DbgPrint("Handle: refcnt[%d]\n", handle->refcnt);
157
158         if (handle->refcnt == 0) {
159                 handle->state = DESTROYED;
160                 shortcut_icon_desc_close(handle->desc);
161                 free(handle);
162                 handle = NULL;
163         }
164
165         return handle;
166 }
167
168
169
170 static inline struct shortcut_icon *shortcut_icon_request_ref(struct shortcut_icon *handle)
171 {
172         handle->refcnt++;
173         DbgPrint("Handle: refcnt[%d]\n", handle->refcnt);
174         return handle;
175 }
176
177
178
179 static int disconnected_cb(int handle, void *data)
180 {
181         if (s_info.fd != handle) {
182                 return 0;
183         }
184
185         ErrPrint("Disconnected\n");
186         s_info.fd = -1;
187         s_info.init_cb = NULL;
188         s_info.cbdata = NULL;
189         s_info.initialized = 0;
190         return 0;
191 }
192
193
194
195 static inline struct shortcut_desc *shortcut_icon_desc_open(void)
196 {
197         struct shortcut_desc *handle;
198
199         handle = calloc(1, sizeof(*handle));
200         if (!handle) {
201                 ErrPrint("Error: %s\n", strerror(errno));
202                 return NULL;
203         }
204
205         return handle;
206 }
207
208
209
210 static inline int shortcut_icon_desc_save(struct shortcut_desc *handle, const char *filename)
211 {
212         struct dlist *l;
213         struct dlist *n;
214         struct block *block;
215         FILE *fp;
216
217         if (!handle) {
218                 return -EINVAL;
219         }
220
221         fp = fopen(filename, "w+t");
222         if (!fp) {
223                 ErrPrint("Error: %s\n", strerror(errno));
224                 return -EIO;
225         }
226
227         DbgPrint("Close and flush\n");
228         dlist_foreach_safe(handle->block_list, l, n, block) {
229                 DbgPrint("{\n");
230                 fprintf(fp, "{\n");
231                 if (block->type) {
232                         fprintf(fp, "type=%s\n", block->type);
233                         DbgPrint("type=%s\n", block->type);
234                 }
235
236                 if (block->part) {
237                         fprintf(fp, "part=%s\n", block->part);
238                         DbgPrint("part=%s\n", block->part);
239                 }
240
241                 if (block->data) {
242                         fprintf(fp, "data=%s\n", block->data);
243                         DbgPrint("data=%s\n", block->data);
244                 }
245
246                 if (block->option) {
247                         fprintf(fp, "option=%s\n", block->option);
248                         DbgPrint("option=%s\n", block->option);
249                 }
250
251                 if (block->id) {
252                         fprintf(fp, "id=%s\n", block->id);
253                         DbgPrint("id=%s\n", block->id);
254                 }
255
256                 if (block->target_id) {
257                         fprintf(fp, "target=%s\n", block->target_id);
258                         DbgPrint("target=%s\n", block->target_id);
259                 }
260
261                 fprintf(fp, "}\n");
262                 DbgPrint("}\n");
263         }
264
265         if (fclose(fp) != 0) {
266                 ErrPrint("fclose: %s\n", strerror(errno));
267         }
268         return 0;
269 }
270
271
272
273 static inline struct block *find_block(struct shortcut_desc *handle, const char *id, const char *part)
274 {
275         struct block *block;
276         struct dlist *l;
277
278         dlist_foreach(handle->block_list, l, block) {
279                 if (!strcmp(block->part, part) && (!id || !strcmp(block->id, id))) {
280                         return block;
281                 }
282         }
283
284         return NULL;
285 }
286
287
288
289 static inline int update_block(struct block *block, const char *data, const char *option)
290 {
291         char *_data = NULL;
292         char *_option = NULL;
293
294         if (data) {
295                 _data = strdup(data);
296                 if (!_data) {
297                         ErrPrint("Heap: %s\n", strerror(errno));
298                         return -ENOMEM;
299                 }
300         }
301
302         if (option) {
303                 _option = strdup(option);
304                 if (!_option) {
305                         ErrPrint("Heap: %s\n", strerror(errno));
306                         return -ENOMEM;
307                 }
308         }
309
310         free(block->data);
311         free(block->option);
312
313         block->data = _data;
314         block->option = _option;
315         return 0;
316 }
317
318
319
320 /*!
321  * \return idx
322  */
323
324
325
326 static inline int shortcut_icon_desc_add_block(struct shortcut_desc *handle, const char *id, const char *type, const char *part, const char *data, const char *option, const char *target_id)
327 {
328         struct block *block;
329
330         if (!handle || !type) {
331                 return SHORTCUT_ERROR_INVALID;
332         }
333
334         if (!part) {
335                 part = "";
336         }
337
338         if (!data) {
339                 data = "";
340         }
341
342         if (target_id) {
343                 if (strcmp(type, SHORTCUT_ICON_TYPE_SCRIPT)) {
344                         ErrPrint("target id only can be used for script type\n");
345                         return -EINVAL;
346                 }
347         }
348
349         block = find_block(handle, id, part);
350         if (!block) {
351                 block = calloc(1, sizeof(*block));
352                 if (!block) {
353                         ErrPrint("Heap: %s\n", strerror(errno));
354                         return SHORTCUT_ERROR_MEMORY;
355                 }
356
357                 block->type = strdup(type);
358                 if (!block->type) {
359                         ErrPrint("Heap: %s\n", strerror(errno));
360                         free(block);
361                         return SHORTCUT_ERROR_MEMORY;
362                 }
363
364                 block->part = strdup(part);
365                 if (!block->part) {
366                         ErrPrint("Heap: %s\n", strerror(errno));
367                         free(block->type);
368                         free(block);
369                         return SHORTCUT_ERROR_MEMORY;
370                 }
371
372                 block->data = strdup(data);
373                 if (!block->data) {
374                         ErrPrint("Heap: %s\n", strerror(errno));
375                         free(block->type);
376                         free(block->part);
377                         free(block);
378                         return SHORTCUT_ERROR_MEMORY;
379                 }
380
381                 if (option) {
382                         block->option = strdup(option);
383                         if (!block->option) {
384                                 ErrPrint("Heap: %s\n", strerror(errno));
385                                 free(block->data);
386                                 free(block->type);
387                                 free(block->part);
388                                 free(block);
389                                 return SHORTCUT_ERROR_MEMORY;
390                         }
391                 }
392
393                 if (id) {
394                         block->id = strdup(id);
395                         if (!block->id) {
396                                 ErrPrint("Heap: %s\n", strerror(errno));
397                                 free(block->option);
398                                 free(block->data);
399                                 free(block->type);
400                                 free(block->part);
401                                 free(block);
402                                 return SHORTCUT_ERROR_MEMORY;
403                         }
404                 }
405
406                 if (target_id) {
407                         block->target_id = strdup(target_id);
408                         if (!block->target_id) {
409                                 ErrPrint("Heap: %s\n", strerror(errno));
410                                 free(block->id);
411                                 free(block->option);
412                                 free(block->data);
413                                 free(block->type);
414                                 free(block->part);
415                                 free(block);
416                                 return SHORTCUT_ERROR_MEMORY;
417                         }
418                 }
419
420                 block->idx = handle->last_idx++;
421                 handle->block_list = dlist_append(handle->block_list, block);
422         } else {
423                 if (strcmp(block->type, type)) {
424                         ErrPrint("type is not valid (%s, %s)\n", block->type, type);
425                         return -EINVAL;
426                 }
427
428                 if ((block->target_id && !target_id) || (!block->target_id && target_id)) {
429                         ErrPrint("type is not valid (%s, %s)\n", block->type, type);
430                         return -EINVAL;
431                 }
432
433                 if (block->target_id && target_id && strcmp(block->target_id, target_id)) {
434                         ErrPrint("type is not valid (%s, %s)\n", block->type, type);
435                         return -EINVAL;
436                 }
437
438                 update_block(block, data, option);
439         }
440
441         return block->idx;
442 }
443
444
445
446 static int icon_request_cb(pid_t pid, int handle, const struct packet *packet, void *data)
447 {
448         struct request_item *item = data;
449         int ret;
450
451         if (!packet) {
452                 ret = -EFAULT;
453                 DbgPrint("Disconnected?\n");
454         } else {
455                 if (packet_get(packet, "i", &ret) != 1) {
456                         DbgPrint("Invalid packet\n");
457                         ret = -EINVAL;
458                 }
459         }
460
461         if (item->result_cb) {
462                 item->result_cb(item->handle, ret, item->data);
463         }
464
465         (void)shortcut_icon_request_unref(item->handle);
466         free(item);
467         return 0;
468 }
469
470
471
472 static inline int make_connection(void)
473 {
474         int ret;
475         static struct method service_table[] = {
476                 {
477                         .cmd = NULL,
478                         .handler = NULL,
479                 },
480         };
481
482         s_info.fd = com_core_packet_client_init(s_info.utility_socket, 0, service_table);
483         if (s_info.fd < 0) {
484                 ret = SHORTCUT_ERROR_COMM;
485
486                 if (s_info.init_cb) {
487                         s_info.init_cb(ret, s_info.cbdata);
488                 }
489         } else {
490                 struct dlist *l;
491                 struct dlist *n;
492                 struct pending_item *pend;
493
494                 if (s_info.init_cb) {
495                         s_info.init_cb(SHORTCUT_SUCCESS, s_info.cbdata);
496                 }
497
498                 dlist_foreach_safe(s_info.pending_list, l, n, pend) {
499                         s_info.pending_list = dlist_remove(s_info.pending_list, l);
500
501                         ret = com_core_packet_async_send(s_info.fd, pend->packet, 0.0f, icon_request_cb, pend->item);
502                         packet_destroy(pend->packet);
503                         if (ret < 0) {
504                                 ErrPrint("ret: %d\n", ret);
505                                 if (pend->item->result_cb) {
506                                         pend->item->result_cb(pend->item->handle, ret, pend->item->data);
507                                 }
508                                 free(pend->item);
509                         }
510
511                         free(pend);
512                 }
513
514                 ret = SHORTCUT_SUCCESS;
515         }
516
517         return ret;
518 }
519
520
521
522 static void master_started_cb(keynode_t *node, void *user_data)
523 {
524         int state = 0;
525
526         if (vconf_get_bool(VCONFKEY_MASTER_STARTED, &state) < 0) {
527                 ErrPrint("Unable to get \"%s\"\n", VCONFKEY_MASTER_STARTED);
528         }
529
530         if (state == 1 && make_connection() == SHORTCUT_SUCCESS) {
531                 int ret;
532                 ret = vconf_ignore_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb);
533                 DbgPrint("Ignore VCONF [%d]\n", ret);
534         }
535 }
536
537
538
539 EAPI int shortcut_icon_service_init(int (*init_cb)(int status, void *data), void *data)
540 {
541         int ret;
542
543         if (s_info.fd >= 0) {
544                 return -EALREADY;
545         }
546
547         if (s_info.initialized) {
548                 s_info.initialized = 1;
549                 com_core_add_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL);
550         }
551
552         s_info.init_cb = init_cb;
553         s_info.cbdata = data;
554
555         ret = vconf_notify_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb, NULL);
556         if (ret < 0) {
557                 ErrPrint("Failed to add vconf for service state [%d]\n", ret);
558         } else {
559                 DbgPrint("vconf is registered\n");
560         }
561
562         master_started_cb(NULL, NULL);
563         return 0;
564 }
565
566
567
568 EAPI int shortcut_icon_service_fini(void)
569 {
570         struct dlist *l;
571         struct dlist *n;
572         struct pending_item *pend;
573
574         if (s_info.initialized) {
575                 com_core_del_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL);
576                 s_info.initialized = 0;
577         }
578
579         if (s_info.fd < 0) {
580                 return -EINVAL;
581         }
582
583         com_core_packet_client_fini(s_info.fd);
584         s_info.init_cb = NULL;
585         s_info.cbdata = NULL;
586         s_info.fd = -1;
587
588         dlist_foreach_safe(s_info.pending_list, l, n, pend) {
589                 s_info.pending_list = dlist_remove(s_info.pending_list, l);
590                 packet_unref(pend->packet);
591                 if (pend->item->result_cb) {
592                         pend->item->result_cb(pend->item->handle, SHORTCUT_ERROR_COMM, pend->item->data);
593                 }
594                 free(pend->item);
595                 free(pend);
596         }
597         return 0;
598 }
599
600
601
602 EAPI struct shortcut_icon *shortcut_icon_request_create(void)
603 {
604         struct shortcut_icon *handle;
605
606         handle = malloc(sizeof(*handle));
607         if (!handle) {
608                 ErrPrint("Heap: %s\n", strerror(errno));
609                 return NULL;
610         }
611
612         handle->desc = shortcut_icon_desc_open();
613         if (!handle->desc) {
614                 ErrPrint("Uanble to open desc\n");
615                 free(handle);
616                 return NULL;
617         }
618
619         handle->state = CREATED;
620         handle->refcnt = 1;
621         return handle;
622 }
623
624
625 EAPI int shortcut_icon_request_set_data(struct shortcut_icon *handle, void *data)
626 {
627         if (!handle || handle->state != CREATED) {
628                 ErrPrint("Handle is not valid\n");
629                 return -EINVAL;
630         }
631
632         handle->data = data;
633         return 0;
634 }
635
636
637
638 EAPI void *shortcut_icon_request_data(struct shortcut_icon *handle)
639 {
640         if (!handle || handle->state != CREATED) {
641                 ErrPrint("Handle is not valid\n");
642                 return NULL;
643         }
644
645         return handle->data;
646 }
647
648
649
650 EAPI int shortcut_icon_request_set_info(struct shortcut_icon *handle, const char *id, const char *type, const char *part, const char *data, const char *option, const char *subid)
651 {
652         if (!handle || handle->state != CREATED) {
653                 ErrPrint("Handle is not valid\n");
654                 return -EINVAL;
655         }
656
657         return shortcut_icon_desc_add_block(handle->desc, id, type, part, data, option, subid);
658 }
659
660
661
662 EAPI int shortcut_icon_request_destroy(struct shortcut_icon *handle)
663 {
664         if (!handle || handle->state != CREATED) {
665                 ErrPrint("Handle is not valid\n");
666                 return -EINVAL;
667         }
668
669         (void)shortcut_icon_request_unref(handle);
670         return 0;
671 }
672
673
674
675 EAPI int shortcut_icon_request_send(struct shortcut_icon *handle, int size_type, const char *layout, const char *group, const char *outfile, icon_request_cb_t result_cb, void *data)
676 {
677         int ret;
678         struct packet *packet;
679         struct request_item *item;
680         char *filename;
681         int len;
682
683         if (!handle || handle->state != CREATED) {
684                 ErrPrint("Handle is not valid\n");
685                 return -EINVAL;
686         }
687
688         if (!layout) {
689                 layout = DEFAULT_ICON_LAYOUT;
690         }
691
692         if (!group) {
693                 group = DEFAULT_ICON_GROUP;
694         }
695
696         len = strlen(outfile) + strlen(".desc") + 1 + 30; /* 30 == strlen(tv.tv_sec) + strlen(tv.tv_usec) + 10 (reserved) */
697         filename = malloc(len);
698         if (!filename) {
699                 ErrPrint("Heap: %s\n", strerror(errno));
700                 return -ENOMEM;
701         }
702
703 #if defined(_USE_ECORE_TIME_GET)
704         struct timespec ts;
705         double tv;
706         do {
707                 if (clock_gettime(s_info.type, &ts) == 0) {
708                         tv = ts.tv_sec + ts.tv_nsec / 1000000000.0f;
709                         break;
710                 }
711
712                 ErrPrint("%d: %s\n", s_info.type, strerror(errno));
713                 if (s_info.type == CLOCK_MONOTONIC) {
714                         s_info.type = CLOCK_REALTIME;
715                 } else if (s_info.type == CLOCK_REALTIME) {
716                         struct timeval _tv;
717                         if (gettimeofday(&_tv, NULL) < 0) {
718                                 ErrPrint("gettimeofday: %s\n", strerror(errno));
719                                 _tv.tv_sec = rand();
720                                 _tv.tv_usec = rand();
721                         }
722
723                         tv = (double)_tv.tv_sec + (double)_tv.tv_usec / 1000000.0f;
724                         break;
725                 }
726         } while (1);
727         ret = snprintf(filename, len, "%s.%lf.desc", outfile, tv);
728 #else
729         struct timeval tv;
730         if (gettimeofday(&tv, NULL) != 0) {
731                 ErrPrint("gettimeofday: %s\n", strerror(errno));
732                 tv.tv_sec = rand();
733                 tv.tv_usec = rand();
734         }
735
736         ret = snprintf(filename, len, "%s.%lu.%lu.desc", outfile, tv.tv_sec, tv.tv_usec);
737 #endif
738         if (ret < 0) {
739                 ErrPrint("snprintf: %s\n", strerror(errno));
740                 goto out;
741         }
742
743         ret = shortcut_icon_desc_save(handle->desc, filename);
744         if (ret < 0) {
745                 goto out;
746         }
747
748         item = malloc(sizeof(*item));
749         if (!item) {
750                 ErrPrint("Heap: %s\n", strerror(errno));
751                 if (unlink(filename) < 0) {
752                         ErrPrint("Unlink: %s\n", strerror(errno));
753                 }
754                 ret = -ENOMEM;
755                 goto out;
756         }
757
758         item->result_cb = result_cb;
759         item->data = data;
760         item->handle = shortcut_icon_request_ref(handle);
761
762         packet = packet_create("icon_create", "sssis", layout, group, filename, size_type, outfile);
763         if (!packet) {
764                 ErrPrint("Failed to create a packet\n");
765                 if (unlink(filename) < 0) {
766                         ErrPrint("Unlink: %s\n", strerror(errno));
767                 }
768                 free(item);
769                 (void)shortcut_icon_request_unref(handle);
770                 ret = -EFAULT;
771                 goto out;
772         }
773
774         if (s_info.fd >= 0 && !s_info.pending_list) {
775                 ret = com_core_packet_async_send(s_info.fd, packet, 0.0f, icon_request_cb, item);
776                 packet_destroy(packet);
777                 if (ret < 0) {
778                         ErrPrint("ret: %d\n", ret);
779                         if (unlink(filename) < 0) {
780                                 ErrPrint("Unlink: %s\n", strerror(errno));
781                         }
782                         free(item);
783                         (void)shortcut_icon_request_unref(handle);
784                 }
785                 DbgPrint("Request is sent\n");
786         } else {
787                 struct pending_item *pend;
788
789                 pend = malloc(sizeof(*pend));
790                 if (!pend) {
791                         ErrPrint("Heap: %s\n", strerror(errno));
792                         packet_destroy(packet);
793                         free(item);
794                         if (unlink(filename) < 0) {
795                                 ErrPrint("Unlink: %s\n", strerror(errno));
796                         }
797                         (void)shortcut_icon_request_unref(handle);
798                         ret = -ENOMEM;
799                         goto out;
800                 }
801
802                 pend->packet = packet;
803                 pend->item = item;
804
805                 s_info.pending_list = dlist_append(s_info.pending_list, pend);
806                 DbgPrint("Request is pended\n");
807
808                 ret = 0;
809         }
810
811 out:
812         free(filename);
813         return ret;
814 }
815
816
817 /* End of a file */