2 * media-thumbnail-server
4 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: Hyunjun Ko <zzoon.ko@samsung.com>
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
32 #define LOG_TAG "MEDIA_SERVER_THUMB"
34 #define THUMB_SERVER_PATH tzplatform_mkpath(TZ_SYS_BIN, "media-thumbnail-server")
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;
43 static GQueue *g_request_queue = NULL;
44 static gboolean g_queue_work = FALSE;
51 extern char MEDIA_IPC_PATH[][70];
53 GMainLoop* ms_get_thumb_thread_mainloop(void)
55 return g_thumb_agent_loop;
58 int ms_thumb_get_server_pid(void)
63 void ms_thumb_reset_server_status(void)
65 g_thumb_server_forked = FALSE;
66 g_shutdowning_thumb_server = FALSE;
69 g_source_destroy(g_main_context_find_source_by_id(g_main_context_get_thread_default(), g_timer_id));
76 static gboolean __ms_thumb_agent_prepare_tcp_socket(int *sock_fd, unsigned short serv_port)
78 if (ms_ipc_create_server_socket(serv_port, sock_fd) < 0) {
79 MS_DBG_ERR("_ms_thumb_create_socket failed");
86 static int __ms_thumb_recv_msg(int sock, thumbMsg *msg)
91 unsigned char *buf = NULL;
92 unsigned int header_size = sizeof(thumbMsg) - sizeof(unsigned char *);
94 MS_MALLOC(buf, header_size);
95 MS_DBG_RETV_IF(!buf, MS_MEDIA_ERR_OUT_OF_MEMORY);
97 while (header_size > 0) {
98 if ((recv_len = recv(sock, buf + recv_pos, header_size, 0)) < 0) {
99 MS_DBG_STRERROR("recv failed");
101 return MS_MEDIA_ERR_IPC;
103 header_size -= recv_len;
104 recv_pos += recv_len;
107 header_size = recv_pos;
110 memcpy(msg, buf, header_size);
113 MS_DBG("status[%d]", msg->status);
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);
121 remain_size = msg->thumb_size;
122 MS_MALLOC(buf, remain_size);
123 MS_DBG_RETV_IF(!buf, MS_MEDIA_ERR_OUT_OF_MEMORY);
125 while (remain_size > 0) {
126 if ((recv_len = recv(sock, buf + recv_pos, remain_size, 0)) < 0) {
127 MS_DBG_STRERROR("recv failed");
129 return MS_MEDIA_ERR_IPC;
133 recv_pos += recv_len;
134 remain_size -= recv_len;
137 MS_SAFE_FREE(msg->thumb_data);
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);
144 return MS_MEDIA_ERR_OUT_OF_MEMORY;
149 return MS_MEDIA_ERR_NONE;
152 static int __ms_thumb_set_buffer(thumbMsg *req_msg, unsigned char **buf, int *buf_size)
154 int header_size = sizeof(thumbMsg) - sizeof(unsigned char *);
155 unsigned int size = 0;
157 MS_DBG_RETV_IF(!req_msg || !buf || req_msg->thumb_size < 0, MS_MEDIA_ERR_INVALID_PARAMETER);
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);
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);
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);
171 return MS_MEDIA_ERR_NONE;
174 static gboolean __ms_thumb_agent_recv_msg_from_server(void)
176 struct sockaddr_un serv_addr;
177 unsigned int serv_addr_len;
178 ms_thumb_server_msg recv_msg;
182 if (g_communicate_sock <= 0)
183 __ms_thumb_agent_prepare_tcp_socket(&g_communicate_sock, MS_THUMB_COMM_PORT);
185 serv_addr_len = sizeof(serv_addr);
187 if ((sockfd = accept(g_communicate_sock, (struct sockaddr*)&serv_addr, &serv_addr_len)) < 0) {
188 MS_DBG_STRERROR("accept failed");
192 if ((len = read(sockfd, &recv_msg, sizeof(ms_thumb_server_msg))) < 0) {
193 MS_DBG_STRERROR("read failed");
198 if (recv_msg.msg_type == MS_MSG_THUMB_SERVER_READY)
199 MS_DBG("Thumbnail server is ready");
205 static gboolean __ms_thumb_agent_execute_server(void)
212 } else if (pid == 0) {
213 execl(THUMB_SERVER_PATH, "media-thumbnail-server", NULL);
215 MS_DBG("Child process is %d", pid);
216 g_thumb_server_forked = TRUE;
221 if (!__ms_thumb_agent_recv_msg_from_server()) {
222 MS_DBG_ERR("_ms_thumb_agent_recv_msg_from_server is failed");
229 static gboolean __ms_thumb_agent_send_msg_to_thumb_server(thumbMsg *recv_msg, thumbMsg *res_msg)
232 struct sockaddr_un serv_addr;
234 unsigned char *buf = NULL;
236 MS_DBG_RETVM_IF(!recv_msg || strlen(recv_msg->org_path) >= MAX_FILEPATH_LEN, FALSE, "Invalid msg");
238 if (ms_ipc_create_client_socket(MS_TIMEOUT_SEC_10, &sock) < 0) {
239 MS_DBG_ERR("ms_ipc_create_client_socket failed");
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));
247 /* Connecting to the thumbnail server */
248 if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
249 MS_DBG_STRERROR("connect failed");
254 __ms_thumb_set_buffer(recv_msg, &buf, &buf_size);
256 if (send(sock, buf, buf_size, 0) < 0) {
257 MS_DBG_STRERROR("send failed");
264 MS_DBG_SLOG("Sending msg to thumbnail server is successful");
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);
272 if (__ms_thumb_recv_msg(sock, res_msg) < 0) {
273 MS_DBG_ERR("_ms_thumb_recv_msg failed");
278 if (res_msg->status == MS_MEDIA_ERR_NONE)
279 MS_DBG_SLOG("recv %s from thumb daemon is successful", res_msg->dst_path);
281 MS_DBG_SLOG("recv %s from thumb daemon is failed (%d)", res_msg->dst_path, res_msg->status);
288 static gboolean __ms_thumb_agent_timer(gpointer data)
291 MS_DBG("Timer is called.. Now killing media-thumbnail-server[%d]", g_server_pid);
293 if (g_server_pid > 0) {
294 /* Kill thumbnail server */
296 memset((void *)&msg, 0, sizeof(msg));
298 msg.msg_type = THUMB_REQUEST_KILL_SERVER;
299 msg.org_path[0] = '\0';
300 msg.dst_path[0] = '\0';
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;
310 MS_DBG_ERR("g_server_pid is %d. Maybe there's problem in thumbnail-server", g_server_pid);
316 static void __ms_thumb_create_timer(int id)
319 g_source_destroy(g_main_context_find_source_by_id(g_main_context_get_thread_default(), id));
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());
326 static gboolean __ms_thumb_request_to_server(gpointer data)
328 thumbRequest *req = NULL;
329 guint req_len = g_queue_get_length(g_request_queue);
331 MS_DBG("Queue length : %d", req_len);
333 g_queue_work = FALSE;
337 if (g_shutdowning_thumb_server) {
338 MS_DBG_WARN("Thumb server is shutting down... wait for complete");
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;
351 __ms_thumb_create_timer(g_timer_id);
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");
356 int client_sock = -1;
357 thumbMsg *recv_msg = NULL;
359 memset((void *)&res_msg, 0, sizeof(res_msg));
361 client_sock = req->client_sock;
362 recv_msg = req->recv_msg;
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);
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");
376 memset((void *)&res_msg, 0, sizeof(res_msg));
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;
386 unsigned char *buf = NULL;
387 __ms_thumb_set_buffer(&res_msg, &buf, &buf_size);
389 if (send(client_sock, buf, buf_size, 0) < 0)
390 MS_DBG_STRERROR("send failed");
392 MS_DBG("Sent Refuse msg from %s", recv_msg->org_path);
397 MS_SAFE_FREE(req->recv_msg);
403 MS_DBG_ERR("recv_msg is NULL from queue request");
406 SAFE_STRLCPY(res_msg.org_path, recv_msg->org_path, sizeof(res_msg.org_path));
411 unsigned char *buf = NULL;
412 __ms_thumb_set_buffer(&res_msg, &buf, &buf_size);
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");
420 send_pos += send_len;
421 buf_size -= send_len;
426 MS_SAFE_FREE(req->recv_msg);
428 MS_SAFE_FREE(res_msg.thumb_data);
433 static gboolean __ms_thumb_agent_read_socket(GIOChannel *src, GIOCondition condition, gpointer data)
435 struct sockaddr_un client_addr;
436 unsigned int client_addr_len;
437 thumbMsg *recv_msg = NULL;
439 int client_sock = -1;
440 unsigned char *buf = NULL;
441 thumbRequest *thumb_req = NULL;
443 sock = g_io_channel_unix_get_fd(src);
444 MS_DBG_RETVM_IF(sock < 0, G_SOURCE_CONTINUE, "sock fd is invalid!");
446 client_addr_len = sizeof(client_addr);
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;
453 MS_DBG("Client[%d] is accepted", client_sock);
455 recv_msg = calloc(1, sizeof(thumbMsg));
457 MS_DBG_ERR("Failed to allocate memory");
459 return G_SOURCE_CONTINUE;
462 if (__ms_thumb_recv_msg(client_sock, recv_msg) < 0) {
463 MS_DBG_ERR("_ms_thumb_recv_msg failed ");
467 MS_MALLOC(thumb_req, sizeof(thumbRequest));
469 MS_DBG_ERR("Failed to create request element");
473 thumb_req->client_sock = client_sock;
474 thumb_req->recv_msg = recv_msg;
476 if (!g_request_queue) {
477 MS_DBG_WARN("Creating queue");
478 g_request_queue = g_queue_new();
481 if (g_queue_get_length(g_request_queue) >= MAX_THUMB_REQUEST) {
482 MS_DBG_WARN("Request Queue is full");
484 memset((void *)&res_msg, 0, sizeof(res_msg));
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;
493 __ms_thumb_set_buffer(&res_msg, &buf, &buf_size);
495 if (send(client_sock, buf, buf_size, 0) < 0)
496 MS_DBG_STRERROR("send failed");
498 MS_DBG("Sent Refuse msg from %s", recv_msg->org_path);
504 MS_DBG_SLOG("%s is queued", recv_msg->org_path);
505 g_queue_push_tail(g_request_queue, (gpointer)thumb_req);
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());
515 return G_SOURCE_CONTINUE;
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;
525 gpointer ms_thumb_agent_start_thread(gpointer data)
528 GSource *source = NULL;
529 GIOChannel *channel = NULL;
530 GMainContext *context = NULL;
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");
538 context = g_main_context_new();
540 MS_DBG_ERR("g_main_context_new failed");
544 g_thumb_agent_loop = g_main_loop_new(context, FALSE);
545 g_main_context_push_thread_default(context);
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);
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);
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");
559 /*close an IO channel*/
560 g_io_channel_shutdown(channel, FALSE, NULL);
561 g_io_channel_unref(channel);
562 close(g_communicate_sock);
564 g_main_loop_unref(g_thumb_agent_loop);