Imported Upstream version 3.2
[platform/upstream/libwebsockets.git] / lib / core / libwebsockets.c
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation:
9  *  version 2.1 of the License.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA  02110-1301  USA
20  */
21
22 #include "core/private.h"
23
24 #ifdef LWS_HAVE_SYS_TYPES_H
25 #include <sys/types.h>
26 #endif
27
28 signed char char_to_hex(const char c)
29 {
30         if (c >= '0' && c <= '9')
31                 return c - '0';
32
33         if (c >= 'a' && c <= 'f')
34                 return c - 'a' + 10;
35
36         if (c >= 'A' && c <= 'F')
37                 return c - 'A' + 10;
38
39         return -1;
40 }
41
42 int
43 lws_hex_to_byte_array(const char *h, uint8_t *dest, int max)
44 {
45         uint8_t *odest = dest;
46
47         while (max-- && *h) {
48                 int t = char_to_hex(*h++), t1;
49
50                 if (!*h || t < 0)
51                         return -1;
52
53                 t1 = char_to_hex(*h++);
54                 if (t1 < 0)
55                         return -1;
56
57                 *dest++ = (t << 4) | t1;
58         }
59
60         if (max < 0)
61                 return -1;
62
63         return dest - odest;
64 }
65
66
67 #if !defined(LWS_PLAT_OPTEE)
68
69 #if !defined(LWS_AMAZON_RTOS)
70 int lws_open(const char *__file, int __oflag, ...)
71 {
72         va_list ap;
73         int n;
74
75         va_start(ap, __oflag);
76         if (((__oflag & O_CREAT) == O_CREAT)
77 #if defined(O_TMPFILE)
78                 || ((__oflag & O_TMPFILE) == O_TMPFILE)
79 #endif
80         )
81                 /* last arg is really a mode_t.  But windows... */
82                 n = open(__file, __oflag, va_arg(ap, uint32_t));
83         else
84                 n = open(__file, __oflag);
85         va_end(ap);
86
87         if (n != -1 && lws_plat_apply_FD_CLOEXEC(n)) {
88                 close(n);
89
90                 return -1;
91         }
92
93         return n;
94 }
95 #endif
96 #endif
97
98 int
99 lws_pthread_self_to_tsi(struct lws_context *context)
100 {
101 #if LWS_MAX_SMP > 1
102         pthread_t ps = pthread_self();
103         struct lws_context_per_thread *pt = &context->pt[0];
104         int n;
105
106         for (n = 0; n < context->count_threads; n++) {
107                 if (pthread_equal(ps, pt->self))
108                         return n;
109                 pt++;
110         }
111
112         return -1;
113 #else
114         return 0;
115 #endif
116 }
117
118 LWS_EXTERN void *
119 lws_context_user(struct lws_context *context)
120 {
121         return context->user_space;
122 }
123
124 LWS_VISIBLE void
125 lws_explicit_bzero(void *p, size_t len)
126 {
127         volatile uint8_t *vp = p;
128
129         while (len--)
130                 *vp++ = 0;
131 }
132
133 #if !(defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK))
134
135 /**
136  * lws_now_secs() - seconds since 1970-1-1
137  *
138  */
139 LWS_VISIBLE LWS_EXTERN unsigned long
140 lws_now_secs(void)
141 {
142         struct timeval tv;
143
144         gettimeofday(&tv, NULL);
145
146         return tv.tv_sec;
147 }
148
149 #endif
150 LWS_VISIBLE extern const char *
151 lws_canonical_hostname(struct lws_context *context)
152 {
153         return (const char *)context->canonical_hostname;
154 }
155
156 #if defined(LWS_WITH_SOCKS5)
157 LWS_VISIBLE int
158 lws_set_socks(struct lws_vhost *vhost, const char *socks)
159 {
160         char *p_at, *p_colon;
161         char user[96];
162         char password[96];
163
164         if (!socks)
165                 return -1;
166
167         vhost->socks_user[0] = '\0';
168         vhost->socks_password[0] = '\0';
169
170         p_at = strrchr(socks, '@');
171         if (p_at) { /* auth is around */
172                 if ((unsigned int)(p_at - socks) > (sizeof(user)
173                         + sizeof(password) - 2)) {
174                         lwsl_err("Socks auth too long\n");
175                         goto bail;
176                 }
177
178                 p_colon = strchr(socks, ':');
179                 if (p_colon) {
180                         if ((unsigned int)(p_colon - socks) > (sizeof(user)
181                                 - 1) ) {
182                                 lwsl_err("Socks user too long\n");
183                                 goto bail;
184                         }
185                         if ((unsigned int)(p_at - p_colon) > (sizeof(password)
186                                 - 1) ) {
187                                 lwsl_err("Socks password too long\n");
188                                 goto bail;
189                         }
190
191                         lws_strncpy(vhost->socks_user, socks, p_colon - socks + 1);
192                         lws_strncpy(vhost->socks_password, p_colon + 1,
193                                 p_at - (p_colon + 1) + 1);
194                 }
195
196                 lwsl_info(" Socks auth, user: %s, password: %s\n",
197                         vhost->socks_user, vhost->socks_password );
198
199                 socks = p_at + 1;
200         }
201
202         lws_strncpy(vhost->socks_proxy_address, socks,
203                     sizeof(vhost->socks_proxy_address));
204
205         p_colon = strchr(vhost->socks_proxy_address, ':');
206         if (!p_colon && !vhost->socks_proxy_port) {
207                 lwsl_err("socks_proxy needs to be address:port\n");
208                 return -1;
209         } else {
210                 if (p_colon) {
211                         *p_colon = '\0';
212                         vhost->socks_proxy_port = atoi(p_colon + 1);
213                 }
214         }
215
216         lwsl_info(" Socks %s:%u\n", vhost->socks_proxy_address,
217                         vhost->socks_proxy_port);
218
219         return 0;
220
221 bail:
222         return -1;
223 }
224 #endif
225
226
227
228 LWS_VISIBLE LWS_EXTERN int
229 lws_get_count_threads(struct lws_context *context)
230 {
231         return context->count_threads;
232 }
233
234 static const unsigned char e0f4[] = {
235         0xa0 | ((2 - 1) << 2) | 1, /* e0 */
236         0x80 | ((4 - 1) << 2) | 1, /* e1 */
237         0x80 | ((4 - 1) << 2) | 1, /* e2 */
238         0x80 | ((4 - 1) << 2) | 1, /* e3 */
239         0x80 | ((4 - 1) << 2) | 1, /* e4 */
240         0x80 | ((4 - 1) << 2) | 1, /* e5 */
241         0x80 | ((4 - 1) << 2) | 1, /* e6 */
242         0x80 | ((4 - 1) << 2) | 1, /* e7 */
243         0x80 | ((4 - 1) << 2) | 1, /* e8 */
244         0x80 | ((4 - 1) << 2) | 1, /* e9 */
245         0x80 | ((4 - 1) << 2) | 1, /* ea */
246         0x80 | ((4 - 1) << 2) | 1, /* eb */
247         0x80 | ((4 - 1) << 2) | 1, /* ec */
248         0x80 | ((2 - 1) << 2) | 1, /* ed */
249         0x80 | ((4 - 1) << 2) | 1, /* ee */
250         0x80 | ((4 - 1) << 2) | 1, /* ef */
251         0x90 | ((3 - 1) << 2) | 2, /* f0 */
252         0x80 | ((4 - 1) << 2) | 2, /* f1 */
253         0x80 | ((4 - 1) << 2) | 2, /* f2 */
254         0x80 | ((4 - 1) << 2) | 2, /* f3 */
255         0x80 | ((1 - 1) << 2) | 2, /* f4 */
256
257         0,                         /* s0 */
258         0x80 | ((4 - 1) << 2) | 0, /* s2 */
259         0x80 | ((4 - 1) << 2) | 1, /* s3 */
260 };
261
262 LWS_EXTERN int
263 lws_check_byte_utf8(unsigned char state, unsigned char c)
264 {
265         unsigned char s = state;
266
267         if (!s) {
268                 if (c >= 0x80) {
269                         if (c < 0xc2 || c > 0xf4)
270                                 return -1;
271                         if (c < 0xe0)
272                                 return 0x80 | ((4 - 1) << 2);
273                         else
274                                 return e0f4[c - 0xe0];
275                 }
276
277                 return s;
278         }
279         if (c < (s & 0xf0) || c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30))
280                 return -1;
281
282         return e0f4[21 + (s & 3)];
283 }
284
285 LWS_EXTERN int
286 lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len)
287 {
288         unsigned char s = *state;
289
290         while (len--) {
291                 unsigned char c = *buf++;
292
293                 if (!s) {
294                         if (c >= 0x80) {
295                                 if (c < 0xc2 || c > 0xf4)
296                                         return 1;
297                                 if (c < 0xe0)
298                                         s = 0x80 | ((4 - 1) << 2);
299                                 else
300                                         s = e0f4[c - 0xe0];
301                         }
302                 } else {
303                         if (c < (s & 0xf0) ||
304                             c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30))
305                                 return 1;
306                         s = e0f4[21 + (s & 3)];
307                 }
308         }
309
310         *state = s;
311
312         return 0;
313 }
314
315
316 char *
317 lws_strdup(const char *s)
318 {
319         char *d = lws_malloc(strlen(s) + 1, "strdup");
320
321         if (d)
322                 strcpy(d, s);
323
324         return d;
325 }
326
327 static const char *hex = "0123456789ABCDEF";
328
329 LWS_VISIBLE LWS_EXTERN const char *
330 lws_sql_purify(char *escaped, const char *string, int len)
331 {
332         const char *p = string;
333         char *q = escaped;
334
335         while (*p && len-- > 2) {
336                 if (*p == '\'') {
337                         *q++ = '\'';
338                         *q++ = '\'';
339                         len --;
340                         p++;
341                 } else
342                         *q++ = *p++;
343         }
344         *q = '\0';
345
346         return escaped;
347 }
348
349 LWS_VISIBLE LWS_EXTERN const char *
350 lws_json_purify(char *escaped, const char *string, int len)
351 {
352         const char *p = string;
353         char *q = escaped;
354
355         if (!p) {
356                 escaped[0] = '\0';
357                 return escaped;
358         }
359
360         while (*p && len-- > 6) {
361                 if (*p == '\t') {
362                         p++;
363                         *q++ = '\\';
364                         *q++ = 't';
365                         continue;
366                 }
367
368                 if (*p == '\n') {
369                         p++;
370                         *q++ = '\\';
371                         *q++ = 'n';
372                         continue;
373                 }
374
375                 if (*p == '\r') {
376                         p++;
377                         *q++ = '\\';
378                         *q++ = 'r';
379                         continue;
380                 }
381
382                 if (*p == '\"' || *p == '\\' || *p < 0x20) {
383                         *q++ = '\\';
384                         *q++ = 'u';
385                         *q++ = '0';
386                         *q++ = '0';
387                         *q++ = hex[((*p) >> 4) & 15];
388                         *q++ = hex[(*p) & 15];
389                         len -= 5;
390                         p++;
391                 } else
392                         *q++ = *p++;
393         }
394         *q = '\0';
395
396         return escaped;
397 }
398
399 LWS_VISIBLE LWS_EXTERN void
400 lws_filename_purify_inplace(char *filename)
401 {
402         while (*filename) {
403
404                 if (*filename == '.' && filename[1] == '.') {
405                         *filename = '_';
406                         filename[1] = '_';
407                 }
408
409                 if (*filename == ':' ||
410                     *filename == '\\' ||
411                     *filename == '$' ||
412                     *filename == '%')
413                         *filename = '_';
414
415                 filename++;
416         }
417 }
418
419 LWS_VISIBLE LWS_EXTERN const char *
420 lws_urlencode(char *escaped, const char *string, int len)
421 {
422         const char *p = string;
423         char *q = escaped;
424
425         while (*p && len-- > 3) {
426                 if (*p == ' ') {
427                         *q++ = '+';
428                         p++;
429                         continue;
430                 }
431                 if ((*p >= '0' && *p <= '9') ||
432                     (*p >= 'A' && *p <= 'Z') ||
433                     (*p >= 'a' && *p <= 'z')) {
434                         *q++ = *p++;
435                         continue;
436                 }
437                 *q++ = '%';
438                 *q++ = hex[(*p >> 4) & 0xf];
439                 *q++ = hex[*p & 0xf];
440
441                 len -= 2;
442                 p++;
443         }
444         *q = '\0';
445
446         return escaped;
447 }
448
449 LWS_VISIBLE LWS_EXTERN int
450 lws_urldecode(char *string, const char *escaped, int len)
451 {
452         int state = 0, n;
453         char sum = 0;
454
455         while (*escaped && len) {
456                 switch (state) {
457                 case 0:
458                         if (*escaped == '%') {
459                                 state++;
460                                 escaped++;
461                                 continue;
462                         }
463                         if (*escaped == '+') {
464                                 escaped++;
465                                 *string++ = ' ';
466                                 len--;
467                                 continue;
468                         }
469                         *string++ = *escaped++;
470                         len--;
471                         break;
472                 case 1:
473                         n = char_to_hex(*escaped);
474                         if (n < 0)
475                                 return -1;
476                         escaped++;
477                         sum = n << 4;
478                         state++;
479                         break;
480
481                 case 2:
482                         n = char_to_hex(*escaped);
483                         if (n < 0)
484                                 return -1;
485                         escaped++;
486                         *string++ = sum | n;
487                         len--;
488                         state = 0;
489                         break;
490                 }
491
492         }
493         *string = '\0';
494
495         return 0;
496 }
497
498 LWS_VISIBLE LWS_EXTERN int
499 lws_finalize_startup(struct lws_context *context)
500 {
501         if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
502                 if (lws_plat_drop_app_privileges(context, 1))
503                         return 1;
504
505         return 0;
506 }
507
508 LWS_VISIBLE LWS_EXTERN void
509 lws_get_effective_uid_gid(struct lws_context *context, int *uid, int *gid)
510 {
511         *uid = context->uid;
512         *gid = context->gid;
513 }
514
515 int
516 lws_snprintf(char *str, size_t size, const char *format, ...)
517 {
518         va_list ap;
519         int n;
520
521         if (!size)
522                 return 0;
523
524         va_start(ap, format);
525         n = vsnprintf(str, size, format, ap);
526         va_end(ap);
527
528         if (n >= (int)size)
529                 return (int)size;
530
531         return n;
532 }
533
534 char *
535 lws_strncpy(char *dest, const char *src, size_t size)
536 {
537         strncpy(dest, src, size - 1);
538         dest[size - 1] = '\0';
539
540         return dest;
541 }
542
543 int
544 lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len)
545 {
546         const uint8_t *pa = a, *pb = b;
547         uint8_t sum = 0;
548
549         while (len--)
550                 sum |= (*pa++ ^ *pb++);
551
552         return sum;
553 }
554
555
556 typedef enum {
557         LWS_TOKZS_LEADING_WHITESPACE,
558         LWS_TOKZS_QUOTED_STRING,
559         LWS_TOKZS_TOKEN,
560         LWS_TOKZS_TOKEN_POST_TERMINAL
561 } lws_tokenize_state;
562
563 #if defined(LWS_AMAZON_RTOS)
564 lws_tokenize_elem
565 #else
566 int
567 #endif
568 lws_tokenize(struct lws_tokenize *ts)
569 {
570         const char *rfc7230_delims = "(),/:;<=>?@[\\]{}";
571         lws_tokenize_state state = LWS_TOKZS_LEADING_WHITESPACE;
572         char c, flo = 0, d_minus = '-', d_dot = '.', s_minus = '\0',
573              s_dot = '\0';
574         signed char num = ts->flags & LWS_TOKENIZE_F_NO_INTEGERS ? 0 : -1;
575         int utf8 = 0;
576
577         /* for speed, compute the effect of the flags outside the loop */
578
579         if (ts->flags & LWS_TOKENIZE_F_MINUS_NONTERM) {
580                 d_minus = '\0';
581                 s_minus = '-';
582         }
583         if (ts->flags & LWS_TOKENIZE_F_DOT_NONTERM) {
584                 d_dot = '\0';
585                 s_dot = '.';
586         }
587
588         ts->token = NULL;
589         ts->token_len = 0;
590
591         while (ts->len) {
592                 c = *ts->start++;
593                 ts->len--;
594
595                 utf8 = lws_check_byte_utf8((unsigned char)utf8, c);
596                 if (utf8 < 0)
597                         return LWS_TOKZE_ERR_BROKEN_UTF8;
598
599                 if (!c)
600                         break;
601
602                 /* whitespace */
603
604                 if (c == ' ' || c == '\t' || c == '\n' || c == '\r' ||
605                     c == '\f') {
606                         switch (state) {
607                         case LWS_TOKZS_LEADING_WHITESPACE:
608                         case LWS_TOKZS_TOKEN_POST_TERMINAL:
609                                 continue;
610                         case LWS_TOKZS_QUOTED_STRING:
611                                 ts->token_len++;
612                                 continue;
613                         case LWS_TOKZS_TOKEN:
614                                 /* we want to scan forward to look for = */
615
616                                 state = LWS_TOKZS_TOKEN_POST_TERMINAL;
617                                 continue;
618                         }
619                 }
620
621                 /* quoted string */
622
623                 if (c == '\"') {
624                         if (state == LWS_TOKZS_QUOTED_STRING)
625                                 return LWS_TOKZE_QUOTED_STRING;
626
627                         /* starting a quoted string */
628
629                         if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) {
630                                 if (ts->delim == LWSTZ_DT_NEED_DELIM)
631                                         return LWS_TOKZE_ERR_COMMA_LIST;
632                                 ts->delim = LWSTZ_DT_NEED_DELIM;
633                         }
634
635                         state = LWS_TOKZS_QUOTED_STRING;
636                         ts->token = ts->start;
637                         ts->token_len = 0;
638
639                         continue;
640                 }
641
642                 /* token= aggregation */
643
644                 if (c == '=' && (state == LWS_TOKZS_TOKEN_POST_TERMINAL ||
645                                  state == LWS_TOKZS_TOKEN)) {
646                         if (num == 1)
647                                 return LWS_TOKZE_ERR_NUM_ON_LHS;
648                         /* swallow the = */
649                         return LWS_TOKZE_TOKEN_NAME_EQUALS;
650                 }
651
652                 /* optional token: aggregation */
653
654                 if ((ts->flags & LWS_TOKENIZE_F_AGG_COLON) && c == ':' &&
655                     (state == LWS_TOKZS_TOKEN_POST_TERMINAL ||
656                      state == LWS_TOKZS_TOKEN))
657                         /* swallow the : */
658                         return LWS_TOKZE_TOKEN_NAME_COLON;
659
660                 /* aggregate . in a number as a float */
661
662                 if (c == '.' && !(ts->flags & LWS_TOKENIZE_F_NO_FLOATS) &&
663                     state == LWS_TOKZS_TOKEN && num == 1) {
664                         if (flo)
665                                 return LWS_TOKZE_ERR_MALFORMED_FLOAT;
666                         flo = 1;
667                         ts->token_len++;
668                         continue;
669                 }
670
671                 /*
672                  * Delimiter... by default anything that:
673                  *
674                  *  - isn't matched earlier, or
675                  *  - is [A-Z, a-z, 0-9, _], and
676                  *  - is not a partial utf8 char
677                  *
678                  * is a "delimiter", it marks the end of a token and is itself
679                  * reported as a single LWS_TOKZE_DELIMITER each time.
680                  *
681                  * However with LWS_TOKENIZE_F_RFC7230_DELIMS flag, tokens may
682                  * contain any noncontrol character that isn't defined in
683                  * rfc7230_delims, and only characters listed there are treated
684                  * as delimiters.
685                  */
686
687                 if (!utf8 &&
688                      ((ts->flags & LWS_TOKENIZE_F_RFC7230_DELIMS &&
689                      strchr(rfc7230_delims, c) && c > 32) ||
690                     ((!(ts->flags & LWS_TOKENIZE_F_RFC7230_DELIMS) &&
691                      (c < '0' || c > '9') && (c < 'A' || c > 'Z') &&
692                      (c < 'a' || c > 'z') && c != '_') &&
693                      c != s_minus && c != s_dot) ||
694                     c == d_minus || c == d_dot
695                     )) {
696                         switch (state) {
697                         case LWS_TOKZS_LEADING_WHITESPACE:
698                                 if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) {
699                                         if (c != ',' ||
700                                             ts->delim != LWSTZ_DT_NEED_DELIM)
701                                                 return LWS_TOKZE_ERR_COMMA_LIST;
702                                         ts->delim = LWSTZ_DT_NEED_NEXT_CONTENT;
703                                 }
704
705                                 ts->token = ts->start - 1;
706                                 ts->token_len = 1;
707                                 return LWS_TOKZE_DELIMITER;
708
709                         case LWS_TOKZS_QUOTED_STRING:
710                                 ts->token_len++;
711                                 continue;
712
713                         case LWS_TOKZS_TOKEN_POST_TERMINAL:
714                         case LWS_TOKZS_TOKEN:
715                                 /* report the delimiter next time */
716                                 ts->start--;
717                                 ts->len++;
718                                 goto token_or_numeric;
719                         }
720                 }
721
722                 /* anything that's not whitespace or delimiter is payload */
723
724                 switch (state) {
725                 case LWS_TOKZS_LEADING_WHITESPACE:
726
727                         if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) {
728                                 if (ts->delim == LWSTZ_DT_NEED_DELIM)
729                                         return LWS_TOKZE_ERR_COMMA_LIST;
730                                 ts->delim = LWSTZ_DT_NEED_DELIM;
731                         }
732
733                         state = LWS_TOKZS_TOKEN;
734                         ts->token = ts->start - 1;
735                         ts->token_len = 1;
736                         goto checknum;
737
738                 case LWS_TOKZS_QUOTED_STRING:
739                 case LWS_TOKZS_TOKEN:
740                         ts->token_len++;
741 checknum:
742                         if (!(ts->flags & LWS_TOKENIZE_F_NO_INTEGERS)) {
743                                 if (c < '0' || c > '9')
744                                         num = 0;
745                                 else
746                                         if (num < 0)
747                                                 num = 1;
748                         }
749                         continue;
750
751                 case LWS_TOKZS_TOKEN_POST_TERMINAL:
752                         /* report the new token next time */
753                         ts->start--;
754                         ts->len++;
755                         goto token_or_numeric;
756                 }
757         }
758
759         /* we ran out of content */
760
761         if (utf8) /* ended partway through a multibyte char */
762                 return LWS_TOKZE_ERR_BROKEN_UTF8;
763
764         if (state == LWS_TOKZS_QUOTED_STRING)
765                 return LWS_TOKZE_ERR_UNTERM_STRING;
766
767         if (state != LWS_TOKZS_TOKEN_POST_TERMINAL &&
768             state != LWS_TOKZS_TOKEN) {
769                 if ((ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) &&
770                      ts->delim == LWSTZ_DT_NEED_NEXT_CONTENT)
771                         return LWS_TOKZE_ERR_COMMA_LIST;
772
773                 return LWS_TOKZE_ENDED;
774         }
775
776         /* report the pending token */
777
778 token_or_numeric:
779
780         if (num != 1)
781                 return LWS_TOKZE_TOKEN;
782         if (flo)
783                 return LWS_TOKZE_FLOAT;
784
785         return LWS_TOKZE_INTEGER;
786 }
787
788
789 LWS_VISIBLE LWS_EXTERN int
790 lws_tokenize_cstr(struct lws_tokenize *ts, char *str, int max)
791 {
792         if (ts->token_len + 1 >= max)
793                 return 1;
794
795         memcpy(str, ts->token, ts->token_len);
796         str[ts->token_len] = '\0';
797
798         return 0;
799 }
800
801 LWS_VISIBLE LWS_EXTERN void
802 lws_tokenize_init(struct lws_tokenize *ts, const char *start, int flags)
803 {
804         ts->start = start;
805         ts->len = 0x7fffffff;
806         ts->flags = flags;
807         ts->delim = LWSTZ_DT_NEED_FIRST_CONTENT;
808 }
809
810 #if LWS_MAX_SMP > 1
811
812 void
813 lws_mutex_refcount_init(struct lws_mutex_refcount *mr)
814 {
815         pthread_mutex_init(&mr->lock, NULL);
816         mr->last_lock_reason = NULL;
817         mr->lock_depth = 0;
818         mr->metadata = 0;
819         mr->lock_owner = 0;
820 }
821
822 void
823 lws_mutex_refcount_destroy(struct lws_mutex_refcount *mr)
824 {
825         pthread_mutex_destroy(&mr->lock);
826 }
827
828 void
829 lws_mutex_refcount_lock(struct lws_mutex_refcount *mr, const char *reason)
830 {
831         /* if true, this sequence is atomic because our thread has the lock
832          *
833          *  - if true, only guy who can race to make it untrue is our thread,
834          *    and we are here.
835          *
836          *  - if false, only guy who could race to make it true is our thread,
837          *    and we are here
838          *
839          *  - it can be false and change to a different tid that is also false
840          */
841         if (mr->lock_owner == pthread_self()) {
842                 /* atomic because we only change it if we own the lock */
843                 mr->lock_depth++;
844                 return;
845         }
846
847         pthread_mutex_lock(&mr->lock);
848         /* atomic because only we can have the lock */
849         mr->last_lock_reason = reason;
850         mr->lock_owner = pthread_self();
851         mr->lock_depth = 1;
852         //lwsl_notice("tid %d: lock %s\n", mr->tid, reason);
853 }
854
855 void
856 lws_mutex_refcount_unlock(struct lws_mutex_refcount *mr)
857 {
858         if (--mr->lock_depth)
859                 /* atomic because only thread that has the lock can unlock */
860                 return;
861
862         mr->last_lock_reason = "free";
863         mr->lock_owner = 0;
864         //lwsl_notice("tid %d: unlock %s\n", mr->tid, mr->last_lock_reason);
865         pthread_mutex_unlock(&mr->lock);
866 }
867
868 #endif /* SMP */
869
870
871 const char *
872 lws_cmdline_option(int argc, const char **argv, const char *val)
873 {
874         int n = (int)strlen(val), c = argc;
875
876         while (--c > 0) {
877
878                 if (!strncmp(argv[c], val, n)) {
879                         if (!*(argv[c] + n) && c < argc - 1) {
880                                 /* coverity treats unchecked argv as "tainted" */
881                                 if (!argv[c + 1] || strlen(argv[c + 1]) > 1024)
882                                         return NULL;
883                                 return argv[c + 1];
884                         }
885
886                         return argv[c] + n;
887                 }
888         }
889
890         return NULL;
891 }
892
893
894 const lws_humanize_unit_t humanize_schema_si[] = {
895         { "Pi ", LWS_PI }, { "Ti ", LWS_TI }, { "Gi ", LWS_GI },
896         { "Mi ", LWS_MI }, { "Ki ", LWS_KI }, { "   ", 1 },
897         { NULL, 0 }
898 };
899 const lws_humanize_unit_t humanize_schema_si_bytes[] = {
900         { "PiB", LWS_PI }, { "TiB", LWS_TI }, { "GiB", LWS_GI },
901         { "MiB", LWS_MI }, { "KiB", LWS_KI }, { "B  ", 1 },
902         { NULL, 0 }
903 };
904 const lws_humanize_unit_t humanize_schema_us[] = {
905         { "y  ",  (uint64_t)365 * 24 * 3600 * LWS_US_PER_SEC },
906         { "d  ",  (uint64_t)24 * 3600 * LWS_US_PER_SEC },
907         { "hr ", (uint64_t)3600 * LWS_US_PER_SEC },
908         { "min", 60 * LWS_US_PER_SEC },
909         { "s  ", LWS_US_PER_SEC },
910         { "ms ", LWS_US_PER_MS },
911         { "us ", 1 },
912         { NULL, 0 }
913 };
914
915 int
916 lws_humanize(char *p, int len, uint64_t v, const lws_humanize_unit_t *schema)
917 {
918         do {
919                 if (v >= schema->factor || schema->factor == 1) {
920                         if (schema->factor == 1)
921                                 return lws_snprintf(p, len,
922                                         " %4"PRIu64"%s    ",
923                                         v / schema->factor, schema->name);
924
925                         return lws_snprintf(p, len, " %4"PRIu64".%03"PRIu64"%s",
926                                 v / schema->factor,
927                                 (v % schema->factor) / (schema->factor / 1000),
928                                 schema->name);
929                 }
930                 schema++;
931         } while (schema->name);
932
933         assert(0);
934
935         return 0;
936 }
937
938 int
939 lws_system_get_info(struct lws_context *context, lws_system_item_t item,
940                     lws_system_arg_t arg, size_t *len)
941 {
942         if (!context->system_ops || !context->system_ops->get_info)
943                 return 1;
944
945         return context->system_ops->get_info(item, arg, len);
946 }
947
948 int
949 lws_system_reboot(struct lws_context *context)
950 {
951         if (!context->system_ops || !context->system_ops->reboot)
952                 return 1;
953
954         return context->system_ops->reboot();
955 }