xref: /third_party/libwebsockets/lib/misc/dir.c (revision d4afb5ce)
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
62static 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)
72static char csep = '/';
73#else
74static char csep = '\\';
75#endif
76
77static void
78lws_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
114int
115lws_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) && !defined(__QNX__)
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) || defined(__QNX__)
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		}
178skip:
179		free(namelist[i]);
180	}
181
182bail:
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
194static int
195lws_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
216int
217lws_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
237int
238lws_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
289struct 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
297static int
298lws_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
345int
346lws_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
405int
406lws_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