xref: /haiku/src/system/libroot/posix/syslog.cpp (revision b55a57da7173b9af0432bd3e148d03f06161d036)
1 /*
2  * Copyright 2003-2008, Axel Dörfler, axeld@pinc-software.de.
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 			sThreadContextSlot = slot;
71 		} else {
72 			while (sThreadContextSlot == -1)
73 				snooze(10000);
74 		}
75 	}
76 
77 	syslog_context *context = (syslog_context *)tls_get(sThreadContextSlot);
78 	if (context == NULL) {
79 		// try to allocate the context again; it might have
80 		// been deleted, or there was not enough memory last
81 		// time
82 		*tls_address(sThreadContextSlot) = context = allocate_context();
83 	}
84 	if (context != NULL)
85 		return context;
86 
87 	return &sTeamContext;
88 }
89 
90 
91 //! Prints simplified syslog message to stderr.
92 static void
93 message_to_console(syslog_context *context, const char *text, va_list args)
94 {
95 	if (context->ident[0])
96 		fprintf(stderr, "'%s' ", context->ident);
97 	if (context->options & LOG_PID)
98 		fprintf(stderr, "[%ld] ", find_thread(NULL));
99 
100 	vfprintf(stderr, text, args);
101 	fputc('\n', stderr);
102 }
103 
104 
105 /*!	Creates the message from the given context and sends it to the syslog
106 	daemon, if the priority mask matches.
107 	If the message couldn't be delivered, and LOG_CONS was set, it will
108 	redirect the message to stderr.
109 */
110 static void
111 send_syslog_message(syslog_context *context, int priority, const char *text,
112 	va_list args)
113 {
114 	int options = context->options;
115 
116 	// do we have to do anything?
117 	if ((context->mask & LOG_MASK(SYSLOG_PRIORITY(priority))) == 0)
118 		return;
119 
120 	port_id port = find_port(SYSLOG_PORT_NAME);
121 	if ((options & LOG_PERROR) != 0
122 		|| ((options & LOG_CONS) != 0 && port < B_OK)) {
123 		// if asked for, print out the (simplified) message on stderr
124 		message_to_console(context, text, args);
125 	}
126 	if (port < B_OK) {
127 		// apparently, there is no syslog daemon running;
128 		return;
129 	}
130 
131 	// adopt facility from openlog() if not yet set
132 	if (SYSLOG_FACILITY(priority) == 0)
133 		priority |= context->facility;
134 
135 	char buffer[2048];
136 	syslog_message &message = *(syslog_message *)&buffer[0];
137 
138 	message.from = find_thread(NULL);
139 	message.when = real_time_clock();
140 	message.options = options;
141 	message.priority = priority;
142 	strcpy(message.ident, context->ident);
143 
144 	int length = vsnprintf(message.message, sizeof(buffer)
145 		- sizeof(syslog_message), text, args);
146 	if (message.message + length - buffer < (int32)sizeof(buffer)) {
147 		if (length == 0 || message.message[length - 1] != '\n')
148 			message.message[length++] = '\n';
149 	} else
150 		buffer[length - 1] = '\n';
151 
152 	status_t status;
153 	do {
154 		// make sure the message gets send (if there is a valid port)
155 		status = write_port(port, SYSLOG_MESSAGE, &message,
156 			sizeof(syslog_message) + length);
157 	} while (status == B_INTERRUPTED);
158 
159 	if (status < B_OK && (options & LOG_CONS) != 0
160 		&& (options & LOG_PERROR) == 0) {
161 		// LOG_CONS redirects all output to the console in case contacting
162 		// the syslog daemon failed
163 		message_to_console(context, text, args);
164 	}
165 }
166 
167 
168 //	#pragma mark - POSIX API
169 
170 
171 void
172 closelog(void)
173 {
174 	closelog_thread();
175 }
176 
177 
178 void
179 openlog(const char *ident, int options, int facility)
180 {
181 	openlog_thread(ident, options, facility);
182 }
183 
184 
185 int
186 setlogmask(int priorityMask)
187 {
188 	return setlogmask_thread(priorityMask);
189 }
190 
191 
192 void
193 syslog(int priority, const char *message, ...)
194 {
195 	va_list args;
196 
197 	va_start(args, message);
198 	send_syslog_message(get_context(), priority, message, args);
199 	va_end(args);
200 }
201 
202 
203 //	#pragma mark - Be extensions
204 // ToDo: it would probably be better to export these symbols as weak symbols only
205 
206 
207 void
208 closelog_team(void)
209 {
210 	// nothing to do here...
211 }
212 
213 
214 void
215 openlog_team(const char *ident, int options, int facility)
216 {
217 	if (ident != NULL)
218 		strcpy(sTeamContext.ident, ident);
219 
220 	sTeamContext.options = options;
221 	sTeamContext.facility = SYSLOG_FACILITY(facility);
222 }
223 
224 
225 int
226 setlogmask_team(int priorityMask)
227 {
228 	int oldMask = sTeamContext.mask;
229 
230 	if (priorityMask != 0)
231 		sTeamContext.mask = priorityMask;
232 
233 	return oldMask;
234 }
235 
236 
237 void
238 log_team(int priority, const char *message, ...)
239 {
240 	va_list args;
241 
242 	va_start(args, message);
243 	send_syslog_message(&sTeamContext, priority, message, args);
244 	va_end(args);
245 }
246 
247 
248 void
249 closelog_thread(void)
250 {
251 	if (sThreadContextSlot < 0)
252 		return;
253 
254 	free(tls_get(sThreadContextSlot));
255 	*tls_address(sThreadContextSlot) = NULL;
256 }
257 
258 
259 void
260 openlog_thread(const char *ident, int options, int facility)
261 {
262 	syslog_context *context = get_context();
263 
264 	if (ident)
265 		strcpy(context->ident, ident);
266 
267 	context->options = options;
268 	context->facility = SYSLOG_FACILITY(facility);
269 }
270 
271 
272 int
273 setlogmask_thread(int priorityMask)
274 {
275 	syslog_context *context = get_context();
276 	int oldMask = context->mask;
277 
278 	if (priorityMask != 0)
279 		context->mask = priorityMask;
280 
281 	return oldMask;
282 }
283 
284 
285 void
286 log_thread(int priority, const char *message, ...)
287 {
288 	va_list args;
289 
290 	va_start(args, message);
291 	send_syslog_message(get_context(), priority, message, args);
292 	va_end(args);
293 }
294 
295 
296 //	#pragma mark - BSD extensions
297 
298 
299 void
300 vsyslog(int priority, const char *message, va_list args)
301 {
302 	send_syslog_message(get_context(), priority, message, args);
303 }
304 
305