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