xref: /haiku/src/system/libroot/posix/syslog.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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 
20 struct syslog_context {
21 	char	ident[B_OS_NAME_LENGTH];
22 	int16	mask;
23 	int16	facility;
24 	int32	options;
25 };
26 
27 static syslog_context sTeamContext = {
28 	"",
29 	-1,
30 	LOG_USER,
31 	LOG_CONS
32 };
33 static int32 sThreadContextSlot = -1;
34 static port_id sSystemLoggerPort = -1;
35 
36 
37 static syslog_context *
38 allocate_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 */
58 static syslog_context *
59 get_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.
96 static void
97 message_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 */
111 static port_id
112 get_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 */
130 static void
131 send_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 
191 void
192 closelog(void)
193 {
194 	closelog_thread();
195 }
196 
197 
198 void
199 openlog(const char *ident, int options, int facility)
200 {
201 	openlog_thread(ident, options, facility);
202 }
203 
204 
205 int
206 setlogmask(int priorityMask)
207 {
208 	return setlogmask_thread(priorityMask);
209 }
210 
211 
212 void
213 syslog(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 
227 void
228 closelog_team(void)
229 {
230 	// nothing to do here...
231 }
232 
233 
234 void
235 openlog_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 
245 int
246 setlogmask_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 
257 void
258 log_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 
268 void
269 closelog_thread(void)
270 {
271 	if (sThreadContextSlot < 0)
272 		return;
273 
274 	free(tls_get(sThreadContextSlot));
275 	*tls_address(sThreadContextSlot) = NULL;
276 }
277 
278 
279 void
280 openlog_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 
292 int
293 setlogmask_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 
305 void
306 log_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 
319 void
320 vsyslog(int priority, const char *message, va_list args)
321 {
322 	send_syslog_message(get_context(), priority, message, args);
323 }
324 
325