Tizen 2.0 Release
[framework/appfw/shortcut.git] / src / main.c
1 /*
2  * Copyright 2012  Samsung Electronics Co., Ltd
3  *
4  * Licensed under the Flora License, Version 1.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.tizenopensource.org/license
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 <stdlib.h>
18 #include <pthread.h>
19 #include <errno.h>
20 #include <glib.h>
21 #include <dlog.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <string.h>
25
26 #include <secom_socket.h>
27 #include <shortcut.h>
28
29 #include <sys/socket.h>
30 #include <sys/ioctl.h>
31
32
33
34 #define EAPI __attribute__((visibility("default")))
35
36
37
38 extern int errno;
39
40
41
42 struct server_cb {
43         request_cb_t request_cb;
44         void *data;
45 };
46
47
48
49 struct client_cb {
50         result_cb_t result_cb;
51         void *data;
52 };
53
54
55
56 static struct info {
57         pthread_mutex_t server_mutex;
58         int server_fd;
59         const char *socket_file;
60         struct server_cb server_cb;
61         unsigned int seq;
62 } s_info = {
63         .server_mutex = PTHREAD_MUTEX_INITIALIZER,
64         .server_fd = -1,
65         .socket_file = "/tmp/.shortcut",
66         .seq = 0,
67 };
68
69
70
71 struct packet {
72         struct {
73                 unsigned int seq;
74                 enum {
75                         PACKET_ERR = 0x0,
76                         PACKET_REQ,
77                         PACKET_ACK,
78                         PACKET_MAX = 0xFF, /* MAX */
79                 } type;
80
81                 int payload_size;
82
83                 union {
84                         struct {
85                                 int shortcut_type;
86                                 struct {
87                                         int pkgname;
88                                         int name;
89                                         int exec;
90                                         int icon;
91                                 } field_size;
92                         } req;
93
94                         struct {
95                                 int ret;
96                         } ack;
97                 } data;
98         } head;
99
100         char payload[];
101 };
102
103
104
105 struct connection_state {
106         void *data;
107         struct packet packet;
108         enum {
109                 BEGIN,
110                 HEADER,
111                 PAYLOAD,
112                 END,
113                 ERROR,
114         } state;
115         int length;
116         int from_pid;
117         char *payload;
118 };
119
120
121
122 static inline
123 gboolean check_reply_service(int conn_fd, struct connection_state *state)
124 {
125         struct client_cb *client_cb = state->data;
126
127         if (client_cb->result_cb) {
128                 /* If the callback return FAILURE,
129                  * remove itself from the callback list */
130                 client_cb->result_cb(state->packet.head.data.ack.ret,
131                                         state->from_pid, client_cb->data);
132         }
133
134         state->state = BEGIN;
135         state->length = 0;
136         state->from_pid = 0;
137
138         /* NOTE: If we want close the connection, returns FALSE */
139         return FALSE;
140 }
141
142
143
144 static inline
145 gboolean do_reply_service(int conn_fd, struct connection_state *state)
146 {
147         int ret;
148         struct packet send_packet;
149
150         ret = -ENOSYS;
151         if (s_info.server_cb.request_cb) {
152                 char *pkgname;
153                 char *name;
154                 char *exec;
155                 char *icon;
156
157                 if (state->packet.head.data.req.field_size.pkgname)
158                         pkgname = state->payload;
159                 else
160                         pkgname = NULL;
161
162                 if (state->packet.head.data.req.field_size.name)
163                         name = state->payload + state->packet.head.data.req.field_size.pkgname;
164                 else
165                         name = NULL;
166
167                 if (state->packet.head.data.req.field_size.exec) {
168                         exec = state->payload + state->packet.head.data.req.field_size.pkgname;
169                         exec += state->packet.head.data.req.field_size.name;
170                 } else {
171                         exec = NULL;
172                 }
173
174                 if (state->packet.head.data.req.field_size.icon) {
175                         icon = state->payload + state->packet.head.data.req.field_size.pkgname;
176                         icon += state->packet.head.data.req.field_size.name;
177                         icon += state->packet.head.data.req.field_size.exec;
178                 } else {
179                         icon = NULL;
180                 }
181
182                 LOGD("Pkgname: [%s] Type: [%x], Name: [%s], Exec: [%s], Icon: [%s]\n",
183                                 pkgname,
184                                 state->packet.head.data.req.shortcut_type,
185                                 name,
186                                 exec,
187                                 icon);
188
189                 ret = s_info.server_cb.request_cb(
190                                 pkgname,
191                                 name,
192                                 state->packet.head.data.req.shortcut_type,
193                                 exec,
194                                 icon,
195                                 state->from_pid,
196                                 s_info.server_cb.data);
197         }
198
199         send_packet.head.type = PACKET_ACK;
200         send_packet.head.payload_size = 0;
201         send_packet.head.seq = state->packet.head.seq;
202         send_packet.head.data.ack.ret = ret;
203
204         if (secom_send(conn_fd, (const char*)&send_packet, sizeof(send_packet)) != sizeof(send_packet)) {
205                 LOGE("Faield to send ack packet\n");
206                 return FALSE;
207         }
208
209         state->state = BEGIN;
210         state->length = 0;
211         state->from_pid = 0;
212         return TRUE;
213 }
214
215
216
217 static inline
218 gboolean filling_payload(int conn_fd, struct connection_state *state)
219 {
220         if (state->length < state->packet.head.payload_size) {
221                 int ret;
222                 int check_pid;
223
224                 ret = secom_recv(conn_fd,
225                         state->payload + state->length,
226                         state->packet.head.payload_size - state->length,
227                         &check_pid);
228                 if (ret < 0)
229                         return FALSE;
230
231                 if (state->from_pid != check_pid) {
232                         LOGD("PID is not matched (%d, expected %d)\n",
233                                                 check_pid, state->from_pid);
234                         return FALSE;
235                 }
236
237                 state->length += ret;
238         }
239
240         if (state->length == state->packet.head.payload_size) {
241                 state->length = 0;
242                 state->state = END;
243         }
244
245         return TRUE;
246 }
247
248
249
250 static inline
251 gboolean deal_error_packet(int conn_fd, struct connection_state *state)
252 {
253         if (state->length < state->packet.head.payload_size) {
254                 int ret;
255                 char *buf;
256                 int check_pid;
257
258                 buf = alloca(state->packet.head.payload_size - state->length);
259
260                 ret = secom_recv(conn_fd,
261                         buf,
262                         state->packet.head.payload_size - state->length,
263                         &check_pid);
264                 if (ret < 0)
265                         return FALSE;
266
267                 if (check_pid != state->from_pid)
268                         LOGD("PID is not matched (%d, expected %d)\n",
269                                                 check_pid, state->from_pid);
270
271                 state->length += ret;
272         }
273
274         if (state->length < state->packet.head.payload_size)
275                 return TRUE;
276
277         /* Now, drop this connection */
278         return FALSE;
279 }
280
281
282
283 static inline
284 gboolean filling_header(int conn_fd, struct connection_state *state)
285 {
286         if (state->length < sizeof(state->packet)) {
287                 int ret;
288                 int check_pid;
289
290                 ret = secom_recv(conn_fd,
291                         ((char*)&state->packet) + state->length, 
292                         sizeof(state->packet) - state->length,
293                         &check_pid);
294                 if (ret < 0)
295                         return FALSE;
296
297                 if (state->from_pid == 0)
298                         state->from_pid = check_pid;
299
300                 if (state->from_pid != check_pid) {
301                         LOGD("PID is not matched (%d, expected %d)\n",
302                                                 check_pid, state->from_pid);
303                         return FALSE;
304                 }
305
306                 state->length += ret;
307         }
308
309         if (state->length < sizeof(state->packet))
310                 return TRUE;
311
312         if (state->packet.head.type == PACKET_ACK) {
313                 state->length = 0;
314
315                 if (state->packet.head.payload_size)
316                         state->state = ERROR;
317                 else
318                         state->state = END;
319         } else if (state->packet.head.type == PACKET_REQ) {
320                 /* Let's take the next part. */
321                 state->state = PAYLOAD;
322                 state->length = 0;
323
324                 state->payload = calloc(1, state->packet.head.payload_size);
325                 if (!state->payload) {
326                         LOGE("Heap: %s\n", strerror(errno));
327                         return FALSE;
328                 }
329         } else {
330                 LOGE("Invalid packet type\n");
331                 return FALSE;
332         }
333
334         return TRUE;
335 }
336
337
338
339 static
340 gboolean client_connection_cb(GIOChannel *src, GIOCondition cond, gpointer data)
341 {
342         int conn_fd = -1;
343         gboolean ret;
344         int read_size;
345         struct connection_state *state = data;
346         struct client_cb *client_cb = state->data;
347
348         if (!(cond & G_IO_IN)) {
349                 LOGE("Condition value is unexpected value\n");
350                 ret = FALSE;
351                 goto out;
352         }
353
354         conn_fd = g_io_channel_unix_get_fd(src);
355
356         if (ioctl(conn_fd, FIONREAD, &read_size) < 0) {
357                 LOGE("Failed to get q size\n");
358                 ret = FALSE;
359                 goto out;
360         }
361
362         if (read_size == 0) {
363                 if (client_cb->result_cb) {
364                         /* If the callback return FAILURE,
365                          * remove itself from the callback list */
366                         client_cb->result_cb(-ECONNABORTED,
367                                         state->from_pid, client_cb->data);
368                 }
369                 ret = FALSE;
370                 goto out;
371         }
372
373         switch (state->state) {
374         case BEGIN:
375                 state->state = HEADER;
376         case HEADER:
377                 ret = filling_header(conn_fd, state);
378                 break;
379         case ERROR:
380                 ret = deal_error_packet(conn_fd, state);
381                 break;
382         default:
383                 ret = FALSE;
384                 break;
385         }
386
387         if (state->state == END)
388                 ret = check_reply_service(conn_fd, state);
389
390 out:
391         if (ret == FALSE) {
392                 if (conn_fd >= 0)
393                         close(conn_fd);
394                 free(state->data);
395                 free(state);
396         }
397
398         return ret;
399 }
400
401
402
403 static
404 gboolean connection_cb(GIOChannel *src, GIOCondition cond, gpointer data)
405 {
406         int conn_fd = -1;
407         struct connection_state *state = data;
408         gboolean ret;
409         int read_size;
410
411         if (!(cond & G_IO_IN)) {
412                 ret = FALSE;
413                 goto out;
414         }
415
416         conn_fd = g_io_channel_unix_get_fd(src);
417
418         if (ioctl(conn_fd, FIONREAD, &read_size) < 0) {
419                 LOGE("Failed to get q size\n");
420                 ret = FALSE;
421                 goto out;
422         }
423
424         if (read_size == 0) {
425                 LOGE("Disconnected\n");
426                 ret = FALSE;
427                 goto out;
428         }
429
430         switch (state->state) {
431         case BEGIN:
432                 state->state = HEADER;
433         case HEADER:
434                 ret = filling_header(conn_fd, state);
435                 break;
436         case PAYLOAD:
437                 ret = filling_payload(conn_fd, state);
438                 break;
439         default:
440                 LOGE("[%s:%d] Invalid state(%x)\n",
441                                         __func__, __LINE__, state->state);
442                 ret = FALSE;
443                 break;
444         }
445
446         if (state->state == END) {
447                 ret = do_reply_service(conn_fd, state);
448                 if (state->payload) {
449                         free(state->payload);
450                         state->payload = NULL;
451                 }
452
453                 memset(&state->packet, 0, sizeof(state->packet));
454         }
455
456 out:
457         if (ret == FALSE) {
458                 if (conn_fd >= 0)
459                         secom_put_connection_handle(conn_fd);
460
461                 if (state->payload)
462                         free(state->payload);
463
464                 free(state);
465         }
466
467         return ret;
468 }
469
470
471
472 static
473 gboolean accept_cb(GIOChannel *src, GIOCondition cond, gpointer data)
474 {
475         int server_fd;
476         int connection_fd;
477         GIOChannel *gio;
478         guint id;
479         struct connection_state *state;
480
481         server_fd = g_io_channel_unix_get_fd(src);
482         if (server_fd != s_info.server_fd) {
483                 LOGE("Unknown FD is gotten.\n");
484                 /* NOTE:
485                  * In this case, don't try to do anything. 
486                  * This is not recoverble error */
487                 return FALSE;
488         }
489
490         if (!(cond & G_IO_IN)) {
491                 close(s_info.server_fd);
492                 if (pthread_mutex_lock(&s_info.server_mutex) != 0)
493                         LOGE("[%s:%d] Failed to get lock (%s)\n",
494                                                 __func__, __LINE__, strerror(errno));
495                 s_info.server_fd = -1;
496                 if (pthread_mutex_unlock(&s_info.server_mutex) != 0)
497                         LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
498                                         __func__, __LINE__, strerror(errno));
499                 return FALSE;
500         }
501
502         connection_fd = secom_get_connection_handle(server_fd);
503         if (connection_fd < 0) {
504                 /* Error log will be printed from
505                  * get_connection_handle function */
506                 return FALSE;
507         }
508
509         if (fcntl(connection_fd, F_SETFD, FD_CLOEXEC) < 0)
510                 LOGE("Error: %s\n", strerror(errno));
511
512         if (fcntl(connection_fd, F_SETFL, O_NONBLOCK) < 0)
513                 LOGE("Error: %s\n", strerror(errno));
514
515         gio = g_io_channel_unix_new(connection_fd);
516         if (!gio) {
517                 LOGE("Failed to create a new connection channel\n");
518                 secom_put_connection_handle(connection_fd);
519                 return FALSE;
520         }
521
522         state = calloc(1, sizeof(*state));
523         if (!state) {
524                 LOGE("Heap: %s\n", strerror(errno));
525                 secom_put_connection_handle(connection_fd);
526                 return FALSE;
527         }
528
529         state->state = BEGIN;
530         id = g_io_add_watch(gio,
531                         G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
532                         (GIOFunc)connection_cb, state);
533         if (id == 0) {
534                 GError *err = NULL;
535                 LOGE("Failed to create g_io watch\n");
536                 free(state);
537                 g_io_channel_unref(gio);
538                 g_io_channel_shutdown(gio, TRUE, &err);
539                 secom_put_connection_handle(connection_fd);
540                 return FALSE;
541         }
542
543         g_io_channel_unref(gio);
544         return TRUE;
545 }
546
547
548
549 static inline int init_client(struct client_cb *client_cb,
550                                         const char *packet, int packet_size)
551 {
552         GIOChannel *gio;
553         guint id;
554         int client_fd;
555         struct connection_state *state;
556
557         client_fd = secom_create_client(s_info.socket_file);
558         if (client_fd < 0) {
559                 LOGE("Failed to make the client FD\n");
560                 return -EFAULT;
561         }
562
563         if (fcntl(client_fd, F_SETFD, FD_CLOEXEC) < 0)
564                 LOGE("Error: %s\n", strerror(errno));
565
566         if (fcntl(client_fd, F_SETFL, O_NONBLOCK) < 0)
567                 LOGE("Error: %s\n", strerror(errno));
568
569         gio = g_io_channel_unix_new(client_fd);
570         if (!gio) {
571                 close(client_fd);
572                 return -EFAULT;
573         }
574
575         state = calloc(1, sizeof(*state));
576         if (!state) {
577                 LOGE("Error: %s\n", strerror(errno));
578                 close(client_fd);
579                 return -ENOMEM;
580         }
581
582         state->data = client_cb;
583
584         id = g_io_add_watch(gio,
585                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
586                 (GIOFunc)client_connection_cb, state);
587         if (id == 0) {
588                 GError *err = NULL;
589                 LOGE("Failed to create g_io watch\n");
590                 free(state);
591                 g_io_channel_unref(gio);
592                 g_io_channel_shutdown(gio, TRUE, &err);
593                 close(client_fd);
594                 return -EFAULT;
595         }
596
597         g_io_channel_unref(gio);
598
599         if (secom_send(client_fd, packet, packet_size) != packet_size) {
600                 GError *err = NULL;
601                 LOGE("Failed to send all packet "
602                         "(g_io_channel is not cleared now\n");
603                 free(state);
604                 g_io_channel_shutdown(gio, TRUE, &err);
605                 close(client_fd);
606                 return -EFAULT;
607         }
608
609         return client_fd;
610 }
611
612
613
614 static inline
615 int init_server(void)
616 {
617         GIOChannel *gio;
618         guint id;
619
620         if (s_info.server_fd != -1) {
621                 LOGE("Already initialized\n");
622                 return 0;
623         }
624
625         if (pthread_mutex_lock(&s_info.server_mutex) != 0) {
626                 LOGE("[%s:%d] Failed to get lock (%s)\n",
627                                         __func__, __LINE__, strerror(errno));
628                 return -EFAULT;
629         }
630
631         unlink(s_info.socket_file);
632         s_info.server_fd = secom_create_server(s_info.socket_file);
633
634         if (s_info.server_fd < 0) {
635                 LOGE("Failed to open a socket (%s)\n", strerror(errno));
636                 if (pthread_mutex_unlock(&s_info.server_mutex) != 0)
637                         LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
638                                         __func__, __LINE__, strerror(errno));
639                 return -EFAULT;
640         }
641
642         if (fcntl(s_info.server_fd, F_SETFD, FD_CLOEXEC) < 0)
643                 LOGE("Error: %s\n", strerror(errno));
644
645         if (fcntl(s_info.server_fd, F_SETFL, O_NONBLOCK) < 0)
646                 LOGE("Error: %s\n", strerror(errno));
647
648         gio = g_io_channel_unix_new(s_info.server_fd);
649         if (!gio) {
650                 close(s_info.server_fd);
651                 s_info.server_fd = -1;
652                 if (pthread_mutex_unlock(&s_info.server_mutex) != 0)
653                         LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
654                                         __func__, __LINE__, strerror(errno));
655                 return -EFAULT;
656         }
657
658         id = g_io_add_watch(gio,
659                         G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
660                         (GIOFunc)accept_cb, NULL);
661         if (id == 0) {
662                 GError *err = NULL;
663                 LOGE("Failed to create g_io watch\n");
664                 g_io_channel_unref(gio);
665                 g_io_channel_shutdown(gio, TRUE, &err);
666                 close(s_info.server_fd);
667                 s_info.server_fd = -1;
668                 if (pthread_mutex_unlock(&s_info.server_mutex) != 0)
669                         LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
670                                         __func__, __LINE__, strerror(errno));
671
672                 return -EFAULT;
673         }
674
675         g_io_channel_unref(gio);
676
677         if (pthread_mutex_unlock(&s_info.server_mutex) != 0) {
678                 GError *err = NULL;
679                 g_io_channel_shutdown(gio, TRUE, &err);
680                 LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
681                                         __func__, __LINE__, strerror(errno));
682                 /* NOTE:
683                  * We couldn't make a lock for this statements.
684                  * We already meet the unrecoverble error
685                  */
686                 close(s_info.server_fd);
687                 s_info.server_fd = -1;
688                 return -EFAULT;
689         }
690
691         return 0;
692 }
693
694
695
696 EAPI int shortcut_set_request_cb(request_cb_t request_cb, void *data)
697 {
698         int ret;
699         s_info.server_cb.request_cb = request_cb;
700         s_info.server_cb.data = data;
701
702         ret = init_server();
703         if (ret != 0) {
704                 LOGE("Failed to initialize the server\n");
705         }
706
707         return ret;
708 }
709
710
711
712 EAPI int add_to_home_shortcut(const char *pkgname, const char *name, int type, const char *content_info, const char *icon, result_cb_t result_cb, void *data)
713 {
714         struct packet *packet;
715         int pkgname_len;
716         int name_len;
717         int exec_len;
718         int icon_len;
719         int packet_size;
720         char *payload;
721         struct client_cb *client_cb;
722         int client_fd;
723
724         pkgname_len = pkgname ? strlen(pkgname) + 1 : 0;
725         name_len = name ? strlen(name) + 1 : 0;
726         exec_len = content_info ? strlen(content_info) + 1 : 0;
727         icon_len = icon ? strlen(icon) + 1 : 0;
728
729         packet_size = sizeof(*packet) + name_len + exec_len + icon_len + pkgname_len + 1;
730
731         packet = alloca(packet_size);
732         if (!packet) {
733                 LOGE("Heap: %s\n", strerror(errno));
734                 return -ENOMEM;
735         }
736
737         packet->head.seq = s_info.seq++;
738         packet->head.type = PACKET_REQ;
739         packet->head.data.req.shortcut_type = type;
740         packet->head.data.req.field_size.pkgname = pkgname_len;
741         packet->head.data.req.field_size.name = name_len;
742         packet->head.data.req.field_size.exec = exec_len;
743         packet->head.data.req.field_size.icon = icon_len;
744         packet->head.payload_size = pkgname_len + name_len + exec_len + icon_len + 1;
745
746         payload = packet->payload;
747
748         if (pkgname_len) {
749                 strncpy(payload, pkgname, pkgname_len);
750                 payload += pkgname_len;
751         }
752
753         if (name_len) {
754                 strncpy(payload, name, name_len);
755                 payload += name_len;
756         }
757
758         if (exec_len) {
759                 strncpy(payload, content_info, exec_len);
760                 payload += exec_len;
761         }
762
763         if (icon_len)
764                 strncpy(payload, icon, icon_len);
765
766         client_cb = malloc(sizeof(*client_cb));
767         if (!client_cb) {
768                 LOGE("Heap: %s\n", strerror(errno));
769                 return -ENOMEM;
770         }
771
772         client_cb->result_cb = result_cb;
773         client_cb->data = data;
774
775         if (init_client(client_cb, (const char*)packet, packet_size) < 0) {
776                 LOGE("Failed to init client FD\n");
777                 free(client_cb);
778                 return -EFAULT;
779         }
780
781         return 0;
782 }
783
784 EAPI int shortcut_add_to_home(const char *pkgname, const char *name, int type, const char *content_info, const char *icon, result_cb_t result_cb, void *data)
785 {
786         return add_to_home_shortcut(pkgname, name, type, content_info, icon, result_cb, data);
787 }
788
789
790
791 /* End of a file */