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