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