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