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