xref: /haiku/src/system/libroot/posix/syslog.cpp (revision e5bc2a9e7a7025a6f1cd44938916ed36c3880253)
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 
149 	status_t status;
150 	do {
151 		// make sure the message gets send (if there is a valid port)
152 		status = write_port(port, SYSLOG_MESSAGE, &message,
153 			sizeof(syslog_message) + length);
154 	} while (status == B_INTERRUPTED);
155 
156 	if (status < B_OK && (options & LOG_CONS) != 0
157 		&& (options & LOG_PERROR) == 0) {
158 		// LOG_CONS redirects all output to the console in case contacting
159 		// the syslog daemon failed
160 		message_to_console(context, text, args);
161 	}
162 }
163 
164 
165 //	#pragma mark - POSIX API
166 
167 
168 void
169 closelog(void)
170 {
171 	closelog_thread();
172 }
173 
174 
175 void
176 openlog(const char *ident, int options, int facility)
177 {
178 	openlog_thread(ident, options, facility);
179 }
180 
181 
182 int
183 setlogmask(int priorityMask)
184 {
185 	return setlogmask_thread(priorityMask);
186 }
187 
188 
189 void
190 syslog(int priority, const char *message, ...)
191 {
192 	va_list args;
193 
194 	va_start(args, message);
195 	send_syslog_message(get_context(), priority, message, args);
196 	va_end(args);
197 }
198 
199 
200 //	#pragma mark - Be extensions
201 // ToDo: it would probably be better to export these symbols as weak symbols only
202 
203 
204 void
205 closelog_team(void)
206 {
207 	// nothing to do here...
208 }
209 
210 
211 void
212 openlog_team(const char *ident, int options, int facility)
213 {
214 	if (ident != NULL)
215 		strcpy(sTeamContext.ident, ident);
216 
217 	sTeamContext.options = options;
218 	sTeamContext.facility = SYSLOG_FACILITY(facility);
219 }
220 
221 
222 int
223 setlogmask_team(int priorityMask)
224 {
225 	int oldMask = sTeamContext.mask;
226 
227 	if (priorityMask != 0)
228 		sTeamContext.mask = priorityMask;
229 
230 	return oldMask;
231 }
232 
233 
234 void
235 log_team(int priority, const char *message, ...)
236 {
237 	va_list args;
238 
239 	va_start(args, message);
240 	send_syslog_message(&sTeamContext, priority, message, args);
241 	va_end(args);
242 }
243 
244 
245 void
246 closelog_thread(void)
247 {
248 	if (sThreadContextSlot < 0)
249 		return;
250 
251 	free(tls_get(sThreadContextSlot));
252 	*tls_address(sThreadContextSlot) = NULL;
253 }
254 
255 
256 void
257 openlog_thread(const char *ident, int options, int facility)
258 {
259 	syslog_context *context = get_context();
260 
261 	if (ident)
262 		strcpy(context->ident, ident);
263 
264 	context->options = options;
265 	context->facility = SYSLOG_FACILITY(facility);
266 }
267 
268 
269 int
270 setlogmask_thread(int priorityMask)
271 {
272 	syslog_context *context = get_context();
273 	int oldMask = context->mask;
274 
275 	if (priorityMask != 0)
276 		context->mask = priorityMask;
277 
278 	return oldMask;
279 }
280 
281 
282 void
283 log_thread(int priority, const char *message, ...)
284 {
285 	va_list args;
286 
287 	va_start(args, message);
288 	send_syslog_message(get_context(), priority, message, args);
289 	va_end(args);
290 }
291 
292 
293 //	#pragma mark - BSD extensions
294 
295 
296 void
297 vsyslog(int priority, const char *message, va_list args)
298 {
299 	send_syslog_message(get_context(), priority, message, args);
300 }
301 
302