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 *
allocate_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 *
get_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
message_to_console(syslog_context * context,const char * text,va_list args)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
get_system_logger_port()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
send_syslog_message(syslog_context * context,int priority,const char * text,va_list args)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
closelog(void)192 closelog(void)
193 {
194 closelog_thread();
195 }
196
197
198 void
openlog(const char * ident,int options,int facility)199 openlog(const char *ident, int options, int facility)
200 {
201 openlog_thread(ident, options, facility);
202 }
203
204
205 int
setlogmask(int priorityMask)206 setlogmask(int priorityMask)
207 {
208 return setlogmask_thread(priorityMask);
209 }
210
211
212 void
syslog(int priority,const char * message,...)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
closelog_team(void)228 closelog_team(void)
229 {
230 // nothing to do here...
231 }
232
233
234 void
openlog_team(const char * ident,int options,int facility)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
setlogmask_team(int priorityMask)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
log_team(int priority,const char * message,...)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
closelog_thread(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
openlog_thread(const char * ident,int options,int facility)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
setlogmask_thread(int priorityMask)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
log_thread(int priority,const char * message,...)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
vsyslog(int priority,const char * message,va_list args)320 vsyslog(int priority, const char *message, va_list args)
321 {
322 send_syslog_message(get_context(), priority, message, args);
323 }
324
325