Add cache support
[platform/framework/web/download-provider.git] / cache / cache-agent-interface.c
1 /*
2  * Copyright (c) 2023 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 "include/cache-agent-interface.h"
18
19 static pthread_mutex_t mutex_ca_block_request = PTHREAD_MUTEX_INITIALIZER;
20 static pthread_mutex_t mutex_ca_capa_handling = PTHREAD_MUTEX_INITIALIZER;
21 static ca_bool_t block_request = CA_TRUE;
22 static pthread_t g_capa_tid = 0;
23
24 static void __set_block_request(ca_bool_t update)
25 {
26         CA_MUTEX_LOCK(&mutex_ca_block_request);
27         block_request = update;
28         CA_MUTEX_UNLOCK(&mutex_ca_block_request);
29 }
30
31 static int __cancel_cache_download(int req_id, ca_bool_t update)
32 {
33         int ret = CA_RESULT_OK;
34         ca_info_t *ca_info = CA_NULL;
35
36         CA_LOGV("cache_id[%d]", req_id);
37         ret = ca_get_info_with_ca_id(req_id, &ca_info);
38         if (ret != CA_RESULT_OK)
39                 goto done;
40
41         ca_info->is_cb_update = update;
42
43         ret = ca_request_to_cancel_cache_download(ca_info);
44         if (ret != CA_RESULT_OK)
45                 goto done;
46
47         CA_LOGI("Cache-download is canceled for cache id[%d]", req_id);
48
49 done:
50         CA_LOGI("Return:id[%d], ret[%d]", req_id, ret);
51         return ret;
52 }
53
54 static int __suspend_cache_download(int req_id, ca_bool_t update)
55 {
56         int ret = CA_RESULT_OK;
57         ca_info_t *ca_info = CA_NULL;
58
59         CA_LOGV("cache_id[%d]", req_id);
60         ret = ca_get_info_with_ca_id(req_id, &ca_info);
61         if (ret != CA_RESULT_OK)
62                 goto done;
63
64         ca_info->is_cb_update = update;
65
66         ret = ca_request_to_suspend_cache_download(ca_info);
67         if (ret != CA_RESULT_OK)
68                 goto done;
69
70         CA_LOGI("Cache-download is paused for cache id[%d]", req_id);
71
72 done:
73         CA_LOGI("Return:id[%d], ret[%d]", req_id, ret);
74         return ret;
75 }
76
77 static int __check_capacity_exceeded(const char *file_path, ca_bool_t *is_allowed)
78 {
79         int ret = CA_RESULT_OK;
80         ca_size_t cache_size = 0;
81         unsigned int max_cache_size = 0;
82         ca_size_t src_file_size = 0;
83         *is_allowed = CA_FALSE;
84
85         if (file_path)
86                 ca_storage_get_file_size(file_path, &src_file_size);
87
88         if (src_file_size < 0) {
89                 CA_LOGE("Unable to get the file size!");
90                 return CA_ERR_FAIL_TO_ACCESS_FILE;
91         }
92
93         ca_config_get_cache_size(&cache_size);
94         ca_config_get_max_cache_size(&max_cache_size);
95         cache_size /= CA_MB_BYTE;
96         src_file_size /= CA_MB_BYTE;
97
98         if (cache_size + src_file_size >= max_cache_size * CA_CAPA_THRESHOLD_RATIO) {
99                 CA_LOGD("Capacity is exceeded!, cache_size: %lld, src_file_size: %lld, max_cache_size * %f: %f",
100                                 cache_size, src_file_size, CA_CAPA_THRESHOLD_RATIO,
101                                 max_cache_size * CA_CAPA_THRESHOLD_RATIO);
102                 ret = CA_ERR_DISK_FULL;
103         }
104
105         if (cache_size + src_file_size < max_cache_size)
106                 *is_allowed = CA_TRUE;
107
108         return ret;
109 }
110
111 static ca_bool_t __check_lifecycle_exceeded(void)
112 {
113         ca_time_t oldest_time;
114         unsigned int max_lifecycle;
115
116         ca_config_get_oldest_time(&oldest_time);
117         if (oldest_time == 0) {
118                 ca_config_set_oldest_time(time(CA_NULL));
119                 return CA_FALSE;
120         }
121
122         ca_config_get_max_lifecycle(&max_lifecycle);
123
124         if (difftime(time(CA_NULL), oldest_time) >= max_lifecycle * CA_CAPA_THRESHOLD_RATIO) {
125                 CA_LOGD("Lifecycle expired!, oldest_time: %ld, max_lifecycle: %u, difftime: %f, max_lifecycle * %f: %f",
126                                         oldest_time, max_lifecycle, difftime(time(CA_NULL), oldest_time),
127                                         CA_CAPA_THRESHOLD_RATIO, max_lifecycle * CA_CAPA_THRESHOLD_RATIO);
128                 return CA_TRUE;
129         }
130
131         return CA_FALSE;
132 }
133
134 static char **__convert_gslist_to_array(GSList *file_list, unsigned int *file_count)
135 {
136         char **file_array = CA_NULL;
137         int index = 0;
138
139         *file_count = g_slist_length(file_list);
140         if (*file_count == 0)
141                 return CA_NULL;
142
143         file_array = (char **)calloc(*file_count, sizeof(char *));
144         if (!file_array)
145                 return CA_NULL;
146
147         for (GSList* node = file_list; node != NULL; node = node->next) {
148                 file_array[index] = (char *)node->data;
149                 index++;
150         }
151
152         return file_array;
153 }
154
155 static void *__thread_clear_exceeded_files(void *data)
156 {
157         GSList *file_list = CA_NULL;
158         char **file_array = CA_NULL;
159         unsigned int file_count = 0;
160         ca_capacity_event_type type = (ca_capacity_event_type)data;
161
162         if (type != CA_CAPACITY_MAX_SIZE && type != CA_CAPACITY_LIFECYCLE)
163                 goto done;
164
165         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, CA_NULL);
166
167         __set_block_request(CA_TRUE);
168
169         file_list = ca_config_call_capacity_cb(type);
170         if (!file_list)
171                 goto done;
172
173         file_array = __convert_gslist_to_array(file_list, &file_count);
174         if (!file_array) {
175                 g_slist_free_full(file_list, g_free);
176                 goto done;
177         }
178
179         ca_storage_remove_files((int)file_count, (const char **) file_array);
180
181         g_slist_free_full(file_list, g_free);
182         free(file_array);
183
184 done:
185         __set_block_request(CA_FALSE);
186
187         CA_LOGI("=====EXIT thread : ca_id[%lu]=====", g_capa_tid);
188
189         CA_MUTEX_LOCK(&(mutex_ca_capa_handling));
190         g_capa_tid = 0;
191         CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
192
193         pthread_exit((void *)CA_NULL);
194         return CA_NULL;
195 }
196
197 static void __create_capacity_thread(ca_capacity_event_type type)
198 {
199         pthread_attr_t thread_attr;
200
201         if (pthread_attr_init(&thread_attr) != 0)
202                 return;
203
204         if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) != 0)
205                 return;
206
207         if (pthread_create(&g_capa_tid, &thread_attr, __thread_clear_exceeded_files, (void *)type) != 0) {
208                 CA_LOGE("Fail to make thread capacity managing: type[%d]", type);
209                 return;
210         }
211
212         CA_LOGI("Thread is created:thread id[%lu]", g_capa_tid);
213 }
214
215 int ca_init(ca_capacity_event_cb capa_cb)
216 {
217         int ret = CA_RESULT_OK;
218         CA_LOGI("");
219
220         ret = ca_storage_init();
221         if (ret != CA_RESULT_OK)
222                 return ret;
223
224         ret = ca_config_init();
225         if (ret != CA_RESULT_OK) {
226                 ca_storage_deinit();
227                 return ret;
228         }
229
230         ca_config_set_capacity_cb(capa_cb);
231         __set_block_request(CA_FALSE);
232
233         return CA_RESULT_OK;
234 }
235
236 int ca_deinit()
237 {
238         int ret = CA_RESULT_OK;
239         CA_LOGI("====== ca_deint EXIT =====");
240
241         __set_block_request(CA_TRUE);
242
243         ca_storage_deinit();
244         ca_config_deinit();
245
246         return ret;
247 }
248
249 char *ca_store_file(const char *file_path)
250 {
251         int ret = CA_RESULT_OK;
252         ca_bool_t is_allowed = CA_TRUE;
253
254         REQ_BLOCK_RET_PTR(&mutex_ca_block_request, block_request);
255
256         if (!file_path)
257                 return CA_NULL;
258
259         CA_MUTEX_LOCK(&(mutex_ca_capa_handling));
260
261         if (g_capa_tid != 0) {
262                 CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
263                 return CA_NULL;
264         }
265
266         ret = __check_capacity_exceeded(file_path, &is_allowed);
267         if (ret == CA_ERR_DISK_FULL)
268                 __create_capacity_thread(CA_CAPACITY_MAX_SIZE);
269
270         if (!is_allowed) {
271                 CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
272                 return CA_NULL;
273         }
274
275         if (g_capa_tid == 0 && __check_lifecycle_exceeded())
276                 __create_capacity_thread(CA_CAPACITY_LIFECYCLE);
277
278         CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
279
280         return ca_storage_store_file(file_path);
281 }
282
283 int ca_remove_files(int file_count, const char *cache_files[])
284 {
285         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
286
287         if (file_count <= 0)
288                 return CA_ERR_INVALID_ARGUMENT;
289
290         return ca_storage_remove_files(file_count, cache_files);
291 }
292
293 int ca_is_file_available(const char *cache_file)
294 {
295         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
296
297         if (!cache_file)
298                 return CA_ERR_INVALID_ARGUMENT;
299
300         return ca_storage_is_file_available(cache_file);
301 }
302
303 int ca_get_used_cache_size(ca_size_t *size)
304 {
305         if (!size)
306                 return CA_ERR_INVALID_ARGUMENT;
307
308         return ca_storage_get_used_cache_size(size);
309 }
310
311 int ca_start_cache_download(const char *cache_file,
312                 ca_req_data_t *ext_data, ca_cb_t *ca_cb_data, int *cache_id)
313 {
314         int ret = CA_RESULT_OK;
315         int ca_id = CA_INVALID_ID;
316         ca_info_t *ca_info = CA_NULL;
317
318         if (!cache_file || !ext_data || !ca_cb_data || !cache_id) {
319                 CA_LOGE("Null Check: cache_file, ext_data, ca_cb_data, cache_id");
320                 return CA_ERR_INVALID_ARGUMENT;
321         }
322
323         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
324
325         ret = ca_get_available_ca_id(&ca_id);
326         if (ret != CA_RESULT_OK) {
327                 CA_LOGE("No available cache id");
328                 return CA_ERR_ALREADY_MAX_COPY;
329         }
330
331         ca_info = ca_get_ca_info(ca_id);
332         if (ca_info == CA_NULL) {
333                 CA_LOGE("No available cache info, ca_id[%d]", ca_id);
334                 return CA_ERR_INVALID_STATE;
335         }
336
337         ca_info->ca_id = ca_id;
338
339         if (ext_data->install_path)
340                 CA_SECURE_LOGI("install path[%s]", ext_data->install_path);
341         if (ext_data->file_name)
342                 CA_SECURE_LOGI("file_name[%s]", ext_data->file_name);
343         if (ext_data->pkg_name)
344                 CA_SECURE_LOGI("pkg_name[%s]", ext_data->pkg_name);
345         if (ext_data->user_req_data)
346                 CA_LOGI("user_req_data[%p]", ext_data->user_req_data);
347         if (ext_data->user_client_data)
348                 CA_LOGI("user_client_data[%p]", ext_data->user_client_data);
349
350         ret = ca_init_cache_download_data(ca_info, cache_file, ext_data);
351         if (ret != CA_RESULT_OK) {
352                 ca_destroy_ca_info(ca_info->ca_id);
353                 CA_LOGE("Unable to init cache download info");
354                 return CA_ERR_INVALID_STATE;
355         }
356
357         ca_info->is_cb_update = CA_TRUE;
358         memcpy(&(ca_info->cb_info), ca_cb_data, sizeof(ca_cb_t));
359
360         ret = ca_storage_copy_file(ca_info);
361
362         if (ret != CA_RESULT_OK) {
363                 if (ca_info->dst_fp && ca_info->dst_file) {
364                         fclose(ca_info->dst_fp);
365                         unlink(ca_info->dst_file);
366                         ca_info->dst_fp = CA_NULL;
367                 }
368
369                 ca_destroy_ca_info(ca_info->ca_id);
370         }
371
372         *cache_id = ca_id;
373         CA_LOGI("Return:id[%d], ret[%d]", *cache_id, ret);
374
375         return ret;
376 }
377
378 int ca_pause_cache_download(int req_id)
379 {
380         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
381         return __suspend_cache_download(req_id, CA_TRUE);
382 }
383
384 int ca_resume_cache_download(int req_id)
385 {
386         int ret = CA_RESULT_OK;
387         ca_info_t *ca_info = CA_NULL;
388
389         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
390
391         CA_LOGV("cache_id[%d]", req_id);
392         ret = ca_get_info_with_ca_id(req_id, &ca_info);
393         if (ret != CA_RESULT_OK)
394                 goto done;
395
396         ca_info->is_cb_update = CA_TRUE;
397
398         ret = ca_request_to_resume_cache_download(ca_info);
399         if (ret != CA_RESULT_OK)
400                 goto done;
401
402         CA_LOGI("Cache-download is resumed for cache id[%d]", req_id);
403
404 done:
405         CA_LOGI("Return:id[%d], ret[%d]", req_id, ret);
406         return ret;
407 }
408
409 int ca_cancel_cache_download(int req_id)
410 {
411         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
412         return __cancel_cache_download(req_id, CA_TRUE);
413 }
414
415 int ca_pause_cache_download_without_update(int req_id)
416 {
417         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
418         return __suspend_cache_download(req_id, CA_FALSE);
419 }
420
421 int ca_cancel_cache_download_without_update(int req_id)
422 {
423         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
424         return __cancel_cache_download(req_id, CA_FALSE);
425 }
426
427 int ca_is_alive_cache_download(int req_id)
428 {
429         int ret = CA_RESULT_OK;
430         ca_info_t *ca_info = CA_NULL;
431
432         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
433
434         ca_info = ca_get_ca_info(req_id);
435         if (ca_info == CA_NULL) {
436                 CA_LOGE("No available cache info, ca_id[%d]", req_id);
437                 ret = CA_ERR_INVALID_ARGUMENT;
438                 goto done;
439         }
440
441         if (CA_RESULT_OK != ca_check_ca_id(ca_info, req_id)) {
442                 CA_LOGE("Invalid ca_id[%d]", ca_info->ca_id);
443                 ret = CA_ERR_INVALID_ARGUMENT;
444         }
445
446 done:
447         CA_LOGI("Return:id[%d], ret[%d]", req_id, ret);
448         return ret;
449 }
450
451 int ca_get_max_cache_size(unsigned int *size)
452 {
453         return ca_config_get_max_cache_size(size);
454 }
455
456 int ca_set_max_cache_size(unsigned int size)
457 {
458         int ret = CA_RESULT_OK;
459         ca_bool_t is_allowed;
460
461         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
462
463         ret = ca_config_set_max_cache_size(size);
464         if (ret != CA_RESULT_OK)
465                 return ret;
466
467         CA_MUTEX_LOCK(&(mutex_ca_capa_handling));
468
469         if (g_capa_tid != 0) {
470                 CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
471                 return ret;
472         }
473
474         ret = __check_capacity_exceeded(CA_NULL, &is_allowed);
475         if (ret == CA_ERR_DISK_FULL)
476                 __create_capacity_thread(CA_CAPACITY_MAX_SIZE);
477
478         CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
479
480         return CA_RESULT_OK;
481 }
482
483 int ca_get_cache_path(char **path)
484 {
485         int ret = CA_RESULT_OK;
486
487         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
488
489         *path = (char *)ca_config_get_cache_path();
490         if (!*path)
491                 ret = CA_ERR_INVALID_STATE;
492
493         return ret;
494 }
495
496 int ca_set_cache_path(const char *path)
497 {
498         int ret = CA_RESULT_OK;
499
500         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
501
502         __set_block_request(CA_TRUE);
503
504         ca_cancel_all_cache_operations();
505
506         ret = ca_clear_all_cache_files();
507         if (ret != CA_RESULT_OK) {
508                 __set_block_request(CA_FALSE);
509                 return ret;
510         }
511
512         ca_config_set_cache_path(path);
513
514         __set_block_request(CA_FALSE);
515
516         return ret;
517 }
518
519 int ca_get_cache_lifecycle(unsigned int *time)
520 {
521         return ca_config_get_max_lifecycle(time);
522 }
523
524 int ca_set_cache_lifecycle(unsigned int time)
525 {
526         int ret = CA_RESULT_OK;
527
528         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
529
530         ret = ca_config_set_max_lifecycle(time);
531         if (ret != CA_RESULT_OK)
532                 return ret;
533
534         CA_MUTEX_LOCK(&(mutex_ca_capa_handling));
535
536         if (g_capa_tid == 0 && __check_lifecycle_exceeded())
537                 __create_capacity_thread(CA_CAPACITY_LIFECYCLE);
538
539         CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
540
541         return ret;
542 }
543
544 int ca_clear_all_files(void)
545 {
546         int ret = CA_RESULT_OK;
547
548         REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
549
550         __set_block_request(CA_TRUE);
551
552         ca_cancel_all_cache_operations();
553         ret = ca_clear_all_cache_files();
554
555         ca_config_set_cache_size(0);
556         ca_config_set_oldest_time(time(CA_NULL));
557
558         __set_block_request(CA_FALSE);
559
560         return ret;
561 }
562
563 int ca_set_oldest_file_time(ca_time_t oldest_time)
564 {
565         return ca_config_set_oldest_time(oldest_time);
566 }