Blame view

error.c 8.61 KB
redmine authored
1
/*
redmine authored
2 3 4 5
    clsync - file tree sync utility based on inotify/kqueue
    
    Copyright (C) 2013-2014 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
    
redmine authored
6 7 8 9
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
redmine authored
10
    
redmine authored
11 12 13 14
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
redmine authored
15
    
redmine authored
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * This file implements way to output debugging information. It's supposed
 * to be slow but convenient functions.
 */

#include <stdlib.h>
#include <execinfo.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <syslog.h>
redmine authored
32
#include <pthread.h>	/* pthread_self() */
redmine authored
33 34 35 36 37 38
#include <sys/types.h>	/* getpid() */
#include <unistd.h>	/* getpid() */


#include "configuration.h"

redmine authored
39
#include "error.h"
redmine authored
40
#include "pthreadex.h"	/* pthread_*_shared() */
redmine authored
41 42

static int zero     = 0;
redmine authored
43
static int three    = 3;
redmine authored
44 45 46 47

static int *outputmethod = &zero;
static int *debug	 = &zero;
static int *quiet	 = &zero;
redmine authored
48
static int *verbose	 = &three;
redmine authored
49

redmine authored
50
pthread_mutex_t *error_mutex_p = NULL;
redmine authored
51

redmine authored
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
static int printf_stderr(const char *fmt, ...) {
	va_list args;
	int rc;

	va_start(args, fmt);
	rc = vfprintf(stderr, fmt, args);
	va_end(args);

	return rc;
}

static int printf_stdout(const char *fmt, ...) {
	va_list args;
	int rc;

	va_start(args, fmt);
	rc = vfprintf(stdout, fmt, args);
	va_end(args);

	return rc;
}

static int vprintf_stderr(const char *fmt, va_list args) {
	return vfprintf(stderr, fmt, args);
}

static int vprintf_stdout(const char *fmt, va_list args) {
	return vfprintf(stdout, fmt, args);
}


static void flush_stderr(int level) {
	fprintf(stderr, "\n");
	fflush(stderr);
}

static void flush_stdout(int level) {
	fprintf(stdout, "\n");
	fflush(stdout);
}


redmine authored
94 95 96 97
static char _syslog_buffer[SYSLOG_BUFSIZ+1] = {0};
size_t      _syslog_buffer_filled = 0;

static int vsyslog_buf(const char *fmt, va_list args) {
redmine authored
98
	int len;
redmine authored
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
	size_t size;

	size = SYSLOG_BUFSIZ - _syslog_buffer_filled;

#ifdef VERYPARANOID
	if (
		(			 size	> SYSLOG_BUFSIZ)	|| 
		(_syslog_buffer_filled + size	> SYSLOG_BUFSIZ)	||
		(_syslog_buffer_filled		> SYSLOG_BUFSIZ)
	) {
		fprintf(stderr, "Security problem while vsyslog_buf(): "
			"_syslog_buffer_filled == %lu; "
			"size == %lu; "
			"SYSLOG_BUFSIZ == "XTOSTR(SYSLOG_BUFSIZ)"\n",
			_syslog_buffer_filled, size);
		exit(ENOBUFS);
	}
#endif
	if (!size)
		return 0;
redmine authored
119 120 121

	len = vsnprintf (
		&_syslog_buffer[_syslog_buffer_filled],
redmine authored
122
		size,
redmine authored
123 124 125 126
		fmt,
		args
	);

Andrew Savchenko authored
127
	if (len>0) {
redmine authored
128
		_syslog_buffer_filled += len;
redmine authored
129 130
		if (_syslog_buffer_filled > SYSLOG_BUFSIZ)
			_syslog_buffer_filled = SYSLOG_BUFSIZ;
Andrew Savchenko authored
131
	}
redmine authored
132 133 134 135

	return 0;
}

redmine authored
136 137 138
static int syslog_buf(const char *fmt, ...) {
	va_list args;
	int rc;
redmine authored
139

redmine authored
140 141 142
	va_start(args, fmt);
	rc = vsyslog_buf(fmt, args);
	va_end(args);
redmine authored
143

redmine authored
144
	return rc;
redmine authored
145
}
redmine authored
146

redmine authored
147 148
static void syslog_flush(int level) {
	syslog(level, "%s", _syslog_buffer);
Andrew Savchenko authored
149
	_syslog_buffer_filled = 0;
redmine authored
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
}

typedef int  *(  *outfunct_t)(const char *format, ...);
typedef int  *( *voutfunct_t)(const char *format, va_list ap);
typedef void *(*flushfunct_t)(int level);

