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_output.h" 8 9 #include <FindDirectory.h> 10 #include <Path.h> 11 #include <driver_settings.h> 12 13 #include <syslog.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <unistd.h> 18 #include <sys/stat.h> 19 20 21 static const char *kFacilities[] = { 22 "KERN", "USER", "MAIL", "DAEMON", 23 "AUTH", "SYSLOGD", "LPR", "NEWS", 24 "UUCP", "CRON", "AUTHPRIV", "FTP", 25 "", "", "", "", 26 "LOCAL0", "LOCAL1", "LOCAL2", "LOCAL3", 27 "LOCAL4", "LOCAL5", "LOCAL6", "LOCAL7", 28 NULL 29 }; 30 static const int32 kNumFacilities = 24; 31 32 static int sLog = -1; 33 static char sLastMessage[1024]; 34 static thread_id sLastThread; 35 static int32 sRepeatCount; 36 static size_t sLogMaxSize = 524288; // 512kB 37 static bool sLogTimeStamps = false; 38 39 40 /*! 41 Creates the log file if not yet existing, or renames the old 42 log file, if it's too big already. 43 */ 44 static status_t 45 prepare_output() 46 { 47 bool needNew = true; 48 bool tooLarge = false; 49 50 if (sLog >= 0) { 51 // check file size 52 struct stat stat; 53 if (fstat(sLog, &stat) == 0) { 54 if (stat.st_size < sLogMaxSize) 55 needNew = false; 56 else 57 tooLarge = true; 58 } 59 } 60 61 if (needNew) { 62 // close old file; it'll be (re)moved soon 63 if (sLog >= 0) 64 close(sLog); 65 66 // get path (and create it if necessary) 67 BPath base; 68 find_directory(B_COMMON_LOG_DIRECTORY, &base, true); 69 70 BPath syslog(base); 71 syslog.Append("syslog"); 72 73 // move old file if it already exists 74 if (tooLarge) { 75 BPath oldlog(base); 76 oldlog.Append("syslog.old"); 77 78 remove(oldlog.Path()); 79 rename(syslog.Path(), oldlog.Path()); 80 81 // ToDo: just remove old file if space on device is tight? 82 } 83 84 bool haveSyslog = sLog >= 0; 85 86 // open file 87 sLog = open(syslog.Path(), O_APPEND | O_CREAT | O_WRONLY, 644); 88 if (!haveSyslog && sLog >=0) { 89 // first time open, check file size again 90 prepare_output(); 91 } 92 } 93 94 return sLog >= 0 ? B_OK : B_ERROR; 95 } 96 97 98 static status_t 99 write_to_log(const char *buffer, int32 length) 100 { 101 if (sRepeatCount > 0) { 102 char repeat[64]; 103 ssize_t size = snprintf(repeat, sizeof(repeat), 104 "Last message repeated %ld time%s\n", sRepeatCount, 105 sRepeatCount > 1 ? "s" : ""); 106 sRepeatCount = 0; 107 if (write(sLog, repeat, strlen(repeat)) < size) 108 return B_ERROR; 109 } 110 111 if (write(sLog, buffer, length) < length) 112 return B_ERROR; 113 114 return B_OK; 115 } 116 117 118 static void 119 syslog_output(syslog_message &message) 120 { 121 char header[128]; 122 int32 headerLength; 123 int32 pos = 0; 124 125 if (sLogTimeStamps) { 126 // parse & nicely print the time stamp from the message 127 struct tm when; 128 localtime_r(&message.when, &when); 129 pos = strftime(header, sizeof(header), "%Y-%m-%d %H:%M:%S ", &when); 130 } 131 132 // add facility 133 int facility = SYSLOG_FACILITY_INDEX(message.priority); 134 if (facility >= kNumFacilities) 135 facility = SYSLOG_FACILITY_INDEX(LOG_USER); 136 pos += snprintf(header + pos, sizeof(header) - pos, "%s", kFacilities[facility]); 137 138 // add ident/thread ID 139 if (message.ident[0] == '\0') { 140 // ToDo: find out team name? 141 } else 142 pos += snprintf(header + pos, sizeof(header) - pos, " '%s'", message.ident); 143 144 if (message.options & LOG_PID) 145 pos += snprintf(header + pos, sizeof(header) - pos, "[%ld]", message.from); 146 147 headerLength = pos + strlcpy(header + pos, ": ", sizeof(header) - pos); 148 if (headerLength >= (int32)sizeof(header)) 149 headerLength = sizeof(header) - 1; 150 151 // add header to every line of the message and write it to the syslog 152 153 char buffer[SYSLOG_MESSAGE_BUFFER_SIZE]; 154 pos = 0; 155 156 while (true) { 157 strcpy(buffer, header); 158 int32 length; 159 160 const char *newLine = strchr(message.message + pos, '\n'); 161 if (newLine != NULL) { 162 length = newLine - message.message + 1 - pos; 163 strlcpy(buffer + headerLength, message.message + pos, length + 1); 164 pos += length; 165 } else { 166 length = strlcpy(buffer + headerLength, message.message + pos, 167 sizeof(buffer) - headerLength); 168 if (length == 0) 169 break; 170 } 171 172 length += headerLength; 173 174 if (length >= (int32)sizeof(buffer)) 175 length = sizeof(buffer) - 1; 176 177 if (message.from == sLastThread 178 && !strncmp(buffer + headerLength, sLastMessage, sizeof(sLastMessage))) { 179 // we got this message already 180 sRepeatCount++; 181 } else { 182 // dump message line 183 184 if (prepare_output() < B_OK 185 || write_to_log(buffer, length) < B_OK) { 186 // cannot write to syslog! 187 break; 188 } 189 190 // save this message to suppress repeated messages 191 strlcpy(sLastMessage, buffer + headerLength, sizeof(sLastMessage)); 192 sLastThread = message.from; 193 } 194 195 if (newLine == NULL || !newLine[1]) { 196 // wrote last line of output 197 break; 198 } 199 } 200 } 201 202 203 void 204 init_syslog_output(SyslogDaemon *daemon) 205 { 206 void *handle; 207 208 // get kernel syslog settings 209 handle = load_driver_settings("kernel"); 210 if (handle != NULL) { 211 sLogTimeStamps = get_driver_boolean_parameter(handle, 212 "syslog_time_stamps", false, false); 213 const char *param = get_driver_parameter(handle, 214 "syslog_max_size", "0", "0"); 215 int maxSize = strtol(param, NULL, 0); 216 if (strchr(param, 'k') || strchr(param, 'K')) 217 maxSize *= 1024; 218 else if (strchr(param, 'm') || strchr(param, 'M')) 219 maxSize *= 1048576; 220 if (maxSize > 0) 221 sLogMaxSize = maxSize; 222 223 unload_driver_settings(handle); 224 } 225 226 daemon->AddHandler(syslog_output); 227 } 228 229