1/*
2 * Copyright 2003-2015, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "SyslogDaemon.h"
8
9#include <stdio.h>
10#include <string.h>
11
12#include <Alert.h>
13#include <Catalog.h>
14#include <FindDirectory.h>
15#include <Font.h>
16#include <Path.h>
17#include <TextView.h>
18
19#include <LaunchRoster.h>
20#include <syscalls.h>
21#include <syslog_daemon.h>
22
23#include "listener_output.h"
24#include "syslog_output.h"
25
26
27#undef B_TRANSLATION_CONTEXT
28#define B_TRANSLATION_CONTEXT "SyslogDaemon"
29
30
31static const int32 kQuitDaemon = 'quit';
32
33
34SyslogDaemon::SyslogDaemon()
35	:
36	BServer(B_SYSTEM_LOGGER_SIGNATURE, false, NULL),
37	fHandlerLock("handler lock")
38{
39}
40
41
42void
43SyslogDaemon::ReadyToRun()
44{
45	fPort = BLaunchRoster().GetPort("logger");
46	fDaemon = spawn_thread(_DaemonThread, "daemon", B_NORMAL_PRIORITY, this);
47
48	if (fPort >= 0 && fDaemon >= 0) {
49		_kern_register_syslog_daemon(fPort);
50
51		init_syslog_output(this);
52		init_listener_output(this);
53
54		resume_thread(fDaemon);
55	} else
56		Quit();
57}
58
59
60void
61SyslogDaemon::AboutRequested()
62{
63	BPath path;
64	find_directory(B_SYSTEM_LOG_DIRECTORY, &path);
65	path.Append("syslog");
66
67	BString name(B_TRANSLATE("Syslog Daemon"));
68	BString message;
69	snprintf(message.LockBuffer(512), 512,
70		B_TRANSLATE("%s\n\nThis daemon collects all system messages and writes them to the "
71			"system-wide log at \"%s\".\n\n"), name.String(), path.Path());
72	message.UnlockBuffer();
73
74	BAlert* alert = new BAlert(name.String(), message.String(),
75		B_TRANSLATE("OK"));
76	BTextView* view = alert->TextView();
77	BFont font;
78
79	view->SetStylable(true);
80
81	view->GetFont(&font);
82	font.SetSize(21);
83	font.SetFace(B_BOLD_FACE);
84	view->SetFontAndColor(0, name.Length(), &font);
85
86	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
87	alert->Go(NULL);
88}
89
90
91bool
92SyslogDaemon::QuitRequested()
93{
94	write_port(fPort, kQuitDaemon, NULL, 0);
95	wait_for_thread(fDaemon, NULL);
96
97	return true;
98}
99
100
101void
102SyslogDaemon::MessageReceived(BMessage* message)
103{
104	switch (message->what) {
105		case SYSLOG_ADD_LISTENER:
106		{
107			BMessenger messenger;
108			if (message->FindMessenger("target", &messenger) == B_OK)
109				add_listener(&messenger);
110			break;
111		}
112		case SYSLOG_REMOVE_LISTENER:
113		{
114			BMessenger messenger;
115			if (message->FindMessenger("target", &messenger) == B_OK)
116				remove_listener(&messenger);
117			break;
118		}
119
120		default:
121			BApplication::MessageReceived(message);
122	}
123}
124
125
126void
127SyslogDaemon::AddHandler(handler_func function)
128{
129	fHandlers.AddItem((void*)function);
130}
131
132
133void
134SyslogDaemon::_Daemon()
135{
136	char buffer[SYSLOG_MESSAGE_BUFFER_SIZE + 1];
137	syslog_message& message = *(syslog_message*)buffer;
138	int32 code;
139
140	while (true) {
141		ssize_t bytesRead = read_port(fPort, &code, &message, sizeof(buffer));
142		if (bytesRead == B_BAD_PORT_ID) {
143			// we've been quit
144			break;
145		}
146
147		if (code == kQuitDaemon)
148			return;
149
150		// if we don't get what we want, ignore it
151		if (bytesRead < (ssize_t)sizeof(syslog_message)
152			|| code != SYSLOG_MESSAGE)
153			continue;
154
155		// add terminating null byte
156		message.message[bytesRead - sizeof(syslog_message)] = '\0';
157
158		if (!message.message[0]) {
159			// ignore empty messages
160			continue;
161		}
162
163		fHandlerLock.Lock();
164
165		for (int32 i = fHandlers.CountItems(); i-- > 0;) {
166			handler_func handle = (handler_func)fHandlers.ItemAt(i);
167
168			handle(message);
169		}
170
171		fHandlerLock.Unlock();
172	}
173}
174
175
176int32
177SyslogDaemon::_DaemonThread(void* data)
178{
179	((SyslogDaemon*)data)->_Daemon();
180	return B_OK;
181}
182
183
184// #pragma mark -
185
186
187int
188main(int argc, char** argv)
189{
190	SyslogDaemon daemon;
191	daemon.Run();
192
193	return 0;
194}
195