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