Fix memory leak
[platform/core/multimedia/media-server.git] / src / server / media-server-thumb.c
1 /*
2  * media-thumbnail-server
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Hyunjun Ko <zzoon.ko@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22 #include "media-util.h"
23 #include "media-common-utils.h"
24 #include "media-server-dbg.h"
25 #include "media-server-thumb.h"
26 #include <tzplatform_config.h>
27
28 #ifdef LOG_TAG
29 #undef LOG_TAG
30 #endif
31
32 #define LOG_TAG "MEDIA_SERVER_THUMB"
33
34 #define THUMB_SERVER_PATH tzplatform_mkpath(TZ_SYS_BIN, "media-thumbnail-server")
35
36 static GMainLoop *g_thumb_agent_loop = NULL;
37 static gboolean g_thumb_server_forked = FALSE;
38 static gboolean g_shutdowning_thumb_server = FALSE;
39 static int g_communicate_sock = 0;
40 static int g_timer_id = 0;
41 static int g_server_pid = 0;
42
43 static GQueue *g_request_queue = NULL;
44 static gboolean g_queue_work = FALSE;
45
46 typedef struct {
47         int client_sock;
48         thumbMsg *recv_msg;
49 } thumbRequest;
50
51 extern char MEDIA_IPC_PATH[][70];
52
53 GMainLoop* ms_get_thumb_thread_mainloop(void)
54 {
55         return g_thumb_agent_loop;
56 }
57
58 int ms_thumb_get_server_pid(void)
59 {
60         return g_server_pid;
61 }
62
63 void ms_thumb_reset_server_status(void)
64 {
65         g_thumb_server_forked = FALSE;
66         g_shutdowning_thumb_server = FALSE;
67
68         if (g_timer_id > 0) {
69                 g_source_destroy(g_main_context_find_source_by_id(g_main_context_get_thread_default(), g_timer_id));
70                 g_timer_id = 0;
71         }
72
73         g_server_pid = 0;
74 }
75
76 static gboolean __ms_thumb_agent_prepare_tcp_socket(int *sock_fd, unsigned short serv_port)
77 {
78         if (ms_ipc_create_server_socket(serv_port, sock_fd) < 0) {
79                 MS_DBG_ERR("_ms_thumb_create_socket failed");
80                 return FALSE;
81         }
82
83         return TRUE;
84 }
85
86 static int __ms_thumb_recv_msg(int sock, thumbMsg *msg)
87 {
88         int remain_size = 0;
89         int recv_pos = 0;
90         int recv_len = 0;
91         unsigned char *buf = NULL;
92         unsigned int header_size = sizeof(thumbMsg) - sizeof(unsigned char *);
93
94         MS_MALLOC(buf, header_size);
95         MS_DBG_RETV_IF(!buf, MS_MEDIA_ERR_OUT_OF_MEMORY);
96
97         while (header_size > 0) {
98                 if ((recv_len = recv(sock, buf + recv_pos, header_size, 0)) < 0) {
99                         MS_DBG_STRERROR("recv failed");
100                         MS_SAFE_FREE(buf);
101                         return MS_MEDIA_ERR_IPC;
102                 }
103                 header_size -= recv_len;
104                 recv_pos += recv_len;
105         }
106
107         header_size = recv_pos;
108         recv_pos = 0;
109
110         memcpy(msg, buf, header_size);
111         MS_SAFE_FREE(buf);
112
113         MS_DBG("status[%d]", msg->status);
114
115         MS_DBG_RETVM_IF(msg->msg_type == THUMB_REQUEST_KILL_SERVER, MS_MEDIA_ERR_IPC, "Wrong msg");
116         MS_DBG_RETVM_IF(strlen(msg->org_path) == 0 || strlen(msg->org_path) >= MS_FILE_PATH_LEN_MAX, MS_MEDIA_ERR_IPC, "Invalid org_path");
117         MS_DBG_RETVM_IF(strlen(msg->dst_path) >= MS_FILE_PATH_LEN_MAX, MS_MEDIA_ERR_IPC, "Invalid dst_path");
118         MS_DBG_RETVM_IF(msg->thumb_size < 0, MS_MEDIA_ERR_IPC, "Invalid thumb_size");
119         MS_DBG_RETV_IF(msg->thumb_size == 0, MS_MEDIA_ERR_NONE);
120
121         remain_size = msg->thumb_size;
122         MS_MALLOC(buf, remain_size);
123         MS_DBG_RETV_IF(!buf, MS_MEDIA_ERR_OUT_OF_MEMORY);
124
125         while (remain_size > 0) {
126                 if ((recv_len = recv(sock, buf + recv_pos, remain_size, 0)) < 0) {
127                         MS_DBG_STRERROR("recv failed");
128                         MS_SAFE_FREE(buf);
129                         return MS_MEDIA_ERR_IPC;
130                 }
131                 fsync(sock);
132
133                 recv_pos += recv_len;
134                 remain_size -= recv_len;
135         }
136
137         MS_SAFE_FREE(msg->thumb_data);
138
139         MS_MALLOC(msg->thumb_data, (unsigned int)(msg->thumb_size));
140         if (msg->thumb_data) {
141                 memcpy(msg->thumb_data, buf, msg->thumb_size);
142         } else {
143                 MS_SAFE_FREE(buf);
144                 return MS_MEDIA_ERR_OUT_OF_MEMORY;
145         }
146
147         MS_SAFE_FREE(buf);
148
149         return MS_MEDIA_ERR_NONE;
150 }
151
152 static int __ms_thumb_set_buffer(thumbMsg *req_msg, unsigned char **buf, int *buf_size)
153 {
154         int header_size = sizeof(thumbMsg) - sizeof(unsigned char *);
155         unsigned int size = 0;
156
157         MS_DBG_RETV_IF(!req_msg || !buf || req_msg->thumb_size < 0, MS_MEDIA_ERR_INVALID_PARAMETER);
158
159         MS_DBG_SLOG("Basic Size[%d] org_path[%s] dst_path[%s] thumb_data[%d]", header_size, req_msg->org_path, req_msg->dst_path, req_msg->thumb_size);
160
161         size = header_size + req_msg->thumb_size;
162         MS_MALLOC(*buf, size);
163         MS_DBG_RETV_IF(!(*buf), MS_MEDIA_ERR_OUT_OF_MEMORY);
164
165         memcpy(*buf, req_msg, header_size);
166         if (req_msg->thumb_size > 0)
167                 memcpy((*buf) + header_size, req_msg->thumb_data, req_msg->thumb_size);
168
169         *buf_size = size;
170
171         return MS_MEDIA_ERR_NONE;
172 }
173
174 static gboolean __ms_thumb_agent_recv_msg_from_server(void)
175 {
176         struct sockaddr_un serv_addr;
177         unsigned int serv_addr_len;
178         ms_thumb_server_msg recv_msg;
179         int len = 0;
180         int sockfd = -1;
181
182         if (g_communicate_sock <= 0)
183                 __ms_thumb_agent_prepare_tcp_socket(&g_communicate_sock, MS_THUMB_COMM_PORT);
184
185         serv_addr_len = sizeof(serv_addr);
186
187         if ((sockfd = accept(g_communicate_sock, (struct sockaddr*)&serv_addr, &serv_addr_len)) < 0) {
188                 MS_DBG_STRERROR("accept failed");
189                 return FALSE;
190         }
191
192         if ((len = read(sockfd, &recv_msg, sizeof(ms_thumb_server_msg))) < 0) {
193                 MS_DBG_STRERROR("read failed");
194                 close(sockfd);
195                 return FALSE;
196         }
197
198         if (recv_msg.msg_type == MS_MSG_THUMB_SERVER_READY)
199                 MS_DBG("Thumbnail server is ready");
200
201         close(sockfd);
202         return TRUE;
203 }
204
205 static gboolean __ms_thumb_agent_execute_server(void)
206 {
207         int pid;
208         pid = fork();
209
210         if (pid < 0) {
211                 return FALSE;
212         } else if (pid == 0) {
213                 execl(THUMB_SERVER_PATH, "media-thumbnail-server", NULL);
214         } else {
215                 MS_DBG("Child process is %d", pid);
216                 g_thumb_server_forked = TRUE;
217         }
218
219         g_server_pid = pid;
220
221         if (!__ms_thumb_agent_recv_msg_from_server()) {
222                 MS_DBG_ERR("_ms_thumb_agent_recv_msg_from_server is failed");
223                 return FALSE;
224         }
225
226         return TRUE;
227 }
228
229 static gboolean __ms_thumb_agent_send_msg_to_thumb_server(thumbMsg *recv_msg, thumbMsg *res_msg)
230 {
231         int sock;
232         struct sockaddr_un serv_addr;
233         int buf_size = 0;
234         unsigned char *buf = NULL;
235
236         MS_DBG_RETVM_IF(!recv_msg || strlen(recv_msg->org_path) >= MAX_FILEPATH_LEN, FALSE, "Invalid msg");
237
238         if (ms_ipc_create_client_socket(MS_TIMEOUT_SEC_10, &sock) < 0) {
239                 MS_DBG_ERR("ms_ipc_create_client_socket failed");
240                 return FALSE;
241         }
242
243         memset(&serv_addr, 0, sizeof(serv_addr));
244         serv_addr.sun_family = AF_UNIX;
245         SAFE_STRLCPY(serv_addr.sun_path, tzplatform_mkpath(TZ_SYS_RUN, MEDIA_IPC_PATH[MS_THUMB_DAEMON_PORT]), sizeof(serv_addr.sun_path));
246
247         /* Connecting to the thumbnail server */
248         if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
249                 MS_DBG_STRERROR("connect failed");
250                 close(sock);
251                 return FALSE;
252         }
253
254         __ms_thumb_set_buffer(recv_msg, &buf, &buf_size);
255
256         if (send(sock, buf, buf_size, 0) < 0) {
257                 MS_DBG_STRERROR("send failed");
258                 MS_SAFE_FREE(buf);
259                 close(sock);
260                 return FALSE;
261         }
262
263         MS_SAFE_FREE(buf);
264         MS_DBG_SLOG("Sending msg to thumbnail server is successful");
265
266         if (recv_msg->msg_type == THUMB_REQUEST_KILL_SERVER) {
267                 MS_DBG_SLOG("No response if msg type is kill server[%d]", recv_msg->msg_type);
268                 close(sock);
269                 return TRUE;
270         }
271
272         if (__ms_thumb_recv_msg(sock, res_msg) < 0) {
273                 MS_DBG_ERR("_ms_thumb_recv_msg failed");
274                 close(sock);
275                 return FALSE;
276         }
277
278         if (res_msg->status == MS_MEDIA_ERR_NONE)
279                 MS_DBG_SLOG("recv %s from thumb daemon is successful", res_msg->dst_path);
280         else
281                 MS_DBG_SLOG("recv %s from thumb daemon is failed (%d)", res_msg->dst_path, res_msg->status);
282
283         close(sock);
284
285         return TRUE;
286 }
287
288 static gboolean __ms_thumb_agent_timer(gpointer data)
289 {
290         g_timer_id = 0;
291         MS_DBG("Timer is called.. Now killing media-thumbnail-server[%d]", g_server_pid);
292
293         if (g_server_pid > 0) {
294                 /* Kill thumbnail server */
295                 thumbMsg msg;
296                 memset((void *)&msg, 0, sizeof(msg));
297
298                 msg.msg_type = THUMB_REQUEST_KILL_SERVER;
299                 msg.org_path[0] = '\0';
300                 msg.dst_path[0] = '\0';
301
302                 /* Command Kill to thumbnail server */
303                 g_shutdowning_thumb_server = TRUE;
304                 if (!__ms_thumb_agent_send_msg_to_thumb_server(&msg, NULL)) {
305                         MS_DBG_ERR("_ms_thumb_agent_send_msg_to_thumb_server is failed");
306                         g_shutdowning_thumb_server = FALSE;
307                 }
308                 usleep(200000);
309         } else {
310                 MS_DBG_ERR("g_server_pid is %d. Maybe there's problem in thumbnail-server", g_server_pid);
311         }
312
313         return FALSE;
314 }
315
316 static void __ms_thumb_create_timer(int id)
317 {
318         if (id > 0)
319                 g_source_destroy(g_main_context_find_source_by_id(g_main_context_get_thread_default(), id));
320
321         GSource *timer_src = g_timeout_source_new_seconds(MS_TIMEOUT_SEC_20);
322         g_source_set_callback(timer_src, __ms_thumb_agent_timer, NULL, NULL);
323         g_timer_id = g_source_attach(timer_src, g_main_context_get_thread_default());
324 }
325
326 static gboolean __ms_thumb_request_to_server(gpointer data)
327 {
328         thumbRequest *req = NULL;
329         guint req_len = g_queue_get_length(g_request_queue);
330
331         MS_DBG("Queue length : %d", req_len);
332         if (req_len == 0) {
333                 g_queue_work = FALSE;
334                 return FALSE;
335         }
336
337         if (g_shutdowning_thumb_server) {
338                 MS_DBG_WARN("Thumb server is shutting down... wait for complete");
339                 usleep(10000);
340                 return TRUE;
341         }
342
343         if (!g_thumb_server_forked) {
344                 MS_DBG_WARN("Thumb server is not running.. so start it");
345                 if (!__ms_thumb_agent_execute_server()) {
346                         MS_DBG_ERR("_ms_thumb_agent_execute_server is failed");
347                         g_queue_work = FALSE;
348                         return FALSE;
349                 }
350         }
351         __ms_thumb_create_timer(g_timer_id);
352
353         req = (thumbRequest *)g_queue_pop_head(g_request_queue);
354         MS_DBG_RETVM_IF(!req, TRUE, "Failed to get a request job from queue");
355
356         int client_sock = -1;
357         thumbMsg *recv_msg = NULL;
358         thumbMsg res_msg;
359         memset((void *)&res_msg, 0, sizeof(res_msg));
360
361         client_sock = req->client_sock;
362         recv_msg = req->recv_msg;
363
364         if (req->client_sock <= 0 || req->recv_msg == NULL) {
365                 MS_DBG_ERR("client sock is below 0 or recv msg is NULL");
366                 MS_SAFE_FREE(req->recv_msg);
367                 MS_SAFE_FREE(req);
368                 return TRUE;
369         }
370
371         if (recv_msg) {
372                 if (!__ms_thumb_agent_send_msg_to_thumb_server(recv_msg, &res_msg)) {
373                         MS_DBG_ERR("_ms_thumb_agent_send_msg_to_thumb_server is failed");
374
375                         thumbMsg res_msg;
376                         memset((void *)&res_msg, 0, sizeof(res_msg));
377
378                         res_msg.msg_type = THUMB_RESPONSE;
379                         res_msg.status = MS_MEDIA_ERR_INTERNAL;
380                         SAFE_STRLCPY(res_msg.org_path, recv_msg->org_path, sizeof(res_msg.org_path));
381                         res_msg.dst_path[0] = '\0';
382                         res_msg.thumb_data = NULL;
383                         res_msg.thumb_size = 0;
384
385                         int buf_size = 0;
386                         unsigned char *buf = NULL;
387                         __ms_thumb_set_buffer(&res_msg, &buf, &buf_size);
388
389                         if (send(client_sock, buf, buf_size, 0) < 0)
390                                 MS_DBG_STRERROR("send failed");
391                         else
392                                 MS_DBG("Sent Refuse msg from %s", recv_msg->org_path);
393
394                         close(client_sock);
395
396                         MS_SAFE_FREE(buf);
397                         MS_SAFE_FREE(req->recv_msg);
398                         MS_SAFE_FREE(req);
399
400                         return TRUE;
401                 }
402         } else {
403                 MS_DBG_ERR("recv_msg is NULL from queue request");
404         }
405
406         SAFE_STRLCPY(res_msg.org_path, recv_msg->org_path, sizeof(res_msg.org_path));
407
408         int buf_size = 0;
409         int send_len = 0;
410         int send_pos = 0;
411         unsigned char *buf = NULL;
412         __ms_thumb_set_buffer(&res_msg, &buf, &buf_size);
413
414         while (buf_size > 0) {
415                 if ((send_len = send(client_sock, buf + send_pos, buf_size, 0)) < 0) {
416                         MS_DBG_STRERROR("send failed");
417                         break;
418                 }
419
420                 send_pos += send_len;
421                 buf_size -= send_len;
422         }
423
424         close(client_sock);
425         MS_SAFE_FREE(buf);
426         MS_SAFE_FREE(req->recv_msg);
427         MS_SAFE_FREE(req);
428         MS_SAFE_FREE(res_msg.thumb_data);
429
430         return TRUE;
431 }
432
433 static gboolean __ms_thumb_agent_read_socket(GIOChannel *src, GIOCondition condition, gpointer data)
434 {
435         struct sockaddr_un client_addr;
436         unsigned int client_addr_len;
437         thumbMsg *recv_msg = NULL;
438         int sock = -1;
439         int client_sock = -1;
440         unsigned char *buf = NULL;
441         thumbRequest *thumb_req = NULL;
442
443         sock = g_io_channel_unix_get_fd(src);
444         MS_DBG_RETVM_IF(sock < 0, G_SOURCE_CONTINUE, "sock fd is invalid!");
445
446         client_addr_len = sizeof(client_addr);
447
448         if ((client_sock = accept(sock, (struct sockaddr*)&client_addr, &client_addr_len)) < 0) {
449                 MS_DBG_STRERROR("accept failed");
450                 return G_SOURCE_CONTINUE;
451         }
452
453         MS_DBG("Client[%d] is accepted", client_sock);
454
455         recv_msg = calloc(1, sizeof(thumbMsg));
456         if (!recv_msg) {
457                 MS_DBG_ERR("Failed to allocate memory");
458                 close(client_sock);
459                 return G_SOURCE_CONTINUE;
460         }
461
462         if (__ms_thumb_recv_msg(client_sock, recv_msg) < 0) {
463                 MS_DBG_ERR("_ms_thumb_recv_msg failed ");
464                 goto ERROR;
465         }
466
467         MS_MALLOC(thumb_req, sizeof(thumbRequest));
468         if (!thumb_req) {
469                 MS_DBG_ERR("Failed to create request element");
470                 goto ERROR;
471         }
472
473         thumb_req->client_sock = client_sock;
474         thumb_req->recv_msg = recv_msg;
475
476         if (!g_request_queue) {
477                 MS_DBG_WARN("Creating queue");
478                 g_request_queue = g_queue_new();
479         }
480
481         if (g_queue_get_length(g_request_queue) >= MAX_THUMB_REQUEST) {
482                 MS_DBG_WARN("Request Queue is full");
483                 thumbMsg res_msg;
484                 memset((void *)&res_msg, 0, sizeof(res_msg));
485
486                 res_msg.msg_type = THUMB_RESPONSE;
487                 res_msg.status = MS_MEDIA_ERR_INTERNAL;
488                 SAFE_STRLCPY(res_msg.org_path, recv_msg->org_path, sizeof(res_msg.org_path));
489                 res_msg.dst_path[0] = '\0';
490                 res_msg.thumb_size = 0;
491
492                 int buf_size = 0;
493                 __ms_thumb_set_buffer(&res_msg, &buf, &buf_size);
494
495                 if (send(client_sock, buf, buf_size, 0) < 0)
496                         MS_DBG_STRERROR("send failed");
497                 else
498                         MS_DBG("Sent Refuse msg from %s", recv_msg->org_path);
499
500                 MS_SAFE_FREE(buf);
501                 goto ERROR;
502         }
503
504         MS_DBG_SLOG("%s is queued", recv_msg->org_path);
505         g_queue_push_tail(g_request_queue, (gpointer)thumb_req);
506
507         if (!g_queue_work) {
508                 GSource *src_request = NULL;
509                 src_request = g_idle_source_new();
510                 g_source_set_callback(src_request, __ms_thumb_request_to_server, NULL, NULL);
511                 g_source_attach(src_request, g_main_context_get_thread_default());
512                 g_queue_work = TRUE;
513         }
514
515         return G_SOURCE_CONTINUE;
516 ERROR:
517         close(client_sock);
518         MS_SAFE_FREE(recv_msg->thumb_data);
519         MS_SAFE_FREE(recv_msg);
520         MS_SAFE_FREE(thumb_req);
521         return G_SOURCE_CONTINUE;
522 }
523
524
525 gpointer ms_thumb_agent_start_thread(gpointer data)
526 {
527         int sockfd = -1;
528         GSource *source = NULL;
529         GIOChannel *channel = NULL;
530         GMainContext *context = NULL;
531
532         /* Create and bind new TCP socket */
533         if (!__ms_thumb_agent_prepare_tcp_socket(&sockfd, MS_THUMB_CREATOR_PORT)) {
534                 MS_DBG_ERR("Failed to create socket");
535                 return NULL;
536         }
537
538         context = g_main_context_new();
539         if (!context) {
540                 MS_DBG_ERR("g_main_context_new failed");
541                 return NULL;
542         }
543
544         g_thumb_agent_loop = g_main_loop_new(context, FALSE);
545         g_main_context_push_thread_default(context);
546
547         /* Create new channel to watch udp socket */
548         channel = g_io_channel_unix_new(sockfd);
549         source = g_io_create_watch(channel, G_IO_IN);
550
551         /* Set callback to be called when socket is readable */
552         g_source_set_callback(source, (GSourceFunc)__ms_thumb_agent_read_socket, NULL, NULL);
553         g_source_attach(source, context);
554
555         MS_DBG_INFO("Thumbnail Agent thread is running");
556         g_main_loop_run(g_thumb_agent_loop);
557         MS_DBG_INFO("Thumbnail Agent thread is shutting down");
558
559         /*close an IO channel*/
560         g_io_channel_shutdown(channel, FALSE, NULL);
561         g_io_channel_unref(channel);
562         close(g_communicate_sock);
563
564         g_main_loop_unref(g_thumb_agent_loop);
565
566         close(sockfd);
567
568         return NULL;
569 }
570