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