1 /* 2 * Copyright 2003-2008, Axel Dörfler, axeld@pinc-software.de. 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 sThreadContextSlot = slot; 71 } else { 72 while (sThreadContextSlot == -1) 73 snooze(10000); 74 } 75 } 76 77 syslog_context *context = (syslog_context *)tls_get(sThreadContextSlot); 78 if (context == NULL) { 79 // try to allocate the context again; it might have 80 // been deleted, or there was not enough memory last 81 // time 82 *tls_address(sThreadContextSlot) = context = allocate_context(); 83 } 84 if (context != NULL) 85 return context; 86 87 return &sTeamContext; 88 } 89 90 91 //! Prints simplified syslog message to stderr. 92 static void 93 message_to_console(syslog_context *context, const char *text, va_list args) 94 { 95 if (context->ident[0]) 96 fprintf(stderr, "'%s' ", context->ident); 97 if (context->options & LOG_PID) 98 fprintf(stderr, "[%ld] ", find_thread(NULL)); 99 100 vfprintf(stderr, text, args); 101 fputc('\n', stderr); 102 } 103 104 105 /*! Creates the message from the given context and sends it to the syslog 106 daemon, if the priority mask matches. 107 If the message couldn't be delivered, and LOG_CONS was set, it will 108 redirect the message to stderr. 109 */ 110 static void 111 send_syslog_message(syslog_context *context, int priority, const char *text, 112 va_list args) 113 { 114 int options = context->options; 115 116 // do we have to do anything? 117 if ((context->mask & LOG_MASK(SYSLOG_PRIORITY(priority))) == 0) 118 return; 119 120 port_id port = find_port(SYSLOG_PORT_NAME); 121 if ((options & LOG_PERROR) != 0 122 || ((options & LOG_CONS) != 0 && port < B_OK)) { 123 // if asked for, print out the (simplified) message on stderr 124 message_to_console(context, text, args); 125 } 126 if (port < B_OK) { 127 // apparently, there is no syslog daemon running; 128 return; 129 } 130 131 // adopt facility from openlog() if not yet set 132 if (SYSLOG_FACILITY(priority) == 0) 133 priority |= context->facility; 134 135 char buffer[2048]; 136 syslog_message &message = *(syslog_message *)&buffer[0]; 137 138 message.from = find_thread(NULL); 139 message.when = real_time_clock(); 140 message.options = options; 141 message.priority = priority; 142 strcpy(message.ident, context->ident); 143 144 int length = vsnprintf(message.message, sizeof(buffer) 145 - sizeof(syslog_message), text, args); 146 if (message.message + length - buffer < (int32)sizeof(buffer)) { 147 if (length == 0 || message.message[length - 1] != '\n') 148 message.message[length++] = '\n'; 149 } else 150 buffer[length - 1] = '\n'; 151 152 status_t status; 153 do { 154 // make sure the message gets send (if there is a valid port) 155 status = write_port(port, SYSLOG_MESSAGE, &message, 156 sizeof(syslog_message) + length); 157 } while (status == B_INTERRUPTED); 158 159 if (status < B_OK && (options & LOG_CONS) != 0 160 && (options & LOG_PERROR) == 0) { 161 // LOG_CONS redirects all output to the console in case contacting 162 // the syslog daemon failed 163 message_to_console(context, text, args); 164 } 165 } 166 167 168 // #pragma mark - POSIX API 169 170 171 void 172 closelog(void) 173 { 174 closelog_thread(); 175 } 176 177 178 void 179 openlog(const char *ident, int options, int facility) 180 { 181 openlog_thread(ident, options, facility); 182 } 183 184 185 int 186 setlogmask(int priorityMask) 187 { 188 return setlogmask_thread(priorityMask); 189 } 190 191 192 void 193 syslog(int priority, const char *message, ...) 194 { 195 va_list args; 196 197 va_start(args, message); 198 send_syslog_message(get_context(), priority, message, args); 199 va_end(args); 200 } 201 202 203 // #pragma mark - Be extensions 204 // ToDo: it would probably be better to export these symbols as weak symbols only 205 206 207 void 208 closelog_team(void) 209 { 210 // nothing to do here... 211 } 212 213 214 void 215 openlog_team(const char *ident, int options, int facility) 216 { 217 if (ident != NULL) 218 strcpy(sTeamContext.ident, ident); 219 220 sTeamContext.options = options; 221 sTeamContext.facility = SYSLOG_FACILITY(facility); 222 } 223 224 225 int 226 setlogmask_team(int priorityMask) 227 { 228 int oldMask = sTeamContext.mask; 229 230 if (priorityMask != 0) 231 sTeamContext.mask = priorityMask; 232 233 return oldMask; 234 } 235 236 237 void 238 log_team(int priority, const char *message, ...) 239 { 240 va_list args; 241 242 va_start(args, message); 243 send_syslog_message(&sTeamContext, priority, message, args); 244 va_end(args); 245 } 246 247 248 void 249 closelog_thread(void) 250 { 251 if (sThreadContextSlot < 0) 252 return; 253 254 free(tls_get(sThreadContextSlot)); 255 *tls_address(sThreadContextSlot) = NULL; 256 } 257 258 259 void 260 openlog_thread(const char *ident, int options, int facility) 261 { 262 syslog_context *context = get_context(); 263 264 if (ident) 265 strcpy(context->ident, ident); 266 267 context->options = options; 268 context->facility = SYSLOG_FACILITY(facility); 269 } 270 271 272 int 273 setlogmask_thread(int priorityMask) 274 { 275 syslog_context *context = get_context(); 276 int oldMask = context->mask; 277 278 if (priorityMask != 0) 279 context->mask = priorityMask; 280 281 return oldMask; 282 } 283 284 285 void 286 log_thread(int priority, const char *message, ...) 287 { 288 va_list args; 289 290 va_start(args, message); 291 send_syslog_message(get_context(), priority, message, args); 292 va_end(args); 293 } 294 295 296 // #pragma mark - BSD extensions 297 298 299 void 300 vsyslog(int priority, const char *message, va_list args) 301 { 302 send_syslog_message(get_context(), priority, message, args); 303 } 304 305