summaryrefslogtreecommitdiffstats
path: root/lib/compat/externalcommandlistener.cpp
blob: b61813beb811377cdbff9c502654a3ec1c8fad5f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */

#include "compat/externalcommandlistener.hpp"
#include "compat/externalcommandlistener-ti.cpp"
#include "icinga/externalcommandprocessor.hpp"
#include "base/configtype.hpp"
#include "base/logger.hpp"
#include "base/exception.hpp"
#include "base/application.hpp"
#include "base/statsfunction.hpp"

using namespace icinga;

REGISTER_TYPE(ExternalCommandListener);

REGISTER_STATSFUNCTION(ExternalCommandListener, &ExternalCommandListener::StatsFunc);

void ExternalCommandListener::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&)
{
	DictionaryData nodes;

	for (const ExternalCommandListener::Ptr& externalcommandlistener : ConfigType::GetObjectsByType<ExternalCommandListener>()) {
		nodes.emplace_back(externalcommandlistener->GetName(), 1); //add more stats
	}

	status->Set("externalcommandlistener", new Dictionary(std::move(nodes)));
}

/**
 * Starts the component.
 */
void ExternalCommandListener::Start(bool runtimeCreated)
{
	ObjectImpl<ExternalCommandListener>::Start(runtimeCreated);

	Log(LogInformation, "ExternalCommandListener")
		<< "'" << GetName() << "' started.";

	Log(LogWarning, "ExternalCommandListener")
		<< "This feature is DEPRECATED and may be removed in future releases. Check the roadmap at https://github.com/Icinga/icinga2/milestones";
#ifndef _WIN32
	String path = GetCommandPath();
	m_CommandThread = std::thread([this, path]() { CommandPipeThread(path); });
	m_CommandThread.detach();
#endif /* _WIN32 */
}

/**
 * Stops the component.
 */
void ExternalCommandListener::Stop(bool runtimeRemoved)
{
	Log(LogInformation, "ExternalCommandListener")
		<< "'" << GetName() << "' stopped.";

	ObjectImpl<ExternalCommandListener>::Stop(runtimeRemoved);
}

#ifndef _WIN32
void ExternalCommandListener::CommandPipeThread(const String& commandPath)
{
	Utility::SetThreadName("Command Pipe");

	struct stat statbuf;
	bool fifo_ok = false;

	if (lstat(commandPath.CStr(), &statbuf) >= 0) {
		if (S_ISFIFO(statbuf.st_mode) && access(commandPath.CStr(), R_OK) >= 0) {
			fifo_ok = true;
		} else {
			Utility::Remove(commandPath);
		}
	}

	mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;

	if (!fifo_ok && mkfifo(commandPath.CStr(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0) {
		Log(LogCritical, "ExternalCommandListener")
			<< "mkfifo() for fifo path '" << commandPath << "' failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
		return;
	}

	/* mkfifo() uses umask to mask off some bits, which means we need to chmod() the
	 * fifo to get the right mask. */
	if (chmod(commandPath.CStr(), mode) < 0) {
		Log(LogCritical, "ExternalCommandListener")
			<< "chmod() on fifo '" << commandPath << "' failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
		return;
	}

	for (;;) {
		int fd = open(commandPath.CStr(), O_RDWR | O_NONBLOCK);

		if (fd < 0) {
			Log(LogCritical, "ExternalCommandListener")
				<< "open() for fifo path '" << commandPath << "' failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
			return;
		}

		FIFO::Ptr fifo = new FIFO();
		Socket::Ptr sock = new Socket(fd);
		StreamReadContext src;

		for (;;) {
			sock->Poll(true, false);

			char buffer[8192];
			size_t rc;

			try {
				rc = sock->Read(buffer, sizeof(buffer));
			} catch (const std::exception& ex) {
				/* We have read all data. */
				if (errno == EAGAIN)
					continue;

				Log(LogWarning, "ExternalCommandListener")
					<< "Cannot read from command pipe." << DiagnosticInformation(ex);
				break;
			}

			/* Empty pipe (EOF) */
			if (rc == 0)
				continue;

			fifo->Write(buffer, rc);

			for (;;) {
				String command;
				StreamReadStatus srs = fifo->ReadLine(&command, src);

				if (srs != StatusNewItem)
					break;

				try {
					Log(LogInformation, "ExternalCommandListener")
						<< "Executing external command: " << command;

					ExternalCommandProcessor::Execute(command);
				} catch (const std::exception& ex) {
					Log(LogWarning, "ExternalCommandListener")
						<< "External command failed: " << DiagnosticInformation(ex, false);
					Log(LogNotice, "ExternalCommandListener")
						<< "External command failed: " << DiagnosticInformation(ex, true);
				}
			}
		}
	}
}
#endif /* _WIN32 */