summaryrefslogtreecommitdiffstats
path: root/src/server/memlog.c
blob: 12de858f270eb01cfd31bc93d4847f945a384514 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*
 * This file is part of the Distributed Network Block Device 3
 *
 * Copyright(c) 2011-2012 Simon Rettberg
 *
 * This file may be licensed under the terms of of the
 * GNU General Public License Version 2 (the ``GPL'').
 *
 * Software distributed under the License is distributed
 * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
 * express or implied. See the GPL for the specific language
 * governing rights and limitations.
 *
 * You should have received a copy of the GPL along with this
 * program. If not, go to http://www.gnu.org/licenses/gpl.html
 * or write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include <stdarg.h>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include "locks.h"

#define MAX(a,b) (a > b ? a : b)

static pthread_spinlock_t logLock;

#define LINE_LEN 500
#define LINE_COUNT 50

typedef struct
{
	uint16_t len;
	char text[LINE_LEN];
} LogLine;

// This will be used as a ring buffer
static volatile LogLine *logBuffer = NULL;
// bufferPos counts up, use modulo LINE_COUNT to get array index
static volatile int bufferPos = 0;

void initmemlog()
{
	// Use main spinlock to make sure we really init only once
	if ( logBuffer ) return;
	spin_init( &logLock, PTHREAD_PROCESS_PRIVATE );
	logBuffer = (LogLine *)calloc( LINE_COUNT, sizeof(LogLine) );
}

void memlogf(const char *fmt, ...)
{
	if ( logBuffer == NULL ) {
		va_list ap;
		va_start( ap, fmt );
		vprintf( fmt, ap );
		va_end( ap );
		printf( "\n" );
		return; // Not initialized yet
	}
	va_list ap;
	int ret;
	time_t rawtime;
	struct tm *timeinfo;
	time( &rawtime );
	timeinfo = localtime( &rawtime );
	spin_lock( &logLock );
	LogLine * const line = (LogLine *)&(logBuffer[bufferPos % LINE_COUNT]);
	const size_t offset = strftime( line->text, LINE_LEN, "[%d.%m. %H:%M:%S] ", timeinfo );
	if ( offset == 0 ) *line->text = '\0';
	va_start( ap, fmt );
	ret = vsnprintf( line->text + offset, LINE_LEN - offset, fmt, ap );
	va_end( ap );
	char *end = line->text + strlen( line->text );
	while ( end > line->text && *--end == '\n' )
		*end = '\0'; // remove trailing \n
	// glibc 2.0 would return -1 if the buffer was too small
	// glibc 2.1 would return the number of bytes required if the buffer was too small
	// so to be safe either way, let strlen do the job
	line->len = strlen( line->text );
	if ( ret > 0 || line->len > 0 ) ++bufferPos;
	spin_unlock( &logLock );
	puts( line->text );
}

char *fetchlog(int maxlines)
{
	if ( !logBuffer ) return NULL ;
	if ( maxlines <= 0 || maxlines > LINE_COUNT ) maxlines = LINE_COUNT;
	const int start = MAX(0, bufferPos - maxlines);
	int len = 1, i;
	//printf("Outputting log from %d to %d\n", start, bufferPos);
	spin_lock( &logLock );
	// Determine required buffer space for all log lines
	for (i = start; i < bufferPos; ++i) {
		if ( logBuffer[i % LINE_COUNT].len > 0 ) {
			len += logBuffer[i % LINE_COUNT].len + 1;
		}
	}
	//printf("Have to allocate %d bytes\n", len);
	// Allocate buffer. If this is a bottleneck because of malloc, consider passing a buffer to the function that the caller allocates on the stack
	char *retval = (char *)calloc( len, sizeof(char) );
	if ( retval == NULL ) goto endFunction;
	// Concatenate all log lines, delimit using '\n'
	char *pos = retval;
	for (i = start; i < bufferPos; ++i) {
		LogLine * const line = (LogLine *)&(logBuffer[i % LINE_COUNT]);
		if ( line->len > 0 ) {
			memcpy( pos, (char *)line->text, line->len );
			pos += line->len;
			*pos++ = '\n';
		}
	}
	*pos = '\0';
	endFunction:
	spin_unlock( &logLock );
	return retval;
}