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 #if !defined(NO_GNU_SOURCE_THIS_TIME)
26 #define NO_GNU_SOURCE_THIS_TIME
27 #endif
28 #if !defined(_DARWIN_C_SOURCE)
29 #define _DARWIN_C_SOURCE
30 #endif
31
32 #include "private-lib-core.h"
33 #include <string.h>
34 #include <stdio.h>
35
36 #include <sys/stat.h>
37 #if defined(WIN32)
38 #include <direct.h>
39 #define read _read
40 #define open _open
41 #define close _close
42 #define write _write
43 #define mkdir(x,y) _mkdir(x)
44 #define rmdir _rmdir
45 #define unlink _unlink
46 #define HAVE_STRUCT_TIMESPEC
47 #if defined(pid_t)
48 #undef pid_t
49 #endif
50 #endif /* win32 */
51
52 #define COMBO_SIZEOF 512
53
54 #if !defined(LWS_PLAT_FREERTOS)
55
56 #if defined(WIN32)
57 #include "../../win32port/dirent/dirent-win32.h"
58 #else
59 #include <dirent.h>
60 #endif
61
filter(const struct dirent * ent)62 static int filter(const struct dirent *ent)
63 {
64 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
65 return 0;
66
67 return 1;
68 }
69
70
71 #if !defined(WIN32)
72 static char csep = '/';
73 #else
74 static char csep = '\\';
75 #endif
76
77 static void
lws_dir_via_stat(char * combo,size_t l,const char * path,struct lws_dir_entry * lde)78 lws_dir_via_stat(char *combo, size_t l, const char *path, struct lws_dir_entry *lde)
79 {
80 struct stat s;
81
82 lws_strncpy(combo + l, path, COMBO_SIZEOF - l);
83
84 lde->type = LDOT_UNKNOWN;
85
86 if (!stat(combo, &s)) {
87 switch (s.st_mode & S_IFMT) {
88 case S_IFBLK:
89 lde->type = LDOT_BLOCK;
90 break;
91 case S_IFCHR:
92 lde->type = LDOT_CHAR;
93 break;
94 case S_IFDIR:
95 lde->type = LDOT_DIR;
96 break;
97 case S_IFIFO:
98 lde->type = LDOT_FIFO;
99 break;
100 #if !defined(WIN32)
101 case S_IFLNK:
102 lde->type = LDOT_LINK;
103 break;
104 #endif
105 case S_IFREG:
106 lde->type = LDOT_FILE;
107 break;
108 default:
109 break;
110 }
111 }
112 }
113
114 int
lws_dir(const char * dirpath,void * user,lws_dir_callback_function cb)115 lws_dir(const char *dirpath, void *user, lws_dir_callback_function cb)
116 {
117 struct lws_dir_entry lde;
118 struct dirent **namelist;
119 int n, i, ret = 1;
120 char combo[COMBO_SIZEOF];
121 size_t l;
122
123 l = (size_t)(ssize_t)lws_snprintf(combo, COMBO_SIZEOF - 2, "%s", dirpath);
124 combo[l++] = csep;
125 combo[l] = '\0';
126
127 n = scandir((char *)dirpath, &namelist, filter, alphasort);
128 if (n < 0) {
129 lwsl_err("Scandir on '%s' failed, errno %d\n", dirpath, LWS_ERRNO);
130 return 1;
131 }
132
133 for (i = 0; i < n; i++) {
134 #if !defined(__sun)
135 unsigned int type = namelist[i]->d_type;
136 #endif
137 if (strchr(namelist[i]->d_name, '~'))
138 goto skip;
139 lde.name = namelist[i]->d_name;
140
141 /*
142 * some filesystems don't report this (ZFS) and tell that
143 * files are LDOT_UNKNOWN
144 */
145
146 #if defined(__sun)
147 lws_dir_via_stat(combo, l, namelist[i]->d_name, &lde);
148 #else
149 /*
150 * XFS on Linux doesn't fill in d_type at all, always zero.
151 */
152
153 if (DT_BLK != DT_UNKNOWN && type == DT_BLK)
154 lde.type = LDOT_BLOCK;
155 else if (DT_CHR != DT_UNKNOWN && type == DT_CHR)
156 lde.type = LDOT_CHAR;
157 else if (DT_DIR != DT_UNKNOWN && type == DT_DIR)
158 lde.type = LDOT_DIR;
159 else if (DT_FIFO != DT_UNKNOWN && type == DT_FIFO)
160 lde.type = LDOT_FIFO;
161 else if (DT_LNK != DT_UNKNOWN && type == DT_LNK)
162 lde.type = LDOT_LINK;
163 else if (DT_REG != DT_UNKNOWN && type == DT_REG)
164 lde.type = LDOT_FILE;
165 else if (DT_SOCK != DT_UNKNOWN && type == DT_SOCK)
166 lde.type = LDOTT_SOCKET;
167 else {
168 lde.type = LDOT_UNKNOWN;
169 lws_dir_via_stat(combo, l, namelist[i]->d_name, &lde);
170 }
171 #endif
172 if (cb(dirpath, user, &lde)) {
173 while (i < n)
174 free(namelist[i++]);
175 ret = 0; /* told to stop by cb */
176 goto bail;
177 }
178 skip:
179 free(namelist[i]);
180 }
181
182 bail:
183 free(namelist);
184
185 return ret;
186 }
187
188 /*
189 * Check filename against one globby filter
190 *
191 * We can support things like "*.rpm"
192 */
193
194 static int
lws_dir_glob_check(const char * nm,const char * filt)195 lws_dir_glob_check(const char *nm, const char *filt)
196 {
197 while (*nm) {
198 if (*filt == '*') {
199 if (!strcmp(nm, filt + 1))
200 return 1;
201 } else {
202 if (*nm != *filt)
203 return 0;
204 filt++;
205 }
206 nm++;
207 }
208
209 return 0;
210 }
211
212 /*
213 * We get passed a single filter string, like "*.txt" or "mydir/\*.rpm" or so.
214 */
215
216 int
lws_dir_glob_cb(const char * dirpath,void * user,struct lws_dir_entry * lde)217 lws_dir_glob_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
218 {
219 lws_dir_glob_t *filter = (lws_dir_glob_t*)user;
220 char path[384];
221
222 if (!strcmp(lde->name, ".") || !strcmp(lde->name, ".."))
223 return 0;
224
225 if (lde->type == LDOT_DIR)
226 return 0;
227
228 if (lws_dir_glob_check(lde->name, filter->filter)) {
229 lws_snprintf(path, sizeof(path), "%s%c%s", dirpath, csep,
230 lde->name);
231 filter->cb(filter->user, path);
232 }
233
234 return 0;
235 }
236
237 int
lws_dir_rm_rf_cb(const char * dirpath,void * user,struct lws_dir_entry * lde)238 lws_dir_rm_rf_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
239 {
240 char path[384];
241
242 if (!strcmp(lde->name, ".") || !strcmp(lde->name, ".."))
243 return 0;
244
245 lws_snprintf(path, sizeof(path), "%s%c%s", dirpath, csep, lde->name);
246
247 if (lde->type == LDOT_DIR) {
248 #if !defined(WIN32) && !defined(_WIN32) && !defined(__COVERITY__)
249 char dummy[8];
250 /*
251 * hm... eg, recursive dir symlinks can show up a LDOT_DIR
252 * here. If it's a symlink, don't recurse into it.
253 *
254 * Notice we immediately discard dummy without looking in it.
255 * There is no way to get into trouble from its lack of NUL
256 * termination in dummy[]. We just wanted to know if it was
257 * a symlink at all.
258 *
259 * Hide this from Coverity since it flags any use of readlink()
260 * even if safe.
261 */
262 if (readlink(path, dummy, sizeof(dummy)) < 0)
263 #endif
264 lws_dir(path, NULL, lws_dir_rm_rf_cb);
265
266 if (rmdir(path))
267 lwsl_warn("%s: rmdir %s failed %d\n", __func__, path, errno);
268 } else {
269 if (unlink(path)) {
270 #if defined(WIN32)
271 SetFileAttributesA(path, FILE_ATTRIBUTE_NORMAL);
272 if (unlink(path))
273 #else
274 if (rmdir(path))
275 #endif
276 lwsl_warn("%s: unlink %s failed %d (type %d)\n",
277 __func__, path, errno, lde->type);
278 }
279 }
280
281 return 0;
282 }
283
284
285 #endif
286
287 #if defined(LWS_WITH_PLUGINS_API)
288
289 struct lws_plugins_args {
290 struct lws_plugin **pplugin;
291 const char *_class;
292 const char *filter;
293 each_plugin_cb_t each;
294 void *each_user;
295 };
296
297 static int
lws_plugins_dir_cb(const char * dirpath,void * user,struct lws_dir_entry * lde)298 lws_plugins_dir_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
299 {
300 struct lws_plugins_args *pa = (struct lws_plugins_args *)user;
301 char path[256], base[64], *q = base;
302 const lws_plugin_header_t *pl;
303 const char *p;
304
305 if (strlen(lde->name) < 7)
306 return 0; /* keep going */
307
308 /*
309 * The actual plugin names for protocol plugins look like
310 * "libprotocol_lws_ssh_base.so" and for event libs
311 * "libwebsockets-evlib_ev.so"... to recover the base name of
312 * "lws_ssh_base" and "evlib_ev" we strip from the left to after the
313 * first _ or -, and then truncate at the first .
314 */
315
316 p = lde->name;
317 while (*p && *p != '_' && *p != '-')
318 p++;
319 if (!*p)
320 return 0;
321 p++;
322 while (*p && *p != '.' && lws_ptr_diff(q, base) < (int)sizeof(base) - 1)
323 *q++ = *p++;
324 *q = '\0';
325
326 /* if he's given a filter, only match if base matches it */
327 if (pa->filter && strcmp(base, pa->filter))
328 return 0; /* keep going */
329
330 lws_snprintf(path, sizeof(path) - 1, "%s/%s", dirpath, lde->name);
331
332 pl = lws_plat_dlopen(pa->pplugin, path, base, pa->_class,
333 pa->each, pa->each_user);
334
335 /*
336 * If we were looking for a specific plugin, finding it should make
337 * us stop looking (eg, to account for directory precedence of the
338 * same plugin). If scanning for plugins in a dir, we always keep
339 * going.
340 */
341
342 return pa->filter && pl;
343 }
344
345 int
lws_plugins_init(struct lws_plugin ** pplugin,const char * const * d,const char * _class,const char * filter,each_plugin_cb_t each,void * each_user)346 lws_plugins_init(struct lws_plugin **pplugin, const char * const *d,
347 const char *_class, const char *filter,
348 each_plugin_cb_t each, void *each_user)
349 {
350 struct lws_plugins_args pa;
351 char *ld_env;
352 int ret = 1;
353
354 pa.pplugin = pplugin;
355 pa._class = _class;
356 pa.each = each;
357 pa.each_user = each_user;
358 pa.filter = filter;
359
360 /*
361 * Check LD_LIBRARY_PATH override path first if present
362 */
363
364 ld_env = getenv("LD_LIBRARY_PATH");
365 if (ld_env) {
366 char temp[128];
367 struct lws_tokenize ts;
368
369 memset(&ts, 0, sizeof(ts));
370 ts.start = ld_env;
371 ts.len = strlen(ld_env);
372 ts.flags = LWS_TOKENIZE_F_SLASH_NONTERM |
373 LWS_TOKENIZE_F_DOT_NONTERM |
374 LWS_TOKENIZE_F_MINUS_NONTERM |
375 LWS_TOKENIZE_F_NO_INTEGERS |
376 LWS_TOKENIZE_F_NO_FLOATS;
377
378 do {
379 ts.e = (int8_t)lws_tokenize(&ts);
380 if (ts.e != LWS_TOKZE_TOKEN)
381 continue;
382
383 lws_strnncpy(temp, ts.token,
384 ts.token_len,
385 sizeof(temp));
386
387 lwsl_info("%s: trying %s\n", __func__, temp);
388 if (!lws_dir(temp, &pa, lws_plugins_dir_cb))
389 ret = 0;
390
391 } while (ts.e > 0);
392 }
393
394 while (d && *d) {
395 lwsl_info("%s: trying %s\n", __func__, *d);
396 if (!lws_dir(*d, &pa, lws_plugins_dir_cb))
397 ret = 0;
398
399 d++;
400 }
401
402 return ret;
403 }
404
405 int
lws_plugins_destroy(struct lws_plugin ** pplugin,each_plugin_cb_t each,void * each_user)406 lws_plugins_destroy(struct lws_plugin **pplugin, each_plugin_cb_t each,
407 void *each_user)
408 {
409 struct lws_plugin *p = *pplugin, *p1;
410
411 while (p) {
412 if (each)
413 each(p, each_user);
414 lws_plat_destroy_dl(p);
415 p1 = p->list;
416 p->list = NULL;
417 lws_free(p);
418 p = p1;
419 }
420
421 *pplugin = NULL;
422
423 return 0;
424 }
425 #endif
426