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