]> granicus.if.org Git - icinga2/blob - lib/cli/nodeutility.cpp
Remove unused includes
[icinga2] / lib / cli / nodeutility.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 "cli/nodeutility.hpp"
21 #include "cli/clicommand.hpp"
22 #include "cli/variableutility.hpp"
23 #include "base/logger.hpp"
24 #include "base/application.hpp"
25 #include "base/tlsutility.hpp"
26 #include "base/convert.hpp"
27 #include "base/utility.hpp"
28 #include "base/scriptglobal.hpp"
29 #include "base/json.hpp"
30 #include "base/netstring.hpp"
31 #include "base/stdiostream.hpp"
32 #include "base/debug.hpp"
33 #include "base/objectlock.hpp"
34 #include "base/console.hpp"
35 #include "base/exception.hpp"
36 #include "base/configwriter.hpp"
37 #include <boost/algorithm/string/join.hpp>
38 #include <boost/algorithm/string/replace.hpp>
39 #include <fstream>
40 #include <iostream>
41
42 using namespace icinga;
43
44 String NodeUtility::GetConstantsConfPath()
45 {
46         return Application::GetSysconfDir() + "/icinga2/constants.conf";
47 }
48
49 /*
50  * Node Setup helpers
51  */
52
53 int NodeUtility::GenerateNodeIcingaConfig(const std::vector<std::string>& endpoints, const std::vector<String>& globalZones)
54 {
55         Array::Ptr my_config = new Array();
56
57         Array::Ptr my_master_zone_members = new Array();
58
59         String master_zone_name = "master"; //TODO: Find a better name.
60
61         for (const String& endpoint : endpoints) {
62                 /* extract all --endpoint arguments and store host,port info */
63                 std::vector<String> tokens = endpoint.Split(",");
64
65                 Dictionary::Ptr my_master_endpoint = new Dictionary();
66
67                 if (tokens.size() > 1) {
68                         String host = tokens[1].Trim();
69
70                         if (!host.IsEmpty())
71                                 my_master_endpoint->Set("host", host);
72                 }
73
74                 if (tokens.size() > 2) {
75                         String port = tokens[2].Trim();
76
77                         if (!port.IsEmpty())
78                                 my_master_endpoint->Set("port", port);
79                 }
80
81                 String cn = tokens[0].Trim();
82                 my_master_endpoint->Set("__name", cn);
83                 my_master_endpoint->Set("__type", "Endpoint");
84
85                 /* save endpoint in master zone */
86                 my_master_zone_members->Add(cn);
87
88                 my_config->Add(my_master_endpoint);
89         }
90
91         /* add the master zone to the config */
92         my_config->Add(new Dictionary({
93                 { "__name", master_zone_name },
94                 { "__type", "Zone" },
95                 { "endpoints", my_master_zone_members }
96         }));
97
98         /* store the local generated node configuration */
99         my_config->Add(new Dictionary({
100                 { "__name", new ConfigIdentifier("NodeName") },
101                 { "__type", "Endpoint" }
102         }));
103
104         my_config->Add(new Dictionary({
105                 { "__name", new ConfigIdentifier("ZoneName") },
106                 { "__type", "Zone" },
107                 { "parent", master_zone_name }, //set the master zone as parent
108                 { "endpoints", new Array({ new ConfigIdentifier("ZoneName") }) }
109         }));
110
111         for (const String& globalzone : globalZones) {
112                 my_config->Add(new Dictionary({
113                         { "__name", globalzone },
114                         { "__type", "Zone" },
115                         { "global", true }
116                 }));
117         }
118
119         /* write the newly generated configuration */
120         String zones_path = Application::GetSysconfDir() + "/icinga2/zones.conf";
121
122         NodeUtility::WriteNodeConfigObjects(zones_path, my_config);
123
124         return 0;
125 }
126
127 int NodeUtility::GenerateNodeMasterIcingaConfig(const std::vector<String>& globalZones)
128 {
129         Array::Ptr my_config = new Array();
130
131         /* store the local generated node master configuration */
132         my_config->Add(new Dictionary({
133                 { "__name", new ConfigIdentifier("NodeName") },
134                 { "__type", "Endpoint" }
135         }));
136
137         my_config->Add(new Dictionary({
138                 { "__name", new ConfigIdentifier("ZoneName") },
139                 { "__type", "Zone" },
140                 { "endpoints", new Array({ new ConfigIdentifier("NodeName") }) }
141         }));
142
143         for (const String& globalzone : globalZones) {
144                 my_config->Add(new Dictionary({
145                         { "__name", globalzone },
146                         { "__type", "Zone" },
147                         { "global", true }
148                 }));
149         }
150
151         /* write the newly generated configuration */
152         String zones_path = Application::GetSysconfDir() + "/icinga2/zones.conf";
153
154         NodeUtility::WriteNodeConfigObjects(zones_path, my_config);
155
156         return 0;
157 }
158
159 bool NodeUtility::WriteNodeConfigObjects(const String& filename, const Array::Ptr& objects)
160 {
161         Log(LogInformation, "cli")
162                 << "Dumping config items to file '" << filename << "'.";
163
164         /* create a backup first */
165         CreateBackupFile(filename);
166
167         String path = Utility::DirName(filename);
168
169         Utility::MkDirP(path, 0755);
170
171         String user = ScriptGlobal::Get("RunAsUser");
172         String group = ScriptGlobal::Get("RunAsGroup");
173
174         if (!Utility::SetFileOwnership(path, user, group)) {
175                 Log(LogWarning, "cli")
176                         << "Cannot set ownership for user '" << user << "' group '" << group << "' on path '" << path << "'. Verify it yourself!";
177         }
178         if (!Utility::SetFileOwnership(filename, user, group)) {
179                 Log(LogWarning, "cli")
180                         << "Cannot set ownership for user '" << user << "' group '" << group << "' on path '" << path << "'. Verify it yourself!";
181         }
182
183         std::fstream fp;
184         String tempFilename = Utility::CreateTempFile(filename + ".XXXXXX", 0644, fp);
185
186         fp << "/*\n";
187         fp << " * Generated by Icinga 2 node setup commands\n";
188         fp << " * on " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << "\n";
189         fp << " */\n\n";
190
191         ObjectLock olock(objects);
192         for (const Dictionary::Ptr& object : objects) {
193                 SerializeObject(fp, object);
194         }
195
196         fp << std::endl;
197         fp.close();
198
199 #ifdef _WIN32
200         _unlink(filename.CStr());
201 #endif /* _WIN32 */
202
203         if (rename(tempFilename.CStr(), filename.CStr()) < 0) {
204                 BOOST_THROW_EXCEPTION(posix_error()
205                         << boost::errinfo_api_function("rename")
206                         << boost::errinfo_errno(errno)
207                         << boost::errinfo_file_name(tempFilename));
208         }
209
210         return true;
211 }
212
213
214 /*
215  * We generally don't overwrite files without backup before
216  */
217 bool NodeUtility::CreateBackupFile(const String& target, bool is_private)
218 {
219         if (!Utility::PathExists(target))
220                 return false;
221
222         String backup = target + ".orig";
223
224         if (Utility::PathExists(backup)) {
225                 Log(LogInformation, "cli")
226                         << "Backup file '" << backup << "' already exists. Skipping backup.";
227                 return false;
228         }
229
230         Utility::CopyFile(target, backup);
231
232 #ifndef _WIN32
233         if (is_private)
234                 chmod(backup.CStr(), 0600);
235 #endif /* _WIN32 */
236
237         Log(LogInformation, "cli")
238                 << "Created backup file '" << backup << "'.";
239
240         return true;
241 }
242
243 void NodeUtility::SerializeObject(std::ostream& fp, const Dictionary::Ptr& object)
244 {
245         fp << "object ";
246         ConfigWriter::EmitIdentifier(fp, object->Get("__type"), false);
247         fp << " ";
248         ConfigWriter::EmitValue(fp, 0, object->Get("__name"));
249         fp << " {\n";
250
251         ObjectLock olock(object);
252         for (const Dictionary::Pair& kv : object) {
253                 if (kv.first == "__type" || kv.first == "__name")
254                         continue;
255
256                 fp << "\t";
257                 ConfigWriter::EmitIdentifier(fp, kv.first, true);
258                 fp << " = ";
259                 ConfigWriter::EmitValue(fp, 1, kv.second);
260                 fp << "\n";
261         }
262
263         fp << "}\n\n";
264 }
265
266 void NodeUtility::UpdateConstant(const String& name, const String& value)
267 {
268         String constantsConfPath = NodeUtility::GetConstantsConfPath();
269
270         Log(LogInformation, "cli")
271                 << "Updating '" << name << "' constant in '" << constantsConfPath << "'.";
272
273         NodeUtility::CreateBackupFile(constantsConfPath);
274
275         std::ifstream ifp(constantsConfPath.CStr());
276         std::fstream ofp;
277         String tempFile = Utility::CreateTempFile(constantsConfPath + ".XXXXXX", 0644, ofp);
278
279         bool found = false;
280
281         std::string line;
282         while (std::getline(ifp, line)) {
283                 if (line.find("const " + name + " = ") != std::string::npos) {
284                         ofp << "const " + name + " = \"" + value + "\"\n";
285                         found = true;
286                 } else
287                         ofp << line << "\n";
288         }
289
290         if (!found)
291                 ofp << "const " + name + " = \"" + value + "\"\n";
292
293         ifp.close();
294         ofp.close();
295
296 #ifdef _WIN32
297         _unlink(constantsConfPath.CStr());
298 #endif /* _WIN32 */
299
300         if (rename(tempFile.CStr(), constantsConfPath.CStr()) < 0) {
301                 BOOST_THROW_EXCEPTION(posix_error()
302                         << boost::errinfo_api_function("rename")
303                         << boost::errinfo_errno(errno)
304                         << boost::errinfo_file_name(constantsConfPath));
305         }
306 }