/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010 - 2019 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #if !defined(NO_GNU_SOURCE_THIS_TIME) #define NO_GNU_SOURCE_THIS_TIME #endif #if !defined(_DARWIN_C_SOURCE) #define _DARWIN_C_SOURCE #endif #include "private-lib-core.h" #include #include #include #if defined(WIN32) #include #define read _read #define open _open #define close _close #define write _write #define mkdir(x,y) _mkdir(x) #define rmdir _rmdir #define unlink _unlink #define HAVE_STRUCT_TIMESPEC #if defined(pid_t) #undef pid_t #endif #endif /* win32 */ #define COMBO_SIZEOF 512 #if !defined(LWS_PLAT_FREERTOS) #if defined(WIN32) #include "../../win32port/dirent/dirent-win32.h" #else #include #endif static int filter(const struct dirent *ent) { if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) return 0; return 1; } #if !defined(WIN32) static char csep = '/'; #else static char csep = '\\'; #endif static void lws_dir_via_stat(char *combo, size_t l, const char *path, struct lws_dir_entry *lde) { struct stat s; lws_strncpy(combo + l, path, COMBO_SIZEOF - l); lde->type = LDOT_UNKNOWN; if (!stat(combo, &s)) { switch (s.st_mode & S_IFMT) { case S_IFBLK: lde->type = LDOT_BLOCK; break; case S_IFCHR: lde->type = LDOT_CHAR; break; case S_IFDIR: lde->type = LDOT_DIR; break; case S_IFIFO: lde->type = LDOT_FIFO; break; #if !defined(WIN32) case S_IFLNK: lde->type = LDOT_LINK; break; #endif case S_IFREG: lde->type = LDOT_FILE; break; default: break; } } } int lws_dir(const char *dirpath, void *user, lws_dir_callback_function cb) { struct lws_dir_entry lde; struct dirent **namelist; int n, i, ret = 1; char combo[COMBO_SIZEOF]; size_t l; l = (size_t)(ssize_t)lws_snprintf(combo, COMBO_SIZEOF - 2, "%s", dirpath); combo[l++] = csep; combo[l] = '\0'; n = scandir((char *)dirpath, &namelist, filter, alphasort); if (n < 0) { lwsl_err("Scandir on '%s' failed, errno %d\n", dirpath, LWS_ERRNO); return 1; } for (i = 0; i < n; i++) { #if !defined(__sun) && !defined(__QNX__) unsigned int type = namelist[i]->d_type; #endif if (strchr(namelist[i]->d_name, '~')) goto skip; lde.name = namelist[i]->d_name; /* * some filesystems don't report this (ZFS) and tell that * files are LDOT_UNKNOWN */ #if defined(__sun) || defined(__QNX__) lws_dir_via_stat(combo, l, namelist[i]->d_name, &lde); #else /* * XFS on Linux doesn't fill in d_type at all, always zero. */ if (DT_BLK != DT_UNKNOWN && type == DT_BLK) lde.type = LDOT_BLOCK; else if (DT_CHR != DT_UNKNOWN && type == DT_CHR) lde.type = LDOT_CHAR; else if (DT_DIR != DT_UNKNOWN && type == DT_DIR) lde.type = LDOT_DIR; else if (DT_FIFO != DT_UNKNOWN && type == DT_FIFO) lde.type = LDOT_FIFO; else if (DT_LNK != DT_UNKNOWN && type == DT_LNK) lde.type = LDOT_LINK; else if (DT_REG != DT_UNKNOWN && type == DT_REG) lde.type = LDOT_FILE; else if (DT_SOCK != DT_UNKNOWN && type == DT_SOCK) lde.type = LDOTT_SOCKET; else { lde.type = LDOT_UNKNOWN; lws_dir_via_stat(combo, l, namelist[i]->d_name, &lde); } #endif if (cb(dirpath, user, &lde)) { while (i < n) free(namelist[i++]); ret = 0; /* told to stop by cb */ goto bail; } skip: free(namelist[i]); } bail: free(namelist); return ret; } /* * Check filename against one globby filter * * We can support things like "*.rpm" */ static int lws_dir_glob_check(const char *nm, const char *filt) { while (*nm) { if (*filt == '*') { if (!strcmp(nm, filt + 1)) return 1; } else { if (*nm != *filt) return 0; filt++; } nm++; } return 0; } /* * We get passed a single filter string, like "*.txt" or "mydir/\*.rpm" or so. */ int lws_dir_glob_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) { lws_dir_glob_t *filter = (lws_dir_glob_t*)user; char path[384]; if (!strcmp(lde->name, ".") || !strcmp(lde->name, "..")) return 0; if (lde->type == LDOT_DIR) return 0; if (lws_dir_glob_check(lde->name, filter->filter)) { lws_snprintf(path, sizeof(path), "%s%c%s", dirpath, csep, lde->name); filter->cb(filter->user, path); } return 0; } int lws_dir_rm_rf_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) { char path[384]; if (!strcmp(lde->name, ".") || !strcmp(lde->name, "..")) return 0; lws_snprintf(path, sizeof(path), "%s%c%s", dirpath, csep, lde->name); if (lde->type == LDOT_DIR) { #if !defined(WIN32) && !defined(_WIN32) && !defined(__COVERITY__) char dummy[8]; /* * hm... eg, recursive dir symlinks can show up a LDOT_DIR * here. If it's a symlink, don't recurse into it. * * Notice we immediately discard dummy without looking in it. * There is no way to get into trouble from its lack of NUL * termination in dummy[]. We just wanted to know if it was * a symlink at all. * * Hide this from Coverity since it flags any use of readlink() * even if safe. */ if (readlink(path, dummy, sizeof(dummy)) < 0) #endif lws_dir(path, NULL, lws_dir_rm_rf_cb); if (rmdir(path)) lwsl_warn("%s: rmdir %s failed %d\n", __func__, path, errno); } else { if (unlink(path)) { #if defined(WIN32) SetFileAttributesA(path, FILE_ATTRIBUTE_NORMAL); if (unlink(path)) #else if (rmdir(path)) #endif lwsl_warn("%s: unlink %s failed %d (type %d)\n", __func__, path, errno, lde->type); } } return 0; } #endif #if defined(LWS_WITH_PLUGINS_API) struct lws_plugin * lws_plugin_alloc(struct lws_plugin **pplugin) { struct lws_plugin *pin = lws_malloc(sizeof(*pin), __func__); if (!pin) return NULL; pin->list = *pplugin; *pplugin = pin; return pin; } #if defined(LWS_BUILTIN_PLUGIN_NAMES) extern lws_plugin_protocol_t lws_sshd_demo, lws_ssh_base; lws_plugin_protocol_t *builtin_pcols[] = { &lws_sshd_demo, &lws_ssh_base }; int lws_plugins_handle_builtin(struct lws_plugin **pplugin, each_plugin_cb_t each, void *each_user) { size_t n = 0; while (n < LWS_ARRAY_SIZE(builtin_pcols)) { struct lws_plugin *pin = lws_plugin_alloc(pplugin); if (!pin) return 1; pin->u.l = NULL; pin->hdr = &builtin_pcols[n]->hdr; if (each) each(pin, each_user); n++; } return 0; } #endif struct lws_plugins_args { struct lws_plugin **pplugin; const char *_class; const char *filter; each_plugin_cb_t each; void *each_user; }; static int lws_plugins_dir_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) { struct lws_plugins_args *pa = (struct lws_plugins_args *)user; char path[256], base[64], *q = base; const lws_plugin_header_t *pl; const char *p; if (strlen(lde->name) < 7) return 0; /* keep going */ /* * The actual plugin names for protocol plugins look like * "libprotocol_lws_ssh_base.so" and for event libs * "libwebsockets-evlib_ev.so"... to recover the base name of * "lws_ssh_base" and "evlib_ev" we strip from the left to after the * first _ or -, and then truncate at the first . */ p = lde->name; while (*p && *p != '_' && *p != '-') p++; if (!*p) return 0; p++; while (*p && *p != '.' && lws_ptr_diff(q, base) < (int)sizeof(base) - 1) *q++ = *p++; *q = '\0'; /* if he's given a filter, only match if base matches it */ if (pa->filter && strcmp(base, pa->filter)) return 0; /* keep going */ lws_snprintf(path, sizeof(path) - 1, "%s/%s", dirpath, lde->name); pl = lws_plat_dlopen(pa->pplugin, path, base, pa->_class, pa->each, pa->each_user); /* * If we were looking for a specific plugin, finding it should make * us stop looking (eg, to account for directory precedence of the * same plugin). If scanning for plugins in a dir, we always keep * going. */ return pa->filter && pl; } 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) { struct lws_plugins_args pa; char *ld_env; int ret = 1; pa.pplugin = pplugin; pa._class = _class; pa.each = each; pa.each_user = each_user; pa.filter = filter; /* * Check LD_LIBRARY_PATH override path first if present */ ld_env = getenv("LD_LIBRARY_PATH"); if (ld_env) { char temp[128]; struct lws_tokenize ts; memset(&ts, 0, sizeof(ts)); ts.start = ld_env; ts.len = strlen(ld_env); ts.flags = LWS_TOKENIZE_F_SLASH_NONTERM | LWS_TOKENIZE_F_DOT_NONTERM | LWS_TOKENIZE_F_MINUS_NONTERM | LWS_TOKENIZE_F_NO_INTEGERS | LWS_TOKENIZE_F_NO_FLOATS; do { ts.e = (int8_t)lws_tokenize(&ts); if (ts.e != LWS_TOKZE_TOKEN) continue; lws_strnncpy(temp, ts.token, ts.token_len, sizeof(temp)); lwsl_info("%s: trying %s\n", __func__, temp); if (!lws_dir(temp, &pa, lws_plugins_dir_cb)) ret = 0; } while (ts.e > 0); } while (d && *d) { lwsl_info("%s: trying %s\n", __func__, *d); if (!lws_dir(*d, &pa, lws_plugins_dir_cb)) ret = 0; d++; } return ret; } int lws_plugins_destroy(struct lws_plugin **pplugin, each_plugin_cb_t each, void *each_user) { struct lws_plugin *p = *pplugin, *p1; while (p) { if (each) each(p, each_user); if (p->u.l) lws_plat_destroy_dl(p); p1 = p->list; p->list = NULL; lws_free(p); p = p1; } *pplugin = NULL; return 0; } #endif