static outfunct_t outfunct[] = {
	[OM_STDERR]	= (outfunct_t)printf_stderr,
	[OM_STDOUT]	= (outfunct_t)printf_stdout,
	[OM_SYSLOG]	= (outfunct_t)syslog_buf,
};

static voutfunct_t voutfunct[] = {
	[OM_STDERR]	= (voutfunct_t)vprintf_stderr,
	[OM_STDOUT]	= (voutfunct_t)vprintf_stdout,
	[OM_SYSLOG]	= (voutfunct_t)vsyslog_buf,
};

static flushfunct_t flushfunct[] = {
	[OM_STDERR]	= (flushfunct_t)flush_stderr,
	[OM_STDOUT]	= (flushfunct_t)flush_stdout,
	[OM_SYSLOG]	= (flushfunct_t)syslog_flush,
};

void _critical(const char *const function_name, const char *fmt, ...) {
	if (*quiet)
		return;

redmine authored
178 179 180 181
	struct timespec abs_time;
	clock_gettime(CLOCK_REALTIME , &abs_time);
	abs_time.tv_sec += 1;

redmine authored
182 183
	if (error_mutex_p != NULL)
		pthread_mutex_timedlock(error_mutex_p, &abs_time);
redmine authored
184

redmine authored
185 186 187 188 189
	outputmethod_t method = *outputmethod;

	{
		va_list args;
		pthread_t thread = pthread_self();
redmine authored
190
		pid_t pid = getpid();
redmine authored
191

redmine authored
192
		outfunct[method]("Critical (pid: %u; thread: %p): %s(): ", pid, thread, function_name);
redmine authored
193 194 195 196 197 198 199
		va_start(args, fmt);
		voutfunct[method](fmt, args);
		va_end(args);
		outfunct[method](" (current errno %i: %s)", errno, strerror(errno));
		flushfunct[method](LOG_CRIT);
	}

redmine authored
200
#ifdef BACKTRACE_SUPPORT
redmine authored
201 202 203 204 205 206 207 208 209 210
	{
		void  *buf[BACKTRACE_LENGTH];
		char **strings;
		int backtrace_len = backtrace((void **)buf, BACKTRACE_LENGTH);

		strings = backtrace_symbols(buf, backtrace_len);
		if (strings == NULL) {
			outfunct[method]("_critical(): Got error, but cannot print the backtrace. Current errno: %u: %s\n",
				errno, strerror(errno));
			flushfunct[method](LOG_CRIT);
redmine authored
211
			pthread_mutex_unlock(error_mutex_p);
redmine authored
212 213 214 215 216 217 218 219
			exit(EXIT_FAILURE);
		}

		for (int j = 1; j < backtrace_len; j++) {
			outfunct[method]("        %s", strings[j]);
			flushfunct[method](LOG_CRIT);
		}
	}
redmine authored
220
#endif
redmine authored
221

redmine authored
222 223
	if (error_mutex_p != NULL)
		pthread_mutex_unlock(error_mutex_p);
redmine authored
224 225

	error_deinit();
redmine authored
226 227 228 229 230 231 232 233 234 235 236 237 238 239
	exit(errno);

	return;
}

void _error(const char *const function_name, const char *fmt, ...) {
	va_list args;

	if (*quiet)
		return;

	if (*verbose < 1)
		return;

redmine authored
240 241
	if (error_mutex_p != NULL)
		pthread_mutex_reltimedlock(error_mutex_p, 0, OUTPUT_LOCK_TIMEOUT);
redmine authored
242

redmine authored
243
	pthread_t thread = pthread_self();
redmine authored
244
	pid_t pid = getpid();
redmine authored
245 246
	outputmethod_t method = *outputmethod;

redmine authored
247
	outfunct[method](*debug ? "Error (pid: %u; thread: %p): %s(): " : "Error: ", pid, thread, function_name);
redmine authored
248 249 250
	va_start(args, fmt);
	voutfunct[method](fmt, args);
	va_end(args);
redmine authored
251 252
	if (errno)
		outfunct[method](" (%i: %s)", errno, strerror(errno));
redmine authored
253 254
	flushfunct[method](LOG_ERR);

redmine authored
255 256
	if (error_mutex_p != NULL)
		pthread_mutex_unlock(error_mutex_p);
redmine authored
257 258 259 260 261 262 263 264 265 266 267 268
	return;
}

