Initialize Tizen 2.3
[apps/livebox/livebox-viewer.git] / 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 <livebox-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 = LB_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 = LB_STATUS_ERROR_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 = LB_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 = LB_STATUS_ERROR_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 = LB_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 = LB_STATUS_ERROR_IO;
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 = LB_STATUS_ERROR_INVALID;
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 = LB_STATUS_ERROR_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 = LB_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 = LB_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 = LB_STATUS_ERROR_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 = LB_STATUS_ERROR_IO;
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 = LB_STATUS_SUCCESS;
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 = LB_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 = LB_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 */