xref: /third_party/libwebsockets/lib/core/logs.c (revision d4afb5ce)
1/*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2021 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#include "private-lib-core.h"
26
27#ifdef LWS_HAVE_SYS_TYPES_H
28#include <sys/types.h>
29#endif
30
31#if defined(LWS_PLAT_OPTEE)
32void lwsl_emit_optee(int level, const char *line);
33#endif
34
35lws_log_cx_t log_cx = {
36#if !defined(LWS_PLAT_OPTEE)
37	.u.emit				= lwsl_emit_stderr,
38#else
39	.u.emit				= lwsl_emit_optee,
40#endif
41	.lll_flags			= LLL_ERR | LLL_WARN | LLL_NOTICE,
42};
43
44#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NO_LOGS)
45static const char * log_level_names ="EWNIDPHXCLUT??";
46#endif
47
48/*
49 * Name an instance tag and attach to a group
50 */
51
52void
53__lws_lc_tag(struct lws_context *context, lws_lifecycle_group_t *grp,
54	     lws_lifecycle_t *lc, const char *format, ...)
55{
56	va_list ap;
57	int n = 1;
58
59	if (*lc->gutag == '[') {
60		/* appending inside [] */
61
62		char *cp = strchr(lc->gutag, ']');
63		char rend[96];
64		size_t ll, k;
65		int n;
66
67		if (!cp)
68			return;
69
70		/* length of closing brace and anything else after it */
71		k = strlen(cp);
72
73		/* compute the remaining gutag unused */
74		ll = sizeof(lc->gutag) - lws_ptr_diff_size_t(cp, lc->gutag) - k - 1;
75		if (ll > sizeof(rend) - 1)
76			ll = sizeof(rend) - 1;
77		va_start(ap, format);
78		n = vsnprintf(rend, ll, format, ap);
79		va_end(ap);
80
81		if ((unsigned int)n > ll)
82			n = (int)ll;
83
84		/* shove the trailer up by what we added */
85		memmove(cp + n, cp, k);
86		assert(k + (unsigned int)n < sizeof(lc->gutag));
87		cp[k + (unsigned int)n] = '\0';
88		/* copy what we added into place */
89		memcpy(cp, rend, (unsigned int)n);
90
91		return;
92	}
93
94	assert(grp);
95	assert(grp->tag_prefix); /* lc group must have a tag prefix string */
96
97	lc->gutag[0] = '[';
98
99#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) /* ie, will have getpid if set */
100	n += lws_snprintf(&lc->gutag[n], sizeof(lc->gutag) -
101					 (unsigned int)n - 1u, "%u|", getpid());
102#endif
103	n += lws_snprintf(&lc->gutag[n], sizeof(lc->gutag) -
104					 (unsigned int)n - 1u, "%s|%lx|",
105					 grp->tag_prefix,
106					 (unsigned long)grp->ordinal++);
107
108	va_start(ap, format);
109	n += vsnprintf(&lc->gutag[n], sizeof(lc->gutag) - (unsigned int)n -
110			1u, format, ap);
111	va_end(ap);
112
113	if (n < (int)sizeof(lc->gutag) - 2) {
114		lc->gutag[n++] = ']';
115		lc->gutag[n++] = '\0';
116	} else {
117		lc->gutag[sizeof(lc->gutag) - 2] = ']';
118		lc->gutag[sizeof(lc->gutag) - 1] = '\0';
119	}
120
121	lc->us_creation = (uint64_t)lws_now_usecs();
122	lws_dll2_add_tail(&lc->list, &grp->owner);
123
124	lwsl_refcount_cx(lc->log_cx, 1);
125
126#if defined(LWS_LOG_TAG_LIFECYCLE)
127	lwsl_cx_notice(context, " ++ %s (%d)", lc->gutag, (int)grp->owner.count);
128#endif
129}
130
131/*
132 * Normally we want to set the tag one time at creation.  But sometimes we
133 * don't have enough information at that point to give it a meaningful tag, eg,
134 * it's an accepted, served connection but we haven't read data from it yet
135 * to find out what it wants to be.
136 *
137 * This allows you to append some extra info to the tag in those cases, the
138 * initial tag remains the same on the lhs so it can be tracked correctly.
139 */
140
141void
142__lws_lc_tag_append(lws_lifecycle_t *lc, const char *app)
143{
144	int n = (int)strlen(lc->gutag);
145
146	if (n && lc->gutag[n - 1] == ']')
147		n--;
148
149	n += lws_snprintf(&lc->gutag[n], sizeof(lc->gutag) - 2u -
150					 (unsigned int)n, "|%s]", app);
151
152	if ((unsigned int)n >= sizeof(lc->gutag) - 2u) {
153		lc->gutag[sizeof(lc->gutag) - 2] = ']';
154		lc->gutag[sizeof(lc->gutag) - 1] = '\0';
155	}
156}
157
158/*
159 * Remove instance from group
160 */
161
162void
163__lws_lc_untag(struct lws_context *context, lws_lifecycle_t *lc)
164{
165	//lws_lifecycle_group_t *grp;
166	char buf[24];
167
168	if (!lc->gutag[0]) { /* we never tagged this object... */
169		lwsl_cx_err(context, "%s never tagged", lc->gutag);
170		assert(0);
171		return;
172	}
173
174	if (!lc->list.owner) { /* we already untagged this object... */
175		lwsl_cx_err(context, "%s untagged twice", lc->gutag);
176		assert(0);
177		return;
178	}
179
180	//grp = lws_container_of(lc->list.owner, lws_lifecycle_group_t, owner);
181
182	lws_humanize(buf, sizeof(buf),
183		     (uint64_t)lws_now_usecs() - lc->us_creation,
184		     humanize_schema_us);
185
186#if defined(LWS_LOG_TAG_LIFECYCLE)
187	lwsl_cx_notice(context, " -- %s (%d) %s", lc->gutag,
188		    (int)lc->list.owner->count - 1, buf);
189#endif
190
191	lws_dll2_remove(&lc->list);
192
193	lwsl_refcount_cx(lc->log_cx, -1);
194}
195
196const char *
197lws_lc_tag(lws_lifecycle_t *lc)
198{
199	return lc->gutag;
200}
201
202
203int
204lwsl_timestamp(int level, char *p, size_t len)
205{
206#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NO_LOGS)
207	time_t o_now;
208	unsigned long long now;
209	struct timeval tv;
210	struct tm *ptm = NULL;
211#if defined(LWS_HAVE_LOCALTIME_R)
212	struct tm tm;
213#endif
214	int n;
215
216	gettimeofday(&tv, NULL);
217	o_now = tv.tv_sec;
218	now = ((unsigned long long)tv.tv_sec * 10000) +
219				(unsigned int)(tv.tv_usec / 100);
220
221#if defined(LWS_HAVE_LOCALTIME_R)
222	ptm = localtime_r(&o_now, &tm);
223#else
224	ptm = localtime(&o_now);
225#endif
226	p[0] = '\0';
227	for (n = 0; n < LLL_COUNT; n++) {
228		if (level != (1 << n))
229			continue;
230
231		if (ptm)
232			n = lws_snprintf(p, len,
233				"[%04d/%02d/%02d %02d:%02d:%02d:%04d] %c: ",
234				ptm->tm_year + 1900,
235				ptm->tm_mon + 1,
236				ptm->tm_mday,
237				ptm->tm_hour,
238				ptm->tm_min,
239				ptm->tm_sec,
240				(int)(now % 10000), log_level_names[n]);
241		else
242			n = lws_snprintf(p, len, "[%llu:%04d] %c: ",
243					(unsigned long long) now / 10000,
244					(int)(now % 10000), log_level_names[n]);
245
246#if defined(LWS_PLAT_FREERTOS)
247		n += lws_snprintf(p + n, len - n, "%6u: ",
248#if defined(LWS_AMAZON_RTOS)
249				  (unsigned int)xPortGetFreeHeapSize());
250#else
251				  (unsigned int)esp_get_free_heap_size());
252#endif
253#endif
254
255		return n;
256	}
257#else
258	p[0] = '\0';
259#endif
260
261	return 0;
262}
263
264#ifndef LWS_PLAT_OPTEE
265static const char * const colours[] = {
266	"[31;1m", /* LLL_ERR */
267	"[36;1m", /* LLL_WARN */
268	"[35;1m", /* LLL_NOTICE */
269	"[32;1m", /* LLL_INFO */
270	"[34;1m", /* LLL_DEBUG */
271	"[33;1m", /* LLL_PARSER */
272	"[33m", /* LLL_HEADER */
273	"[33m", /* LLL_EXT */
274	"[33m", /* LLL_CLIENT */
275	"[33;1m", /* LLL_LATENCY */
276        "[0;1m", /* LLL_USER */
277	"[31m", /* LLL_THREAD */
278};
279
280static char tty;
281
282static void
283_lwsl_emit_stderr(int level, const char *line)
284{
285	int n, m = LWS_ARRAY_SIZE(colours) - 1;
286
287	if (!tty)
288		tty = (char)(isatty(2) | 2);
289
290	if (tty == 3) {
291		n = 1 << (LWS_ARRAY_SIZE(colours) - 1);
292		while (n) {
293			if (level & n)
294				break;
295			m--;
296			n >>= 1;
297		}
298		fprintf(stderr, "%c%s%s%c[0m", 27, colours[m], line, 27);
299	} else
300		fprintf(stderr, "%s", line);
301}
302
303void
304lwsl_emit_stderr(int level, const char *line)
305{
306	_lwsl_emit_stderr(level, line);
307}
308
309void
310lwsl_emit_stderr_notimestamp(int level, const char *line)
311{
312	_lwsl_emit_stderr(level, line);
313}
314
315#if !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE)
316
317/*
318 * Helper to emit to a file
319 */
320
321void
322lws_log_emit_cx_file(struct lws_log_cx *cx, int level, const char *line,
323			size_t len)
324{
325	int fd = (int)(intptr_t)cx->stg;
326
327	if (fd >= 0)
328		if (write(fd, line, (unsigned int)len) != (ssize_t)len)
329			fprintf(stderr, "Unable to write log to file\n");
330}
331
332/*
333 * Helper to use a .refcount_cb to store logs in a file
334 */
335
336void
337lws_log_use_cx_file(struct lws_log_cx *cx, int _new)
338{
339	int fd;
340
341	if (_new > 0 && cx->refcount == 1) {
342		fd = open((const char *)cx->opaque,
343				LWS_O_CREAT | LWS_O_TRUNC | LWS_O_WRONLY, 0600);
344		if (fd < 0)
345			fprintf(stderr, "Unable to open log %s: errno %d\n",
346				(const char *)cx->opaque, errno);
347		cx->stg = (void *)(intptr_t)fd;
348
349		return;
350	}
351
352	fd = (int)(intptr_t)cx->stg;
353
354	if (_new <= 0 && cx->refcount == 0 && fd >= 0) {
355		close(fd);
356		cx->stg = (void *)(intptr_t)-1;
357	}
358}
359
360#endif
361
362#endif
363
364#if !(defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK))
365void
366__lws_logv(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj,
367	   int filter, const char *_fun, const char *format, va_list vl)
368{
369#if LWS_MAX_SMP == 1 && !defined(LWS_WITH_THREADPOOL)
370	/* this is incompatible with multithreaded logging */
371	static char buf[256];
372#else
373	char buf[1024];
374#endif
375	char *p = buf, *end = p + sizeof(buf) - 1;
376	lws_log_cx_t *cxp;
377	int n, back = 0;
378
379	/*
380	 * We need to handle NULL wsi etc at the wrappers as gracefully as
381	 * possible
382	 */
383
384	if (!cx) {
385		lws_strncpy(p, "NULL log cx: ", sizeof(buf) - 1);
386		p += 13;
387		/* use the processwide one for lack of anything better */
388		cx = &log_cx;
389	}
390
391	cxp = cx;
392
393	if (!(cx->lll_flags & (uint32_t)filter))
394		/*
395		 * logs may be produced and built in to the code but disabled
396		 * at runtime
397		 */
398		return;
399
400#if !defined(LWS_LOGS_TIMESTAMP)
401	if (cx->lll_flags & LLLF_LOG_TIMESTAMP)
402#endif
403	{
404		buf[0] = '\0';
405		lwsl_timestamp(filter, buf, sizeof(buf));
406		p += strlen(buf);
407	}
408
409	/*
410	 * prepend parent log ctx content first
411	 * top level cx also gets an opportunity to prepend
412	 */
413
414	while (cxp->parent) {
415		cxp = cxp->parent;
416		back++;
417	}
418
419	do {
420		int b = back;
421
422		cxp = cx;
423		while (b--)
424			cxp = cxp->parent;
425		if (cxp->prepend)
426			cxp->prepend(cxp, NULL, &p, end);
427
428		back--;
429	} while (back > 0);
430
431	if (prep)
432		prep(cxp, obj, &p, end);
433
434	if (_fun)
435		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "%s: ", _fun);
436
437	/*
438	 * The actual log content
439	 */
440
441	n = vsnprintf(p, lws_ptr_diff_size_t(end, p), format, vl);
442
443	/* vnsprintf returns what it would have written, even if truncated */
444	if (p + n > end - 2) {
445		p = end - 5;
446		*p++ = '.';
447		*p++ = '.';
448		*p++ = '.';
449		*p++ = '\n';
450		*p++ = '\0';
451	} else
452		if (n > 0) {
453			p += n;
454			if (p[-1] != '\n')
455				*p++ = '\n';
456			*p = '\0';
457		}
458
459	/*
460	 * The actual emit
461	 */
462
463	if (cx->lll_flags & LLLF_LOG_CONTEXT_AWARE)
464		cx->u.emit_cx(cx, filter, buf, lws_ptr_diff_size_t(p, buf));
465	else
466		cx->u.emit(filter, buf);
467}
468
469void _lws_logv(int filter, const char *format, va_list vl)
470{
471	__lws_logv(&log_cx, NULL, NULL, filter, NULL, format, vl);
472}
473
474void _lws_log(int filter, const char *format, ...)
475{
476	va_list ap;
477
478	va_start(ap, format);
479	__lws_logv(&log_cx, NULL, NULL, filter, NULL, format, ap);
480	va_end(ap);
481}
482
483void _lws_log_cx(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj,
484		 int filter, const char *_fun, const char *format, ...)
485{
486	va_list ap;
487
488	if (!cx)
489		cx = &log_cx;
490
491	va_start(ap, format);
492	__lws_logv(cx, prep, obj, filter, _fun, format, ap);
493	va_end(ap);
494}
495#endif
496
497void
498lws_set_log_level(int flags, lws_log_emit_t func)
499{
500	log_cx.lll_flags = (uint32_t)(flags & (~LLLF_LOG_CONTEXT_AWARE));
501
502	if (func)
503		log_cx.u.emit = func;
504}
505
506int lwsl_visible(int level)
507{
508	return !!(log_cx.lll_flags & (uint32_t)level);
509}
510
511int lwsl_visible_cx(lws_log_cx_t *cx, int level)
512{
513	return !!(cx->lll_flags & (uint32_t)level);
514}
515
516void
517lwsl_refcount_cx(lws_log_cx_t *cx, int _new)
518{
519#if LWS_MAX_SMP > 1
520	volatile lws_log_cx_t *vcx = (volatile lws_log_cx_t *)cx;
521#endif
522
523	if (!cx)
524		return;
525
526#if LWS_MAX_SMP > 1
527	if (!vcx->inited) {
528		vcx->inited = 1;
529		lws_pthread_mutex_init(&cx->refcount_lock);
530		vcx->inited = 2;
531	}
532	while (vcx->inited != 2)
533		;
534	lws_pthread_mutex_lock(&cx->refcount_lock);
535#endif
536
537	if (_new > 0)
538		cx->refcount++;
539	else {
540		assert(cx->refcount);
541		cx->refcount--;
542	}
543
544	if (cx->refcount_cb)
545		cx->refcount_cb(cx, _new);
546
547#if LWS_MAX_SMP > 1
548	lws_pthread_mutex_unlock(&cx->refcount_lock);
549#endif
550}
551
552void
553lwsl_hexdump_level_cx(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj,
554		      int hexdump_level, const void *vbuf, size_t len)
555{
556	unsigned char *buf = (unsigned char *)vbuf;
557	unsigned int n;
558
559	if (!lwsl_visible_cx(cx, hexdump_level))
560		return;
561
562	if (!len) {
563		_lws_log_cx(cx, prep, obj, hexdump_level, NULL,
564					"(hexdump: zero length)\n");
565		return;
566	}
567
568	if (!vbuf) {
569		_lws_log_cx(cx, prep, obj, hexdump_level, NULL,
570					"(hexdump: NULL ptr)\n");
571		return;
572	}
573
574	_lws_log_cx(cx, prep, obj, hexdump_level, NULL, "\n");
575
576	for (n = 0; n < len;) {
577		unsigned int start = n, m;
578		char line[80], *p = line;
579
580		p += lws_snprintf(p, 10, "%04X: ", start);
581
582		for (m = 0; m < 16 && n < len; m++)
583			p += lws_snprintf(p, 5, "%02X ", buf[n++]);
584		while (m++ < 16)
585			p += lws_snprintf(p, 5, "   ");
586
587		p += lws_snprintf(p, 6, "   ");
588
589		for (m = 0; m < 16 && (start + m) < len; m++) {
590			if (buf[start + m] >= ' ' && buf[start + m] < 127)
591				*p++ = (char)buf[start + m];
592			else
593				*p++ = '.';
594		}
595		while (m++ < 16)
596			*p++ = ' ';
597
598		*p++ = '\n';
599		*p = '\0';
600		_lws_log_cx(cx, prep, obj, hexdump_level, NULL, "%s", line);
601		(void)line;
602	}
603
604	_lws_log_cx(cx, prep, obj, hexdump_level, NULL, "\n");
605}
606
607void
608lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len)
609{
610	lwsl_hexdump_level_cx(&log_cx, NULL, NULL, hexdump_level, vbuf, len);
611}
612
613void
614lwsl_hexdump(const void *vbuf, size_t len)
615{
616#if defined(_DEBUG)
617	lwsl_hexdump_level(LLL_DEBUG, vbuf, len);
618#endif
619}
620