xref: /haiku/src/servers/syslog_daemon/syslog_output.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
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