1 | /* |
2 | * libwebsockets - small server side websockets and web server implementation |
3 | * |
4 | * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | * of this software and associated documentation files (the "Software"), to |
8 | * deal in the Software without restriction, including without limitation the |
9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
10 | * sell copies of the Software, and to permit persons to whom the Software is |
11 | * furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
22 | * IN THE SOFTWARE. |
23 | */ |
24 | |
25 | /*! \defgroup diskcache LWS disk cache |
26 | * ## Disk cache API |
27 | * |
28 | * Lws provides helper apis useful if you need a disk cache containing hashed |
29 | * files and need to delete files from it on an LRU basis to keep it below some |
30 | * size limit. |
31 | * |
32 | * The API `lws_diskcache_prepare()` deals with creating the cache dir and |
33 | * 256 subdirs, which are used according to the first two chars of the hex |
34 | * hash of the cache file. |
35 | * |
36 | * `lws_diskcache_create()` and `lws_diskcache_destroy()` allocate and free |
37 | * an opaque struct that represents the disk cache. |
38 | * |
39 | * `lws_diskcache_trim()` should be called at eg, 1s intervals to perform the |
40 | * cache dir monitoring and LRU autodelete in the background lazily. It can |
41 | * be done in its own thread or on a timer... it monitors the directories in a |
42 | * stateful way that stats one or more file in the cache per call, and keeps |
43 | * a list of the oldest files as it goes. When it completes a scan, if the |
44 | * aggregate size is over the limit, it will delete oldest files first to try |
45 | * to keep it under the limit. |
46 | * |
47 | * The cache size monitoring is extremely efficient in time and memory even when |
48 | * the cache directory becomes huge. |
49 | * |
50 | * `lws_diskcache_query()` is used to determine if the file already exists in |
51 | * the cache, or if it must be created. If it must be created, then the file |
52 | * is opened using a temp name that must be converted to a findable name with |
53 | * `lws_diskcache_finalize_name()` when the generation of the file contents are |
54 | * complete. Aborted cached files that did not complete generation will be |
55 | * flushed by the LRU eventually. If the file already exists, it is 'touched' |
56 | * to make it new again and the fd returned. |
57 | * |
58 | */ |
59 | ///@{ |
60 | |
61 | struct lws_diskcache_scan; |
62 | |
63 | /** |
64 | * lws_diskcache_create() - creates an opaque struct representing the disk cache |
65 | * |
66 | * \param cache_dir_base: The cache dir path, eg `/var/cache/mycache` |
67 | * \param cache_size_limit: maximum size on disk the cache is allowed to use |
68 | * |
69 | * This returns an opaque `struct lws_diskcache_scan *` which represents the |
70 | * disk cache, the trim scanning state and so on. You should use |
71 | * `lws_diskcache_destroy()` to free it to destroy it. |
72 | */ |
73 | LWS_VISIBLE LWS_EXTERN struct lws_diskcache_scan * |
74 | lws_diskcache_create(const char *cache_dir_base, uint64_t cache_size_limit); |
75 | |
76 | /** |
77 | * lws_diskcache_destroy() - destroys the pointer returned by ...create() |
78 | * |
79 | * \param lds: pointer to the pointer returned by lws_diskcache_create() |
80 | * |
81 | * Frees *lds and any allocations it did, and then sets *lds to NULL and |
82 | * returns. |
83 | */ |
84 | LWS_VISIBLE LWS_EXTERN void |
85 | lws_diskcache_destroy(struct lws_diskcache_scan **lds); |
86 | |
87 | /** |
88 | * lws_diskcache_prepare() - ensures the cache dir structure exists on disk |
89 | * |
90 | * \param cache_base_dir: The cache dir path, eg `/var/cache/mycache` |
91 | * \param mode: octal dir mode to enforce, like 0700 |
92 | * \param uid: uid the cache dir should belong to |
93 | * |
94 | * This should be called while your app is still privileged. It will create |
95 | * the cache directory structure on disk as necessary, enforce the given access |
96 | * mode on it and set the given uid as the owner. It won't make any trouble |
97 | * if the cache already exists. |
98 | * |
99 | * Typically the mode is 0700 and the owner is the user that your application |
100 | * will transition to use when it drops root privileges. |
101 | */ |
102 | LWS_VISIBLE LWS_EXTERN int |
103 | lws_diskcache_prepare(const char *cache_base_dir, int mode, uid_t uid); |
104 | |
105 | #define LWS_DISKCACHE_QUERY_NO_CACHE 0 |
106 | #define LWS_DISKCACHE_QUERY_EXISTS 1 |
107 | #define LWS_DISKCACHE_QUERY_CREATING 2 |
108 | #define LWS_DISKCACHE_QUERY_ONGOING 3 /* something else is creating it */ |
109 | |
110 | /** |
111 | * lws_diskcache_query() - ensures the cache dir structure exists on disk |
112 | * |
113 | * \param lds: The opaque struct representing the disk cache |
114 | * \param is_bot: nonzero means the request is from a bot. Don't create new cache contents if so. |
115 | * \param hash_hex: hex string representation of the cache object hash |
116 | * \param _fd: pointer to the fd to be set |
117 | * \param cache: destination string to take the cache filepath |
118 | * \param cache_len: length of the buffer at `cache` |
119 | * \param extant_cache_len: pointer to a size_t to take any extant cached file size |
120 | * |
121 | * This function is called when you want to find if the hashed name already |
122 | * exists in the cache. The possibilities for the return value are |
123 | * |
124 | * - LWS_DISKCACHE_QUERY_NO_CACHE: It's not in the cache and you can't create |
125 | * it in the cache for whatever reason. |
126 | * - LWS_DISKCACHE_QUERY_EXISTS: It exists in the cache. It's open RDONLY and |
127 | * *_fd has been set to the file descriptor. *extant_cache_len has been set |
128 | * to the size of the cached file in bytes. cache has been set to the |
129 | * full filepath of the cached file. Closing _fd is your responsibility. |
130 | * - LWS_DISKCACHE_QUERY_CREATING: It didn't exist, but a temp file has been |
131 | * created in the cache and *_fd set to a file descriptor opened on it RDWR. |
132 | * You should create the contents, and call `lws_diskcache_finalize_name()` |
133 | * when it is done. Closing _fd is your responsibility. |
134 | * - LWS_DISKCACHE_QUERY_ONGOING: not returned by this api, but you may find it |
135 | * desirable to make a wrapper function which can handle another asynchronous |
136 | * process that is already creating the cached file. This can be used to |
137 | * indicate that situation externally... how to determine the same thing is |
138 | * already being generated is out of scope of this api. |
139 | */ |
140 | LWS_VISIBLE LWS_EXTERN int |
141 | lws_diskcache_query(struct lws_diskcache_scan *lds, int is_bot, |
142 | const char *hash_hex, int *_fd, char *cache, int cache_len, |
143 | size_t *extant_cache_len); |
144 | |
145 | /** |
146 | * lws_diskcache_query() - ensures the cache dir structure exists on disk |
147 | * |
148 | * \param cache: The cache file temp name returned with LWS_DISKCACHE_QUERY_CREATING |
149 | * |
150 | * This renames the cache file you are creating to its final name. It should |
151 | * be called on the temp name returned by `lws_diskcache_query()` if it gave a |
152 | * LWS_DISKCACHE_QUERY_CREATING return, after you have filled the cache file and |
153 | * closed it. |
154 | */ |
155 | LWS_VISIBLE LWS_EXTERN int |
156 | lws_diskcache_finalize_name(char *cache); |
157 | |
158 | /** |
159 | * lws_diskcache_trim() - performs one or more file checks in the cache for size management |
160 | * |
161 | * \param lds: The opaque object representing the cache |
162 | * |
163 | * This should be called periodically to statefully walk the cache on disk |
164 | * collecting the oldest files. When it has visited every file, if the cache |
165 | * is oversize it will delete the oldest files until it's back under size again. |
166 | * |
167 | * Each time it's called, it will look at one or more dir in the cache. If |
168 | * called when the cache is oversize, it increases the amount of work done each |
169 | * call until it is reduced again. Typically it will take 256 calls before it |
170 | * deletes anything, so if called once per second, it will delete files once |
171 | * every 4 minutes. Each call is very inexpensive both in memory and time. |
172 | */ |
173 | LWS_VISIBLE LWS_EXTERN int |
174 | lws_diskcache_trim(struct lws_diskcache_scan *lds); |
175 | |
176 | |
177 | /** |
178 | * lws_diskcache_secs_to_idle() - see how long to idle before calling trim |
179 | * |
180 | * \param lds: The opaque object representing the cache |
181 | * |
182 | * If the cache is undersize, there's no need to monitor it immediately. This |
183 | * suggests how long to "sleep" before calling `lws_diskcache_trim()` again. |
184 | */ |
185 | LWS_VISIBLE LWS_EXTERN int |
186 | lws_diskcache_secs_to_idle(struct lws_diskcache_scan *lds); |
187 | ///@} |
188 | |