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