vfs: check userland buffers before reading them.
[haiku.git] / src / servers / syslog_daemon / syslog_output.cpp
blob0678ee2993fbf0a4c4018e52e3b8d2ce2aa571bf
1 /*
2 * Copyright 2003-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "syslog_output.h"
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>
16 #include <FindDirectory.h>
17 #include <Path.h>
18 #include <driver_settings.h>
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
30 static const int32 kNumFacilities = 24;
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;
40 /*! Creates the log file if not yet existing, or renames the old
41 log file, if it's too big already.
43 static status_t
44 prepare_output()
46 bool needNew = true;
47 bool tooLarge = false;
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;
60 if (needNew) {
61 // close old file; it'll be (re)moved soon
62 if (sLog >= 0)
63 close(sLog);
65 // get path (and create it if necessary)
66 BPath base;
67 find_directory(B_SYSTEM_LOG_DIRECTORY, &base, true);
69 BPath syslog(base);
70 syslog.Append("syslog");
72 // move old file if it already exists
73 if (tooLarge) {
74 BPath oldlog(base);
75 oldlog.Append("syslog.old");
77 remove(oldlog.Path());
78 rename(syslog.Path(), oldlog.Path());
80 // ToDo: just remove old file if space on device is tight?
83 bool haveSyslog = sLog >= 0;
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();
93 return sLog >= 0 ? B_OK : B_ERROR;
97 static status_t
98 write_to_log(const char *buffer, int32 length)
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;
110 if (write(sLog, buffer, length) < length)
111 return B_ERROR;
113 return B_OK;
117 static void
118 syslog_output(syslog_message &message)
120 char header[128];
121 int32 headerLength;
122 int32 pos = 0;
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);
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]);
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);
146 if ((message.options & LOG_PID) != 0) {
147 pos += snprintf(header + pos, sizeof(header) - pos, "[%" B_PRId32 "]",
148 message.from);
151 headerLength = pos + strlcpy(header + pos, ": ", sizeof(header) - pos);
152 if (headerLength >= (int32)sizeof(header))
153 headerLength = sizeof(header) - 1;
155 // add header to every line of the message and write it to the syslog
157 char buffer[SYSLOG_MESSAGE_BUFFER_SIZE];
158 pos = 0;
160 while (true) {
161 strcpy(buffer, header);
162 int32 length;
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;
176 length += headerLength;
178 if (length >= (int32)sizeof(buffer))
179 length = sizeof(buffer) - 1;
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
189 if (prepare_output() < B_OK
190 || write_to_log(buffer, length) < B_OK) {
191 // cannot write to syslog!
192 break;
195 // save this message to suppress repeated messages
196 strlcpy(sLastMessage, buffer + headerLength, sizeof(sLastMessage));
197 sLastThread = message.from;
200 if (newLine == NULL || !newLine[1]) {
201 // wrote last line of output
202 break;
208 void
209 init_syslog_output(SyslogDaemon *daemon)
211 void *handle;
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;
228 unload_driver_settings(handle);
231 daemon->AddHandler(syslog_output);