2 * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include <sys/socket.h>
25 #include <sys/types.h>
30 #include <systemd/sd-daemon.h>
31 #include <glib-object.h>
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>
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;
57 void dp_terminate(int signo)
59 TRACE_DEBUG("Received SIGTERM:%d", signo);
62 if (g_client_manager_tid > 0)
63 pthread_kill(g_client_manager_tid, SIGUSR1);
66 void dp_broadcast_signal()
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);
81 static int __dp_db_open_client_manager()
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);
89 CLIENT_MUTEX_UNLOCK(&g_db_mutex);
93 static void __dp_db_free_client_manager()
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);
101 CLIENT_MUTEX_UNLOCK(&g_db_mutex);
104 static int __dp_accept_socket_new()
106 int fd_base, listen_fds = sd_listen_fds(1);
107 TRACE_DEBUG("sd_listen_fds:%d", listen_fds);
109 if (listen_fds > FD_SETSIZE - 3) {
110 TRACE_DEBUG("sd_listen_fds:%d", listen_fds);
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;
123 int dp_client_slot_free(dp_client_slots_fmt *slot)
125 if (slot->client.channel >= 0) {
126 close(slot->client.channel);
127 slot->client.channel = -1;
129 if (slot->client.dbhandle != 0) {
130 dp_db_close(slot->client.dbhandle);
131 slot->client.dbhandle = 0;
134 // remove notify fifo
135 if (slot->client.notify >= 0) {
136 close(slot->client.notify);
137 slot->client.notify = -1;
139 dp_notify_deinit(slot->credential.pid);
141 if (slot->thread != 0)
142 pthread_cancel(slot->thread);
144 if (slot->pkgname != NULL) {
145 TRACE_SECURE_DEBUG("TRY to close [%s]", slot->pkgname);
147 slot->pkgname = NULL;
152 // precondition : all slots are empty
153 static int __dp_manage_client_requests(dp_client_slots_fmt *clients)
155 int errorcode = DP_ERROR_NONE;
159 dp_notification_manager_kill();
160 dp_queue_manager_kill();
162 // get all clients info from clients database.
164 int *ids = (int *)calloc(DP_MAX_CLIENTS, sizeof(int));
166 TRACE_ERROR("failed to allocate the clients");
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;
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]);
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
187 TRACE_SECURE_INFO("remove database for %s", pkgname);
192 dp_credential credential;
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);
200 if (dp_mutex_init(&clients[slot_index].mutex, NULL) != 0) {
201 TRACE_SECURE_ERROR("failed to initialize mutex for %s", pkgname);
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);
215 TRACE_SECURE_ERROR("failed to remove database for %s", pkgname);
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);
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);
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]);
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;
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;
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;
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;
277 dp_request_create(&clients[slot_index].client, request);
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;
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);
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);
304 TRACE_DEBUG("slot_index:%d", slot_index);
306 dp_queue_manager_wake_up();
310 static int __dp_client_run(int clientfd, dp_client_slots_fmt *slot,
311 dp_credential credential)
313 int errorcode = DP_ERROR_NONE;
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;
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));
326 CLIENT_MUTEX_UNLOCK(&g_db_mutex);
328 if (errorcode == DP_ERROR_NONE) {
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;
336 dp_client_slot_free(slot); // => make pkgname as NULL
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;
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)
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
358 TRACE_INFO("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_INFO("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;
373 dp_notify_deinit(clients[i].credential.pid);
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);
382 if (clients[i].client.requests == NULL) { // clear
383 dp_client_slot_free(&clients[i]);
384 CLIENT_MUTEX_UNLOCK(&clients[i].mutex);
385 dp_mutex_destroy(&clients[i].mutex);
389 CLIENT_MUTEX_UNLOCK(&clients[i].mutex);
394 static int __dp_client_find_empty_slot(dp_client_slots_fmt *clients)
397 for (int i = 0; i < DP_MAX_CLIENTS; i++) {
398 int locked = CLIENT_MUTEX_TRYLOCK(&clients[i].mutex);
399 if (locked == EINVAL) {
400 if (dp_mutex_init(&clients[i].mutex, NULL) == 0)
404 CLIENT_MUTEX_UNLOCK(&clients[i].mutex);
409 static int __dp_client_run_in_empty_slot(dp_client_slots_fmt *client,
410 int clientfd, dp_credential credential, char *pkgname)
412 int errorcode = DP_ERROR_NONE;
413 CLIENT_MUTEX_LOCK(&client->mutex);
414 client->pkgname = pkgname;
415 client->client.dbhandle = 0;
416 client->client.requests = NULL;
417 errorcode = __dp_client_run(clientfd, client, credential);
418 CLIENT_MUTEX_UNLOCK(&client->mutex);
419 if (errorcode != DP_ERROR_NONE)
420 dp_mutex_destroy(&client->mutex);
424 static int __dp_client_new(int clientfd, dp_client_slots_fmt *clients,
425 dp_credential credential)
427 // search in clients list.
428 // if same pkgname. update it.
429 // search same pkg or pid in clients
430 int errorcode = DP_ERROR_NONE;
431 char *pkgname = NULL;
432 char buffer[256] = { 0, };
434 // getting the package name via pid
435 if (aul_app_get_appid_bypid_for_uid(credential.pid, buffer, sizeof(buffer), credential.uid) != AUL_R_OK)
436 TRACE_ERROR("[CRITICAL] aul_app_get_appid_bypid_for_uid");
438 #ifdef TIZEN_FEATURE_UNITTEST
439 pkgname = strdup("download-provider");
441 pkgname = strdup(buffer);
443 if (!pkgname || strlen(pkgname) <= 0) {
444 TRACE_ERROR("[CRITICAL] pkgname:%s", pkgname);
446 return DP_ERROR_INVALID_PARAMETER;
449 errorcode = dp_check_permission(clientfd, pkgname);
450 if (errorcode != DP_ERROR_NONE) {
451 TRACE_ERROR("permission denied");
456 // EINVAL: empty slot
457 // EBUSY : occupied slot
458 // locked & thread == 0 : downloading without client <= check target
459 // thread == 0, requests == NULL : clear target
461 // Have this client ever been connected before ?
462 if (__dp_client_run_with_same_client(clients, clientfd, pkgname, credential, &errorcode)) {
467 TRACE_DEBUG("search empty client[%s]", pkgname);
468 int empty_slot = __dp_client_find_empty_slot(clients);
469 if (empty_slot >= 0) {
470 TRACE_DEBUG("found empty client[%s] slot:%d", pkgname, empty_slot);
471 return __dp_client_run_in_empty_slot(&clients[empty_slot], clientfd, credential, pkgname);
474 TRACE_SECURE_INFO("busy client[%s] pid:%d sock:%d", pkgname,
475 credential.pid, clientfd);
477 return DP_ERROR_TOO_MANY_DOWNLOADS;
480 static void __dp_rebuild_dir()
483 dp_rebuild_dir(PROVIDER_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
486 dp_rebuild_dir(DATABASE_DIR, S_IRWXU);
488 #ifdef DATABASE_CLIENT_DIR
489 dp_rebuild_dir(DATABASE_CLIENT_DIR, S_IRWXU);
492 dp_rebuild_dir(NOTIFY_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
496 static bool __dp_client_add_signal_handler()
498 if (signal(SIGTERM, dp_terminate) == SIG_ERR ||
499 signal(SIGPIPE, SIG_IGN) == SIG_ERR ||
500 signal(SIGINT, dp_terminate) == SIG_ERR) {
501 TRACE_ERROR("failed to register signal callback");
507 static bool __dp_client_create_slots()
509 dp_client_slots_fmt *clients =
510 (dp_client_slots_fmt *)calloc(DP_MAX_CLIENTS,
511 sizeof(dp_client_slots_fmt));
512 if (clients == NULL) {
513 TRACE_ERROR("failed to allocate client slots");
516 g_dp_client_slots = clients;
517 for (int i = 0; i < DP_MAX_CLIENTS; i++)
518 dp_mutex_destroy(&clients[i].mutex); // clear mutex init
522 static int __dp_client_get_credential(int clientfd, dp_credential *credential)
524 #ifdef SO_PEERCRED // getting the info of client
525 socklen_t cr_len = sizeof(*credential);
526 if (getsockopt(clientfd, SOL_SOCKET, SO_PEERCRED,
527 credential, &cr_len) < 0) {
528 TRACE_ERROR("failed to cred from sock:%d", clientfd);
529 return DP_ERROR_PERMISSION_DENIED;
531 #else // In case of not supported SO_PEERCRED
532 if (read(clientfd, credential, sizeof(dp_credential)) <= 0) {
533 TRACE_ERROR("failed to cred from client:%d", clientfd);
534 return DP_ERROR_PERMISSION_DENIED;
537 return DP_ERROR_NONE;
540 static bool __is_ipc_info_for_init(dp_ipc_fmt ipc_info) {
541 return ipc_info.section == DP_SEC_INIT &&
542 ipc_info.property == DP_PROP_NONE &&
547 static int __dp_client_handle_init_request(int clientfd, dp_ipc_fmt *ipc_info)
549 int errorcode = DP_ERROR_NONE;
550 memset(ipc_info, 0x00, sizeof(dp_ipc_fmt));
551 if (read(clientfd, ipc_info, sizeof(dp_ipc_fmt)) <= 0) {
552 TRACE_ERROR("peer terminate ? ignore this connection");
553 return DP_ERROR_INVALID_PARAMETER;
556 if (__is_ipc_info_for_init(*ipc_info) == false) {
557 TRACE_ERROR("peer terminate ? ignore this connection");
558 return DP_ERROR_INVALID_PARAMETER;
561 dp_credential credential = {-1, -1, -1};
562 errorcode = __dp_client_get_credential(clientfd, &credential);
563 if (errorcode != DP_ERROR_NONE)
566 errorcode = __dp_db_open_client_manager();
567 if (errorcode == DP_ERROR_NONE)
568 errorcode = __dp_client_new(clientfd, g_dp_client_slots, credential);
573 static void __dp_client_handle_event()
575 int errorcode = DP_ERROR_NONE;
576 struct sockaddr_un clientaddr;
578 // Anyway accept client.
579 socklen_t clientlen = sizeof(clientaddr);
580 int clientfd = accept(g_dp_sock, (struct sockaddr *)&clientaddr, &clientlen);
582 TRACE_ERROR("too many client ? accept failure");
583 // provider need the time of refresh.
584 errorcode = DP_ERROR_DISK_BUSY;
588 // blocking & timeout to prevent the lockup by client.
589 struct timeval tv_timeo = {5, 500000}; // 5.5 sec
590 if (setsockopt(clientfd, SOL_SOCKET, SO_RCVTIMEO, &tv_timeo,
591 sizeof(tv_timeo)) < 0) {
592 TRACE_ERROR("failed to set timeout in blocking socket");
593 close(clientfd); // ban this client
598 errorcode = __dp_client_handle_init_request(clientfd, &ipc_info);
600 if (dp_ipc_query(clientfd, -1, ipc_info.section, DP_PROP_NONE, errorcode, 0) < 0)
601 TRACE_ERROR("check ipc sock:%d", clientfd);
603 if (errorcode != DP_ERROR_NONE) {
604 TRACE_ERROR("sock:%d id:%d section:%s property:%s errorcode:%s size:%zd",
605 clientfd, ipc_info.id,
606 dp_print_section(ipc_info.section),
607 dp_print_property(ipc_info.property),
608 dp_print_errorcode(ipc_info.errorcode),
610 close(clientfd); // ban this client
612 if (errorcode == DP_ERROR_NO_SPACE || errorcode == DP_ERROR_DISK_BUSY) {
613 TRACE_ERROR("provider can't work anymore errorcode:%s", dp_print_errorcode(errorcode));
614 //break; // provider will be terminated after sending errorcode by each thread
618 // take care zombie client, slots
619 static bool __dp_client_handle_zombie_client()
621 unsigned connected_clients = 0;
622 for (int i = 0; i < DP_MAX_CLIENTS; i++) {
624 int locked = CLIENT_MUTEX_TRYLOCK(&g_dp_client_slots[i].mutex);
625 if (locked == EINVAL) { // not initialized
627 } else if (locked == EBUSY) { // already locked
632 if (locked == 0) { // locked
633 // if no client thread, requests should be checked here
634 // if no queued, connecting or downloading, close the slot
635 if (g_dp_client_slots[i].pkgname != NULL) {
636 if (g_dp_client_slots[i].thread == 0) {
637 dp_client_clear_requests(&g_dp_client_slots[i]);
638 if (g_dp_client_slots[i].client.requests == NULL) {
639 dp_client_slot_free(&g_dp_client_slots[i]);
640 CLIENT_MUTEX_UNLOCK(&g_dp_client_slots[i].mutex);
641 dp_mutex_destroy(&g_dp_client_slots[i].mutex);
647 CLIENT_MUTEX_UNLOCK(&g_dp_client_slots[i].mutex);
650 TRACE_DEBUG("%d clients are active now", connected_clients);
651 // terminating download-provider if no clients.
652 if (connected_clients == 0) {
653 if (__dp_manage_client_requests(g_dp_client_slots) <= 0) // if no crashed job
656 dp_queue_manager_wake_up();
657 dp_notification_manager_wake_up();
662 void *dp_client_manager(void *arg)
664 fd_set rset, eset, listen_fdset, except_fdset;
665 struct timeval timeout; // for timeout of select
666 GMainLoop *event_loop = (GMainLoop *)arg;
668 if (__dp_client_add_signal_handler() == false)
671 dp_notification_clear_ongoings();
674 if (__dp_client_create_slots() == false)
677 g_dp_sock = __dp_accept_socket_new();
679 TRACE_ERROR("failed to open listen socket");
682 FD_ZERO(&listen_fdset);
683 FD_ZERO(&except_fdset);
684 FD_SET(g_dp_sock, &listen_fdset);
685 FD_SET(g_dp_sock, &except_fdset);
687 int maxfd = g_dp_sock;
688 while (g_dp_sock >= 0) {
689 // initialize timeout structure for calling timeout exactly
690 memset(&timeout, 0x00, sizeof(struct timeval));
691 timeout.tv_sec = DP_CARE_CLIENT_MANAGER_INTERVAL;
695 if (select((maxfd + 1), &rset, 0, &eset, &timeout) < 0) {
696 TRACE_ERROR("interrupted by terminating");
701 TRACE_DEBUG("queue-manager is closed by other thread");
705 if (FD_ISSET(g_dp_sock, &eset) > 0) {
706 TRACE_ERROR("exception of socket");
710 if (FD_ISSET(g_dp_sock, &rset) > 0)
711 __dp_client_handle_event();
712 else if (__dp_client_handle_zombie_client() == false)
720 dp_queue_manager_kill();
721 dp_notification_clear_ongoings();
722 dp_notification_manager_kill();
724 __dp_db_free_client_manager();
726 // kill other clients
727 TRACE_INFO("try to deallocate the resources for all clients");
728 for (int i = 0; i < DP_MAX_CLIENTS; i++) {
729 int locked = CLIENT_MUTEX_TRYLOCK(&g_dp_client_slots[i].mutex);
730 if (locked == EBUSY) { // already locked
731 CLIENT_MUTEX_LOCK(&g_dp_client_slots[i].mutex);
732 } else if (locked == EINVAL) { // not initialized, empty slot
735 dp_client_slot_free(&g_dp_client_slots[i]);
736 CLIENT_MUTEX_UNLOCK(&g_dp_client_slots[i].mutex);
737 dp_mutex_destroy(&g_dp_client_slots[i].mutex);
739 free(g_dp_client_slots);
740 // free all resources
742 TRACE_INFO("client-manager's working is done");
745 g_main_loop_quit(event_loop);