void _info(const char *const function_name, const char *fmt, ...) {
	va_list args;

	if (*quiet)
		return;

	if (*verbose < 3)
		return;

redmine authored
269 270
	if (error_mutex_p != NULL)
		pthread_mutex_reltimedlock(error_mutex_p, 0, OUTPUT_LOCK_TIMEOUT);
redmine authored
271

redmine authored
272
	pthread_t thread = pthread_self();
redmine authored
273
	pid_t pid = getpid();
redmine authored
274 275
	outputmethod_t method = *outputmethod;

redmine authored
276
	outfunct[method](*debug ? "Info (pid: %u; thread: %p): %s(): " : "Info: ", pid, thread, function_name);
redmine authored
277 278 279 280 281
	va_start(args, fmt);
	voutfunct[method](fmt, args);
	va_end(args);
	flushfunct[method](LOG_INFO);

redmine authored
282 283
	if (error_mutex_p != NULL)
		pthread_mutex_unlock(error_mutex_p);
redmine authored
284 285 286 287 288 289 290 291 292 293 294 295
	return;
}

void _warning(const char *const function_name, const char *fmt, ...) {
	va_list args;

	if (*quiet)
		return;

	if (*verbose < 2)
		return;

redmine authored
296 297
	if (error_mutex_p != NULL)
		pthread_mutex_reltimedlock(error_mutex_p, 0, OUTPUT_LOCK_TIMEOUT);
redmine authored
298

redmine authored
299
	pthread_t thread = pthread_self();
redmine authored
300
	pid_t pid = getpid();
redmine authored
301 302
	outputmethod_t method = *outputmethod;

redmine authored
303
	outfunct[method](*debug ? "Warning (pid: %u; thread: %p): %s(): " : "Warning: ", pid, thread, function_name);
redmine authored
304 305 306 307 308
	va_start(args, fmt);
	voutfunct[method](fmt, args);
	va_end(args);
	flushfunct[method](LOG_WARNING);

redmine authored
309 310
	if (error_mutex_p != NULL)
		pthread_mutex_unlock(error_mutex_p);
redmine authored
311 312 313
	return;
}

redmine authored
314
#ifdef _DEBUG_SUPPORT
redmine authored
315 316 317 318 319 320 321 322 323
void _debug(int debug_level, const char *const function_name, const char *fmt, ...) {
	va_list args;

	if (*quiet)
		return;

	if (debug_level > *debug)
		return;

redmine authored
324 325
	if (error_mutex_p != NULL)
		pthread_mutex_reltimedlock(error_mutex_p, 0, OUTPUT_LOCK_TIMEOUT);
redmine authored
326

redmine authored
327
	pthread_t thread = pthread_self();
redmine authored
328
	pid_t pid = getpid();
redmine authored
329 330
	outputmethod_t method = *outputmethod;

redmine authored
331
	outfunct[method]("Debug%u (pid: %u; thread: %p): %s(): ", debug_level, pid, thread, function_name);
redmine authored
332 333 334 335 336
	va_start(args, fmt);
	voutfunct[method](fmt, args);
	va_end(args);
	flushfunct[method](LOG_DEBUG);

redmine authored
337 338
	if (error_mutex_p != NULL)
		pthread_mutex_unlock(error_mutex_p);
redmine authored
339 340
	return;
}
redmine authored
341
#endif
redmine authored
342 343 344 345 346 347 348

void error_init(void *_outputmethod, int *_quiet, int *_verbose, int *_debug) {
	outputmethod 	= _outputmethod;
	quiet		= _quiet;
	verbose		= _verbose;
	debug		= _debug;

redmine authored
349
	openlog(NULL, SYSLOG_FLAGS, SYSLOG_FACILITY);
redmine authored
350

redmine authored
351 352 353
	return;
}

redmine authored
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
ipc_type_t ipc_type;
void error_init_ipc(ipc_type_t _ipc_type) {
	static pthread_mutex_t error_mutex = PTHREAD_MUTEX_INITIALIZER;
	ipc_type = _ipc_type;

	switch (ipc_type) {
		case IPCT_SHARED:
			pthread_mutex_init_shared(&error_mutex_p);
			break;
		case IPCT_PRIVATE:
			error_mutex_p = &error_mutex;
			pthread_mutex_init(error_mutex_p, NULL);
			break;
		default:
			critical ("Unknown ipc_type: %i", ipc_type);
	}

	return;
}

void error_deinit() {
	switch (ipc_type) {
		case IPCT_SHARED:
			pthread_mutex_destroy_shared(error_mutex_p);
redmine authored
378
			error_mutex_p = NULL;
redmine authored
379 380 381 382 383 384 385 386
			break;
		case IPCT_PRIVATE:
			break;
	}

	return;
}