1 /* 2 * Copyright 2003-2010, Axel Dörfler, axeld@pinc-software.de. 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 /*! Creates the log file if not yet existing, or renames the old 41 log file, if it's too big already. 42 */ 43 static status_t 44 prepare_output() 45 { 46 bool needNew = true; 47 bool tooLarge = false; 48 49 if (sLog >= 0) { 50 // check file size 51 struct stat stat; 52 if (fstat(sLog, &stat) == 0) { 53 if (stat.st_size < (off_t)sLogMaxSize) 54 needNew = false; 55 else 56 tooLarge = true; 57 } 58 } 59 60 if (needNew) { 61 // close old file; it'll be (re)moved soon 62 if (sLog >= 0) 63 close(sLog); 64 65 // get path (and create it if necessary) 66 BPath base; 67 find_directory(B_COMMON_LOG_DIRECTORY, &base, true); 68 69 BPath syslog(base); 70 syslog.Append("syslog"); 71 72 // move old file if it already exists 73 if (tooLarge) { 74 BPath oldlog(base); 75 oldlog.Append("syslog.old"); 76 77 remove(oldlog.Path()); 78 rename(syslog.Path(), oldlog.Path()); 79 80 // ToDo: just remove old file if space on device is tight? 81 } 82 83 bool haveSyslog = sLog >= 0; 84 85 // open file 86 sLog = open(syslog.Path(), O_APPEND | O_CREAT | O_WRONLY, 0644); 87 if (!haveSyslog && sLog >= 0) { 88 // first time open, check file size again 89 prepare_output(); 90 } 91 } 92 93 return sLog >= 0 ? B_OK : B_ERROR; 94 } 95 96 97 static status_t 98 write_to_log(const char *buffer, int32 length) 99 { 100 if (sRepeatCount > 0) { 101 char repeat[64]; 102 ssize_t size = snprintf(repeat, sizeof(repeat), 103 "Last message repeated %" B_PRId32 " time%s\n", sRepeatCount, 104 sRepeatCount > 1 ? "s" : ""); 105 sRepeatCount = 0; 106 if (write(sLog, repeat, strlen(repeat)) < size) 107 return B_ERROR; 108 } 109 110 if (write(sLog, buffer, length) < length) 111 return B_ERROR; 112 113 return B_OK; 114 } 115 116 117 static void 118 syslog_output(syslog_message &message) 119 { 120 char header[128]; 121 int32 headerLength; 122 int32 pos = 0; 123 124 if (sLogTimeStamps) { 125 // parse & nicely print the time stamp from the message 126 struct tm when; 127 localtime_r(&message.when, &when); 128 pos = strftime(header, sizeof(header), "%Y-%m-%d %H:%M:%S ", &when); 129 } 130 131 // add facility 132 int facility = SYSLOG_FACILITY_INDEX(message.priority); 133 if (facility >= kNumFacilities) 134 facility = SYSLOG_FACILITY_INDEX(LOG_USER); 135 pos += snprintf(header + pos, sizeof(header) - pos, "%s", 136 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'", 143 message.ident); 144 } 145 146 if ((message.options & LOG_PID) != 0) { 147 pos += snprintf(header + pos, sizeof(header) - pos, "[%" B_PRId32 "]", 148 message.from); 149 } 150 151 headerLength = pos + strlcpy(header + pos, ": ", sizeof(header) - pos); 152 if (headerLength >= (int32)sizeof(header)) 153 headerLength = sizeof(header) - 1; 154 155 // add header to every line of the message and write it to the syslog 156 157 char buffer[SYSLOG_MESSAGE_BUFFER_SIZE]; 158 pos = 0; 159 160 while (true) { 161 strcpy(buffer, header); 162 int32 length; 163 164 const char *newLine = strchr(message.message + pos, '\n'); 165 if (newLine != NULL) { 166 length = newLine - message.message + 1 - pos; 167 strlcpy(buffer + headerLength, message.message + pos, length + 1); 168 pos += length; 169 } else { 170 length = strlcpy(buffer + headerLength, message.message + pos, 171 sizeof(buffer) - headerLength); 172 if (length == 0) 173 break; 174 } 175 176 length += headerLength; 177 178 if (length >= (int32)sizeof(buffer)) 179 length = sizeof(buffer) - 1; 180 181 if (message.from == sLastThread 182 && !strncmp(buffer + headerLength, sLastMessage, 183 sizeof(sLastMessage))) { 184 // we got this message already 185 sRepeatCount++; 186 } else { 187 // dump message line 188 189 if (prepare_output() < B_OK 190 || write_to_log(buffer, length) < B_OK) { 191 // cannot write to syslog! 192 break; 193 } 194 195 // save this message to suppress repeated messages 196 strlcpy(sLastMessage, buffer + headerLength, sizeof(sLastMessage)); 197 sLastThread = message.from; 198 } 199 200 if (newLine == NULL || !newLine[1]) { 201 // wrote last line of output 202 break; 203 } 204 } 205 } 206 207 208 void 209 init_syslog_output(SyslogDaemon *daemon) 210 { 211 void *handle; 212 213 // get kernel syslog settings 214 handle = load_driver_settings("kernel"); 215 if (handle != NULL) { 216 sLogTimeStamps = get_driver_boolean_parameter(handle, 217 "syslog_time_stamps", false, false); 218 const char *param = get_driver_parameter(handle, 219 "syslog_max_size", "0", "0"); 220 int maxSize = strtol(param, NULL, 0); 221 if (strchr(param, 'k') || strchr(param, 'K')) 222 maxSize *= 1024; 223 else if (strchr(param, 'm') || strchr(param, 'M')) 224 maxSize *= 1048576; 225 if (maxSize > 0) 226 sLogMaxSize = maxSize; 227 228 unload_driver_settings(handle); 229 } 230 231 daemon->AddHandler(syslog_output); 232 } 233