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