Merge "support thumbnail-util." into tizen
[platform/core/multimedia/libmedia-thumbnail.git] / server / thumb-server-internal.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 "thumb-server-internal.h"
23
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <vconf.h>
29
30 #ifdef LOG_TAG
31 #undef LOG_TAG
32 #endif
33
34 #define LOG_TAG "MEDIA_THUMBNAIL_SERVER"
35
36 static __thread char **arr_path;
37 static __thread uid_t *arr_uid;
38 static __thread int g_idx = 0;
39 static __thread int g_cur_idx = 0;
40
41 GMainLoop *g_thumb_server_mainloop; // defined in thumb-server.c as extern
42
43 gboolean _thumb_server_send_msg_to_agent(int msg_type);
44 void _thumb_daemon_stop_job();
45
46 gboolean _thumb_daemon_start_jobs(gpointer data)
47 {
48         thumb_dbg("");
49
50         _thumb_server_send_msg_to_agent(MS_MSG_THUMB_SERVER_READY);
51
52         return FALSE;
53 }
54
55 void _thumb_daemon_finish_jobs(void)
56 {
57         sqlite3 *sqlite_db_handle = _media_thumb_db_get_handle();
58
59         if (sqlite_db_handle != NULL) {
60                 _media_thumb_db_disconnect();
61                 thumb_dbg("sqlite3 handle is alive. So disconnect to sqlite3");
62         }
63
64         g_main_loop_quit(g_thumb_server_mainloop);
65
66         return;
67 }
68
69 int _thumb_daemon_mmc_status(void)
70 {
71         int err = -1;
72         int status = -1;
73
74         err = vconf_get_int(VCONFKEY_SYSMAN_MMC_STATUS, &status);
75         if (err == 0) {
76                 return status;
77         } else if (err == -1) {
78                 thumb_err("vconf_get_int failed : %d", err);
79         } else {
80                 thumb_err("vconf_get_int Unexpected error code: %d", err);
81         }
82
83         return status;
84 }
85
86 void _thumb_daemon_mmc_eject_vconf_cb(void *data)
87 {
88         int err = -1;
89         int status = 0;
90
91         thumb_warn("_thumb_daemon_vconf_cb called");
92
93         err = vconf_get_int(VCONFKEY_SYSMAN_MMC_STATUS, &status);
94         if (err == 0) {
95                 if (status == VCONFKEY_SYSMAN_MMC_REMOVED || status == VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED) {
96                         thumb_warn("SD card is ejected or not mounted. So media-thumbnail-server stops jobs to extract all thumbnails");
97
98                         _thumb_daemon_stop_job();
99                 }
100         } else if (err == -1) {
101                 thumb_err("vconf_get_int failed : %d", err);
102         } else {
103                 thumb_err("vconf_get_int Unexpected error code: %d", err);
104         }
105
106         return;
107 }
108
109 void _thumb_daemon_vconf_cb(void *data)
110 {
111         int err = -1;
112         int status = 0;
113
114         thumb_warn("_thumb_daemon_vconf_cb called");
115
116         err = vconf_get_int(VCONFKEY_SYSMAN_MMC_FORMAT, &status);
117         if (err == 0) {
118                 if (status == VCONFKEY_SYSMAN_MMC_FORMAT_COMPLETED) {
119                         thumb_warn("SD card format is completed. So media-thumbnail-server stops jobs to extract all thumbnails");
120
121                         _thumb_daemon_stop_job();
122                 } else {
123                         thumb_dbg("not completed");
124                 }
125         } else if (err == -1) {
126                 thumb_err("vconf_get_int failed : %d", err);
127         } else {
128                 thumb_err("vconf_get_int Unexpected error code: %d", err);
129         }
130
131         return;
132 }
133
134 void _thumb_daemon_stop_job()
135 {
136         int i = 0;
137         char *path = NULL;
138
139         thumb_warn("There are %d jobs in the queue. But all jobs will be stopped", g_idx - g_cur_idx);
140
141         for (i = g_cur_idx; i < g_idx; i++) {
142                 path = arr_path[g_cur_idx++];
143                 SAFE_FREE(path);
144         }
145
146         return;
147 }
148
149 int _thumb_daemon_process_job(thumbMsg *req_msg, thumbMsg *res_msg, uid_t uid)
150 {
151         int err = MS_MEDIA_ERR_NONE;
152
153         err = _media_thumb_process(req_msg, res_msg, uid);
154         if (err != MS_MEDIA_ERR_NONE) {
155                 if (req_msg->msg_type == THUMB_REQUEST_SAVE_FILE) {
156                         thumb_err("_media_thumb_process is failed: %d", err);
157                         res_msg->status = THUMB_FAIL;
158                 } else {
159                         thumb_warn("_media_thumb_process is failed: %d, So use default thumb", err);
160                         res_msg->status = THUMB_SUCCESS;
161                 }
162         } else {
163                 res_msg->status = THUMB_SUCCESS;
164         }
165
166         return err;
167 }
168
169 static int __thumb_daemon_process_job_raw(thumbMsg *req_msg, thumbMsg *res_msg, thumbRawAddMsg *res_raw_msg, uid_t uid)
170 {
171         int err = MS_MEDIA_ERR_NONE;
172
173         err = _media_thumb_process_raw(req_msg, res_msg, res_raw_msg, uid);
174         if (err != MS_MEDIA_ERR_NONE) {
175                 if (err != MS_MEDIA_ERR_FILE_NOT_EXIST) {
176                         thumb_warn("_media_thumb_process is failed: %d, So use default thumb", err);
177                         res_msg->status = THUMB_SUCCESS;
178                 } else {
179                         thumb_warn("_media_thumb_process is failed: %d, (file not exist) ", err);
180                         res_msg->status = THUMB_FAIL;
181                 }
182         } else {
183                 res_msg->status = THUMB_SUCCESS;
184         }
185
186         return err;
187 }
188
189 int _thumb_daemon_all_extract(uid_t uid)
190 {
191         int err = MS_MEDIA_ERR_NONE;
192         int count = 0;
193         char query_string[MAX_PATH_SIZE + 1] = { 0, };
194         char path[MAX_PATH_SIZE + 1] = { 0, };
195         sqlite3 *sqlite_db_handle = NULL;
196         sqlite3_stmt *sqlite_stmt = NULL;
197
198         err = _media_thumb_db_connect(uid);
199         if (err != MS_MEDIA_ERR_NONE) {
200                 thumb_err("_media_thumb_db_connect failed: %d", err);
201                 return err;
202         }
203
204         sqlite_db_handle = _media_thumb_db_get_handle();
205         if (sqlite_db_handle == NULL) {
206                 thumb_err("sqlite handle is NULL");
207                 return MS_MEDIA_ERR_INTERNAL;
208         }
209
210         if (_thumb_daemon_mmc_status() == VCONFKEY_SYSMAN_MMC_MOUNTED) {
211                 snprintf(query_string, sizeof(query_string), SELECT_PATH_FROM_UNEXTRACTED_THUMB_MEDIA);
212         } else {
213                 snprintf(query_string, sizeof(query_string), SELECT_PATH_FROM_UNEXTRACTED_THUMB_INTERNAL_MEDIA);
214         }
215
216         thumb_warn("Query: %s", query_string);
217
218         err = sqlite3_prepare_v2(sqlite_db_handle, query_string, strlen(query_string), &sqlite_stmt, NULL);
219         if (SQLITE_OK != err) {
220                 thumb_err("prepare error [%s]", sqlite3_errmsg(sqlite_db_handle));
221                 _media_thumb_db_disconnect();
222                 return MS_MEDIA_ERR_INTERNAL;
223         }
224
225         while(1) {
226                 err = sqlite3_step(sqlite_stmt);
227                 if (err != SQLITE_ROW) {
228                         thumb_dbg("end of row [%s]", sqlite3_errmsg(sqlite_db_handle));
229                         break;
230                 }
231
232                 strncpy(path, (const char *)sqlite3_column_text(sqlite_stmt, 0), sizeof(path));
233                 path[sizeof(path) - 1] = '\0';
234                 count = sqlite3_column_int(sqlite_stmt, 1);
235
236                 thumb_dbg("Path : %s", path);
237
238                 if (g_idx == 0) {
239                         arr_path = (char**)malloc(sizeof(char*));
240                         arr_uid = (uid_t*)malloc(sizeof(uid_t));
241                 } else {
242                         arr_path = (char**)realloc(arr_path, (g_idx + 1) * sizeof(char*));
243                         arr_uid = (uid_t*)realloc(arr_uid, (g_idx + 1) * sizeof(uid_t));
244                 }
245                 arr_uid[g_idx] = uid;
246                 arr_path[g_idx++] = strdup(path);
247         }
248
249         sqlite3_finalize(sqlite_stmt);
250         _media_thumb_db_disconnect();
251
252         return MS_MEDIA_ERR_NONE;
253 }
254
255 int _thumb_daemon_process_queue_jobs(gpointer data)
256 {
257         int err = MS_MEDIA_ERR_NONE;
258         char *path = NULL;
259         uid_t uid = NULL;
260
261         if (g_cur_idx < g_idx) {
262                 thumb_warn("There are %d jobs in the queue", g_idx - g_cur_idx);
263                 thumb_dbg("Current idx : [%d]", g_cur_idx);
264                 uid = arr_uid[g_cur_idx];
265                 path = arr_path[g_cur_idx++];
266
267                 thumbMsg recv_msg, res_msg;
268                 memset(&recv_msg, 0x00, sizeof(thumbMsg));
269                 memset(&res_msg, 0x00, sizeof(thumbMsg));
270
271                 recv_msg.msg_type = THUMB_REQUEST_DB_INSERT;
272                 recv_msg.thumb_type = MEDIA_THUMB_LARGE;
273                 strncpy(recv_msg.org_path, path, sizeof(recv_msg.org_path));
274                 recv_msg.org_path[sizeof(recv_msg.org_path) - 1] = '\0';
275
276                 _thumb_daemon_process_job(&recv_msg, &res_msg,uid );
277
278                 if (res_msg.status == THUMB_SUCCESS) {
279
280                         err = _media_thumb_db_connect(uid);
281                         if (err != MS_MEDIA_ERR_NONE) {
282                                 thumb_err("_media_thumb_mb_svc_connect failed: %d", err);
283                                 return TRUE;
284                         }
285
286                         /* Need to update DB once generating thumb is done */
287                         err = _media_thumb_update_db(recv_msg.org_path,
288                                                                                 res_msg.dst_path,
289                                                                                 res_msg.origin_width,
290                                                                                 res_msg.origin_height,
291                                                                                 uid);
292                         if (err < 0) {
293                                 thumb_err("_media_thumb_update_db failed : %d", err);
294                         }
295
296                         _media_thumb_db_disconnect();
297                 }
298
299                 SAFE_FREE(path);
300         } else {
301                 g_cur_idx = 0;
302                 g_idx = 0;
303                 thumb_warn("Deleting array");
304                 SAFE_FREE(arr_path);
305                 SAFE_FREE(arr_uid);
306                 //_media_thumb_db_disconnect();
307
308                 _thumb_server_send_msg_to_agent(MS_MSG_THUMB_EXTRACT_ALL_DONE); // MS_MSG_THUMB_EXTRACT_ALL_DONE
309
310                 return FALSE;
311         }
312
313         return TRUE;
314 }
315
316 gboolean _thumb_server_read_socket(GIOChannel *src,
317                                                                         GIOCondition condition,
318                                                                         gpointer data)
319 {
320         struct sockaddr_un client_addr;
321         unsigned int client_addr_len;
322         int client_sock;
323         thumbMsg recv_msg;
324         thumbMsg res_msg;
325         thumbRawAddMsg res_raw_msg;
326
327         int sock = -1;
328         int header_size = 0;
329
330         memset((void *)&recv_msg, 0, sizeof(recv_msg));
331         memset((void *)&res_msg, 0, sizeof(res_msg));
332         memset((void *)&res_raw_msg, 0, sizeof(res_raw_msg));
333
334         sock = g_io_channel_unix_get_fd(src);
335         if (sock < 0) {
336                 thumb_err("sock fd is invalid!");
337                 return TRUE;
338         }
339
340         header_size = sizeof(thumbMsg) - MAX_PATH_SIZE * 2 - sizeof(unsigned char *);
341
342         if (_media_thumb_recv_udp_msg(sock, header_size, &recv_msg, &client_addr, &client_addr_len) < 0) {
343                 thumb_err("_media_thumb_recv_udp_msg failed");
344                 return TRUE;
345         }
346
347         thumb_warn("Received [%d] %s(%d) from PID(%d) \n", recv_msg.msg_type, recv_msg.org_path, strlen(recv_msg.org_path), recv_msg.pid);
348
349         if (recv_msg.msg_type == THUMB_REQUEST_ALL_MEDIA) {
350                 thumb_dbg("All thumbnails are being extracted now");
351                 _thumb_daemon_all_extract(recv_msg.uid);
352                 g_idle_add(_thumb_daemon_process_queue_jobs, NULL);
353         } else if(recv_msg.msg_type == THUMB_REQUEST_RAW_DATA) {
354                 __thumb_daemon_process_job_raw(&recv_msg, &res_msg, &res_raw_msg, recv_msg.uid);
355         } else if(recv_msg.msg_type == THUMB_REQUEST_KILL_SERVER) {
356                 thumb_warn("received KILL msg from thumbnail agent.");
357         } else {
358                 long start = thumb_get_debug_time();
359
360                 _thumb_daemon_process_job(&recv_msg, &res_msg,recv_msg.uid);
361
362                 long end = thumb_get_debug_time();
363                 thumb_dbg("Time : %f (%s)", ((double)(end - start) / (double)CLOCKS_PER_SEC), recv_msg.org_path);
364         }
365
366         if(res_msg.msg_type == 0)
367                 res_msg.msg_type = recv_msg.msg_type;
368         res_msg.request_id = recv_msg.request_id;
369         strncpy(res_msg.org_path, recv_msg.org_path, recv_msg.origin_path_size);
370         res_msg.origin_path_size = recv_msg.origin_path_size;
371         res_msg.thumb_data = (unsigned char *)"\0";
372         if(res_msg.msg_type != THUMB_RESPONSE_RAW_DATA) {
373                 res_msg.dest_path_size = strlen(res_msg.dst_path) + 1;
374                 res_msg.thumb_size = 1;
375         } else {
376                 res_msg.dest_path_size = 1;
377                 res_msg.dst_path[0] = '\0';
378         }
379
380         int buf_size = 0;
381         int block_size = 0;
382         int sent_size = 0;
383         unsigned char *buf = NULL;
384         _media_thumb_set_buffer_for_response(&res_msg, &buf, &buf_size);
385
386         if (sendto(sock, buf, buf_size, 0, (struct sockaddr *)&client_addr, sizeof(client_addr)) != buf_size) {
387                 thumb_stderror("sendto failed");
388                 SAFE_FREE(buf);
389                 return TRUE;
390         }
391
392         thumb_warn("Sent %s(%d)", res_msg.dst_path, strlen(res_msg.dst_path));
393
394         SAFE_FREE(buf);
395
396         //Add sendto_raw_data
397         if(recv_msg.msg_type == THUMB_REQUEST_RAW_DATA) {
398                 _media_thumb_set_add_raw_data_buffer(&res_raw_msg, &buf, &buf_size);
399                 block_size = 512;
400                 while(buf_size > 0) {
401                         if (buf_size < 512) {
402                                 block_size = buf_size;
403                         }
404                         if (sendto(sock, buf + sent_size, block_size, 0, (struct sockaddr *)&client_addr, sizeof(client_addr)) != block_size) {
405                                 thumb_err("sendto failed: %s", strerror(errno));
406                                 SAFE_FREE(buf);
407                                 return TRUE;
408                         }
409                         sent_size += block_size;
410                         buf_size -= block_size;
411                 }
412                 thumb_warn_slog("Sent [%s] additional data[%d]", res_msg.org_path, sent_size);
413         }
414
415         SAFE_FREE(buf);
416
417         if(recv_msg.msg_type == THUMB_REQUEST_KILL_SERVER) {
418                 thumb_warn("Shutting down...");
419                 g_main_loop_quit(g_thumb_server_mainloop);
420         }
421
422         return TRUE;
423 }
424
425 gboolean _thumb_server_send_msg_to_agent(int msg_type)
426 {
427         int sock;
428         const char *serv_ip = "127.0.0.1";
429         struct sockaddr_un serv_addr;
430         ms_thumb_server_msg send_msg;
431
432         if (ms_ipc_create_client_socket(MS_PROTOCOL_UDP, MS_TIMEOUT_SEC_10, &sock, MS_THUMB_COMM_PORT) < 0) {
433                 thumb_err("ms_ipc_create_server_socket failed");
434                 return FALSE;
435         }
436
437         memset(&serv_addr, 0, sizeof(serv_addr));
438
439         serv_addr.sun_family = AF_UNIX;
440         strcpy(serv_addr.sun_path, "/var/run/media-server/media_ipc_thumbcomm.socket");
441
442         send_msg.msg_type = msg_type;
443
444
445     if (connect(sock, &serv_addr, sizeof(serv_addr)) < 0) {
446         thumb_err("connect failed [%s]",strerror(errno));
447         close(sock);
448         return FALSE;
449     }
450
451         if (send(sock, &send_msg, sizeof(ms_thumb_server_msg), 0) != sizeof(ms_thumb_server_msg)) {
452                 thumb_err("sendto failed: %s\n", strerror(errno));
453                 close(sock);
454                 return FALSE;
455         }
456
457         thumb_dbg("Sending msg to thumbnail agent[%d] is successful", send_msg.msg_type);
458
459         close(sock);
460         return TRUE;
461 }
462
463 gboolean _thumb_server_prepare_socket(int *sock_fd)
464 {
465         int sock;
466         unsigned short serv_port;
467
468         thumbMsg recv_msg;
469         thumbMsg res_msg;
470
471         memset((void *)&recv_msg, 0, sizeof(recv_msg));
472         memset((void *)&res_msg, 0, sizeof(res_msg));
473         serv_port = MS_THUMB_DAEMON_PORT;
474
475         if (ms_ipc_create_server_socket(MS_PROTOCOL_UDP, serv_port, &sock) < 0) {
476                 thumb_err("ms_ipc_create_server_socket failed");
477                 return FALSE;
478         }
479
480         *sock_fd = sock;
481
482         return TRUE;
483 }
484