xref: /haiku/src/system/libroot/posix/syslog.cpp (revision 582da17386c4a192ca30270d6b0b95f561cf5843)
1 /*
2  * Copyright 2003-2007, 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 - public Be API
166 // ToDo: it would probably be better to export these symbols as weak symbols only
167 
168 
169 void
170 closelog_team(void)
171 {
172 	// nothing to do here...
173 }
174 
175 
176 void
177 openlog_team(const char *ident, int options, int facility)
178 {
179 	if (ident != NULL)
180 		strcpy(sTeamContext.ident, ident);
181 
182 	sTeamContext.options = options;
183 	sTeamContext.facility = SYSLOG_FACILITY(facility);
184 }
185 
186 
187 int
188 setlogmask_team(int priorityMask)
189 {
190 	int oldMask = sTeamContext.mask;
191 
192 	if (priorityMask != 0)
193 		sTeamContext.mask = priorityMask;
194 
195 	return oldMask;
196 }
197 
198 
199 void
200 log_team(int priority, const char *message, ...)
201 {
202 	va_list args;
203 
204 	va_start(args, message);
205 	send_syslog_message(&sTeamContext, priority, message, args);
206 	va_end(args);
207 }
208 
209 
210 void
211 closelog_thread(void)
212 {
213 	if (sThreadContextSlot < 0)
214 		return;
215 
216 	free(tls_get(sThreadContextSlot));
217 	*tls_address(sThreadContextSlot) = NULL;
218 }
219 
220 
221 void
222 openlog_thread(const char *ident, int options, int facility)
223 {
224 	syslog_context *context = get_context();
225 
226 	if (ident)
227 		strcpy(context->ident, ident);
228 
229 	context->options = options;
230 	context->facility = SYSLOG_FACILITY(facility);
231 }
232 
233 
234 int
235 setlogmask_thread(int priorityMask)
236 {
237 	syslog_context *context = get_context();
238 	int oldMask = context->mask;
239 
240 	if (priorityMask != 0)
241 		context->mask = priorityMask;
242 
243 	return oldMask;
244 }
245 
246 
247 void
248 log_thread(int priority, const char *message, ...)
249 {
250 	va_list args;
251 
252 	va_start(args, message);
253 	send_syslog_message(get_context(), priority, message, args);
254 	va_end(args);
255 }
256 
257 
258 //	#pragma mark - POSIX API
259 
260 
261 void
262 closelog(void)
263 {
264 	closelog_thread();
265 }
266 
267 
268 void
269 openlog(const char *ident, int options, int facility)
270 {
271 	openlog_thread(ident, options, facility);
272 }
273 
274 
275 int
276 setlogmask(int priorityMask)
277 {
278 	return setlogmask_thread(priorityMask);
279 }
280 
281 
282 void
283 syslog(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