15c230421c324dbb79bbe96255dac6316f54813b
[apps/livebox/data-provider-master.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 #include <stdio.h>
19 #include <pthread.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25
26 #include <Eina.h>
27
28 #include <dlog.h>
29
30 #include <livebox-errno.h>
31 #include <packet.h>
32 #include <com-core.h>
33
34 #include "file_service.h"
35 #include "service_common.h"
36 #include "debug.h"
37 #include "util.h"
38 #include "conf.h"
39 #include "buffer_handler.h"
40
41 #define FILE_SERVICE_ADDR       "remote://:8209"
42 #define FILE_PUSH_ADDR          "remote://:8210"
43
44 #define PUSH_EXIT       'e'
45 #define PUSH_ITEM       'i'
46
47 #define PKT_CHUNKSZ     4096
48
49 static struct info {
50         struct service_context *svc_ctx;
51
52         pthread_t push_thid;
53
54         Eina_List *request_list;
55         pthread_mutex_t request_list_lock;
56
57         int request_pipe[PIPE_MAX];
58 } s_info = {
59         .svc_ctx = NULL,
60         .request_list = NULL,
61         .request_list_lock = PTHREAD_MUTEX_INITIALIZER,
62         .request_pipe = { 0, },
63 };
64
65 struct request_item {
66         enum {
67                 REQUEST_TYPE_FILE = 0x00,
68                 REQUEST_TYPE_SHM = 0x01,
69                 REQUEST_TYPE_PIXMAP = 0x02,
70                 REQUEST_TYPE_MAX = 0x02,
71         } type;
72         union {
73                 char *filename;
74                 int shm;
75                 unsigned int pixmap;
76         } data;
77         struct tcb *tcb;
78 };
79
80 typedef int (*send_data_func_t)(int fd, const struct request_item *item);
81
82 /*!
83  * File transfer header.
84  * This must should be shared with client.
85  */
86 struct burst_head {
87         off_t size;
88         int flen;
89         char fname[];
90 };
91
92 struct burst_data {
93         int size;
94         char data[];
95 };
96
97 static inline struct request_item *create_request_item(struct tcb *tcb, int type, void *data)
98 {
99         struct request_item *item;
100
101         item = malloc(sizeof(*item));
102         if (!item) {
103                 ErrPrint("Heap: %s\n", strerror(errno));
104                 return NULL;
105         }
106
107         switch (type) {
108         case REQUEST_TYPE_FILE:
109                 item->data.filename = strdup(data);
110                 if (!item->data.filename) {
111                         ErrPrint("Heap: %s\n", strerror(errno));
112                         free(item);
113                         return NULL;
114                 }
115                 break;
116         case REQUEST_TYPE_PIXMAP:
117                 item->data.pixmap = (unsigned int)data;
118                 break;
119         case REQUEST_TYPE_SHM:
120                 item->data.shm = (int)data;
121                 break;
122         default:
123                 return NULL;
124         }
125
126         item->type = type;
127         item->tcb = tcb;
128         return item;
129 }
130
131 static inline int destroy_request_item(struct request_item *item)
132 {
133         switch (item->type) {
134         case REQUEST_TYPE_FILE:
135                 free(item->data.filename);
136                 break;
137         case REQUEST_TYPE_SHM:
138         case REQUEST_TYPE_PIXMAP:
139                 break;
140         default:
141                 return LB_STATUS_ERROR_INVALID;
142         }
143
144         free(item);
145         return LB_STATUS_SUCCESS;
146 }
147
148 static int request_file_handler(struct tcb *tcb, struct packet *packet, struct request_item **item)
149 {
150         const char *filename;
151
152         if (packet_get(packet, "s", &filename) != 1) {
153                 ErrPrint("Invalid packet\n");
154                 return LB_STATUS_ERROR_INVALID;
155         }
156
157         *item = create_request_item(tcb, REQUEST_TYPE_FILE, (void *)filename);
158         return *item ? LB_STATUS_SUCCESS : LB_STATUS_ERROR_MEMORY;
159 }
160
161 static int request_pixmap_handler(struct tcb *tcb, struct packet *packet, struct request_item **item)
162 {
163         unsigned int pixmap;
164
165         if (packet_get(packet, "i", &pixmap) != 1) {
166                 ErrPrint("Invalid packet\n");
167                 return LB_STATUS_ERROR_INVALID;
168         }
169
170         if (pixmap == 0) {
171                 ErrPrint("pixmap is not valid\n");
172                 return LB_STATUS_ERROR_INVALID;
173         }
174
175         /*!
176          * \TODO
177          * Attach to pixmap and copy its data to the client
178          */
179         *item = create_request_item(tcb, REQUEST_TYPE_PIXMAP, (void *)pixmap);
180         return *item ? LB_STATUS_SUCCESS : LB_STATUS_ERROR_MEMORY;
181 }
182
183 static int request_shm_handler(struct tcb *tcb, struct packet *packet, struct request_item **item)
184 {
185         int shm;
186
187         if (packet_get(packet, "i", &shm) != 1) {
188                 ErrPrint("Invalid packet\n");
189                 return LB_STATUS_ERROR_INVALID;
190         }
191
192         if (shm < 0) {
193                 ErrPrint("shm is not valid: %d\n", shm);
194                 return LB_STATUS_ERROR_INVALID;
195         }
196
197         /*!
198          * \TODO
199          * Attach to SHM and copy its buffer to the client
200          */
201         *item = create_request_item(tcb, REQUEST_TYPE_SHM, (void *)shm);
202         return *item ? LB_STATUS_SUCCESS : LB_STATUS_ERROR_MEMORY;
203 }
204
205 /* SERVER THREAD */
206 static int service_thread_main(struct tcb *tcb, struct packet *packet, void *data)
207 {
208         const char *cmd;
209         char ch = PUSH_ITEM;
210         int ret;
211         int i;
212         struct request_item *item;
213         struct packet *reply;
214         struct {
215                 const char *cmd;
216                 int (*request_handler)(struct tcb *tcb, struct packet *packet, struct request_item **item);
217         } cmd_table[] = {
218                 {
219                         .cmd = "request,file",
220                         .request_handler = request_file_handler,
221                 },
222                 {
223                         .cmd = "request,pixmap",
224                         .request_handler = request_pixmap_handler,
225                 },
226                 {
227                         .cmd = "request,shm",
228                         .request_handler = request_shm_handler,
229                 },
230                 {
231                         .cmd = NULL,
232                         .request_handler = NULL,
233                 },
234         };
235
236         if (!packet) {
237                 DbgPrint("TCB %p is disconnected\n", tcb);
238                 return LB_STATUS_SUCCESS;
239         }
240
241         cmd = packet_command(packet);
242         if (!cmd) {
243                 ErrPrint("Invalid packet. cmd is not valid\n");
244                 return LB_STATUS_ERROR_INVALID;
245         }
246
247         switch (packet_type(packet)) {
248         case PACKET_REQ:
249                 for (i = 0; cmd_table[i].cmd; i++) {
250                         /*!
251                          * Protocol sequence
252                          * FILE REQUEST COMMAND (Client -> Server)
253                          * REPLY FOR REQUEST (Client <- Server)
254                          * PUSH FILE (Client <- Server)
255                          *
256                          * Client & Server must has to keep this communication sequence.
257                          */
258                         if (strcmp(cmd, cmd_table[i].cmd))
259                                 continue;
260
261                         item = NULL;
262                         ret = cmd_table[i].request_handler(tcb, packet, &item);
263
264                         reply = packet_create_reply(packet, "i", ret);
265                         if (!reply) {
266                                 ErrPrint("Failed to create a reply packet\n");
267                                 break;
268                         }
269
270                         if (service_common_unicast_packet(tcb, reply) < 0)
271                                 ErrPrint("Unable to send reply packet\n");
272
273                         packet_destroy(reply);
274
275                         /*!
276                          * \note
277                          * After send the reply packet, file push thread can sending a file
278                          */
279                         if (ret != LB_STATUS_SUCCESS || !item)
280                                 break;
281
282                         CRITICAL_SECTION_BEGIN(&s_info.request_list_lock);
283                         s_info.request_list = eina_list_append(s_info.request_list, item);
284                         CRITICAL_SECTION_END(&s_info.request_list_lock);
285
286                         ret = write(s_info.request_pipe[PIPE_WRITE], &ch, sizeof(ch));
287                         if (ret < 0) {
288                                 ErrPrint("write: %s\n", strerror(errno));
289
290                                 CRITICAL_SECTION_BEGIN(&s_info.request_list_lock);
291                                 s_info.request_list = eina_list_remove(s_info.request_list, item);
292                                 CRITICAL_SECTION_END(&s_info.request_list_lock);
293
294                                 destroy_request_item(item);
295                                 /*!
296                                  * \note for the client
297                                  * In this case, the client can waiting files forever.
298                                  * So the client must has to wait only a few seconds.
299                                  * If the client could not get the any data in that time,
300                                  * it should cancel the waiting.
301                                  */
302                         }
303                 }
304
305                 break;
306         case PACKET_REQ_NOACK:
307         case PACKET_ACK:
308                 /* File service has no this case, it is passive service type */
309                 ErrPrint("Invalid packet.\n");
310                 break;
311         default:
312                 break;
313         }
314
315         return LB_STATUS_SUCCESS;
316 }
317
318 static int send_file(int handle, const struct request_item *item)
319 {
320         struct burst_head *head;
321         struct burst_data *body;
322         int pktsz;
323         int flen;
324         off_t fsize;
325         int fd;
326         int ret = 0;
327
328         /* TODO: push a file to the client */
329         fd = open(item->data.filename, O_RDONLY);
330         if (fd < 0) {
331                 ErrPrint("open: %s\n", strerror(errno));
332                 return -EIO;
333         }
334
335         flen = strlen(item->data.filename);
336         if (flen == 0) {
337                 ret = -EINVAL;
338                 goto errout;
339         }
340
341         pktsz = sizeof(*head) + flen + 1;
342
343         head = malloc(pktsz);
344         if (!head) {
345                 ErrPrint("heap: %s\n", strerror(errno));
346                 ret = -ENOMEM;
347                 goto errout;
348         }
349
350         fsize = lseek(fd, 0L, SEEK_END);
351         if (fsize == (off_t)-1) {
352                 ErrPrint("heap: %s\n", strerror(errno));
353                 free(head);
354                 ret = -EIO;
355                 goto errout;
356         }
357
358         head->flen = flen;
359         head->size = fsize;
360         strcpy(head->fname, item->data.filename);
361
362         /* Anytime we can fail to send packet */
363         ret = com_core_send(handle, (void *)head, pktsz, 2.0f);
364         free(head);
365         if (ret < 0) {
366                 ret = -EFAULT;
367                 goto errout;
368         }
369
370         if (lseek(fd, 0L, SEEK_SET) == (off_t)-1) {
371                 ErrPrint("seek: %s\n", strerror(errno));
372
373                 body = malloc(sizeof(*body));
374                 if (!body) {
375                         ErrPrint("Heap: %s\n", strerror(errno));
376                         return -ENOMEM;
377                 }
378
379                 body->size = -1;
380                 ret = com_core_send(handle, (void *)body, sizeof(*body), 2.0f);
381                 free(body);
382
383                 if (ret < 0)
384                         ret = -EFAULT;
385                 else
386                         ret = -EIO;
387
388                 goto errout;
389         }
390
391         body = malloc(PKT_CHUNKSZ + sizeof(*body));
392         if (!body) {
393                 ErrPrint("heap: %s\n", strerror(errno));
394                 goto errout;
395         }
396
397         /* Burst pushing. */
398         while (fsize > 0) {
399                 if (fsize > PKT_CHUNKSZ) {
400                         body->size = PKT_CHUNKSZ;
401                         fsize -= PKT_CHUNKSZ;
402                 } else {
403                         body->size = fsize;
404                         fsize = 0;
405                 }
406
407                 pktsz = sizeof(*body) + body->size;
408
409                 ret = read(fd, body->data, body->size); 
410                 if (ret < 0) {
411                         ErrPrint("read: %s\n", strerror(errno));
412                         ret = -EIO;
413                         break;
414                 }
415
416                 /* Send BODY */
417                 ret = com_core_send(handle, (void *)body, pktsz, 2.0f);
418                 if (ret != pktsz) {
419                         ret = -EFAULT;
420                         break;
421                 }
422         }
423
424         /* Send EOF */
425         body->size = -1;
426         ret = com_core_send(handle, (void *)body, sizeof(*body), 2.0f);
427         if (ret < 0)
428                 ret = -EFAULT;
429
430         free(body);
431
432 errout:
433         if (close(fd) < 0)
434                 ErrPrint("close: %s\n", strerror(errno));
435
436         return ret;
437 }
438
439 static int send_buffer(int handle, const struct request_item *item)
440 {
441         struct buffer *buffer;
442         struct burst_head *head;
443         struct burst_data *body;
444         char *data;
445         int pktsz;
446         int ret;
447         int size;
448         int offset;
449         int type;
450
451         if (item->type == REQUEST_TYPE_SHM)
452                 type = BUFFER_TYPE_SHM;
453         else
454                 type = BUFFER_TYPE_PIXMAP;
455
456         buffer = buffer_handler_raw_open(type, (void *)item->data.shm);
457         if (!buffer)
458                 return -EINVAL;
459
460         pktsz = sizeof(*head);
461
462         head = malloc(pktsz);
463         if (!head) {
464                 ErrPrint("Heap: %s\n", strerror(errno));
465                 (void)buffer_handler_raw_close(buffer);
466                 return -ENOMEM;
467         }
468
469         size = head->size = buffer_handler_raw_size(buffer);
470         head->flen = 0;
471
472         /* Anytime we can fail to send packet */
473         ret = com_core_send(handle, (void *)head, pktsz, 2.0f);
474         free(head);
475         if (ret < 0) {
476                 ret = -EFAULT;
477                 goto errout;
478         }
479
480         body = malloc(sizeof(*body) + PKT_CHUNKSZ);
481         if (!body) {
482                 ret = -ENOMEM;
483                 goto errout;
484         }
485
486         data = (char *)buffer_handler_raw_data(buffer);
487         offset = 0;
488         while (offset < size) {
489                 body->size = size - offset;
490
491                 if (body->size > PKT_CHUNKSZ)
492                         body->size = PKT_CHUNKSZ;
493
494                 memcpy(body->data, data, body->size);
495                 pktsz = sizeof(*body) + body->size;
496
497                 ret = com_core_send(handle, (void *)body, pktsz, 2.0f);
498                 if (ret < 0) {
499                         ret = -EFAULT;
500                         break;
501                 }
502
503                 offset += body->size;
504         }
505
506         free(body);
507
508 errout:
509         (void)buffer_handler_raw_close(buffer);
510         return ret;
511 }
512
513 static void *push_main(void *data)
514 {
515         fd_set set;
516         int ret;
517         char ch;
518         struct request_item *item;
519         int conn_fd;
520         send_data_func_t send_data[] = {
521                 send_file,
522                 send_buffer,
523                 send_buffer,
524         };
525
526         while (1) {
527                 FD_ZERO(&set);
528                 FD_SET(s_info.request_pipe[PIPE_READ], &set);
529
530                 ret = select(s_info.request_pipe[PIPE_READ] + 1, &set, NULL, NULL, NULL);
531                 if (ret < 0) {
532                         ret = -errno;
533                         if (errno == EINTR) {
534                                 ErrPrint("INTERRUPTED\n");
535                                 ret = 0;
536                                 continue;
537                         }
538                         ErrPrint("Error: %s\n", strerror(errno));
539                         break;
540                 } else if (ret == 0) {
541                         ErrPrint("Timeout\n");
542                         ret = -ETIMEDOUT;
543                         break;
544                 }
545
546                 if (!FD_ISSET(s_info.request_pipe[PIPE_READ], &set)) {
547                         DbgPrint("Unknown data\n");
548                         ret = -EINVAL;
549                         break;
550                 }
551
552                 ret = read(s_info.request_pipe[PIPE_READ], &ch, sizeof(ch));
553                 if (ret != sizeof(ch)) {
554                         ErrPrint("read: %s\n", strerror(errno));
555                         ret = -EFAULT;
556                         break;
557                 }
558
559                 if (ch == PUSH_EXIT) {
560                         DbgPrint("Thread is terminating\n");
561                         ret = -ECANCELED;
562                         break;
563                 }
564
565                 CRITICAL_SECTION_BEGIN(&s_info.request_list_lock);
566                 item = eina_list_nth(s_info.request_list, 0);
567                 s_info.request_list = eina_list_remove(s_info.request_list, item);
568                 CRITICAL_SECTION_END(&s_info.request_list_lock);
569
570                 if (!item) {
571                         ErrPrint("Request item is not valid\n");
572                         continue;
573                 }
574
575                 /* Validate the TCB? */
576                 conn_fd = tcb_is_valid(s_info.svc_ctx, item->tcb);
577                 if (conn_fd < 0) {
578                         ErrPrint("TCB is not valid\n");
579                         destroy_request_item(item);
580                         continue;
581                 }
582
583                 /*
584                  * \note
585                  * From now, we cannot believe the conn_fd.
586                  * It can be closed any time.
587                  * Even though we using it.
588                  */
589                 if (item->type < REQUEST_TYPE_MAX && item->type >= 0)
590                         (void)send_data[item->type](conn_fd, item);
591                 else
592                         ErrPrint("Invalid type\n");
593
594                 destroy_request_item(item);
595         }
596
597         return (void *)ret;
598 }
599
600 /* MAIN THREAD */
601 int file_service_init(void)
602 {
603         int status;
604
605         if (s_info.svc_ctx) {
606                 ErrPrint("Already initialized\n");
607                 return LB_STATUS_ERROR_ALREADY;
608         }
609
610         if (pipe2(s_info.request_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
611                 ErrPrint("pipe: %s\n", strerror(errno));
612                 return LB_STATUS_ERROR_FAULT;
613         }
614
615         status = pthread_mutex_init(&s_info.request_list_lock, NULL);
616         if (status != 0) {
617                 ErrPrint("Failed to create lock: %s\n", strerror(status));
618                 CLOSE_PIPE(s_info.request_pipe);
619                 return LB_STATUS_ERROR_FAULT;
620         }
621
622         s_info.svc_ctx = service_common_create(FILE_SERVICE_ADDR, service_thread_main, NULL);
623         if (!s_info.svc_ctx) {
624                 ErrPrint("Unable to activate service thread\n");
625
626                 status = pthread_mutex_destroy(&s_info.request_list_lock);
627                 if (status != 0)
628                         ErrPrint("Destroy lock: %s\n", strerror(status));
629
630                 CLOSE_PIPE(s_info.request_pipe);
631                 return LB_STATUS_ERROR_FAULT;
632         }
633
634         status = pthread_create(&s_info.push_thid, NULL, push_main, NULL);
635         if (status != 0) {
636                 ErrPrint("Failed to create a push service: %s\n", strerror(status));
637
638                 service_common_destroy(s_info.svc_ctx);
639                 s_info.svc_ctx = NULL;
640
641                 status = pthread_mutex_destroy(&s_info.request_list_lock);
642                 if (status != 0)
643                         ErrPrint("Destroy lock: %s\n", strerror(status));
644
645                 CLOSE_PIPE(s_info.request_pipe);
646                 return LB_STATUS_ERROR_FAULT;
647         }
648
649         /*!
650          * \note
651          * Remote service doesn't need to set the additional SMAK label.
652          */
653
654         DbgPrint("Successfully initiated\n");
655         return LB_STATUS_SUCCESS;
656 }
657
658 /* MAIN THREAD */
659 int file_service_fini(void)
660 {
661         struct request_item *item;
662         int status;
663         char ch;
664         void *retval;
665
666         if (!s_info.svc_ctx)
667                 return LB_STATUS_ERROR_INVALID;
668
669         ch = PUSH_EXIT;
670         status = write(s_info.request_pipe[PIPE_WRITE], &ch, sizeof(ch));
671         if (status != sizeof(ch)) {
672                 ErrPrint("write: %s\n", strerror(errno));
673                 /* Forcely terminate the thread */
674                 status = pthread_cancel(s_info.push_thid);
675                 if (status != 0)
676                         ErrPrint("cancel: %s\n", strerror(status));
677         }
678
679         status = pthread_join(s_info.push_thid, &retval);
680         if (status != 0)
681                 ErrPrint("join: %s\n", strerror(status));
682
683         CRITICAL_SECTION_BEGIN(&s_info.request_list_lock);
684         EINA_LIST_FREE(s_info.request_list, item) {
685                 destroy_request_item(item);
686         }
687         CRITICAL_SECTION_END(&s_info.request_list_lock);
688
689         service_common_destroy(s_info.svc_ctx);
690         s_info.svc_ctx = NULL;
691
692         status = pthread_mutex_destroy(&s_info.request_list_lock);
693         if (status != 0)
694                 ErrPrint("destroy mutex: %s\n", strerror(status));
695
696         CLOSE_PIPE(s_info.request_pipe);
697
698         DbgPrint("Successfully Finalized\n");
699         return LB_STATUS_SUCCESS;
700 }
701
702 /* End of a file */