]> granicus.if.org Git - icinga2/blob - lib/compat/externalcommandlistener.cpp
Merge pull request #6009 from Icinga/fix/build-fix-gcc
[icinga2] / lib / compat / externalcommandlistener.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/)  *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "compat/externalcommandlistener.hpp"
21 #include "compat/externalcommandlistener-ti.cpp"
22 #include "icinga/externalcommandprocessor.hpp"
23 #include "base/configtype.hpp"
24 #include "base/logger.hpp"
25 #include "base/exception.hpp"
26 #include "base/application.hpp"
27 #include "base/statsfunction.hpp"
28
29 using namespace icinga;
30
31 REGISTER_TYPE(ExternalCommandListener);
32
33 REGISTER_STATSFUNCTION(ExternalCommandListener, &ExternalCommandListener::StatsFunc);
34
35 void ExternalCommandListener::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&)
36 {
37         DictionaryData nodes;
38
39         for (const ExternalCommandListener::Ptr& externalcommandlistener : ConfigType::GetObjectsByType<ExternalCommandListener>()) {
40                 nodes.emplace_back(externalcommandlistener->GetName(), 1); //add more stats
41         }
42
43         status->Set("externalcommandlistener", new Dictionary(std::move(nodes)));
44 }
45
46 /**
47  * Starts the component.
48  */
49 void ExternalCommandListener::Start(bool runtimeCreated)
50 {
51         ObjectImpl<ExternalCommandListener>::Start(runtimeCreated);
52
53         Log(LogInformation, "ExternalCommandListener")
54                 << "'" << GetName() << "' started.";
55
56 #ifndef _WIN32
57         m_CommandThread = std::thread(std::bind(&ExternalCommandListener::CommandPipeThread, this, GetCommandPath()));
58         m_CommandThread.detach();
59 #endif /* _WIN32 */
60 }
61
62 /**
63  * Stops the component.
64  */
65 void ExternalCommandListener::Stop(bool runtimeRemoved)
66 {
67         Log(LogInformation, "ExternalCommandListener")
68                 << "'" << GetName() << "' stopped.";
69
70         ObjectImpl<ExternalCommandListener>::Stop(runtimeRemoved);
71 }
72
73 #ifndef _WIN32
74 void ExternalCommandListener::CommandPipeThread(const String& commandPath)
75 {
76         Utility::SetThreadName("Command Pipe");
77
78         struct stat statbuf;
79         bool fifo_ok = false;
80
81         if (lstat(commandPath.CStr(), &statbuf) >= 0) {
82                 if (S_ISFIFO(statbuf.st_mode) && access(commandPath.CStr(), R_OK) >= 0) {
83                         fifo_ok = true;
84                 } else {
85                         if (unlink(commandPath.CStr()) < 0) {
86                                 BOOST_THROW_EXCEPTION(posix_error()
87                                         << boost::errinfo_api_function("unlink")
88                                         << boost::errinfo_errno(errno)
89                                         << boost::errinfo_file_name(commandPath));
90                         }
91                 }
92         }
93
94         mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
95
96         if (!fifo_ok && mkfifo(commandPath.CStr(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0) {
97                 Log(LogCritical, "ExternalCommandListener")
98                         << "mkfifo() for fifo path '" << commandPath << "' failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
99                 return;
100         }
101
102         /* mkfifo() uses umask to mask off some bits, which means we need to chmod() the
103          * fifo to get the right mask. */
104         if (chmod(commandPath.CStr(), mode) < 0) {
105                 Log(LogCritical, "ExternalCommandListener")
106                         << "chmod() on fifo '" << commandPath << "' failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
107                 return;
108         }
109
110         for (;;) {
111                 int fd = open(commandPath.CStr(), O_RDWR | O_NONBLOCK);
112
113                 if (fd < 0) {
114                         Log(LogCritical, "ExternalCommandListener")
115                                 << "open() for fifo path '" << commandPath << "' failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
116                         return;
117                 }
118
119                 FIFO::Ptr fifo = new FIFO();
120                 Socket::Ptr sock = new Socket(fd);
121                 StreamReadContext src;
122
123                 for (;;) {
124                         sock->Poll(true, false);
125
126                         char buffer[8192];
127                         size_t rc;
128
129                         try {
130                                 rc = sock->Read(buffer, sizeof(buffer));
131                         } catch (const std::exception& ex) {
132                                 /* We have read all data. */
133                                 if (errno == EAGAIN)
134                                         continue;
135
136                                 Log(LogWarning, "ExternalCommandListener")
137                                         << "Cannot read from command pipe." << DiagnosticInformation(ex);
138                                 break;
139                         }
140
141                         /* Empty pipe (EOF) */
142                         if (rc == 0)
143                                 continue;
144
145                         fifo->Write(buffer, rc);
146
147                         for (;;) {
148                                 String command;
149                                 StreamReadStatus srs = fifo->ReadLine(&command, src);
150
151                                 if (srs != StatusNewItem)
152                                         break;
153
154                                 try {
155                                         Log(LogInformation, "ExternalCommandListener")
156                                                 << "Executing external command: " << command;
157
158                                         ExternalCommandProcessor::Execute(command);
159                                 } catch (const std::exception& ex) {
160                                         Log(LogWarning, "ExternalCommandListener")
161                                                 << "External command failed: " << DiagnosticInformation(ex, false);
162                                         Log(LogNotice, "ExternalCommandListener")
163                                                 << "External command failed: " << DiagnosticInformation(ex, true);
164                                 }
165                         }
166                 }
167         }
168 }
169 #endif /* _WIN32 */