1 /*
2 	newsstar - advanced news fetcher
3 	Copyright (C) 2001-2002 Tony Houghton <h@realh.co.uk>
4 
5 	This program is free software; you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation; either version 2 of the License, or
8 	(at your option) any later version.
9 
10 	This program is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with this program; if not, write to the Free Software
17 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 /* Logging for debugging */
21 
22 
23 #include "common.h"
24 #include "child.h"
25 #include "intercom.h"
26 #include "nscurses.h"
27 #include "stats.h"
28 #ifdef ENABLE_KEYED_LOG
29 #include "vector.h"
30 #endif
31 
32 #if USE_PTHREADS
33 
34 #include <pthread.h>
35 #include "main.h"
36 
deblog_get_thread_idx(void)37 static int deblog_get_thread_idx(void)
38 {
39 	return child_lookup_current_thread_idx();
40 }
41 
42 #else
43 
44 static rw_buf *deblog_buf = NULL;
45 
46 /* When other processes are forked this is updated to their thread index */
47 static int deblog_thread_idx = -1;
48 
deblog_use_pipe(rw_buf * buf,int thread_idx)49 void deblog_use_pipe(rw_buf *buf, int thread_idx)
50 {
51 	deblog_buf = buf;
52 	deblog_thread_idx = thread_idx;
53 	deblog_debug(_("Ready to log via pipe\n"), thread_idx);
54 }
55 
deblog_get_thread_idx(void)56 static int deblog_get_thread_idx(void)
57 {
58 	return deblog_thread_idx;
59 }
60 
61 #endif /* !USE_PTHREADS */
62 
63 BOOL deblog_be_brief = FALSE;
64 
65 static deblog_mask_t deblog_mask = deblog_Mask_Default;
66 
67 static BOOL deblog_need_lf_flag = FALSE;
68 
69 
70 static BOOL deblog_recursion_allowed = FALSE;
71 static BOOL deblog_merged_sinks = TRUE;
72 static BOOL deblog_stats_no_update = FALSE;
73 
74 #ifdef ENABLE_KEYED_LOG
75 /* Using a vector could get horribly inefficient if there are loads of
76  * different keys, but hopefully there shouldn't be enough to warrant the
77  * overhead of something more sophisticated */
78 struct VECTOR(const char *) deblog_keys;
79 
deblog_init_keys(void)80 static void deblog_init_keys(void)
81 {
82 	static BOOL inited = FALSE;
83 
84 	if (!inited)
85 	{
86 		inited = TRUE;
87 		VECTOR_INIT(deblog_keys, const char *);
88 	}
89 }
90 
91 /* Returns pointer to found string in keys vector, or NULL */
deblog_find_key(const char * key)92 static char const **deblog_find_key(const char *key)
93 {
94 	size_t k;
95 
96 	deblog_init_keys();
97 	for (k = 0; k < deblog_keys.vec_used_members; ++k)
98 	{
99 		if (!strcmp(deblog_keys.vec_data[k], key))
100 		{
101 			return &deblog_keys.vec_data[k];
102 		}
103 	}
104 	return NULL;
105 }
106 
107 #endif
108 
109 /* Makes sure we haven't left cursor at end of stats line at exit */
deblog_clear_line(void)110 static void deblog_clear_line(void)
111 {
112 	if (deblog_get_thread_idx() == -1 &&
113 					deblog_need_lf_flag && !nscurses_active())
114 	{
115 		putchar('\n');
116 		deblog_need_lf_flag = FALSE;
117 	}
118 }
119 
deblog_separate_sinks(void)120 void deblog_separate_sinks(void)
121 {
122 	deblog_merged_sinks = FALSE;
123 }
124 
deblog_allow_recursion(BOOL allowed)125 void deblog_allow_recursion(BOOL allowed)
126 {
127 	deblog_recursion_allowed = allowed;
128 }
129 
deblog_print_to_stream(int thread_idx,FILE * sink,deblog_level_t level,const char * message)130 static void deblog_print_to_stream(int thread_idx, FILE *sink, deblog_level_t
131 		level, const char *message)
132 {
133 	/* Don't print thread indexes to stdout in brief mode */
134 	if (!deblog_be_brief || sink == stderr)
135 	{
136 		if (thread_idx != -1)
137 		{
138 			fprintf(sink, _("<%d> "), thread_idx);
139 		}
140 		else
141 		{
142 			fputs(_("<M> "), sink);		/* M for Main/Master */
143 		}
144 	}
145 	switch (level)
146 	{
147 		case deblog_Level_Error:
148 			fputs(_("Error: "), sink);
149 			break;
150 		case deblog_Level_Critical:
151 			fputs(_("Critical: "), sink);
152 			break;
153 		case deblog_Level_Warning:
154 			fputs(_("Warning: "), sink);
155 			break;
156 		case deblog_Level_Brief:
157 			if (sink == stderr)
158 				fputs(_("Brief: "), sink);
159 			break;
160 		case deblog_Level_Message:
161 			if (sink == stderr)
162 				fputs(_("Message: "), sink);
163 			break;
164 		case deblog_Level_Info:
165 			fputs(_("Info: "), sink);
166 			break;
167 		case deblog_Level_Debug:
168 			fputs(_("Debug: "), sink);
169 			break;
170 #ifdef ENABLE_KEYED_LOG
171 		case deblog_Level_Keyed:
172 			fputs(_("Debug: "), sink);
173 			break;
174 #endif
175 		default:
176 			break;
177 	}
178 	fputs(message, sink);
179 	fflush(sink);
180 }
181 
deblog_choose_stream_and_print(int thread_idx,deblog_level_t level,const char * message)182 static void deblog_choose_stream_and_print(int thread_idx, deblog_level_t level,
183 		const char *message)
184 {
185 	FILE *sink = (level == deblog_Level_Error
186 			|| level == deblog_Level_Critical
187 			|| level == deblog_Level_Warning
188 			|| (level == deblog_Level_Brief && deblog_be_brief)
189 			|| (level == deblog_Level_Message && !deblog_be_brief)
190 			) ? stdout : stderr;
191 
192 	/* First decide whether to use curses. Errors cause shutdown and are
193 	 * printed to stdout; info and debug are sent to stderr if using -s */
194 	if (nscurses_active())
195 	{
196 		if (level == deblog_Level_Error)
197 		{
198 			nscurses_stop();
199 			deblog_print_to_stream(thread_idx, stdout, level, message);
200 		}
201 		else if (sink == stdout || deblog_merged_sinks)
202 		{
203 			nscurses_deblog(thread_idx, level, message);
204 		}
205 		if (sink == stderr && !deblog_merged_sinks)
206 		{
207 			deblog_print_to_stream(thread_idx, stderr, level, message);
208 		}
209 	}
210 	else
211 	{
212 		/* stdout or stderr. First, do we need a newline to get us clear of
213 		 * stats display? */
214 		if (deblog_need_lf_flag && (deblog_merged_sinks || sink == stdout))
215 			stats_clear_line();
216 		deblog_print_to_stream(thread_idx, sink, level, message);
217 		if (sink == stdout || deblog_merged_sinks)
218 		{
219 			deblog_need_lf_flag = FALSE;
220 		}
221 	}
222 	/* Finally, whether curses or not, duplicate stdout messages on stderr
223 	 * if -s */
224 	if (sink == stdout && !deblog_merged_sinks)
225 	{
226 		deblog_print_to_stream(thread_idx, stderr, level, message);
227 	}
228 	if (!deblog_stats_no_update && level != deblog_Level_Error)
229 	{
230 		stats_update(sink == stdout);
231 	}
232 }
233 
deblog_print_thread(int thread_idx,deblog_level_t level,const char * message)234 void deblog_print_thread(int thread_idx, deblog_level_t level,
235 		const char *message)
236 {
237 #if USE_PTHREADS
238 	static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
239 #else
240 	static BOOL recursion = FALSE;
241 
242 	if (recursion && !deblog_recursion_allowed)
243 	{
244 		if (nscurses_active())
245 		{
246 			nscurses_emergency_stop();
247 		}
248 		fprintf(stderr, _("Error: Logging system called recursively\n"));
249 		exit(1);
250 	}
251 #endif /* !USE_PTHREADS */
252 	if (!(deblog_mask & (1 << level)))
253 		return;
254 #ifdef ENABLE_KEYED_LOG
255 	if (level == deblog_Level_Keyed)
256 	{
257 		char *key = XSTRDUP(message);
258 		char *key_end = strchr(key, ' ');
259 		char const **found = NULL;
260 
261 		if (key_end)
262 			*key_end = 0;
263 		found = deblog_find_key(key);
264 		free(key);
265 		if (!found)
266 			return;
267 	}
268 #endif
269 	/* Send message to parent process if we're a thread; sometimes this gets
270 	 * called by main thread for a child thread so need to check real current
271 	 * thread rather than thread_idx. */
272 #if !USE_PTHREADS
273 	recursion = TRUE;
274 	if (deblog_buf)
275 	{
276 		size_t len;
277 		char *lftocr = XSTRDUP(message);
278 
279 		/* Replace trailing LF with CR to avoid upsetting rw_read_line */
280 		len = strlen(lftocr) - 1;
281 		if (lftocr[len] == '\n')
282 			lftocr[len] = '\r';
283 		intercom_send_command(deblog_buf, command_Log, thread_idx,
284 				"%d %s", level, lftocr);
285 		free(lftocr);
286 	}
287 	else
288 	{
289 		deblog_choose_stream_and_print(thread_idx, level, message);
290 	}
291 	recursion = FALSE;
292 #else /* USE_PTHREADS */
293 	pthread_mutex_lock(&mutex);
294 	deblog_choose_stream_and_print(thread_idx, level, message);
295 	pthread_mutex_unlock(&mutex);
296 #endif /* USE_PTHREADS */
297 	if (level == deblog_Level_Error)
298 		exit(1);
299 }
300 
deblog_vprintf_thread(int thread_idx,deblog_level_t level,const char * format,va_list ap)301 void deblog_vprintf_thread(int thread_idx, deblog_level_t level,
302 		const char *format, va_list ap)
303 {
304 	char *formatted;
305 
306 	if (vasprintf(&formatted, format, ap) <= 0)
307 	{
308 		fprintf(stderr, _("Unable to format logging message\n"));
309 		exit(1);
310 	}
311 	deblog_print_thread(thread_idx, level, formatted);
312 	free(formatted);
313 }
314 
deblog_printf_thread(int thread_idx,deblog_level_t level,const char * format,...)315 void deblog_printf_thread(int thread_idx, deblog_level_t level,
316 		const char *format, ...)
317 {
318 	va_list ap;
319 	va_start(ap, format);
320 	deblog_vprintf_thread(thread_idx, level, format, ap);
321 	va_end(ap);
322 }
323 
deblog_error_thread(int thread_idx,const char * format,...)324 void deblog_error_thread(int thread_idx, const char *format, ...)
325 {
326 	va_list ap;
327 	va_start(ap, format);
328 	deblog_vprintf_thread(thread_idx, deblog_Level_Error, format, ap);
329 	va_end(ap);
330 }
331 
deblog_critical_thread(int thread_idx,const char * format,...)332 void deblog_critical_thread(int thread_idx, const char *format, ...)
333 {
334 	va_list ap;
335 	va_start(ap, format);
336 	deblog_vprintf_thread(thread_idx, deblog_Level_Critical, format, ap);
337 	va_end(ap);
338 }
339 
deblog_warning_thread(int thread_idx,const char * format,...)340 void deblog_warning_thread(int thread_idx, const char *format, ...)
341 {
342 	va_list ap;
343 	va_start(ap, format);
344 	deblog_vprintf_thread(thread_idx, deblog_Level_Warning, format, ap);
345 	va_end(ap);
346 }
347 
deblog_brief_thread(int thread_idx,const char * format,...)348 void deblog_brief_thread(int thread_idx, const char *format, ...)
349 {
350 	va_list ap;
351 	va_start(ap, format);
352 	deblog_vprintf_thread(thread_idx, deblog_Level_Brief, format, ap);
353 	va_end(ap);
354 }
355 
deblog_message_thread(int thread_idx,const char * format,...)356 void deblog_message_thread(int thread_idx, const char *format, ...)
357 {
358 	va_list ap;
359 	va_start(ap, format);
360 	deblog_vprintf_thread(thread_idx, deblog_Level_Message, format, ap);
361 	va_end(ap);
362 }
363 
deblog_info_thread(int thread_idx,const char * format,...)364 void deblog_info_thread(int thread_idx, const char *format, ...)
365 {
366 	va_list ap;
367 	va_start(ap, format);
368 	deblog_vprintf_thread(thread_idx, deblog_Level_Info, format, ap);
369 	va_end(ap);
370 }
371 
deblog_debug_thread(int thread_idx,const char * format,...)372 void deblog_debug_thread(int thread_idx, const char *format, ...)
373 {
374 	va_list ap;
375 	va_start(ap, format);
376 	deblog_vprintf_thread(thread_idx, deblog_Level_Debug, format, ap);
377 	va_end(ap);
378 }
379 
deblog_verror_thread(int thread_idx,const char * format,va_list ap)380 void deblog_verror_thread(int thread_idx, const char *format, va_list ap)
381 {
382 	deblog_vprintf_thread(thread_idx, deblog_Level_Error, format, ap);
383 }
384 
deblog_vcritical_thread(int thread_idx,const char * format,va_list ap)385 void deblog_vcritical_thread(int thread_idx, const char *format, va_list ap)
386 {
387 	deblog_vprintf_thread(thread_idx, deblog_Level_Critical, format, ap);
388 }
389 
deblog_vwarning_thread(int thread_idx,const char * format,va_list ap)390 void deblog_vwarning_thread(int thread_idx, const char *format, va_list ap)
391 {
392 	deblog_vprintf_thread(thread_idx, deblog_Level_Warning, format, ap);
393 }
394 
deblog_vbrief_thread(int thread_idx,const char * format,va_list ap)395 void deblog_vbrief_thread(int thread_idx, const char *format, va_list ap)
396 {
397 	deblog_vprintf_thread(thread_idx, deblog_Level_Brief, format, ap);
398 }
399 
deblog_vmessage_thread(int thread_idx,const char * format,va_list ap)400 void deblog_vmessage_thread(int thread_idx, const char *format, va_list ap)
401 {
402 	deblog_vprintf_thread(thread_idx, deblog_Level_Message, format, ap);
403 }
404 
deblog_vinfo_thread(int thread_idx,const char * format,va_list ap)405 void deblog_vinfo_thread(int thread_idx, const char *format, va_list ap)
406 {
407 	deblog_vprintf_thread(thread_idx, deblog_Level_Info, format, ap);
408 }
409 
deblog_vdebug_thread(int thread_idx,const char * format,va_list ap)410 void deblog_vdebug_thread(int thread_idx, const char *format, va_list ap)
411 {
412 	deblog_vprintf_thread(thread_idx, deblog_Level_Debug, format, ap);
413 }
414 
deblog_vprintf(deblog_level_t level,const char * format,va_list ap)415 void deblog_vprintf(deblog_level_t level, const char *format, va_list ap)
416 {
417 	deblog_vprintf_thread(deblog_get_thread_idx(), level, format, ap);
418 }
419 
deblog_printf(deblog_level_t level,const char * format,...)420 void deblog_printf(deblog_level_t level, const char *format, ...)
421 {
422 	va_list ap;
423 	va_start(ap, format);
424 	deblog_vprintf_thread(deblog_get_thread_idx(), level, format, ap);
425 	va_end(ap);
426 }
427 
deblog_error(const char * format,...)428 void deblog_error(const char *format, ...)
429 {
430 	va_list ap;
431 	va_start(ap, format);
432 	deblog_vprintf_thread(deblog_get_thread_idx(),
433 					deblog_Level_Error, format, ap);
434 	va_end(ap);
435 }
436 
deblog_critical(const char * format,...)437 void deblog_critical(const char *format, ...)
438 {
439 	va_list ap;
440 	va_start(ap, format);
441 	deblog_vprintf_thread(deblog_get_thread_idx(),
442 					deblog_Level_Critical, format, ap);
443 	va_end(ap);
444 }
445 
deblog_warning(const char * format,...)446 void deblog_warning(const char *format, ...)
447 {
448 	va_list ap;
449 	va_start(ap, format);
450 	deblog_vprintf_thread(deblog_get_thread_idx(),
451 					deblog_Level_Warning, format, ap);
452 	va_end(ap);
453 }
454 
deblog_brief(const char * format,...)455 void deblog_brief(const char *format, ...)
456 {
457 	va_list ap;
458 	va_start(ap, format);
459 	deblog_vprintf_thread(deblog_get_thread_idx(),
460 					deblog_Level_Brief, format, ap);
461 	va_end(ap);
462 }
463 
deblog_message(const char * format,...)464 void deblog_message(const char *format, ...)
465 {
466 	va_list ap;
467 	va_start(ap, format);
468 	deblog_vprintf_thread(deblog_get_thread_idx(),
469 					deblog_Level_Message, format, ap);
470 	va_end(ap);
471 }
472 
deblog_info(const char * format,...)473 void deblog_info(const char *format, ...)
474 {
475 	va_list ap;
476 	va_start(ap, format);
477 	deblog_vprintf_thread(deblog_get_thread_idx(),
478 					deblog_Level_Info, format, ap);
479 	va_end(ap);
480 }
481 
deblog_debug(const char * format,...)482 void deblog_debug(const char *format, ...)
483 {
484 	va_list ap;
485 	va_start(ap, format);
486 	deblog_vprintf_thread(deblog_get_thread_idx(),
487 					deblog_Level_Debug, format, ap);
488 	va_end(ap);
489 }
490 
deblog_verror(const char * format,va_list ap)491 void deblog_verror(const char *format, va_list ap)
492 {
493 	deblog_vprintf_thread(deblog_get_thread_idx(),
494 					deblog_Level_Error, format, ap);
495 }
496 
deblog_vcritical(const char * format,va_list ap)497 void deblog_vcritical(const char *format, va_list ap)
498 {
499 	deblog_vprintf_thread(deblog_get_thread_idx(),
500 					deblog_Level_Critical, format, ap);
501 }
502 
deblog_vwarning(const char * format,va_list ap)503 void deblog_vwarning(const char *format, va_list ap)
504 {
505 	deblog_vprintf_thread(deblog_get_thread_idx(),
506 					deblog_Level_Warning, format, ap);
507 }
508 
deblog_vbrief(const char * format,va_list ap)509 void deblog_vbrief(const char *format, va_list ap)
510 {
511 	deblog_vprintf_thread(deblog_get_thread_idx(),
512 					deblog_Level_Brief, format, ap);
513 }
514 
deblog_vmessage(const char * format,va_list ap)515 void deblog_vmessage(const char *format, va_list ap)
516 {
517 	deblog_vprintf_thread(deblog_get_thread_idx(),
518 					deblog_Level_Message, format, ap);
519 }
520 
deblog_vinfo(const char * format,va_list ap)521 void deblog_vinfo(const char *format, va_list ap)
522 {
523 	deblog_vprintf_thread(deblog_get_thread_idx(),
524 					deblog_Level_Info, format, ap);
525 }
526 
deblog_vdebug(const char * format,va_list ap)527 void deblog_vdebug(const char *format, va_list ap)
528 {
529 	deblog_vprintf_thread(deblog_get_thread_idx(),
530 					deblog_Level_Debug, format, ap);
531 }
532 
deblog_need_lf(void)533 void deblog_need_lf(void)
534 {
535 	static BOOL atexit_registered = FALSE;
536 
537 	deblog_need_lf_flag = TRUE;
538 	if (!atexit_registered)
539 	{
540 		atexit_registered = TRUE;
541 		atexit(deblog_clear_line);
542 	}
543 }
544 
deblog_set_mask(deblog_mask_t mask)545 void deblog_set_mask(deblog_mask_t mask)
546 {
547 	deblog_mask = mask;
548 }
549 
deblog_enable_level(deblog_level_t level)550 void deblog_enable_level(deblog_level_t level)
551 {
552 	deblog_mask |= 1 << level;
553 }
554 
deblog_disable_level(deblog_level_t level)555 void deblog_disable_level(deblog_level_t level)
556 {
557 	deblog_mask &= ~(1 << level);
558 }
559 
deblog_disable_stats_update(void)560 void deblog_disable_stats_update(void)
561 {
562 	deblog_stats_no_update = TRUE;
563 }
564 
565 #ifdef ENABLE_KEYED_LOG
deblog_keyed(const char * format,...)566 void deblog_keyed(const char *format, ...)
567 {
568 	va_list ap;
569 	va_start(ap, format);
570 	deblog_vprintf_thread(deblog_get_thread_idx(),
571 					deblog_Level_Keyed, format, ap);
572 	va_end(ap);
573 }
574 
deblog_vkeyed(const char * format,va_list ap)575 void deblog_vkeyed(const char *format, va_list ap)
576 {
577 	deblog_vprintf_thread(deblog_get_thread_idx(),
578 					deblog_Level_Keyed, format, ap);
579 }
580 
deblog_keyed_thread(int thread_idx,const char * format,...)581 void deblog_keyed_thread(int thread_idx, const char *format, ...)
582 {
583 	va_list ap;
584 	va_start(ap, format);
585 	deblog_vprintf_thread(thread_idx, deblog_Level_Keyed, format, ap);
586 	va_end(ap);
587 }
588 
deblog_vkeyed_thread(int thread_idx,const char * format,va_list ap)589 void deblog_vkeyed_thread(int thread_idx, const char *format, va_list ap)
590 {
591 	deblog_vprintf_thread(thread_idx, deblog_Level_Keyed, format, ap);
592 }
593 
deblog_enable_key(const char * key)594 void deblog_enable_key(const char *key)
595 {
596 	char const **fkey = deblog_find_key(key);
597 
598 	if (!fkey)
599 	{
600 		VECTOR_APPEND(deblog_keys, key);
601 	}
602 }
603 
deblog_disable_key(const char * key)604 void deblog_disable_key(const char *key)
605 {
606 	char const **fkey = deblog_find_key(key);
607 
608 	if (fkey)
609 	{
610 		*fkey = NULL;
611 	}
612 }
613 
614 #endif
615