SAM: remove Global Variable violations
[platform/framework/web/download-provider.git] / provider / download-provider-client-manager.c
1 /*
2  * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (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://www.apache.org/licenses/LICENSE-2.0
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 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <time.h>
21 #include <sys/time.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <fcntl.h>
27 #include <signal.h>
28
29 #include <aul.h>
30 #include <systemd/sd-daemon.h>
31 #include <glib-object.h>
32
33 #include <download-provider.h>
34 #include <download-provider-log.h>
35 #include <download-provider-config.h>
36 #include <download-provider-pthread.h>
37 #include <download-provider-security.h>
38 #include <download-provider-client.h>
39 #include <download-provider-notification.h>
40 #include <download-provider-notification-manager.h>
41 #include <download-provider-utils.h>
42 #include <download-provider-ipc.h>
43 #include <download-provider-notify.h>
44 #include <download-provider-db-defs.h>
45 #include <download-provider-db.h>
46 #include <download-provider-queue-manager.h>
47 #include <download-provider-client-manager.h>
48 #include <download-provider-plugin-download-agent.h>
49 #include <download-provider-network.h>
50
51 static int g_dp_sock = -1;
52 static dp_client_slots_fmt *g_dp_client_slots = NULL;
53 static void *g_db_handle = 0;
54 static pthread_mutex_t g_db_mutex = PTHREAD_MUTEX_INITIALIZER;
55 extern pthread_t g_client_manager_tid;
56
57 void dp_terminate(int signo)
58 {
59         TRACE_DEBUG("Received SIGTERM:%d", signo);
60         close(g_dp_sock);
61         g_dp_sock = -1;
62         if (g_client_manager_tid > 0)
63                 pthread_kill(g_client_manager_tid, SIGUSR1);
64 }
65
66 void dp_broadcast_signal()
67 {
68         TRACE_INFO("broadcast");
69         // announce to all thread for clients
70         // signo 10 : ip changed
71         if (g_dp_client_slots != NULL) {
72                 for (int i = 0; i < DP_MAX_CLIENTS; i++) {
73                         if (g_dp_client_slots[i].thread > 0 &&
74                                         pthread_kill(g_dp_client_slots[i].thread, 0) != ESRCH)
75                                 pthread_kill(g_dp_client_slots[i].thread, SIGUSR1);
76                 }
77         }
78
79 }
80
81 static int __dp_db_open_client_manager()
82 {
83         int errorcode = DP_ERROR_NONE;
84         CLIENT_MUTEX_LOCK(&g_db_mutex);
85         if (g_db_handle == 0 || dp_db_check_connection(g_db_handle) < 0) {
86                 if (dp_db_open_client_manager(&g_db_handle, &errorcode) < 0)
87                         TRACE_ERROR("failed to open database errorcode:%d", errorcode);
88         }
89         CLIENT_MUTEX_UNLOCK(&g_db_mutex);
90         return errorcode;
91 }
92
93 static void __dp_db_free_client_manager()
94 {
95         CLIENT_MUTEX_LOCK(&g_db_mutex);
96         if (g_db_handle != 0) {
97                 TRACE_SECURE_DEBUG("TRY to close [%s]", DP_DBFILE_CLIENTS);
98                 dp_db_close(g_db_handle);
99                 g_db_handle = 0;
100         }
101         CLIENT_MUTEX_UNLOCK(&g_db_mutex);
102 }
103
104 static int __dp_accept_socket_new()
105 {
106         int fd_base, listen_fds = sd_listen_fds(1);
107         TRACE_DEBUG("sd_listen_fds:%d", listen_fds);
108
109         if (listen_fds > FD_SETSIZE - 3) {
110                 TRACE_DEBUG("sd_listen_fds:%d", listen_fds);
111                 return -1;
112         }
113
114         for (fd_base = 0 ; fd_base < listen_fds; fd_base++) {
115                 if (sd_is_socket_unix(fd_base + SD_LISTEN_FDS_START, SOCK_STREAM, 1, IPC_SOCKET, 0) >= 0) {
116                         TRACE_INFO("listen systemd socket:%d", fd_base + SD_LISTEN_FDS_START);
117                         return fd_base + SD_LISTEN_FDS_START;
118                 }
119         }
120         return -1;
121 }
122
123 int dp_client_slot_free(dp_client_slots_fmt *slot)
124 {
125         if (slot->client.channel >= 0) {
126                 close(slot->client.channel);
127                 slot->client.channel = -1;
128         }
129         if (slot->client.dbhandle != 0) {
130                 dp_db_close(slot->client.dbhandle);
131                 slot->client.dbhandle = 0;
132         }
133         // free all requests
134         // remove notify fifo
135         if (slot->client.notify >= 0) {
136                 close(slot->client.notify);
137                 slot->client.notify = -1;
138         }
139         dp_notify_deinit(slot->credential.pid);
140         // kill thread
141         if (slot->thread != 0)
142                 pthread_cancel(slot->thread);
143         slot->thread = 0;
144         if (slot->pkgname != NULL) {
145                 TRACE_SECURE_DEBUG("TRY to close [%s]", slot->pkgname);
146                 free(slot->pkgname);
147                 slot->pkgname = NULL;
148         }
149         return 0;
150 }
151
152 // precondition : all slots are empty
153 static int __dp_manage_client_requests(dp_client_slots_fmt *clients)
154 {
155         int errorcode = DP_ERROR_NONE;
156         int i = 0;
157         int slot_index = 0;
158
159         dp_notification_manager_kill();
160         dp_queue_manager_kill();
161
162         // get all clients info from clients database.
163
164         int *ids = (int *)calloc(DP_MAX_CLIENTS, sizeof(int));
165         if (ids == NULL) {
166                 TRACE_ERROR("failed to allocate the clients");
167                 return -1;
168         }
169         // getting ids of clients
170         int rows_count = dp_db_get_ids(g_db_handle, DP_TABLE_CLIENTS, NULL, ids, NULL, DP_MAX_CLIENTS, DP_DB_COL_ACCESS_TIME, "ASC", &errorcode);
171         for (; i < rows_count; i++) {
172                 char *pkgname = NULL;
173                 unsigned length = 0;
174                 errorcode = DP_ERROR_NONE;
175                 if (dp_db_get_property_string(g_db_handle, ids[i], DP_TABLE_CLIENTS, DP_DB_COL_PACKAGE, (unsigned char **)&pkgname, &length, &errorcode) < 0) {
176                         TRACE_ERROR("failed to get pkgname for id:%d", ids[i]);
177                         continue;
178                 }
179
180                 if (pkgname != NULL) {
181                         if (dp_db_remove_database(pkgname, time(NULL), DP_CARE_CLIENT_INFO_PERIOD * 3600) == 0) { // old database
182                                 // remove info from client database;
183                                 if (dp_db_delete(g_db_handle, ids[i], DP_TABLE_CLIENTS, &errorcode) == 0) {
184                                         TRACE_SECURE_ERROR("clear info for %s", pkgname);
185                                         // remove database file
186                                 }
187                                 TRACE_SECURE_INFO("remove database for %s", pkgname);
188                                 free(pkgname);
189                                 continue;
190                         }
191
192                         dp_credential credential;
193                         credential.pid = 0;
194                         if (dp_db_get_property_int(g_db_handle, ids[i], DP_TABLE_CLIENTS, DP_DB_COL_UID, &credential.uid, &errorcode) < 0 ||
195                                         dp_db_get_property_int(g_db_handle, ids[i], DP_TABLE_CLIENTS, DP_DB_COL_GID, &credential.gid, &errorcode) < 0) {
196                                 TRACE_SECURE_ERROR("failed to get credential for %s", pkgname);
197                                 free(pkgname);
198                                 continue;
199                         }
200                         if (dp_mutex_init(&clients[slot_index].mutex, NULL) != 0) {
201                                 TRACE_SECURE_ERROR("failed to initialize mutex for %s", pkgname);
202                                 free(pkgname);
203                                 continue;
204                         }
205                         // open database of a clients
206                         if (dp_db_open_client_v2(&clients[slot_index].client.dbhandle, pkgname) < 0) {
207                                 TRACE_SECURE_ERROR("failed to open database for %s", pkgname);
208                                 // remove this client from clients database
209                                 if (dp_db_delete(g_db_handle, ids[i], DP_TABLE_CLIENTS, &errorcode) == 0) {
210                                         TRACE_SECURE_ERROR("clear info for %s", pkgname);
211                                         // remove database file
212                                         if (dp_db_remove_database(pkgname, time(NULL), 0) == 0)
213                                                 TRACE_SECURE_INFO("remove database for %s", pkgname);
214                                         else
215                                                 TRACE_SECURE_ERROR("failed to remove database for %s", pkgname);
216                                 }
217                                 free(pkgname);
218                                 continue;
219                         }
220
221                         // get ids if state is QUEUED, CONNECTING or DOWNLOADING with auto_download
222                         int *request_ids = (int *)calloc(DP_MAX_REQUEST, sizeof(int));
223                         if (request_ids == NULL) {
224                                 TRACE_SECURE_ERROR("failed to allocate the requests for %s", pkgname);
225                                 free(pkgname);
226                                 continue;
227                         }
228                         int request_count = dp_db_get_crashed_ids(clients[slot_index].client.dbhandle, DP_TABLE_LOGGING, request_ids, DP_MAX_REQUEST, &errorcode);
229                         TRACE_DEBUG("client: %s requests:%d", pkgname, request_count);
230                         int ids_i = 0;
231                         if (request_count > 0) {
232                                 clients[slot_index].pkgname = pkgname;
233                                 clients[slot_index].client.channel = -1;
234                                 clients[slot_index].client.notify = -1;
235                                 clients[slot_index].credential.pid = credential.pid;
236                                 clients[slot_index].credential.uid = credential.uid;
237                                 clients[slot_index].credential.gid = credential.gid;
238                                 for (ids_i = 0; ids_i < request_count; ids_i++) {
239                                         // loading requests from client's database... attach to client.requests
240                                         dp_request_fmt *request = (dp_request_fmt *) calloc(1, sizeof(dp_request_fmt));
241                                         if (request == NULL) {
242                                                 TRACE_ERROR("check memory download-id:%d", request_ids[ids_i]);
243                                                 break;
244                                         }
245                                         request->id = request_ids[ids_i];
246                                         request->agent_id = -1;
247                                         request->state = DP_STATE_QUEUED;
248                                         request->error = DP_ERROR_NONE;
249                                         if (dp_db_get_property_int(clients[slot_index].client.dbhandle, request->id, DP_TABLE_REQUEST, DP_DB_COL_NETWORK_TYPE, &request->network_type, &errorcode) < 0) {
250                                                 TRACE_ERROR("failed to get network type for id:%d", request->id);
251                                                 request->network_type = DP_NETWORK_WIFI;
252                                         }
253                                         request->access_time = (int)time(NULL);
254                                         request->state_cb = 0;
255                                         request->progress_cb = 0;
256                                         if (dp_db_get_property_int(clients[slot_index].client.dbhandle, request->id, DP_TABLE_LOGGING, DP_DB_COL_STARTCOUNT, &request->startcount, &errorcode) < 0) {
257                                                 TRACE_ERROR("failed to get start count for id:%d", request->id);
258                                                 request->startcount = 0;
259                                         }
260                                         request->startcount++;
261                                         request->noti_type = DP_NOTIFICATION_TYPE_NONE;
262                                         if (dp_db_get_property_int(clients[slot_index].client.dbhandle, request->id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE, &request->noti_type, &errorcode) < 0)
263                                                 TRACE_ERROR("failed to get notification type for id:%d", request->id);
264                                         if (request->noti_type == DP_NOTIFICATION_TYPE_NONE) {
265                                                 TRACE_INFO("enable notification for id:%d", request->id);
266                                                 request->noti_type = DP_NOTIFICATION_TYPE_COMPLETE_ONLY;
267                                         }
268                                         request->progress_lasttime = 0;
269                                         request->received_size = 0; // ?
270                                         request->content_type = DP_CONTENT_UNKNOWN;
271                                         request->file_size = 0; // ?
272                                         if (dp_db_get_property_int(clients[slot_index].client.dbhandle, request->id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_PRIV_ID, &request->noti_priv_id, &errorcode) < 0) {
273                                                 TRACE_ERROR("failed to get notification noti_priv_id for id:%d", request->id);
274                                                 request->noti_priv_id = -1;
275                                         }
276
277                                         dp_request_create(&clients[slot_index].client, request);
278
279                                         if (dp_db_update_logging(clients[slot_index].client.dbhandle, request->id, DP_STATE_QUEUED, DP_ERROR_NONE, &errorcode) < 0) {
280                                                 TRACE_ERROR("update log download-id:%d", request->id);
281                                                 errorcode = DP_ERROR_DISK_BUSY;
282                                                 break;
283                                         }
284                                         if (dp_queue_manager_push_queue((void *)&clients[slot_index], (void *)request) < 0) {
285                                                 errorcode = DP_ERROR_QUEUE_FULL;
286                                                 TRACE_INFO("failed to push to queue for id:%d", request->id);
287                                                 dp_request_destroy(&(clients[slot_index].client), NULL, request);
288                                                 break;
289                                         }
290                                         // notification
291                                         if (request->noti_type != DP_NOTIFICATION_TYPE_COMPLETE_ONLY
292                                                         && dp_notification_manager_push_notification((void *)&clients[slot_index], (void *)request, DP_NOTIFICATION_ONGOING) < 0)
293                                                 TRACE_ERROR("failed to register notification for id:%d", request->id);
294                                 }
295                                 slot_index++;
296
297                         } else {
298                                 free(pkgname);
299                         }
300                         free(request_ids);
301                 }
302         }
303         free(ids);
304         TRACE_DEBUG("slot_index:%d", slot_index);
305         if (slot_index > 0)
306                 dp_queue_manager_wake_up();
307         return slot_index;
308 }
309
310 static int __dp_client_run(int clientfd, dp_client_slots_fmt *slot,
311                 dp_credential credential)
312 {
313         int errorcode = DP_ERROR_NONE;
314         // make notify fifo
315         slot->client.notify = dp_notify_init(credential.pid);
316         if (slot->client.notify < 0) {
317                 TRACE_ERROR("failed to open fifo slot:%d", clientfd);
318                 errorcode = DP_ERROR_IO_ERROR;
319         } else {
320                 // save client info to database
321                 CLIENT_MUTEX_LOCK(&g_db_mutex);
322                 if (dp_db_update_client_info(g_db_handle, slot->pkgname,
323                         credential.uid, credential.gid, &errorcode) < 0) {
324                         TRACE_ERROR("check error:%s", dp_print_errorcode(errorcode));
325                 }
326                 CLIENT_MUTEX_UNLOCK(&g_db_mutex);
327         }
328         if (errorcode == DP_ERROR_NONE) {
329
330                 // create a thread for client
331                 if (pthread_create(&slot->thread, NULL,
332                                         dp_client_request_thread, (void *)slot) != 0) {
333                         TRACE_ERROR("failed to create client thread slot:%d", clientfd);
334                         errorcode = DP_ERROR_OUT_OF_MEMORY;
335                         slot->thread = 0;
336                         dp_client_slot_free(slot); // => make pkgname as NULL
337                 } else {
338                         pthread_detach(slot->thread);
339                         TRACE_SECURE_INFO("accept client[%s] pid:%d sock:%d",
340                                         slot->pkgname, credential.pid, clientfd);
341                         slot->client.channel = clientfd;
342                         slot->credential.pid = credential.pid;
343                         slot->credential.uid = credential.uid;
344                         slot->credential.gid = credential.gid;
345                 }
346         }
347         return errorcode;
348 }
349
350 static bool __dp_client_run_with_same_client(dp_client_slots_fmt *clients, int clientfd,
351                 const char *pkgname, dp_credential credential, int *errorcode)
352 {
353         for (int i = 0; i < DP_MAX_CLIENTS; i++) {
354                 int locked = CLIENT_MUTEX_TRYLOCK(&clients[i].mutex);
355                 if (locked != 0) // empty or used by other thread. it would be same client, but it's busy
356                         continue;
357
358                 TRACE_DEBUG("locked slot:%d", i);
359                 if (locked == 0 && clients[i].thread == 0) { // this slot has run without the client
360                         if (clients[i].pkgname != NULL) {
361                                 // check package name.
362                                 TRACE_DEBUG("check client[%s] slot:%d", clients[i].pkgname, i);
363                                 if (strcmp(clients[i].pkgname, pkgname) == 0) {
364                                         TRACE_SECURE_INFO("update client[%s] slot:%d pid:%d sock:%d",
365                                                         pkgname, i, credential.pid, clientfd);
366                                         if (clients[i].client.channel >= 0 &&
367                                                         clients[i].client.channel != clientfd) {
368                                                 dp_ipc_socket_free(clients[i].client.channel);
369                                                 if (clients[i].client.notify >= 0) {
370                                                         close(clients[i].client.notify);
371                                                         clients[i].client.notify = -1;
372                                                 }
373                                                 dp_notify_deinit(clients[i].credential.pid);
374                                         }
375                                         *errorcode = __dp_client_run(clientfd, &clients[i], credential);
376                                         CLIENT_MUTEX_UNLOCK(&clients[i].mutex);
377                                         if (*errorcode != DP_ERROR_NONE)
378                                                 dp_mutex_destroy(&clients[i].mutex);
379                                         return true;
380                                 }
381                         }
382                         if (clients[i].client.requests == NULL) { // clear
383                                 dp_client_slot_free(&clients[i]);
384                                 dp_mutex_destroy(&clients[i].mutex);
385                                 continue;
386                         }
387                 }
388                 CLIENT_MUTEX_UNLOCK(&clients[i].mutex);
389         }
390         return false;
391 }
392
393 static int __dp_client_find_empty_slot(dp_client_slots_fmt *clients)
394 {
395         // search empty slot
396         for (int i = 0; i < DP_MAX_CLIENTS; i++) {
397                 int locked = CLIENT_MUTEX_TRYLOCK(&clients[i].mutex);
398                 if (locked == EINVAL) {
399                         if (dp_mutex_init(&clients[i].mutex, NULL) == 0)
400                                 return i;
401                 }
402                 if (locked == 0)
403                         CLIENT_MUTEX_UNLOCK(&clients[i].mutex);
404         }
405         return -1;
406 }
407
408 static int __dp_client_run_in_empty_slot(dp_client_slots_fmt *client,
409                 int clientfd, dp_credential credential, char *pkgname)
410 {
411         int errorcode = DP_ERROR_NONE;
412         CLIENT_MUTEX_LOCK(&client->mutex);
413         client->pkgname = pkgname;
414         client->client.dbhandle = 0;
415         client->client.requests = NULL;
416         errorcode = __dp_client_run(clientfd, client, credential);
417         CLIENT_MUTEX_UNLOCK(&client->mutex);
418         if (errorcode != DP_ERROR_NONE)
419                 dp_mutex_destroy(&client->mutex);
420         return errorcode;
421 }
422
423 static int __dp_client_new(int clientfd, dp_client_slots_fmt *clients,
424                 dp_credential credential)
425 {
426         // search in clients list.
427         // if same pkgname. update it.
428         // search same pkg or pid in clients
429         int errorcode = DP_ERROR_NONE;
430         char *pkgname = NULL;
431         char buffer[256] = { 0, };
432
433         // getting the package name via pid
434         if (aul_app_get_appid_bypid_for_uid(credential.pid, buffer, sizeof(buffer), credential.uid) != AUL_R_OK)
435                 TRACE_ERROR("[CRITICAL] aul_app_get_appid_bypid_for_uid");
436
437 #ifdef TIZEN_FEATURE_UNITTEST
438         pkgname = strdup("download-provider");
439 #else
440         pkgname = strdup(buffer);
441 #endif
442         if (!pkgname || strlen(pkgname) <= 0) {
443                 TRACE_ERROR("[CRITICAL] pkgname:%s", pkgname);
444                 free(pkgname);
445                 return DP_ERROR_INVALID_PARAMETER;
446         }
447
448         errorcode = dp_check_permission(clientfd, pkgname);
449         if (errorcode != DP_ERROR_NONE) {
450                 TRACE_ERROR("permission denied");
451                 free(pkgname);
452                 return errorcode;
453         }
454
455         // EINVAL: empty slot
456         // EBUSY : occupied slot
457         // locked & thread == 0 : downloading without client <= check target
458         // thread == 0, requests == NULL : clear target
459
460         // Have this client ever been connected before ?
461         if (__dp_client_run_with_same_client(clients, clientfd, pkgname, credential, &errorcode)) {
462                 free(pkgname);
463                 return errorcode;
464         }
465
466         TRACE_DEBUG("search empty client[%s]", pkgname);
467         int empty_slot = __dp_client_find_empty_slot(clients);
468         if (empty_slot >= 0) {
469                 TRACE_DEBUG("found empty client[%s] slot:%d", pkgname, empty_slot);
470                 return __dp_client_run_in_empty_slot(&clients[empty_slot], clientfd, credential, pkgname);
471         }
472
473         TRACE_SECURE_INFO("busy client[%s] pid:%d sock:%d", pkgname,
474                         credential.pid, clientfd);
475         free(pkgname);
476         return DP_ERROR_TOO_MANY_DOWNLOADS;
477 }
478
479 static void __dp_rebuild_dir()
480 {
481 #ifdef PROVIDER_DIR
482         dp_rebuild_dir(PROVIDER_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
483 #endif
484 #ifdef DATABASE_DIR
485         dp_rebuild_dir(DATABASE_DIR, S_IRWXU);
486 #endif
487 #ifdef DATABASE_CLIENT_DIR
488         dp_rebuild_dir(DATABASE_CLIENT_DIR, S_IRWXU);
489 #endif
490 #ifdef NOTIFY_DIR
491         dp_rebuild_dir(NOTIFY_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
492 #endif
493 }
494
495 static bool __dp_client_add_signal_handler()
496 {
497         if (signal(SIGTERM, dp_terminate) == SIG_ERR ||
498                         signal(SIGPIPE, SIG_IGN) == SIG_ERR ||
499                         signal(SIGINT, dp_terminate) == SIG_ERR) {
500                 TRACE_ERROR("failed to register signal callback");
501                 return false;
502         }
503         return true;
504 }
505
506 static bool __dp_client_create_slots()
507 {
508         dp_client_slots_fmt *clients =
509                 (dp_client_slots_fmt *)calloc(DP_MAX_CLIENTS,
510                                 sizeof(dp_client_slots_fmt));
511         if (clients == NULL) {
512                 TRACE_ERROR("failed to allocate client slots");
513                 return false;
514         }
515         g_dp_client_slots = clients;
516         for (int i = 0; i < DP_MAX_CLIENTS; i++)
517                 dp_mutex_destroy(&clients[i].mutex); // clear mutex init
518         return true;
519 }
520
521 static int __dp_client_get_credential(int clientfd, dp_credential *credential)
522 {
523 #ifdef SO_PEERCRED // getting the info of client
524         socklen_t cr_len = sizeof(*credential);
525         if (getsockopt(clientfd, SOL_SOCKET, SO_PEERCRED,
526                                 credential, &cr_len) < 0) {
527                 TRACE_ERROR("failed to cred from sock:%d", clientfd);
528                 return DP_ERROR_PERMISSION_DENIED;
529         }
530 #else // In case of not supported SO_PEERCRED
531         if (read(clientfd, credential, sizeof(dp_credential)) <= 0) {
532                 TRACE_ERROR("failed to cred from client:%d", clientfd);
533                 return DP_ERROR_PERMISSION_DENIED;
534         }
535 #endif
536         return DP_ERROR_NONE;
537 }
538
539 static bool __is_ipc_info_for_init(dp_ipc_fmt ipc_info) {
540         return ipc_info.section == DP_SEC_INIT &&
541                 ipc_info.property == DP_PROP_NONE &&
542                 ipc_info.id == -1 &&
543                 ipc_info.size == 0;
544 }
545
546 static int __dp_client_handle_init_request(int clientfd, dp_ipc_fmt *ipc_info)
547 {
548         int errorcode = DP_ERROR_NONE;
549         memset(ipc_info, 0x00, sizeof(dp_ipc_fmt));
550         if (read(clientfd, ipc_info, sizeof(dp_ipc_fmt)) <= 0) {
551                 TRACE_ERROR("peer terminate ? ignore this connection");
552                 return DP_ERROR_INVALID_PARAMETER;
553         }
554
555         if (__is_ipc_info_for_init(*ipc_info) == false) {
556                 TRACE_ERROR("peer terminate ? ignore this connection");
557                 return DP_ERROR_INVALID_PARAMETER;
558         }
559
560         dp_credential credential = {-1, -1, -1};
561         errorcode = __dp_client_get_credential(clientfd, &credential);
562         if (errorcode != DP_ERROR_NONE)
563                 return errorcode;
564
565         errorcode = __dp_db_open_client_manager();
566         if (errorcode == DP_ERROR_NONE)
567                 errorcode = __dp_client_new(clientfd, g_dp_client_slots, credential);
568
569         return errorcode;
570 }
571
572 static void __dp_client_handle_event()
573 {
574         int errorcode = DP_ERROR_NONE;
575         struct sockaddr_un clientaddr;
576
577         // Anyway accept client.
578         socklen_t clientlen = sizeof(clientaddr);
579         int clientfd = accept(g_dp_sock, (struct sockaddr *)&clientaddr, &clientlen);
580         if (clientfd < 0) {
581                 TRACE_ERROR("too many client ? accept failure");
582                 // provider need the time of refresh.
583                 errorcode = DP_ERROR_DISK_BUSY;
584                 return;
585         }
586
587         // blocking & timeout to prevent the lockup by client.
588         struct timeval tv_timeo = {5, 500000}; // 5.5 sec
589         if (setsockopt(clientfd, SOL_SOCKET, SO_RCVTIMEO, &tv_timeo,
590                                 sizeof(tv_timeo)) < 0) {
591                 TRACE_ERROR("failed to set timeout in blocking socket");
592                 errorcode = DP_ERROR_IO_ERROR;
593         }
594
595         dp_ipc_fmt ipc_info;
596         errorcode = __dp_client_handle_init_request(clientfd, &ipc_info);
597
598         if (dp_ipc_query(clientfd, -1, ipc_info.section, DP_PROP_NONE, errorcode, 0) < 0)
599                 TRACE_ERROR("check ipc sock:%d", clientfd);
600
601         if (errorcode != DP_ERROR_NONE) {
602                 TRACE_ERROR("sock:%d id:%d section:%s property:%s errorcode:%s size:%zd",
603                                 clientfd, ipc_info.id,
604                                 dp_print_section(ipc_info.section),
605                                 dp_print_property(ipc_info.property),
606                                 dp_print_errorcode(ipc_info.errorcode),
607                                 ipc_info.size);
608                 close(clientfd); // ban this client
609         }
610         if (errorcode == DP_ERROR_NO_SPACE || errorcode == DP_ERROR_DISK_BUSY) {
611                 TRACE_ERROR("provider can't work anymore errorcode:%s", dp_print_errorcode(errorcode));
612                 //break;  // provider will be terminated after sending errorcode by each thread
613         }
614 }
615
616 // take care zombie client, slots
617 static bool __dp_client_handle_zombie_client()
618 {
619         unsigned connected_clients = 0;
620         for (int i = 0; i < DP_MAX_CLIENTS; i++) {
621
622                 int locked = CLIENT_MUTEX_TRYLOCK(&g_dp_client_slots[i].mutex);
623                 if (locked == EINVAL) { // not initialized
624                         continue;
625                 } else if (locked == EBUSY) { // already locked
626                         connected_clients++;
627                         continue;
628                 }
629
630                 if (locked == 0) { // locked
631                         // if no client thread, requests should be checked here
632                         // if no queued, connecting or downloading, close the slot
633                         if (g_dp_client_slots[i].pkgname != NULL) {
634                                 if (g_dp_client_slots[i].thread == 0) {
635                                         dp_client_clear_requests(&g_dp_client_slots[i]);
636                                         if (g_dp_client_slots[i].client.requests == NULL) {
637                                                 dp_client_slot_free(&g_dp_client_slots[i]);
638                                                 CLIENT_MUTEX_UNLOCK(&g_dp_client_slots[i].mutex);
639                                                 dp_mutex_destroy(&g_dp_client_slots[i].mutex);
640                                                 continue;
641                                         }
642                                 }
643                                 connected_clients++;
644                         }
645                         CLIENT_MUTEX_UNLOCK(&g_dp_client_slots[i].mutex);
646                 }
647         }
648         TRACE_DEBUG("%d clients are active now", connected_clients);
649         // terminating download-provider if no clients.
650         if (connected_clients == 0) {
651                 if (__dp_manage_client_requests(g_dp_client_slots) <= 0) // if no crashed job
652                         return false;
653         } else {
654                 dp_queue_manager_wake_up();
655                 dp_notification_manager_wake_up();
656         }
657         return true;
658 }
659
660 void *dp_client_manager(void *arg)
661 {
662         fd_set rset, eset, listen_fdset, except_fdset;
663         struct timeval timeout; // for timeout of select
664         GMainLoop *event_loop = (GMainLoop *)arg;
665
666         if (__dp_client_add_signal_handler() == false)
667                 goto ERR;
668
669         dp_notification_clear_ongoings();
670         __dp_rebuild_dir();
671
672         if (__dp_client_create_slots() == false)
673                 goto ERR;
674
675         g_dp_sock = __dp_accept_socket_new();
676         if (g_dp_sock < 0) {
677                 TRACE_ERROR("failed to open listen socket");
678                 goto ERR;
679         }
680         FD_ZERO(&listen_fdset);
681         FD_ZERO(&except_fdset);
682         FD_SET(g_dp_sock, &listen_fdset);
683         FD_SET(g_dp_sock, &except_fdset);
684
685         int maxfd = g_dp_sock;
686         while (g_dp_sock >= 0) {
687                 // initialize timeout structure for calling timeout exactly
688                 memset(&timeout, 0x00, sizeof(struct timeval));
689                 timeout.tv_sec = DP_CARE_CLIENT_MANAGER_INTERVAL;
690
691                 rset = listen_fdset;
692                 eset = except_fdset;
693                 if (select((maxfd + 1), &rset, 0, &eset, &timeout) < 0) {
694                         TRACE_ERROR("interrupted by terminating");
695                         break;
696                 }
697
698                 if (g_dp_sock < 0) {
699                         TRACE_DEBUG("queue-manager is closed by other thread");
700                         break;
701                 }
702
703                 if (FD_ISSET(g_dp_sock, &eset) > 0) {
704                         TRACE_ERROR("exception of socket");
705                         break;
706                 }
707
708                 if (FD_ISSET(g_dp_sock, &rset) > 0)
709                         __dp_client_handle_event();
710                 else if (__dp_client_handle_zombie_client() == false)
711                         break;
712
713         }
714         if (g_dp_sock >= 0)
715                 close(g_dp_sock);
716         g_dp_sock = -1;
717
718         dp_queue_manager_kill();
719         dp_notification_clear_ongoings();
720         dp_notification_manager_kill();
721
722         __dp_db_free_client_manager();
723
724         // kill other clients
725         TRACE_DEBUG("try to deallocate the resources for all clients");
726         for (int i = 0; i < DP_MAX_CLIENTS; i++) {
727                 int locked = CLIENT_MUTEX_TRYLOCK(&g_dp_client_slots[i].mutex);
728                 if (locked == EBUSY) { // already locked
729                         CLIENT_MUTEX_LOCK(&g_dp_client_slots[i].mutex);
730                 } else if (locked == EINVAL) { // not initialized, empty slot
731                         continue;
732                 }
733                 dp_client_slot_free(&g_dp_client_slots[i]);
734                 CLIENT_MUTEX_UNLOCK(&g_dp_client_slots[i].mutex);
735                 dp_mutex_destroy(&g_dp_client_slots[i].mutex);
736         }
737         free(g_dp_client_slots);
738         // free all resources
739
740         TRACE_INFO("client-manager's working is done");
741
742 ERR:
743         g_main_loop_quit(event_loop);
744         return 0;
745 }