1*1c60b9acSAndroid Build Coastguard Worker /*
2*1c60b9acSAndroid Build Coastguard Worker * libwebsockets - small server side websockets and web server implementation
3*1c60b9acSAndroid Build Coastguard Worker *
4*1c60b9acSAndroid Build Coastguard Worker * Copyright (C) 2010 - 2019 Andy Green <[email protected]>
5*1c60b9acSAndroid Build Coastguard Worker *
6*1c60b9acSAndroid Build Coastguard Worker * Permission is hereby granted, free of charge, to any person obtaining a copy
7*1c60b9acSAndroid Build Coastguard Worker * of this software and associated documentation files (the "Software"), to
8*1c60b9acSAndroid Build Coastguard Worker * deal in the Software without restriction, including without limitation the
9*1c60b9acSAndroid Build Coastguard Worker * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10*1c60b9acSAndroid Build Coastguard Worker * sell copies of the Software, and to permit persons to whom the Software is
11*1c60b9acSAndroid Build Coastguard Worker * furnished to do so, subject to the following conditions:
12*1c60b9acSAndroid Build Coastguard Worker *
13*1c60b9acSAndroid Build Coastguard Worker * The above copyright notice and this permission notice shall be included in
14*1c60b9acSAndroid Build Coastguard Worker * all copies or substantial portions of the Software.
15*1c60b9acSAndroid Build Coastguard Worker *
16*1c60b9acSAndroid Build Coastguard Worker * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*1c60b9acSAndroid Build Coastguard Worker * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*1c60b9acSAndroid Build Coastguard Worker * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19*1c60b9acSAndroid Build Coastguard Worker * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*1c60b9acSAndroid Build Coastguard Worker * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21*1c60b9acSAndroid Build Coastguard Worker * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22*1c60b9acSAndroid Build Coastguard Worker * IN THE SOFTWARE.
23*1c60b9acSAndroid Build Coastguard Worker */
24*1c60b9acSAndroid Build Coastguard Worker
25*1c60b9acSAndroid Build Coastguard Worker #if !defined(_GNU_SOURCE)
26*1c60b9acSAndroid Build Coastguard Worker #define _GNU_SOURCE
27*1c60b9acSAndroid Build Coastguard Worker #endif
28*1c60b9acSAndroid Build Coastguard Worker #include <pthread.h>
29*1c60b9acSAndroid Build Coastguard Worker
30*1c60b9acSAndroid Build Coastguard Worker #include <libwebsockets.h>
31*1c60b9acSAndroid Build Coastguard Worker #include "private-lib-core.h"
32*1c60b9acSAndroid Build Coastguard Worker
33*1c60b9acSAndroid Build Coastguard Worker #include <string.h>
34*1c60b9acSAndroid Build Coastguard Worker #include <stdio.h>
35*1c60b9acSAndroid Build Coastguard Worker #include <unistd.h>
36*1c60b9acSAndroid Build Coastguard Worker #include <fcntl.h>
37*1c60b9acSAndroid Build Coastguard Worker #include <dirent.h>
38*1c60b9acSAndroid Build Coastguard Worker #include <time.h>
39*1c60b9acSAndroid Build Coastguard Worker #include <errno.h>
40*1c60b9acSAndroid Build Coastguard Worker #include <stdarg.h>
41*1c60b9acSAndroid Build Coastguard Worker
42*1c60b9acSAndroid Build Coastguard Worker #include <sys/stat.h>
43*1c60b9acSAndroid Build Coastguard Worker #include <sys/time.h>
44*1c60b9acSAndroid Build Coastguard Worker #include <sys/types.h>
45*1c60b9acSAndroid Build Coastguard Worker
46*1c60b9acSAndroid Build Coastguard Worker #if defined(__APPLE__)
47*1c60b9acSAndroid Build Coastguard Worker #include <sys/dirent.h>
48*1c60b9acSAndroid Build Coastguard Worker /* Travis OSX does not have DT_REG... */
49*1c60b9acSAndroid Build Coastguard Worker #if !defined(DT_REG)
50*1c60b9acSAndroid Build Coastguard Worker #define DT_REG 8
51*1c60b9acSAndroid Build Coastguard Worker #endif
52*1c60b9acSAndroid Build Coastguard Worker #endif
53*1c60b9acSAndroid Build Coastguard Worker
54*1c60b9acSAndroid Build Coastguard Worker struct file_entry {
55*1c60b9acSAndroid Build Coastguard Worker lws_list_ptr sorted;
56*1c60b9acSAndroid Build Coastguard Worker lws_list_ptr prev;
57*1c60b9acSAndroid Build Coastguard Worker char name[64];
58*1c60b9acSAndroid Build Coastguard Worker time_t modified;
59*1c60b9acSAndroid Build Coastguard Worker size_t size;
60*1c60b9acSAndroid Build Coastguard Worker };
61*1c60b9acSAndroid Build Coastguard Worker
62*1c60b9acSAndroid Build Coastguard Worker struct lws_diskcache_scan {
63*1c60b9acSAndroid Build Coastguard Worker struct file_entry *batch;
64*1c60b9acSAndroid Build Coastguard Worker const char *cache_dir_base;
65*1c60b9acSAndroid Build Coastguard Worker lws_list_ptr head;
66*1c60b9acSAndroid Build Coastguard Worker time_t last_scan_completed;
67*1c60b9acSAndroid Build Coastguard Worker uint64_t agg_size;
68*1c60b9acSAndroid Build Coastguard Worker uint64_t cache_size_limit;
69*1c60b9acSAndroid Build Coastguard Worker uint64_t avg_size;
70*1c60b9acSAndroid Build Coastguard Worker uint64_t cache_tries;
71*1c60b9acSAndroid Build Coastguard Worker uint64_t cache_hits;
72*1c60b9acSAndroid Build Coastguard Worker int cache_subdir;
73*1c60b9acSAndroid Build Coastguard Worker int batch_in_use;
74*1c60b9acSAndroid Build Coastguard Worker int agg_file_count;
75*1c60b9acSAndroid Build Coastguard Worker int secs_waiting;
76*1c60b9acSAndroid Build Coastguard Worker };
77*1c60b9acSAndroid Build Coastguard Worker
78*1c60b9acSAndroid Build Coastguard Worker #define KIB (1024)
79*1c60b9acSAndroid Build Coastguard Worker #define MIB (KIB * KIB)
80*1c60b9acSAndroid Build Coastguard Worker
81*1c60b9acSAndroid Build Coastguard Worker #define lp_to_fe(p, _n) lws_list_ptr_container(p, struct file_entry, _n)
82*1c60b9acSAndroid Build Coastguard Worker
83*1c60b9acSAndroid Build Coastguard Worker static const char *hex = "0123456789abcdef";
84*1c60b9acSAndroid Build Coastguard Worker
85*1c60b9acSAndroid Build Coastguard Worker #define BATCH_COUNT 128
86*1c60b9acSAndroid Build Coastguard Worker
87*1c60b9acSAndroid Build Coastguard Worker static int
fe_modified_sort(lws_list_ptr a,lws_list_ptr b)88*1c60b9acSAndroid Build Coastguard Worker fe_modified_sort(lws_list_ptr a, lws_list_ptr b)
89*1c60b9acSAndroid Build Coastguard Worker {
90*1c60b9acSAndroid Build Coastguard Worker struct file_entry *p1 = lp_to_fe(a, sorted), *p2 = lp_to_fe(b, sorted);
91*1c60b9acSAndroid Build Coastguard Worker
92*1c60b9acSAndroid Build Coastguard Worker return (int)((long)p2->modified - (long)p1->modified);
93*1c60b9acSAndroid Build Coastguard Worker }
94*1c60b9acSAndroid Build Coastguard Worker
95*1c60b9acSAndroid Build Coastguard Worker struct lws_diskcache_scan *
lws_diskcache_create(const char * cache_dir_base,uint64_t cache_size_limit)96*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_create(const char *cache_dir_base, uint64_t cache_size_limit)
97*1c60b9acSAndroid Build Coastguard Worker {
98*1c60b9acSAndroid Build Coastguard Worker struct lws_diskcache_scan *lds = lws_malloc(sizeof(*lds), "cachescan");
99*1c60b9acSAndroid Build Coastguard Worker
100*1c60b9acSAndroid Build Coastguard Worker if (!lds)
101*1c60b9acSAndroid Build Coastguard Worker return NULL;
102*1c60b9acSAndroid Build Coastguard Worker
103*1c60b9acSAndroid Build Coastguard Worker memset(lds, 0, sizeof(*lds));
104*1c60b9acSAndroid Build Coastguard Worker
105*1c60b9acSAndroid Build Coastguard Worker lds->cache_dir_base = cache_dir_base;
106*1c60b9acSAndroid Build Coastguard Worker lds->cache_size_limit = cache_size_limit;
107*1c60b9acSAndroid Build Coastguard Worker
108*1c60b9acSAndroid Build Coastguard Worker return lds;
109*1c60b9acSAndroid Build Coastguard Worker }
110*1c60b9acSAndroid Build Coastguard Worker
111*1c60b9acSAndroid Build Coastguard Worker void
lws_diskcache_destroy(struct lws_diskcache_scan ** lds)112*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_destroy(struct lws_diskcache_scan **lds)
113*1c60b9acSAndroid Build Coastguard Worker {
114*1c60b9acSAndroid Build Coastguard Worker if ((*lds)->batch)
115*1c60b9acSAndroid Build Coastguard Worker lws_free((*lds)->batch);
116*1c60b9acSAndroid Build Coastguard Worker lws_free(*lds);
117*1c60b9acSAndroid Build Coastguard Worker *lds = NULL;
118*1c60b9acSAndroid Build Coastguard Worker }
119*1c60b9acSAndroid Build Coastguard Worker
120*1c60b9acSAndroid Build Coastguard Worker int
lws_diskcache_prepare(const char * cache_base_dir,int mode,uid_t uid)121*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_prepare(const char *cache_base_dir, int mode, uid_t uid)
122*1c60b9acSAndroid Build Coastguard Worker {
123*1c60b9acSAndroid Build Coastguard Worker char dir[256];
124*1c60b9acSAndroid Build Coastguard Worker int n, m;
125*1c60b9acSAndroid Build Coastguard Worker
126*1c60b9acSAndroid Build Coastguard Worker (void)mkdir(cache_base_dir, (unsigned short)mode);
127*1c60b9acSAndroid Build Coastguard Worker if (chown(cache_base_dir, uid, (gid_t)-1))
128*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: %s: unable to chown %d\n", __func__,
129*1c60b9acSAndroid Build Coastguard Worker cache_base_dir, uid);
130*1c60b9acSAndroid Build Coastguard Worker
131*1c60b9acSAndroid Build Coastguard Worker for (n = 0; n < 16; n++) {
132*1c60b9acSAndroid Build Coastguard Worker lws_snprintf(dir, sizeof(dir), "%s/%c", cache_base_dir, hex[n]);
133*1c60b9acSAndroid Build Coastguard Worker (void)mkdir(dir, (mode_t)mode);
134*1c60b9acSAndroid Build Coastguard Worker if (chown(dir, uid, (uid_t)-1))
135*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: %s: unable to chown %d\n", __func__,
136*1c60b9acSAndroid Build Coastguard Worker dir, uid);
137*1c60b9acSAndroid Build Coastguard Worker for (m = 0; m < 16; m++) {
138*1c60b9acSAndroid Build Coastguard Worker lws_snprintf(dir, sizeof(dir), "%s/%c/%c",
139*1c60b9acSAndroid Build Coastguard Worker cache_base_dir, hex[n], hex[m]);
140*1c60b9acSAndroid Build Coastguard Worker (void)mkdir(dir, (mode_t)mode);
141*1c60b9acSAndroid Build Coastguard Worker if (chown(dir, uid, (uid_t)-1))
142*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: %s: unable to chown %d\n",
143*1c60b9acSAndroid Build Coastguard Worker __func__, dir, uid);
144*1c60b9acSAndroid Build Coastguard Worker }
145*1c60b9acSAndroid Build Coastguard Worker }
146*1c60b9acSAndroid Build Coastguard Worker
147*1c60b9acSAndroid Build Coastguard Worker return 0;
148*1c60b9acSAndroid Build Coastguard Worker }
149*1c60b9acSAndroid Build Coastguard Worker
150*1c60b9acSAndroid Build Coastguard Worker /* copies and then truncates the incoming name, and renames the file at the
151*1c60b9acSAndroid Build Coastguard Worker * untruncated path to have the new truncated name */
152*1c60b9acSAndroid Build Coastguard Worker
153*1c60b9acSAndroid Build Coastguard Worker int
lws_diskcache_finalize_name(char * cache)154*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_finalize_name(char *cache)
155*1c60b9acSAndroid Build Coastguard Worker {
156*1c60b9acSAndroid Build Coastguard Worker char ren[256], *p;
157*1c60b9acSAndroid Build Coastguard Worker
158*1c60b9acSAndroid Build Coastguard Worker strncpy(ren, cache, sizeof(ren) - 1);
159*1c60b9acSAndroid Build Coastguard Worker ren[sizeof(ren) - 1] = '\0';
160*1c60b9acSAndroid Build Coastguard Worker p = strchr(cache, '~');
161*1c60b9acSAndroid Build Coastguard Worker if (p) {
162*1c60b9acSAndroid Build Coastguard Worker *p = '\0';
163*1c60b9acSAndroid Build Coastguard Worker if (rename(ren, cache)) {
164*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: problem renaming %s to %s\n", __func__,
165*1c60b9acSAndroid Build Coastguard Worker ren, cache);
166*1c60b9acSAndroid Build Coastguard Worker return 1;
167*1c60b9acSAndroid Build Coastguard Worker }
168*1c60b9acSAndroid Build Coastguard Worker
169*1c60b9acSAndroid Build Coastguard Worker return 0;
170*1c60b9acSAndroid Build Coastguard Worker }
171*1c60b9acSAndroid Build Coastguard Worker
172*1c60b9acSAndroid Build Coastguard Worker return 1;
173*1c60b9acSAndroid Build Coastguard Worker }
174*1c60b9acSAndroid Build Coastguard Worker
175*1c60b9acSAndroid Build Coastguard Worker int
lws_diskcache_query(struct lws_diskcache_scan * lds,int is_bot,const char * hash_hex,int * _fd,char * cache,int cache_len,size_t * extant_cache_len)176*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_query(struct lws_diskcache_scan *lds, int is_bot,
177*1c60b9acSAndroid Build Coastguard Worker const char *hash_hex, int *_fd, char *cache, int cache_len,
178*1c60b9acSAndroid Build Coastguard Worker size_t *extant_cache_len)
179*1c60b9acSAndroid Build Coastguard Worker {
180*1c60b9acSAndroid Build Coastguard Worker struct stat s;
181*1c60b9acSAndroid Build Coastguard Worker int n;
182*1c60b9acSAndroid Build Coastguard Worker
183*1c60b9acSAndroid Build Coastguard Worker /* caching is disabled? */
184*1c60b9acSAndroid Build Coastguard Worker if (!lds->cache_dir_base)
185*1c60b9acSAndroid Build Coastguard Worker return LWS_DISKCACHE_QUERY_NO_CACHE;
186*1c60b9acSAndroid Build Coastguard Worker
187*1c60b9acSAndroid Build Coastguard Worker if (!is_bot)
188*1c60b9acSAndroid Build Coastguard Worker lds->cache_tries++;
189*1c60b9acSAndroid Build Coastguard Worker
190*1c60b9acSAndroid Build Coastguard Worker n = lws_snprintf(cache, (size_t)cache_len, "%s/%c/%c/%s", lds->cache_dir_base,
191*1c60b9acSAndroid Build Coastguard Worker hash_hex[0], hash_hex[1], hash_hex);
192*1c60b9acSAndroid Build Coastguard Worker
193*1c60b9acSAndroid Build Coastguard Worker lwsl_info("%s: job cache %s\n", __func__, cache);
194*1c60b9acSAndroid Build Coastguard Worker
195*1c60b9acSAndroid Build Coastguard Worker *_fd = open(cache, O_RDONLY);
196*1c60b9acSAndroid Build Coastguard Worker if (*_fd >= 0) {
197*1c60b9acSAndroid Build Coastguard Worker int fd;
198*1c60b9acSAndroid Build Coastguard Worker
199*1c60b9acSAndroid Build Coastguard Worker if (!is_bot)
200*1c60b9acSAndroid Build Coastguard Worker lds->cache_hits++;
201*1c60b9acSAndroid Build Coastguard Worker
202*1c60b9acSAndroid Build Coastguard Worker if (fstat(*_fd, &s)) {
203*1c60b9acSAndroid Build Coastguard Worker close(*_fd);
204*1c60b9acSAndroid Build Coastguard Worker
205*1c60b9acSAndroid Build Coastguard Worker return LWS_DISKCACHE_QUERY_NO_CACHE;
206*1c60b9acSAndroid Build Coastguard Worker }
207*1c60b9acSAndroid Build Coastguard Worker
208*1c60b9acSAndroid Build Coastguard Worker *extant_cache_len = (size_t)s.st_size;
209*1c60b9acSAndroid Build Coastguard Worker
210*1c60b9acSAndroid Build Coastguard Worker /* "touch" the hit cache file so it's last for LRU now */
211*1c60b9acSAndroid Build Coastguard Worker fd = open(cache, O_RDWR);
212*1c60b9acSAndroid Build Coastguard Worker if (fd >= 0)
213*1c60b9acSAndroid Build Coastguard Worker close(fd);
214*1c60b9acSAndroid Build Coastguard Worker
215*1c60b9acSAndroid Build Coastguard Worker return LWS_DISKCACHE_QUERY_EXISTS;
216*1c60b9acSAndroid Build Coastguard Worker }
217*1c60b9acSAndroid Build Coastguard Worker
218*1c60b9acSAndroid Build Coastguard Worker /* bots are too random to pollute the cache with their antics */
219*1c60b9acSAndroid Build Coastguard Worker if (is_bot)
220*1c60b9acSAndroid Build Coastguard Worker return LWS_DISKCACHE_QUERY_NO_CACHE;
221*1c60b9acSAndroid Build Coastguard Worker
222*1c60b9acSAndroid Build Coastguard Worker /* let's create it first with a unique temp name */
223*1c60b9acSAndroid Build Coastguard Worker
224*1c60b9acSAndroid Build Coastguard Worker lws_snprintf(cache + n, (size_t)cache_len - (unsigned int)n, "~%d-%p", (int)getpid(),
225*1c60b9acSAndroid Build Coastguard Worker extant_cache_len);
226*1c60b9acSAndroid Build Coastguard Worker
227*1c60b9acSAndroid Build Coastguard Worker *_fd = open(cache, O_RDWR | O_CREAT | O_TRUNC, 0600);
228*1c60b9acSAndroid Build Coastguard Worker if (*_fd < 0) {
229*1c60b9acSAndroid Build Coastguard Worker /* well... ok... we will proceed without cache then... */
230*1c60b9acSAndroid Build Coastguard Worker lwsl_notice("%s: Problem creating cache %s: errno %d\n",
231*1c60b9acSAndroid Build Coastguard Worker __func__, cache, errno);
232*1c60b9acSAndroid Build Coastguard Worker return LWS_DISKCACHE_QUERY_NO_CACHE;
233*1c60b9acSAndroid Build Coastguard Worker }
234*1c60b9acSAndroid Build Coastguard Worker
235*1c60b9acSAndroid Build Coastguard Worker return LWS_DISKCACHE_QUERY_CREATING;
236*1c60b9acSAndroid Build Coastguard Worker }
237*1c60b9acSAndroid Build Coastguard Worker
238*1c60b9acSAndroid Build Coastguard Worker int
lws_diskcache_secs_to_idle(struct lws_diskcache_scan * lds)239*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_secs_to_idle(struct lws_diskcache_scan *lds)
240*1c60b9acSAndroid Build Coastguard Worker {
241*1c60b9acSAndroid Build Coastguard Worker return lds->secs_waiting;
242*1c60b9acSAndroid Build Coastguard Worker }
243*1c60b9acSAndroid Build Coastguard Worker
244*1c60b9acSAndroid Build Coastguard Worker /*
245*1c60b9acSAndroid Build Coastguard Worker * The goal is to collect the oldest BATCH_COUNT filepaths and filesizes from
246*1c60b9acSAndroid Build Coastguard Worker * the dirs under the cache dir. Since we don't need or want a full list of
247*1c60b9acSAndroid Build Coastguard Worker * files in there in memory at once, we restrict the linked-list size to
248*1c60b9acSAndroid Build Coastguard Worker * BATCH_COUNT entries, and once it is full, simply ignore any further files
249*1c60b9acSAndroid Build Coastguard Worker * that are newer than the newest one on that list. Files older than the
250*1c60b9acSAndroid Build Coastguard Worker * newest guy already on the list evict the newest guy already on the list
251*1c60b9acSAndroid Build Coastguard Worker * and are sorted into the correct order. In this way no matter the number
252*1c60b9acSAndroid Build Coastguard Worker * of files to be processed the memory requirement is fixed at BATCH_COUNT
253*1c60b9acSAndroid Build Coastguard Worker * struct file_entry-s.
254*1c60b9acSAndroid Build Coastguard Worker *
255*1c60b9acSAndroid Build Coastguard Worker * The oldest subset of BATCH_COUNT files are sorted into the cd->batch
256*1c60b9acSAndroid Build Coastguard Worker * allocation in more recent -> least recent order.
257*1c60b9acSAndroid Build Coastguard Worker *
258*1c60b9acSAndroid Build Coastguard Worker * We want to track the total size of all files we saw as well, so we know if
259*1c60b9acSAndroid Build Coastguard Worker * we need to actually do anything yet to restrict how much space it's taking
260*1c60b9acSAndroid Build Coastguard Worker * up.
261*1c60b9acSAndroid Build Coastguard Worker *
262*1c60b9acSAndroid Build Coastguard Worker * And we want to do those things statefully and incrementally instead of one
263*1c60b9acSAndroid Build Coastguard Worker * big atomic operation, since the user may want a huge cache, so we look in
264*1c60b9acSAndroid Build Coastguard Worker * one cache dir at a time and track state in the repodir struct.
265*1c60b9acSAndroid Build Coastguard Worker *
266*1c60b9acSAndroid Build Coastguard Worker * When we have seen everything, we add the doubly-linked prev pointers and then
267*1c60b9acSAndroid Build Coastguard Worker * if we are over the limit, start deleting up to BATCH_COUNT files working back
268*1c60b9acSAndroid Build Coastguard Worker * from the end.
269*1c60b9acSAndroid Build Coastguard Worker */
270*1c60b9acSAndroid Build Coastguard Worker
271*1c60b9acSAndroid Build Coastguard Worker int
lws_diskcache_trim(struct lws_diskcache_scan * lds)272*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_trim(struct lws_diskcache_scan *lds)
273*1c60b9acSAndroid Build Coastguard Worker {
274*1c60b9acSAndroid Build Coastguard Worker size_t cache_size_limit = (size_t)lds->cache_size_limit;
275*1c60b9acSAndroid Build Coastguard Worker char dirpath[132], filepath[132 + 32];
276*1c60b9acSAndroid Build Coastguard Worker lws_list_ptr lp, op = NULL;
277*1c60b9acSAndroid Build Coastguard Worker int files_trimmed = 0;
278*1c60b9acSAndroid Build Coastguard Worker struct file_entry *p;
279*1c60b9acSAndroid Build Coastguard Worker int fd, n, ret = -1;
280*1c60b9acSAndroid Build Coastguard Worker size_t trimmed = 0;
281*1c60b9acSAndroid Build Coastguard Worker struct dirent *de;
282*1c60b9acSAndroid Build Coastguard Worker struct stat s;
283*1c60b9acSAndroid Build Coastguard Worker DIR *dir;
284*1c60b9acSAndroid Build Coastguard Worker
285*1c60b9acSAndroid Build Coastguard Worker if (!lds->cache_subdir) {
286*1c60b9acSAndroid Build Coastguard Worker
287*1c60b9acSAndroid Build Coastguard Worker if (lds->last_scan_completed + lds->secs_waiting > time(NULL))
288*1c60b9acSAndroid Build Coastguard Worker return 0;
289*1c60b9acSAndroid Build Coastguard Worker
290*1c60b9acSAndroid Build Coastguard Worker lds->batch = lws_malloc(sizeof(struct file_entry) *
291*1c60b9acSAndroid Build Coastguard Worker BATCH_COUNT, "cache_trim");
292*1c60b9acSAndroid Build Coastguard Worker if (!lds->batch) {
293*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: OOM\n", __func__);
294*1c60b9acSAndroid Build Coastguard Worker
295*1c60b9acSAndroid Build Coastguard Worker return 1;
296*1c60b9acSAndroid Build Coastguard Worker }
297*1c60b9acSAndroid Build Coastguard Worker lds->agg_size = 0;
298*1c60b9acSAndroid Build Coastguard Worker lds->head = NULL;
299*1c60b9acSAndroid Build Coastguard Worker lds->batch_in_use = 0;
300*1c60b9acSAndroid Build Coastguard Worker lds->agg_file_count = 0;
301*1c60b9acSAndroid Build Coastguard Worker }
302*1c60b9acSAndroid Build Coastguard Worker
303*1c60b9acSAndroid Build Coastguard Worker lws_snprintf(dirpath, sizeof(dirpath), "%s/%c/%c",
304*1c60b9acSAndroid Build Coastguard Worker lds->cache_dir_base, hex[(lds->cache_subdir >> 4) & 15],
305*1c60b9acSAndroid Build Coastguard Worker hex[lds->cache_subdir & 15]);
306*1c60b9acSAndroid Build Coastguard Worker
307*1c60b9acSAndroid Build Coastguard Worker dir = opendir(dirpath);
308*1c60b9acSAndroid Build Coastguard Worker if (!dir) {
309*1c60b9acSAndroid Build Coastguard Worker lwsl_err("Unable to walk repo dir '%s'\n",
310*1c60b9acSAndroid Build Coastguard Worker lds->cache_dir_base);
311*1c60b9acSAndroid Build Coastguard Worker return -1;
312*1c60b9acSAndroid Build Coastguard Worker }
313*1c60b9acSAndroid Build Coastguard Worker
314*1c60b9acSAndroid Build Coastguard Worker do {
315*1c60b9acSAndroid Build Coastguard Worker de = readdir(dir);
316*1c60b9acSAndroid Build Coastguard Worker if (!de)
317*1c60b9acSAndroid Build Coastguard Worker break;
318*1c60b9acSAndroid Build Coastguard Worker
319*1c60b9acSAndroid Build Coastguard Worker if (de->d_type != DT_REG)
320*1c60b9acSAndroid Build Coastguard Worker continue;
321*1c60b9acSAndroid Build Coastguard Worker
322*1c60b9acSAndroid Build Coastguard Worker lds->agg_file_count++;
323*1c60b9acSAndroid Build Coastguard Worker
324*1c60b9acSAndroid Build Coastguard Worker lws_snprintf(filepath, sizeof(filepath), "%s/%s", dirpath,
325*1c60b9acSAndroid Build Coastguard Worker de->d_name);
326*1c60b9acSAndroid Build Coastguard Worker
327*1c60b9acSAndroid Build Coastguard Worker fd = open(filepath, O_RDONLY);
328*1c60b9acSAndroid Build Coastguard Worker if (fd < 0) {
329*1c60b9acSAndroid Build Coastguard Worker lwsl_err("%s: cannot open %s\n", __func__, filepath);
330*1c60b9acSAndroid Build Coastguard Worker
331*1c60b9acSAndroid Build Coastguard Worker continue;
332*1c60b9acSAndroid Build Coastguard Worker }
333*1c60b9acSAndroid Build Coastguard Worker
334*1c60b9acSAndroid Build Coastguard Worker n = fstat(fd, &s);
335*1c60b9acSAndroid Build Coastguard Worker close(fd);
336*1c60b9acSAndroid Build Coastguard Worker if (n) {
337*1c60b9acSAndroid Build Coastguard Worker lwsl_notice("%s: cannot stat %s\n", __func__, filepath);
338*1c60b9acSAndroid Build Coastguard Worker continue;
339*1c60b9acSAndroid Build Coastguard Worker }
340*1c60b9acSAndroid Build Coastguard Worker
341*1c60b9acSAndroid Build Coastguard Worker lds->agg_size += (uint64_t)s.st_size;
342*1c60b9acSAndroid Build Coastguard Worker
343*1c60b9acSAndroid Build Coastguard Worker if (lds->batch_in_use == BATCH_COUNT) {
344*1c60b9acSAndroid Build Coastguard Worker /*
345*1c60b9acSAndroid Build Coastguard Worker * once we filled up the batch with candidates, we don't
346*1c60b9acSAndroid Build Coastguard Worker * need to consider any files newer than the newest guy
347*1c60b9acSAndroid Build Coastguard Worker * on the list...
348*1c60b9acSAndroid Build Coastguard Worker */
349*1c60b9acSAndroid Build Coastguard Worker if (lp_to_fe(lds->head, sorted)->modified < s.st_mtime)
350*1c60b9acSAndroid Build Coastguard Worker continue;
351*1c60b9acSAndroid Build Coastguard Worker
352*1c60b9acSAndroid Build Coastguard Worker /*
353*1c60b9acSAndroid Build Coastguard Worker * ... and if we find an older file later, we know it
354*1c60b9acSAndroid Build Coastguard Worker * will be replacing the newest guy on the list, so use
355*1c60b9acSAndroid Build Coastguard Worker * that directly...
356*1c60b9acSAndroid Build Coastguard Worker */
357*1c60b9acSAndroid Build Coastguard Worker p = lds->head;
358*1c60b9acSAndroid Build Coastguard Worker lds->head = p->sorted;
359*1c60b9acSAndroid Build Coastguard Worker } else
360*1c60b9acSAndroid Build Coastguard Worker /* we are still accepting anything to fill the batch */
361*1c60b9acSAndroid Build Coastguard Worker
362*1c60b9acSAndroid Build Coastguard Worker p = &lds->batch[lds->batch_in_use++];
363*1c60b9acSAndroid Build Coastguard Worker
364*1c60b9acSAndroid Build Coastguard Worker p->sorted = NULL;
365*1c60b9acSAndroid Build Coastguard Worker strncpy(p->name, de->d_name, sizeof(p->name) - 1);
366*1c60b9acSAndroid Build Coastguard Worker p->name[sizeof(p->name) - 1] = '\0';
367*1c60b9acSAndroid Build Coastguard Worker p->modified = s.st_mtime;
368*1c60b9acSAndroid Build Coastguard Worker p->size = (size_t)s.st_size;
369*1c60b9acSAndroid Build Coastguard Worker
370*1c60b9acSAndroid Build Coastguard Worker lws_list_ptr_insert(&lds->head, &p->sorted, fe_modified_sort);
371*1c60b9acSAndroid Build Coastguard Worker } while (de);
372*1c60b9acSAndroid Build Coastguard Worker
373*1c60b9acSAndroid Build Coastguard Worker ret = 0;
374*1c60b9acSAndroid Build Coastguard Worker
375*1c60b9acSAndroid Build Coastguard Worker lds->cache_subdir++;
376*1c60b9acSAndroid Build Coastguard Worker if (lds->cache_subdir != 0x100)
377*1c60b9acSAndroid Build Coastguard Worker goto done;
378*1c60b9acSAndroid Build Coastguard Worker
379*1c60b9acSAndroid Build Coastguard Worker /* we completed the whole scan... */
380*1c60b9acSAndroid Build Coastguard Worker
381*1c60b9acSAndroid Build Coastguard Worker /* if really no guidence, then 256MiB */
382*1c60b9acSAndroid Build Coastguard Worker if (!cache_size_limit)
383*1c60b9acSAndroid Build Coastguard Worker cache_size_limit = 256 * 1024 * 1024;
384*1c60b9acSAndroid Build Coastguard Worker
385*1c60b9acSAndroid Build Coastguard Worker if (lds->agg_size > cache_size_limit) {
386*1c60b9acSAndroid Build Coastguard Worker
387*1c60b9acSAndroid Build Coastguard Worker /* apply prev pointers to make the list doubly-linked */
388*1c60b9acSAndroid Build Coastguard Worker
389*1c60b9acSAndroid Build Coastguard Worker lp = lds->head;
390*1c60b9acSAndroid Build Coastguard Worker while (lp) {
391*1c60b9acSAndroid Build Coastguard Worker p = lp_to_fe(lp, sorted);
392*1c60b9acSAndroid Build Coastguard Worker
393*1c60b9acSAndroid Build Coastguard Worker p->prev = op;
394*1c60b9acSAndroid Build Coastguard Worker op = &p->prev;
395*1c60b9acSAndroid Build Coastguard Worker lp = p->sorted;
396*1c60b9acSAndroid Build Coastguard Worker }
397*1c60b9acSAndroid Build Coastguard Worker
398*1c60b9acSAndroid Build Coastguard Worker /*
399*1c60b9acSAndroid Build Coastguard Worker * reverse the list (start from tail, now traverse using
400*1c60b9acSAndroid Build Coastguard Worker * .prev)... it's oldest-first now...
401*1c60b9acSAndroid Build Coastguard Worker */
402*1c60b9acSAndroid Build Coastguard Worker
403*1c60b9acSAndroid Build Coastguard Worker lp = op;
404*1c60b9acSAndroid Build Coastguard Worker
405*1c60b9acSAndroid Build Coastguard Worker while (lp && lds->agg_size > cache_size_limit) {
406*1c60b9acSAndroid Build Coastguard Worker p = lp_to_fe(lp, prev);
407*1c60b9acSAndroid Build Coastguard Worker
408*1c60b9acSAndroid Build Coastguard Worker lws_snprintf(filepath, sizeof(filepath), "%s/%c/%c/%s",
409*1c60b9acSAndroid Build Coastguard Worker lds->cache_dir_base, p->name[0],
410*1c60b9acSAndroid Build Coastguard Worker p->name[1], p->name);
411*1c60b9acSAndroid Build Coastguard Worker
412*1c60b9acSAndroid Build Coastguard Worker if (!unlink(filepath)) {
413*1c60b9acSAndroid Build Coastguard Worker lds->agg_size -= p->size;
414*1c60b9acSAndroid Build Coastguard Worker trimmed += p->size;
415*1c60b9acSAndroid Build Coastguard Worker files_trimmed++;
416*1c60b9acSAndroid Build Coastguard Worker } else
417*1c60b9acSAndroid Build Coastguard Worker lwsl_notice("%s: Failed to unlink %s\n",
418*1c60b9acSAndroid Build Coastguard Worker __func__, filepath);
419*1c60b9acSAndroid Build Coastguard Worker
420*1c60b9acSAndroid Build Coastguard Worker lp = p->prev;
421*1c60b9acSAndroid Build Coastguard Worker }
422*1c60b9acSAndroid Build Coastguard Worker
423*1c60b9acSAndroid Build Coastguard Worker if (files_trimmed)
424*1c60b9acSAndroid Build Coastguard Worker lwsl_notice("%s: %s: trimmed %d files totalling "
425*1c60b9acSAndroid Build Coastguard Worker "%lldKib, leaving %lldMiB\n", __func__,
426*1c60b9acSAndroid Build Coastguard Worker lds->cache_dir_base, files_trimmed,
427*1c60b9acSAndroid Build Coastguard Worker ((unsigned long long)trimmed) / KIB,
428*1c60b9acSAndroid Build Coastguard Worker ((unsigned long long)lds->agg_size) / MIB);
429*1c60b9acSAndroid Build Coastguard Worker }
430*1c60b9acSAndroid Build Coastguard Worker
431*1c60b9acSAndroid Build Coastguard Worker if (lds->agg_size && lds->agg_file_count)
432*1c60b9acSAndroid Build Coastguard Worker lds->avg_size = lds->agg_size / (uint64_t)lds->agg_file_count;
433*1c60b9acSAndroid Build Coastguard Worker
434*1c60b9acSAndroid Build Coastguard Worker /*
435*1c60b9acSAndroid Build Coastguard Worker * estimate how long we can go before scanning again... default we need
436*1c60b9acSAndroid Build Coastguard Worker * to start again immediately
437*1c60b9acSAndroid Build Coastguard Worker */
438*1c60b9acSAndroid Build Coastguard Worker
439*1c60b9acSAndroid Build Coastguard Worker lds->last_scan_completed = time(NULL);
440*1c60b9acSAndroid Build Coastguard Worker lds->secs_waiting = 1;
441*1c60b9acSAndroid Build Coastguard Worker
442*1c60b9acSAndroid Build Coastguard Worker if (lds->agg_size < cache_size_limit) {
443*1c60b9acSAndroid Build Coastguard Worker uint64_t avg = 4096, capacity, projected;
444*1c60b9acSAndroid Build Coastguard Worker
445*1c60b9acSAndroid Build Coastguard Worker /* let's use 80% of the real average for margin */
446*1c60b9acSAndroid Build Coastguard Worker if (lds->agg_size && lds->agg_file_count)
447*1c60b9acSAndroid Build Coastguard Worker avg = ((lds->agg_size * 8) / (uint64_t)lds->agg_file_count) / 10;
448*1c60b9acSAndroid Build Coastguard Worker
449*1c60b9acSAndroid Build Coastguard Worker /*
450*1c60b9acSAndroid Build Coastguard Worker * if we collected BATCH_COUNT files of the average size,
451*1c60b9acSAndroid Build Coastguard Worker * how much can we clean up in 256s?
452*1c60b9acSAndroid Build Coastguard Worker */
453*1c60b9acSAndroid Build Coastguard Worker
454*1c60b9acSAndroid Build Coastguard Worker capacity = avg * BATCH_COUNT;
455*1c60b9acSAndroid Build Coastguard Worker
456*1c60b9acSAndroid Build Coastguard Worker /*
457*1c60b9acSAndroid Build Coastguard Worker * if the cache grew by 10%, would we hit the limit even then?
458*1c60b9acSAndroid Build Coastguard Worker */
459*1c60b9acSAndroid Build Coastguard Worker projected = (lds->agg_size * 11) / 10;
460*1c60b9acSAndroid Build Coastguard Worker if (projected < cache_size_limit)
461*1c60b9acSAndroid Build Coastguard Worker /* no... */
462*1c60b9acSAndroid Build Coastguard Worker lds->secs_waiting = (int)((256 / 2) * ((cache_size_limit -
463*1c60b9acSAndroid Build Coastguard Worker projected) / capacity));
464*1c60b9acSAndroid Build Coastguard Worker
465*1c60b9acSAndroid Build Coastguard Worker /*
466*1c60b9acSAndroid Build Coastguard Worker * large waits imply we may not have enough info yet, so
467*1c60b9acSAndroid Build Coastguard Worker * check once an hour at least.
468*1c60b9acSAndroid Build Coastguard Worker */
469*1c60b9acSAndroid Build Coastguard Worker
470*1c60b9acSAndroid Build Coastguard Worker if (lds->secs_waiting > 3600)
471*1c60b9acSAndroid Build Coastguard Worker lds->secs_waiting = 3600;
472*1c60b9acSAndroid Build Coastguard Worker } else
473*1c60b9acSAndroid Build Coastguard Worker lds->secs_waiting = 0;
474*1c60b9acSAndroid Build Coastguard Worker
475*1c60b9acSAndroid Build Coastguard Worker lwsl_info("%s: cache %s: %lldKiB / %lldKiB, next scan %ds\n", __func__,
476*1c60b9acSAndroid Build Coastguard Worker lds->cache_dir_base,
477*1c60b9acSAndroid Build Coastguard Worker (unsigned long long)lds->agg_size / KIB,
478*1c60b9acSAndroid Build Coastguard Worker (unsigned long long)cache_size_limit / KIB,
479*1c60b9acSAndroid Build Coastguard Worker lds->secs_waiting);
480*1c60b9acSAndroid Build Coastguard Worker
481*1c60b9acSAndroid Build Coastguard Worker lws_free(lds->batch);
482*1c60b9acSAndroid Build Coastguard Worker lds->batch = NULL;
483*1c60b9acSAndroid Build Coastguard Worker
484*1c60b9acSAndroid Build Coastguard Worker lds->cache_subdir = 0;
485*1c60b9acSAndroid Build Coastguard Worker
486*1c60b9acSAndroid Build Coastguard Worker done:
487*1c60b9acSAndroid Build Coastguard Worker closedir(dir);
488*1c60b9acSAndroid Build Coastguard Worker
489*1c60b9acSAndroid Build Coastguard Worker return ret;
490*1c60b9acSAndroid Build Coastguard Worker }
491