1 /* 2 ** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 ** Distributed under the terms of the OpenBeOS 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 static status_t 39 prepare_output() 40 { 41 bool needNew = true; 42 bool tooLarge = false; 43 44 if (sLog >= 0) { 45 // check file size 46 struct stat stat; 47 if (fstat(sLog, &stat) == 0) { 48 if (stat.st_size < kMaxLogSize) 49 needNew = false; 50 else 51 tooLarge = true; 52 } 53 } 54 55 if (needNew) { 56 // close old file; it'll be (re)moved soon 57 if (sLog >= 0) 58 close(sLog); 59 60 // get path (and create it if necessary) 61 BPath base; 62 find_directory(B_COMMON_LOG_DIRECTORY, &base, true); 63 64 BPath syslog(base); 65 syslog.Append("syslog"); 66 67 // move old file if it already exists 68 if (tooLarge) { 69 BPath oldlog(base); 70 oldlog.Append("syslog.old"); 71 72 remove(oldlog.Path()); 73 rename(syslog.Path(), oldlog.Path()); 74 75 // ToDo: just remove old file if space on device is tight? 76 } 77 78 bool haveSyslog = sLog >= 0; 79 80 // open file 81 sLog = open(syslog.Path(), O_APPEND | O_CREAT | O_WRONLY, 644); 82 if (!haveSyslog && sLog >=0) { 83 // first time open, check file size again 84 prepare_output(); 85 } 86 } 87 88 return sLog >= 0 ? B_OK : B_ERROR; 89 } 90 91 92 static status_t 93 write_to_log(const char *buffer, int32 length) 94 { 95 if (sRepeatCount > 0) { 96 char repeat[64]; 97 ssize_t size = snprintf(repeat, sizeof(repeat), 98 "Last message repeated %ld time%s\n", sRepeatCount, 99 sRepeatCount > 1 ? "s" : ""); 100 sRepeatCount = 0; 101 if (write(sLog, repeat, strlen(repeat)) < size) 102 return B_ERROR; 103 } 104 105 if (write(sLog, buffer, length) < length) 106 return B_ERROR; 107 108 return B_OK; 109 } 110 111 112 static void 113 syslog_output(syslog_message &message) 114 { 115 // did we get this message already? 116 if (message.from == sLastThread 117 && !strncmp(message.message, sLastMessage, sizeof(sLastMessage))) { 118 sRepeatCount++; 119 return; 120 } 121 122 char buffer[4096]; 123 124 #if 0 125 // parse & nicely print the time stamp from the message 126 struct tm when; 127 localtime_r(&message.when, &when); 128 int32 pos = strftime(buffer, sizeof(buffer), "%b %d, %H:%M:%S ", &when); 129 #else 130 int32 pos = 0; 131 #endif 132 133 // add facility 134 int facility = SYSLOG_FACILITY_INDEX(message.priority); 135 if (facility >= kNumFacilities) 136 facility = SYSLOG_FACILITY_INDEX(LOG_USER); 137 pos += snprintf(buffer + pos, sizeof(buffer) - pos, "%s", kFacilities[facility]); 138 139 // add ident/thread ID 140 if (message.ident[0] == '\0') { 141 // ToDo: find out team name? 142 } else 143 pos += snprintf(buffer + pos, sizeof(buffer) - pos, " '%s'", message.ident); 144 145 if (message.options & LOG_PID) 146 pos += snprintf(buffer + pos, sizeof(buffer) - pos, "[%ld]", message.from); 147 148 // add message itself 149 int32 length = pos + snprintf(buffer + pos, sizeof(buffer) - pos, ": %s\n", message.message); 150 151 // ToDo: be less lazy about it - there is virtually no reason to truncate the message 152 if (strlen(message.message) > sizeof(buffer) - pos - 1) 153 strcpy(&buffer[sizeof(buffer) - 8], "<TRUNC>\n"); 154 155 // dump message 156 157 if (prepare_output() < B_OK 158 || write_to_log(buffer, length) < B_OK) { 159 // cannot write to syslog! 160 fputs(buffer, stderr); 161 } 162 163 // save this message to suppress repeated messages 164 strlcpy(sLastMessage, message.message, sizeof(sLastMessage)); 165 sLastThread = message.from; 166 } 167 168 169 void 170 init_syslog_output(SyslogDaemon *daemon) 171 { 172 daemon->AddHandler(syslog_output); 173 } 174 175