Apply smack patch
[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;
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                 close(conn_fd);
393                 free(state->data);
394                 free(state);
395         }
396
397         return ret;
398 }
399
400
401
402 static
403 gboolean connection_cb(GIOChannel *src, GIOCondition cond, gpointer data)
404 {
405         int conn_fd;
406         struct connection_state *state = data;
407         gboolean ret;
408         int read_size;
409
410         if (!(cond & G_IO_IN)) {
411                 ret = FALSE;
412                 goto out;
413         }
414
415         conn_fd = g_io_channel_unix_get_fd(src);
416
417         if (ioctl(conn_fd, FIONREAD, &read_size) < 0) {
418                 LOGE("Failed to get q size\n");
419                 ret = FALSE;
420                 goto out;
421         }
422
423         if (read_size == 0) {
424                 LOGE("Disconnected\n");
425                 ret = FALSE;
426                 goto out;
427         }
428
429         switch (state->state) {
430         case BEGIN:
431                 state->state = HEADER;
432         case HEADER:
433                 ret = filling_header(conn_fd, state);
434                 break;
435         case PAYLOAD:
436                 ret = filling_payload(conn_fd, state);
437                 break;
438         default:
439                 LOGE("[%s:%d] Invalid state(%x)\n",
440                                         __func__, __LINE__, state->state);
441                 ret = FALSE;
442                 break;
443         }
444
445         if (state->state == END) {
446                 ret = do_reply_service(conn_fd, state);
447                 if (state->payload) {
448                         free(state->payload);
449                         state->payload = NULL;
450                 }
451
452                 memset(&state->packet, 0, sizeof(state->packet));
453         }
454
455 out:
456         if (ret == FALSE) {
457                 secom_put_connection_handle(conn_fd);
458
459                 if (state->payload)
460                         free(state->payload);
461
462                 free(state);
463         }
464
465         return ret;
466 }
467
468
469
470 static
471 gboolean accept_cb(GIOChannel *src, GIOCondition cond, gpointer data)
472 {
473         int server_fd;
474         int connection_fd;
475         GIOChannel *gio;
476         guint id;
477         struct connection_state *state;
478
479         server_fd = g_io_channel_unix_get_fd(src);
480         if (server_fd != s_info.server_fd) {
481                 LOGE("Unknown FD is gotten.\n");
482                 /* NOTE:
483                  * In this case, don't try to do anything. 
484                  * This is not recoverble error */
485                 return FALSE;
486         }
487
488         if (!(cond & G_IO_IN)) {
489                 close(s_info.server_fd);
490                 s_info.server_fd = -1;
491                 return FALSE;
492         }
493
494         connection_fd = secom_get_connection_handle(server_fd);
495         if (connection_fd < 0) {
496                 /* Error log will be printed from
497                  * get_connection_handle function */
498                 return FALSE;
499         }
500
501         if (fcntl(connection_fd, F_SETFD, FD_CLOEXEC) < 0)
502                 LOGE("Error: %s\n", strerror(errno));
503
504         if (fcntl(connection_fd, F_SETFL, O_NONBLOCK) < 0)
505                 LOGE("Error: %s\n", strerror(errno));
506
507         gio = g_io_channel_unix_new(connection_fd);
508         if (!gio) {
509                 LOGE("Failed to create a new connection channel\n");
510                 secom_put_connection_handle(connection_fd);
511                 return FALSE;
512         }
513
514         state = calloc(1, sizeof(*state));
515         if (!state) {
516                 LOGE("Heap: %s\n", strerror(errno));
517                 secom_put_connection_handle(connection_fd);
518                 return FALSE;
519         }
520
521         state->state = BEGIN;
522         id = g_io_add_watch(gio,
523                         G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
524                         (GIOFunc)connection_cb, state);
525         if (id < 0) {
526                 GError *err = NULL;
527                 LOGE("Failed to create g_io watch\n");
528                 free(state);
529                 g_io_channel_unref(gio);
530                 g_io_channel_shutdown(gio, TRUE, &err);
531                 secom_put_connection_handle(connection_fd);
532                 return FALSE;
533         }
534
535         g_io_channel_unref(gio);
536         return TRUE;
537 }
538
539
540
541 static inline int init_client(struct client_cb *client_cb,
542                                         const char *packet, int packet_size)
543 {
544         GIOChannel *gio;
545         guint id;
546         int client_fd;
547         struct connection_state *state;
548
549         client_fd = secom_create_client(s_info.socket_file);
550         if (client_fd < 0) {
551                 LOGE("Failed to make the client FD\n");
552                 return -EFAULT;
553         }
554
555         if (fcntl(client_fd, F_SETFD, FD_CLOEXEC) < 0)
556                 LOGE("Error: %s\n", strerror(errno));
557
558         if (fcntl(client_fd, F_SETFL, O_NONBLOCK) < 0)
559                 LOGE("Error: %s\n", strerror(errno));
560
561         gio = g_io_channel_unix_new(client_fd);
562         if (!gio) {
563                 close(client_fd);
564                 return -EFAULT;
565         }
566
567         state = calloc(1, sizeof(*state));
568         if (!state) {
569                 LOGE("Error: %s\n", strerror(errno));
570                 close(client_fd);
571                 return -ENOMEM;
572         }
573
574         state->data = client_cb;
575
576         id = g_io_add_watch(gio,
577                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
578                 (GIOFunc)client_connection_cb, state);
579         if (id < 0) {
580                 GError *err = NULL;
581                 LOGE("Failed to create g_io watch\n");
582                 free(state);
583                 g_io_channel_unref(gio);
584                 g_io_channel_shutdown(gio, TRUE, &err);
585                 close(client_fd);
586                 return -EFAULT;
587         }
588
589         g_io_channel_unref(gio);
590
591         if (secom_send(client_fd, packet, packet_size) != packet_size) {
592                 GError *err = NULL;
593                 LOGE("Failed to send all packet "
594                         "(g_io_channel is not cleared now\n");
595                 free(state);
596                 g_io_channel_shutdown(gio, TRUE, &err);
597                 close(client_fd);
598                 return -EFAULT;
599         }
600
601         return client_fd;
602 }
603
604
605
606 static inline
607 int init_server(void)
608 {
609         GIOChannel *gio;
610         guint id;
611
612         if (s_info.server_fd != -1) {
613                 LOGE("Already initialized\n");
614                 return 0;
615         }
616
617         if (pthread_mutex_lock(&s_info.server_mutex) != 0) {
618                 LOGE("[%s:%d] Failed to get lock (%s)\n",
619                                         __func__, __LINE__, strerror(errno));
620                 return -EFAULT;
621         }
622
623         unlink(s_info.socket_file);
624         s_info.server_fd = secom_create_server(s_info.socket_file);
625
626         if (s_info.server_fd < 0) {
627                 LOGE("Failed to open a socket (%s)\n", strerror(errno));
628                 if (pthread_mutex_unlock(&s_info.server_mutex) != 0)
629                         LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
630                                         __func__, __LINE__, strerror(errno));
631                 return -EFAULT;
632         }
633
634         if (fcntl(s_info.server_fd, F_SETFD, FD_CLOEXEC) < 0)
635                 LOGE("Error: %s\n", strerror(errno));
636
637         if (fcntl(s_info.server_fd, F_SETFL, O_NONBLOCK) < 0)
638                 LOGE("Error: %s\n", strerror(errno));
639
640         gio = g_io_channel_unix_new(s_info.server_fd);
641         if (!gio) {
642                 close(s_info.server_fd);
643                 s_info.server_fd = -1;
644                 if (pthread_mutex_unlock(&s_info.server_mutex) != 0)
645                         LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
646                                         __func__, __LINE__, strerror(errno));
647                 return -EFAULT;
648         }
649
650         id = g_io_add_watch(gio,
651                         G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
652                         (GIOFunc)accept_cb, NULL);
653         if (id < 0) {
654                 GError *err = NULL;
655                 LOGE("Failed to create g_io watch\n");
656                 g_io_channel_unref(gio);
657                 g_io_channel_shutdown(gio, TRUE, &err);
658                 close(s_info.server_fd);
659                 s_info.server_fd = -1;
660                 if (pthread_mutex_unlock(&s_info.server_mutex) != 0)
661                         LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
662                                         __func__, __LINE__, strerror(errno));
663
664                 return -EFAULT;
665         }
666
667         g_io_channel_unref(gio);
668
669         if (pthread_mutex_unlock(&s_info.server_mutex) != 0) {
670                 GError *err = NULL;
671                 g_io_channel_shutdown(gio, TRUE, &err);
672                 LOGE("[%s:%d] Failed to do unlock mutex (%s)\n",
673                                         __func__, __LINE__, strerror(errno));
674                 /* NOTE:
675                  * We couldn't make a lock for this statements.
676                  * We already meet the unrecoverble error
677                  */
678                 close(s_info.server_fd);
679                 s_info.server_fd = -1;
680                 return -EFAULT;
681         }
682
683         return 0;
684 }
685
686
687
688 EAPI int shortcut_set_request_cb(request_cb_t request_cb, void *data)
689 {
690         int ret;
691         s_info.server_cb.request_cb = request_cb;
692         s_info.server_cb.data = data;
693
694         ret = init_server();
695         if (ret != 0) {
696                 LOGE("Failed to initialize the server\n");
697         }
698
699         return ret;
700 }
701
702
703
704 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)
705 {
706         struct packet *packet;
707         int pkgname_len;
708         int name_len;
709         int exec_len;
710         int icon_len;
711         int packet_size;
712         char *payload;
713         struct client_cb *client_cb;
714         int client_fd;
715
716         pkgname_len = pkgname ? strlen(pkgname) + 1 : 0;
717         name_len = name ? strlen(name) + 1 : 0;
718         exec_len = content_info ? strlen(content_info) + 1 : 0;
719         icon_len = icon ? strlen(icon) + 1 : 0;
720
721         packet_size = sizeof(*packet) + name_len + exec_len + icon_len + pkgname_len + 1;
722
723         packet = alloca(packet_size);
724         if (!packet) {
725                 LOGE("Heap: %s\n", strerror(errno));
726                 return -ENOMEM;
727         }
728
729         packet->head.seq = s_info.seq++;
730         packet->head.type = PACKET_REQ;
731         packet->head.data.req.shortcut_type = type;
732         packet->head.data.req.field_size.pkgname = pkgname_len;
733         packet->head.data.req.field_size.name = name_len;
734         packet->head.data.req.field_size.exec = exec_len;
735         packet->head.data.req.field_size.icon = icon_len;
736         packet->head.payload_size = pkgname_len + name_len + exec_len + icon_len + 1;
737
738         payload = packet->payload;
739
740         strncpy(payload, pkgname, pkgname_len);
741         payload += pkgname_len;
742         strncpy(payload, name, name_len);
743         payload += name_len;
744         strncpy(payload, content_info, exec_len);
745         payload += exec_len;
746         strncpy(payload, icon, icon_len);
747
748         client_cb = malloc(sizeof(*client_cb));
749         if (!client_cb) {
750                 LOGE("Heap: %s\n", strerror(errno));
751                 return -ENOMEM;
752         }
753
754         client_cb->result_cb = result_cb;
755         client_cb->data = data;
756
757         if (init_client(client_cb, (const char*)packet, packet_size) < 0) {
758                 LOGE("Failed to init client FD\n");
759                 free(client_cb);
760                 return -EFAULT;
761         }
762
763         return 0;
764 }
765
766 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)
767 {
768         return add_to_home_shortcut(pkgname, name, type, content_info, icon, result_cb, data);
769 }
770
771
772
773 /* End of a file */