tizen 2.3 release
[apps/livebox/livebox-viewer.git] / dynamicbox_viewer / src / file_service.c
1 /*
2  * Copyright 2013  Samsung Electronics Co., Ltd
3  *
4  * Licensed under the Flora License, Version 1.1 (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://floralicense.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 #define _GNU_SOURCE
18
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <pthread.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29
30 #include <glib.h>
31
32 #include <dlog.h>
33 #include <secure_socket.h>
34 #include <dynamicbox_errno.h>
35
36 #include "client.h"
37 #include "debug.h"
38 #include "dlist.h"
39
40 #define FILE_SERVICE_PORT    8209
41
42 #define CRITICAL_SECTION_BEGIN(handle) \
43     do { \
44         int ret; \
45         ret = pthread_mutex_lock(handle); \
46         if (ret != 0) { \
47             ErrPrint("Failed to lock: %s\n", strerror(ret)); \
48         } \
49     } while (0)
50
51 #define CRITICAL_SECTION_END(handle) \
52     do { \
53         int ret; \
54         ret = pthread_mutex_unlock(handle); \
55         if (ret != 0) { \
56             ErrPrint("Failed to unlock: %s\n", strerror(ret)); \
57         } \
58     } while (0)
59
60 #define CANCEL_SECTION_BEGIN() do { \
61     int ret; \
62     ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); \
63     if (ret != 0) { \
64         ErrPrint("Unable to set cancelate state: %s\n", strerror(ret)); \
65     } \
66 } while (0)
67
68 #define CANCEL_SECTION_END() do { \
69     int ret; \
70     ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); \
71     if (ret != 0) { \
72         ErrPrint("Unable to set cancelate state: %s\n", strerror(ret)); \
73     } \
74 } while (0)
75
76 #define CLOSE_PIPE(p)    do { \
77     int status; \
78     status = close(p[PIPE_READ]); \
79     if (status < 0) { \
80         ErrPrint("close: %s\n", strerror(errno)); \
81     } \
82     status = close(p[PIPE_WRITE]); \
83     if (status < 0) { \
84         ErrPrint("close: %s\n", strerror(errno)); \
85     } \
86 } while (0)
87
88 #define PIPE_READ 0
89 #define PIPE_WRITE 1
90 #define PIPE_MAX 2
91
92 #define EVT_END_CH    'c'
93 #define EVT_CH        'e'
94
95 static struct {
96     pthread_t file_svc_thid;
97     pthread_mutex_t file_svc_lock;
98     int ctrl_pipe[PIPE_MAX];
99     int evt_pipe[PIPE_MAX];
100     struct dlist *request_list;
101     int file_service_fd;
102 } s_info = {
103     .ctrl_pipe = { -1, -1 },
104     .evt_pipe = { -1, -1 },
105     .request_list = NULL,
106     .file_service_fd = -1,
107 };
108
109 struct request_item {
110     char *filename;
111     char *save_to;
112     void (*result_cb)(const char *filename, const char *save_to, int ret, void *data);
113     void *data;
114     int ret;
115 };
116
117 /*!
118  * File transfer header.
119  * This must should be shared with client.
120  */
121 struct burst_head {
122     off_t size;
123     int flen;
124     char fname[];
125 };
126
127 struct burst_data {
128     int size;
129     char data[];
130 };
131
132 static inline int put_event_ch(int fd, char ch)
133 {
134     int ret;
135
136     ret = write(fd, &ch, sizeof(ch));
137     if (ret != sizeof(ch)) {
138         ErrPrint("write: %s\n", strerror(errno));
139         return ret;
140     }
141
142     return 0;
143 }
144
145 static inline int get_event_ch(int fd)
146 {
147     int ret;
148     char ch;
149
150     ret = read(fd, &ch, sizeof(ch));
151     if (ret != sizeof(ch)) {
152         ErrPrint("read: %s\n", strerror(errno));
153         return ret;
154     }
155
156     ret = (int)((unsigned int)ch);
157     return ret;
158 }
159
160 static inline int file_service_close(int fd)
161 {
162     return secure_socket_destroy_handle(fd);
163 }
164
165 static inline int file_service_open(void)
166 {
167     char *addr;
168     int port;
169     char *file_addr;
170     int len;
171     int fd;
172
173     addr = malloc(strlen(client_addr()) + 1);
174     if (!addr) {
175         ErrPrint("Heap: %s\n", strerror(errno));
176         return -ENOMEM;
177     }
178
179     if (sscanf(client_addr(), COM_CORE_REMOTE_SCHEME"%[^:]:%d", addr, &port) != 2) {
180         ErrPrint("Invalid URL\n");
181         free(addr);
182         return -EINVAL;
183     }
184
185     len = strlen(COM_CORE_REMOTE_SCHEME);
186     len+= strlen(addr);
187     len+= 6;    /* Port length? */
188
189     file_addr = malloc(len);
190     if (!file_addr) {
191         ErrPrint("Heap: %s\n", strerror(errno));
192         free(addr);
193         return -ENOMEM;
194     }
195
196     snprintf(file_addr, len, COM_CORE_REMOTE_SCHEME"%s:%d", addr, FILE_SERVICE_PORT);
197     DbgPrint("File service: %s\n", file_addr);
198     fd = secure_socket_create_client(file_addr);
199     free(file_addr);
200     free(addr);
201
202     return fd;
203 }
204
205 /*!
206  * Service Thread
207  */
208 static void write_item_to_pipe(struct request_item *item, int ret)
209 {
210     item->ret = DBOX_STATUS_ERROR_FAULT;
211     if (write(s_info.evt_pipe[PIPE_WRITE], &item, sizeof(item)) != sizeof(item)) {
212         ErrPrint("write: %s\n", strerror(errno));
213         free(item->filename);
214         free(item->save_to);
215         free(item);
216         item = NULL;
217     }
218 }
219
220 /*!
221  * Service Thread
222  */
223 static void *file_service_main(void *data)
224 {
225     int ret = 0;
226     int select_fd;
227     struct timeval tv;
228     fd_set set;
229     int offset;
230     enum {
231         RECV_INIT,
232         RECV_HEADER,
233         RECV_DATA,
234     } recv_state;
235     struct burst_head *head;
236     struct burst_data *body;
237     int recvsz;
238     struct request_item *item;
239     int file_offset;
240     int file_fd;
241
242     head = NULL;
243     item = NULL;
244     recv_state = RECV_INIT;
245     select_fd = (s_info.file_service_fd > s_info.ctrl_pipe[PIPE_READ] ? s_info.file_service_fd : s_info.ctrl_pipe[PIPE_READ]) + 1;
246     while (ret == 0) {
247         FD_ZERO(&set);
248         FD_SET(s_info.file_service_fd, &set);
249         FD_SET(s_info.ctrl_pipe[PIPE_READ], &set);
250
251         tv.tv_sec = 3;
252         tv.tv_usec = 0;
253         ret = select(select_fd , &set, NULL, NULL, &tv);
254         if (ret < 0) {
255             ret = -errno;
256             if (errno == EINTR) {
257                 ErrPrint("INTERRUPTED\n");
258                 ret = 0;
259                 continue;
260             }
261             ErrPrint("Error: %s\n", strerror(errno));
262             break;
263         } else if (ret == 0) {
264             ErrPrint("Timeout\n");
265             ret = -ETIMEDOUT;
266             break;
267         }
268
269         if (item && FD_ISSET(s_info.file_service_fd, &set)) {
270             switch (recv_state) {
271                 case RECV_INIT:
272                     if (head == NULL) {
273                         recvsz = sizeof(*head);
274
275                         head = malloc(recvsz);
276                         if (!head) {
277                             ErrPrint("Heap: %s\n", strerror(errno));
278                             ret = DBOX_STATUS_ERROR_OUT_OF_MEMORY;
279                             write_item_to_pipe(item, ret);
280                             item = NULL;
281                             break;
282                         }
283
284                         offset = 0;
285                         recv_state = RECV_HEADER;
286                     }
287                 case RECV_HEADER:
288                     if (offset < recvsz) {
289                         ret = secure_socket_recv(s_info.file_service_fd, (char *)head + offset, recvsz - offset, NULL);
290                         if (ret > 0) {
291                             offset += ret;
292                         } else {
293                             free(head);
294                             head = NULL;
295                             recv_state = RECV_INIT;
296                             ret = DBOX_STATUS_ERROR_FAULT;
297                             write_item_to_pipe(item, ret);
298                             item = NULL;
299                             break;
300                         }
301                     }
302
303                     if (offset == sizeof(*head)) {
304                         void *tmp;
305
306                         recvsz += head->flen;
307
308                         tmp = realloc(head, recvsz);
309                         if (!tmp) {
310                             ErrPrint("Heap: %s\n", strerror(errno));
311
312                             free(head);
313                             head = NULL;
314                             recv_state = RECV_INIT;
315
316                             ret = DBOX_STATUS_ERROR_OUT_OF_MEMORY;
317                             write_item_to_pipe(item, ret);
318                             item = NULL;
319                             break;
320                         }
321
322                         head = tmp;
323                     } else if (offset == recvsz) {
324                         DbgPrint("Filesize: %d, name[%s]\n", head->size, head->fname);
325                         if (strcmp(item->filename, head->fname)) {
326                             ErrPrint("Invalid data sequence (%s <> %s)\n", item->filename, head->fname);
327
328                             free(head);
329                             head = NULL;
330                             recv_state = RECV_INIT;
331                             ret = DBOX_STATUS_ERROR_FAULT;
332                             write_item_to_pipe(item, ret);
333                             item = NULL;
334                             break;
335                         }
336
337                         file_fd = open(item->save_to, O_WRONLY|O_CREAT, 0644);
338                         if (file_fd < 0) {
339                             ErrPrint("open: %s\n", strerror(errno));
340                             free(head);
341                             head = NULL;
342                             recv_state = RECV_INIT;
343
344                             ret = DBOX_STATUS_ERROR_IO_ERROR;
345                             write_item_to_pipe(item, ret);
346                             item = NULL;
347                             break;
348                         }
349
350                         recv_state = RECV_DATA;
351                         body = NULL;
352
353                     } else {
354                         ErrPrint("Invalid state\n");
355                         free(head);
356                         head = NULL;
357                         recv_state = RECV_INIT;
358                         ret = DBOX_STATUS_ERROR_INVALID_PARAMETER;
359                         write_item_to_pipe(item, ret);
360                         item = NULL;
361                     }
362                     break;
363                 case RECV_DATA:
364                     if (!body) {
365                         body = malloc(sizeof(*body));
366                         if (!body) {
367                             free(head);
368                             head = NULL;
369                             recv_state = RECV_INIT;
370                             ret = DBOX_STATUS_ERROR_OUT_OF_MEMORY;
371                             write_item_to_pipe(item, ret);
372                             item = NULL;
373                             break;
374                         }
375
376                         recvsz = sizeof(*body);
377                         offset = 0;
378                     }
379
380                     ret = secure_socket_recv(s_info.file_service_fd, (char *)body + offset, recvsz - offset, NULL);
381                     if (ret > 0) {
382                         offset += ret;
383                     } else {
384                         free(head);
385                         head = NULL;
386                         free(body);
387                         body = NULL;
388                         recv_state = RECV_INIT;
389                         ret = DBOX_STATUS_ERROR_FAULT;
390                         write_item_to_pipe(item, ret);
391                         item = NULL;
392                         break;
393                     }
394
395                     if (offset == sizeof(*body)) {
396                         void *tmp;
397
398                         if (body->size < 0) {
399                             ErrPrint("body->size: %d\n", body->size);
400                             free(head);
401                             head = NULL;
402                             free(body);
403                             body = NULL;
404                             recv_state = RECV_INIT;
405                             ret = DBOX_STATUS_ERROR_FAULT;
406                             write_item_to_pipe(item, ret);
407                             item = NULL;
408                             break;
409                         }
410
411                         recvsz += body->size;
412
413                         tmp = realloc(body, recvsz);
414                         if (!tmp) {
415                             ErrPrint("Heap: %s\n", strerror(errno));
416                             free(head);
417                             head = NULL;
418
419                             free(body);
420                             body = NULL;
421                             recv_state = RECV_INIT;
422
423                             ret = DBOX_STATUS_ERROR_OUT_OF_MEMORY;
424                             write_item_to_pipe(item, ret);
425                             item = NULL;
426                             break;
427                         }
428                     } else if (offset == recvsz) {
429                         /* Flush this to the file */
430                         ret = write(file_fd, body->data, body->size);
431                         if (ret < 0) {
432                             ErrPrint("write: %s\n", strerror(errno));
433                             free(head);
434                             head = NULL;
435
436                             free(body);
437                             body = NULL;
438                             recv_state = RECV_INIT;
439
440                             ret = DBOX_STATUS_ERROR_IO_ERROR;
441                             write_item_to_pipe(item, ret);
442                             item = NULL;
443                             break;
444                         } else {
445                             if (body->size != ret) {
446                                 DbgPrint("Body is not flushed correctly: %d, %d\n", ret, body->size);
447                                 ret = body->size;
448                             }
449
450                             file_offset += ret;
451                             if (file_offset == head->size) {
452                                 if (close(file_fd) < 0) {
453                                     ErrPrint("close: %s\n", strerror(errno));
454                                 }
455                                 ret = DBOX_STATUS_ERROR_NONE;
456                                 write_item_to_pipe(item, ret);
457                                 item = NULL;
458                             }
459                         }
460
461                         free(body);
462                         body = NULL;
463
464                         free(head);
465                         head = NULL;
466
467                         recv_state = RECV_INIT;
468                     } else {
469                         ErrPrint("Invalid state\n");
470
471                         ret = -EFAULT;
472                         free(body);
473                         body = NULL;
474                         free(head);
475                         head = NULL;
476                         recv_state = RECV_INIT;
477
478                         ret = DBOX_STATUS_ERROR_FAULT;
479                         write_item_to_pipe(item, ret);
480                         item = NULL;
481                     }
482                     break;
483                 default:
484                     ErrPrint("Unknown event: %d\n", recv_state);
485                     ret = DBOX_STATUS_ERROR_FAULT;
486                     write_item_to_pipe(item, ret);
487                     item = NULL;
488                     break;
489             }
490         } else if (item == NULL && recv_state == RECV_INIT && FD_ISSET(s_info.ctrl_pipe[PIPE_READ], &set)) {
491             int ch;
492             struct dlist *l;
493
494             /* Only if the recv state is not changed, we can get next request item */
495             ch = get_event_ch(s_info.ctrl_pipe[PIPE_READ]);
496             if (ch == EVT_END_CH) {
497                 DbgPrint("Service thread is canceled\n");
498                 break;
499             }
500
501             CRITICAL_SECTION_BEGIN(&s_info.file_svc_lock);
502             l = dlist_nth(s_info.request_list, 0);
503             item = dlist_data(l);
504             s_info.request_list = dlist_remove(s_info.request_list, l);
505             CRITICAL_SECTION_END(&s_info.file_svc_lock);
506         }
507     }
508
509     return (void *)ret;
510 }
511
512 /* Master */
513 static gboolean evt_cb(GIOChannel *src, GIOCondition cond, gpointer data)
514 {
515     int fd;
516     struct request_item *item;
517
518     fd = g_io_channel_unix_get_fd(src);
519
520     if (!(cond & G_IO_IN)) {
521         DbgPrint("Client is disconencted\n");
522         return FALSE;
523     }
524
525     if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) {
526         DbgPrint("Client connection is lost\n");
527         return FALSE;
528     }
529
530     if (read(fd, &item, sizeof(item)) != sizeof(item)) {
531         ErrPrint("read: %s\n", strerror(errno));
532     } else {
533         if (item->result_cb) {
534             item->result_cb(item->filename, item->save_to, item->ret, item->data);
535         }
536
537         free(item->filename);
538         free(item->save_to);
539         free(item);
540     }
541
542     return TRUE;
543 }
544
545 int file_service_send_request(const char *filename, const char *save_to, void (*result_cb)(const char *filename, const char *save_to, int ret, void *data), void *data)
546 {
547     struct request_item *item;
548
549     item = malloc(sizeof(*item));
550     if (!item) {
551         ErrPrint("Heap: %s\n", strerror(errno));
552         return -ENOMEM;
553     }
554
555     item->filename = strdup(filename);
556     if (!item->filename) {
557         ErrPrint("Heap: %s\n", strerror(errno));
558         free(item);
559         return -ENOMEM;
560     }
561
562     item->save_to = strdup(save_to);
563     if (!item->save_to) {
564         ErrPrint("Heap: %s\n", strerror(errno));
565         free(item->filename);
566         free(item);
567         return -ENOMEM;
568     }
569
570     item->result_cb = result_cb;
571     item->data = data;
572
573     CRITICAL_SECTION_BEGIN(&s_info.file_svc_lock);
574     s_info.request_list = dlist_append(s_info.request_list, item);
575     CRITICAL_SECTION_END(&s_info.file_svc_lock);
576     return 0;
577 }
578
579 int file_service_init(void)
580 {
581     int status;
582     GIOChannel *gio;
583     guint id;
584
585     if (strncmp(client_addr(), COM_CORE_REMOTE_SCHEME, strlen(COM_CORE_REMOTE_SCHEME))) {
586         return 0;
587     }
588
589     s_info.file_service_fd = file_service_open();
590     if (s_info.file_service_fd < 0) {
591         return -EFAULT;
592     }
593
594     if (pipe2(s_info.ctrl_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
595         ErrPrint("file service: %s\n", strerror(errno));
596         file_service_close(s_info.file_service_fd);
597         s_info.file_service_fd = -1;
598         return -EFAULT;
599     }
600
601     if (pipe2(s_info.evt_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
602         ErrPrint("file service: %s\n", strerror(errno));
603         CLOSE_PIPE(s_info.ctrl_pipe);
604         file_service_close(s_info.file_service_fd);
605         s_info.file_service_fd = -1;
606         return -EFAULT;
607     }
608
609     status = pthread_mutex_init(&s_info.file_svc_lock, NULL);
610     if (status != 0) {
611         ErrPrint("Mutex: %s\n", strerror(status));
612         CLOSE_PIPE(s_info.ctrl_pipe);
613         CLOSE_PIPE(s_info.evt_pipe);
614         file_service_close(s_info.file_service_fd);
615         s_info.file_service_fd = -1;
616         return -EFAULT;
617     }
618
619     gio = g_io_channel_unix_new(s_info.evt_pipe[PIPE_READ]);
620     if (!gio) {
621         ErrPrint("io channel new\n");
622         status = pthread_mutex_destroy(&s_info.file_svc_lock);
623         if (status != 0) {
624             ErrPrint("destroy: %s\n", strerror(status));
625         }
626         CLOSE_PIPE(s_info.ctrl_pipe);
627         CLOSE_PIPE(s_info.evt_pipe);
628         file_service_close(s_info.file_service_fd);
629         s_info.file_service_fd = -1;
630         return -EFAULT;
631     }
632
633     g_io_channel_set_close_on_unref(gio, FALSE);
634
635     id = g_io_add_watch(gio, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)evt_cb, NULL);
636     if (id <= 0) {
637         GError *err = NULL;
638         ErrPrint("Failed to add IO watch\n");
639         g_io_channel_shutdown(gio, TRUE, &err);
640         if (err) {
641             ErrPrint("Shutdown: %s\n", err->message);
642             g_error_free(err);
643         }
644         g_io_channel_unref(gio);
645
646         status = pthread_mutex_destroy(&s_info.file_svc_lock);
647         if (status != 0) {
648             ErrPrint("destroy: %s\n", strerror(status));
649         }
650         CLOSE_PIPE(s_info.ctrl_pipe);
651         CLOSE_PIPE(s_info.evt_pipe);
652         file_service_close(s_info.file_service_fd);
653         s_info.file_service_fd = -1;
654         return -EIO;
655     }
656
657     status = pthread_create(&s_info.file_svc_thid, NULL, file_service_main, NULL);
658     if (status != 0) {
659         GError *err = NULL;
660         ErrPrint("Failed to add IO watch\n");
661         g_io_channel_shutdown(gio, TRUE, &err);
662         if (err) {
663             ErrPrint("Shutdown: %s\n", err->message);
664             g_error_free(err);
665         }
666         g_io_channel_unref(gio);
667
668         ErrPrint("file service: %s\n", strerror(status));
669         CLOSE_PIPE(s_info.ctrl_pipe);
670         CLOSE_PIPE(s_info.evt_pipe);
671         file_service_close(s_info.file_service_fd);
672         s_info.file_service_fd = -1;
673
674         status = pthread_mutex_destroy(&s_info.file_svc_lock);
675         if (status != 0) {
676             ErrPrint("destroy: %s\n", strerror(status));
677         }
678
679         return -EFAULT;
680     }
681
682     g_io_channel_unref(gio);
683     return 0;
684 }
685
686 int file_service_fini(void)
687 {
688     void *svc_ret;
689     int ret;
690
691     if (strncmp(client_addr(), COM_CORE_REMOTE_SCHEME, strlen(COM_CORE_REMOTE_SCHEME))) {
692         return 0;
693     }
694
695     (void)put_event_ch(s_info.ctrl_pipe[PIPE_WRITE], EVT_END_CH);
696
697     ret = pthread_join(s_info.file_svc_thid, &svc_ret);
698     if (ret != 0) {
699         ErrPrint("join: %s\n", strerror(ret));
700     } else {
701         DbgPrint("file svc returns: %d\n", (int)svc_ret);
702     }
703
704     ret = pthread_mutex_destroy(&s_info.file_svc_lock);
705     if (ret != 0) {
706         ErrPrint("destroy: %s\n", strerror(ret));
707     }
708
709     CLOSE_PIPE(s_info.evt_pipe);
710     CLOSE_PIPE(s_info.ctrl_pipe);
711
712     return 0;
713 }
714
715 /* End of a file */