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 <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <syslog.h>
14 #include <unistd.h>
15
16 #include <FindDirectory.h>
17 #include <Path.h>
18 #include <driver_settings.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
prepare_output()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_SYSTEM_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
write_to_log(const char * buffer,int32 length)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
syslog_output(syslog_message & message)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
init_syslog_output(SyslogDaemon * daemon)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