1/*
2 * Copyright 2003-2015, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <syslog.h>
8
9#include <stdlib.h>
10#include <stdio.h>
11#include <string.h>
12#include <stdarg.h>
13
14#include <launch.h>
15#include <syslog_daemon.h>
16#include <TLS.h>
17#include <util/KMessage.h>
18
19
20struct syslog_context {
21	char	ident[B_OS_NAME_LENGTH];
22	int16	mask;
23	int16	facility;
24	int32	options;
25};
26
27static syslog_context sTeamContext = {
28	"",
29	-1,
30	LOG_USER,
31	LOG_CONS
32};
33static int32 sThreadContextSlot = -1;
34static port_id sSystemLoggerPort = -1;
35
36
37static syslog_context *
38allocate_context()
39{
40	syslog_context *context = (syslog_context *)malloc(sizeof(syslog_context));
41	if (context == NULL)
42		return NULL;
43
44	// inherit the attributes of the team context
45	memcpy(context, &sTeamContext, sizeof(syslog_context));
46	return context;
47}
48
49
50/*! This function returns the current syslog context structure.
51	If there is none for the current thread, it will create one
52	that inherits the context attributes from the team and put it
53	into TLS.
54	If it could not allocate a thread context, it will return the
55	team context; this function is guaranteed to return a valid
56	syslog context.
57*/
58static syslog_context *
59get_context()
60{
61	if (sThreadContextSlot == B_NO_MEMORY)
62		return &sTeamContext;
63
64	if (sThreadContextSlot < 0) {
65		static int32 lock = 0;
66		if (atomic_add(&lock, 1) == 0) {
67			int32 slot = tls_allocate();
68
69			if (slot < 0) {
70				sThreadContextSlot = B_NO_MEMORY;
71				return &sTeamContext;
72			}
73
74			sThreadContextSlot = slot;
75		} else {
76			while (sThreadContextSlot == -1)
77				snooze(10000);
78		}
79	}
80
81	syslog_context *context = (syslog_context *)tls_get(sThreadContextSlot);
82	if (context == NULL) {
83		// try to allocate the context again; it might have
84		// been deleted, or there was not enough memory last
85		// time
86		*tls_address(sThreadContextSlot) = context = allocate_context();
87	}
88	if (context != NULL)
89		return context;
90
91	return &sTeamContext;
92}
93
94
95//! Prints simplified syslog message to stderr.
96static void
97message_to_console(syslog_context *context, const char *text, va_list args)
98{
99	if (context->ident[0])
100		fprintf(stderr, "'%s' ", context->ident);
101	if (context->options & LOG_PID)
102		fprintf(stderr, "[%" B_PRId32 "] ", find_thread(NULL));
103
104	vfprintf(stderr, text, args);
105	fputc('\n', stderr);
106}
107
108
109/*!	Retrieves the port of the system logger from the launch_daemon.
110*/
111static port_id
112get_system_logger_port()
113{
114	if (sSystemLoggerPort >= 0)
115		return sSystemLoggerPort;
116
117	BPrivate::KMessage data;
118	if (BPrivate::get_launch_data(B_SYSTEM_LOGGER_SIGNATURE, data) == B_OK)
119		sSystemLoggerPort = data.GetInt32("logger_port", -1);
120
121	return sSystemLoggerPort;
122}
123
124
125/*!	Creates the message from the given context and sends it to the syslog
126	daemon, if the priority mask matches.
127	If the message couldn't be delivered, and LOG_CONS was set, it will
128	redirect the message to stderr.
129*/
130static void
131send_syslog_message(syslog_context *context, int priority, const char *text,
132	va_list args)
133{
134	int options = context->options;
135
136	// do we have to do anything?
137	if ((context->mask & LOG_MASK(SYSLOG_PRIORITY(priority))) == 0)
138		return;
139
140	port_id port = get_system_logger_port();
141	if ((options & LOG_PERROR) != 0
142		|| ((options & LOG_CONS) != 0 && port < B_OK)) {
143		// if asked for, print out the (simplified) message on stderr
144		message_to_console(context, text, args);
145	}
146	if (port < B_OK) {
147		// apparently, there is no syslog daemon running;
148		return;
149	}
150
151	// adopt facility from openlog() if not yet set
152	if (SYSLOG_FACILITY(priority) == 0)
153		priority |= context->facility;
154
155	char buffer[2048];
156	syslog_message &message = *(syslog_message *)&buffer[0];
157
158	message.from = find_thread(NULL);
159	message.when = real_time_clock();
160	message.options = options;
161	message.priority = priority;
162	strcpy(message.ident, context->ident);
163
164	int length = vsnprintf(message.message, sizeof(buffer)
165		- sizeof(syslog_message), text, args);
166	if (message.message + length - buffer < (int32)sizeof(buffer)) {
167		if (length == 0 || message.message[length - 1] != '\n')
168			message.message[length++] = '\n';
169	} else
170		buffer[length - 1] = '\n';
171
172	status_t status;
173	do {
174		// make sure the message gets send (if there is a valid port)
175		status = write_port(port, SYSLOG_MESSAGE, &message,
176			sizeof(syslog_message) + length);
177	} while (status == B_INTERRUPTED);
178
179	if (status < B_OK && (options & LOG_CONS) != 0
180		&& (options & LOG_PERROR) == 0) {
181		// LOG_CONS redirects all output to the console in case contacting
182		// the syslog daemon failed
183		message_to_console(context, text, args);
184	}
185}
186
187
188//	#pragma mark - POSIX API
189
190
191void
192closelog(void)
193{
194	closelog_thread();
195}
196
197
198void
199openlog(const char *ident, int options, int facility)
200{
201	openlog_thread(ident, options, facility);
202}
203
204
205int
206setlogmask(int priorityMask)
207{
208	return setlogmask_thread(priorityMask);
209}
210
211
212void
213syslog(int priority, const char *message, ...)
214{
215	va_list args;
216
217	va_start(args, message);
218	send_syslog_message(get_context(), priority, message, args);
219	va_end(args);
220}
221
222
223//	#pragma mark - Be extensions
224// ToDo: it would probably be better to export these symbols as weak symbols only
225
226
227void
228closelog_team(void)
229{
230	// nothing to do here...
231}
232
233
234void
235openlog_team(const char *ident, int options, int facility)
236{
237	if (ident != NULL)
238		strlcpy(sTeamContext.ident, ident, sizeof(sTeamContext.ident));
239
240	sTeamContext.options = options;
241	sTeamContext.facility = SYSLOG_FACILITY(facility);
242}
243
244
245int
246setlogmask_team(int priorityMask)
247{
248	int oldMask = sTeamContext.mask;
249
250	if (priorityMask != 0)
251		sTeamContext.mask = priorityMask;
252
253	return oldMask;
254}
255
256
257void
258log_team(int priority, const char *message, ...)
259{
260	va_list args;
261
262	va_start(args, message);
263	send_syslog_message(&sTeamContext, priority, message, args);
264	va_end(args);
265}
266
267
268void
269closelog_thread(void)
270{
271	if (sThreadContextSlot < 0)
272		return;
273
274	free(tls_get(sThreadContextSlot));
275	*tls_address(sThreadContextSlot) = NULL;
276}
277
278
279void
280openlog_thread(const char *ident, int options, int facility)
281{
282	syslog_context *context = get_context();
283
284	if (ident)
285		strcpy(context->ident, ident);
286
287	context->options = options;
288	context->facility = SYSLOG_FACILITY(facility);
289}
290
291
292int
293setlogmask_thread(int priorityMask)
294{
295	syslog_context *context = get_context();
296	int oldMask = context->mask;
297
298	if (priorityMask != 0)
299		context->mask = priorityMask;
300
301	return oldMask;
302}
303
304
305void
306log_thread(int priority, const char *message, ...)
307{
308	va_list args;
309
310	va_start(args, message);
311	send_syslog_message(get_context(), priority, message, args);
312	va_end(args);
313}
314
315
316//	#pragma mark - BSD extensions
317
318
319void
320vsyslog(int priority, const char *message, va_list args)
321{
322	send_syslog_message(get_context(), priority, message, args);
323}
324
325