xref: /haiku/src/servers/syslog_daemon/syslog_output.cpp (revision d5cd5d63ff0ad395989db6cf4841a64d5b545d1d)
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
61 		BPath base;
62 		find_directory(/*B_COMMON_LOG_DIRECTORY*/B_COMMON_TEMP_DIRECTORY, &base);
63 			// ToDo: change to correct "which" parameter!
64 
65 		BPath syslog(base);
66 		syslog.Append("syslog");
67 
68 		// move old file if it already exists
69 		if (tooLarge) {
70 			BPath oldlog(base);
71 			oldlog.Append("syslog.old");
72 
73 			remove(oldlog.Path());
74 			rename(syslog.Path(), oldlog.Path());
75 
76 			// ToDo: just remove old file if space on device is tight?
77 		}
78 
79 		bool haveSyslog = sLog >= 0;
80 
81 		// open file
82 		sLog = open(syslog.Path(), O_APPEND | O_CREAT | O_WRONLY, 644);
83 		if (!haveSyslog && sLog >=0) {
84 			// first time open, check file size again
85 			prepare_output();
86 		}
87 	}
88 
89 	return sLog >= 0 ? B_OK : B_ERROR;
90 }
91 
92 
93 static status_t
94 write_to_log(const char *buffer, int32 length)
95 {
96 	if (sRepeatCount > 0) {
97 		char repeat[64];
98 		ssize_t size = snprintf(repeat, sizeof(repeat),
99 							"Last message repeated %ld time%s\n", sRepeatCount,
100 							sRepeatCount > 1 ? "s" : "");
101 		sRepeatCount = 0;
102 		if (write(sLog, repeat, strlen(repeat)) < size)
103 			return B_ERROR;
104 	}
105 
106 	if (write(sLog, buffer, length) < length)
107 		return B_ERROR;
108 
109 	return B_OK;
110 }
111 
112 
113 static void
114 syslog_output(syslog_message &message)
115 {
116 	// did we get this message already?
117 	if (message.from == sLastThread
118 		&& !strncmp(message.message, sLastMessage, sizeof(sLastMessage))) {
119 		sRepeatCount++;
120 		return;
121 	}
122 
123 	char buffer[4096];
124 
125 #if 0
126 	// parse & nicely print the time stamp from the message
127 	struct tm when;
128 	localtime_r(&message.when, &when);
129 	int32 pos = strftime(buffer, sizeof(buffer), "%b %d, %H:%M:%S ", &when);
130 #else
131 	int32 pos = 0;
132 #endif
133 
134 	// add facility
135 	int facility = SYSLOG_FACILITY_INDEX(message.priority);
136 	if (facility >= kNumFacilities)
137 		facility = SYSLOG_FACILITY_INDEX(LOG_USER);
138 	pos += snprintf(buffer + pos, sizeof(buffer) - pos, "%s", kFacilities[facility]);
139 
140 	// add ident/thread ID
141 	if (message.ident[0] == '\0') {
142 		// ToDo: find out team name?
143 	} else
144 		pos += snprintf(buffer + pos, sizeof(buffer) - pos, " '%s'", message.ident);
145 
146 	if (message.options & LOG_PID)
147 		pos += snprintf(buffer + pos, sizeof(buffer) - pos, "[%ld]", message.from);
148 
149 	// add message itself
150 	int32 length = pos + snprintf(buffer + pos, sizeof(buffer) - pos, ": %s\n", message.message);
151 
152 	// ToDo: be less lazy about it - there is virtually no reason to truncate the message
153 	if (strlen(message.message) > sizeof(buffer) - pos - 1)
154 		strcpy(&buffer[sizeof(buffer) - 8], "<TRUNC>\n");
155 
156 	// dump message
157 
158 	if (prepare_output() < B_OK
159 		|| write_to_log(buffer, length) < B_OK) {
160 		// cannot write to syslog!
161 		fputs(buffer, stderr);
162 	}
163 
164 	// save this message to suppress repeated messages
165 	strlcpy(sLastMessage, message.message, sizeof(sLastMessage));
166 	sLastThread = message.from;
167 }
168 
169 
170 void
171 init_syslog_output(SyslogDaemon *daemon)
172 {
173 	daemon->AddHandler(syslog_output);
174 }
175 
176