Mercurial > irccd
annotate irccdctl/main.cpp @ 514:16b9ebfd3f08
Irccd: get rid of fs.hpp, closes #594
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 24 Oct 2017 12:52:20 +0200 |
parents | 928a40398dec |
children | e03521cf207b |
rev | line source |
---|---|
0 | 1 /* |
2 * main.cpp -- irccd controller main | |
3 * | |
394 | 4 * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> |
0 | 5 * |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
348 | 19 #include <unordered_map> |
20 | |
477
bf201d09ee6a
Irccd: use boost::filesystem (fs::exists), #594
David Demelier <markand@malikania.fr>
parents:
453
diff
changeset
|
21 #include <boost/filesystem.hpp> |
489
349fe29d86d5
Tests: switch to Boost, closes #680
David Demelier <markand@malikania.fr>
parents:
488
diff
changeset
|
22 #include <boost/timer/timer.hpp> |
477
bf201d09ee6a
Irccd: use boost::filesystem (fs::exists), #594
David Demelier <markand@malikania.fr>
parents:
453
diff
changeset
|
23 |
348 | 24 #include "alias.hpp" |
25 #include "cli.hpp" | |
340 | 26 #include "client.hpp" |
348 | 27 #include "ini.hpp" |
340 | 28 #include "irccdctl.hpp" |
348 | 29 #include "logger.hpp" |
30 #include "options.hpp" | |
31 #include "system.hpp" | |
32 #include "util.hpp" | |
0 | 33 |
348 | 34 using namespace std::string_literals; |
0 | 35 using namespace irccd; |
36 | |
348 | 37 namespace { |
38 | |
39 std::vector<std::unique_ptr<Cli>> commands; | |
40 std::unordered_map<std::string, Alias> aliases; | |
41 std::unique_ptr<Client> client; | |
42 std::unique_ptr<Irccdctl> irccdctl; | |
43 net::Address address; | |
44 | |
45 void usage() | |
46 { | |
47 bool first = true; | |
48 | |
49 for (const auto &cmd : commands) { | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
50 log::warning() << (first ? "usage: " : " ") << sys::program_name() << " " |
348 | 51 << cmd->usage() << std::endl; |
52 first = false; | |
53 } | |
54 | |
55 std::exit(1); | |
56 } | |
57 | |
58 void help() | |
59 { | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
60 log::warning() << "usage: " << sys::program_name() << " [options...] <command> [command-options...] [command-args...]\n\n"; |
348 | 61 log::warning() << "General options:\n"; |
62 log::warning() << "\t-c, --config file\tspecify the configuration file\n"; | |
357
c50769df3493
Irccdctl: improve help summary
David Demelier <markand@malikania.fr>
parents:
355
diff
changeset
|
63 log::warning() << "\t --help\t\tshow this help\n"; |
348 | 64 log::warning() << "\t-t, --type type\t\tspecify connection type\n"; |
65 log::warning() << "\t-v, --verbose\t\tbe verbose\n\n"; | |
66 log::warning() << "Available options for type ip and ipv6 (-t, --type):\n"; | |
67 log::warning() << "\t-h, --host address\tconnect to the specified address\n"; | |
68 log::warning() << "\t-p, --port port\t\tuse the specified port number\n\n"; | |
69 log::warning() << "Available options for type unix (-t, --type):\n"; | |
70 log::warning() << "\t-P, --path file\t\tconnect to the specified socket file\n\n"; | |
71 log::warning() << "Available commands:\n"; | |
72 | |
73 for (const auto &cmd : commands) | |
74 log::warning() << "\t" << std::left << std::setw(32) | |
75 << cmd->name() << cmd->summary() << std::endl; | |
76 | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
77 log::warning() << "\nFor more information on a command, type " << sys::program_name() << " help <command>" << std::endl; |
348 | 78 std::exit(1); |
79 } | |
80 | |
352
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
81 void help(const std::string &command) |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
82 { |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
83 auto it = std::find_if(commands.begin(), commands.end(), [&] (const auto &c) { |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
84 return c->name() == command; |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
85 }); |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
86 |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
87 if (it == commands.end()) { |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
88 log::warning() << "no command named " << command << std::endl; |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
89 } else { |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
90 log::warning() << "usage: " << sys::program_name() << " " << (*it)->usage() << "\n" << std::endl; |
352
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
91 log::warning() << (*it)->help() << std::endl; |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
92 } |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
93 |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
94 std::exit(1); |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
95 } |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
96 |
348 | 97 /* |
98 * Configuration file parsing. | |
99 * ------------------------------------------------------------------- | |
100 */ | |
101 | |
102 /* | |
103 * readConnectIp | |
104 * ------------------------------------------------------------------- | |
105 * | |
106 * Extract IP connection information from the config file. | |
107 * | |
108 * [connect] | |
109 * type = "ip" | |
110 * host = "ip or hostname" | |
111 * port = "port number or service" | |
112 * domain = "ipv4 or ipv6" (Optional, default: ipv4) | |
113 * ssl = true | false | |
114 */ | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
115 void readConnectIp(const ini::section &sc) |
348 | 116 { |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
117 ini::section::const_iterator it; |
348 | 118 std::string host, port; |
119 | |
120 if ((it = sc.find("host")) == sc.end()) | |
121 throw std::invalid_argument("missing host parameter"); | |
122 | |
123 host = it->value(); | |
124 | |
125 if ((it = sc.find("port")) == sc.end()) | |
126 throw std::invalid_argument("missing port parameter"); | |
127 | |
128 port = it->value(); | |
129 | |
130 int domain = AF_INET; | |
131 | |
428
ce1903f4bdb7
Fix #637: check for domain and family options
David Demelier <markand@malikania.fr>
parents:
394
diff
changeset
|
132 if ((it = sc.find("domain")) != sc.end() || (it = sc.find("family")) != sc.end()) { |
348 | 133 if (it->value() == "ipv6") { |
134 domain = AF_INET6; | |
135 } else if (it->value() == "ipv4") { | |
136 domain = AF_INET; | |
137 } else { | |
428
ce1903f4bdb7
Fix #637: check for domain and family options
David Demelier <markand@malikania.fr>
parents:
394
diff
changeset
|
138 throw std::invalid_argument("invalid family: " + it->value()); |
348 | 139 } |
140 } | |
141 | |
142 address = net::resolveOne(host, port, domain, SOCK_STREAM); | |
143 | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
144 if ((it = sc.find("ssl")) != sc.end() && util::is_boolean(it->value())) |
513
928a40398dec
CMake: split options and conditions, closes #716
David Demelier <markand@malikania.fr>
parents:
498
diff
changeset
|
145 #if defined(HAVE_SSL) |
348 | 146 client = std::make_unique<TlsClient>(); |
387
94b18a90e8f7
Misc: fix build without SSL
David Demelier <markand@malikania.fr>
parents:
372
diff
changeset
|
147 #else |
94b18a90e8f7
Misc: fix build without SSL
David Demelier <markand@malikania.fr>
parents:
372
diff
changeset
|
148 throw std::runtime_error("SSL disabled"); |
94b18a90e8f7
Misc: fix build without SSL
David Demelier <markand@malikania.fr>
parents:
372
diff
changeset
|
149 #endif |
348 | 150 else |
151 client = std::make_unique<Client>(); | |
152 } | |
153 | |
154 /* | |
155 * readConnectLocal | |
156 * ------------------------------------------------------------------- | |
157 * | |
158 * Extract local connection for Unix. | |
159 * | |
160 * [connect] | |
161 * type = "unix" | |
162 * path = "path to socket file" | |
163 */ | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
164 void readConnectLocal(const ini::section &sc) |
348 | 165 { |
166 #if !defined(IRCCD_SYSTEM_WINDOWS) | |
167 auto it = sc.find("path"); | |
168 | |
169 if (it == sc.end()) | |
170 throw std::invalid_argument("missing path parameter"); | |
171 | |
172 address = net::local::create(it->value()); | |
173 client = std::make_unique<Client>(); | |
174 #else | |
175 (void)sc; | |
176 | |
177 throw std::invalid_argument("unix connection not supported on Windows"); | |
178 #endif | |
179 } | |
180 | |
181 /* | |
182 * readConnect | |
183 * ------------------------------------------------------------------- | |
184 * | |
185 * Generic function for reading the [connect] section. | |
186 */ | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
187 void readConnect(const ini::section &sc) |
348 | 188 { |
189 auto it = sc.find("type"); | |
190 | |
191 if (it == sc.end()) | |
192 throw std::invalid_argument("missing type parameter"); | |
193 | |
194 if (it->value() == "ip") { | |
195 readConnectIp(sc); | |
196 } else if (it->value() == "unix") { | |
197 readConnectLocal(sc); | |
198 } else { | |
199 throw std::invalid_argument("invalid type given: " + it->value()); | |
200 } | |
201 | |
202 auto password = sc.find("password"); | |
203 | |
204 if (password != sc.end()) | |
205 client->setPassword(password->value()); | |
206 } | |
207 | |
208 /* | |
209 * readGeneral | |
210 * ------------------------------------------------------------------- | |
211 * | |
212 * Read the general section. | |
213 * | |
214 * [general] | |
215 * verbose = true | |
216 */ | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
217 void readGeneral(const ini::section &sc) |
348 | 218 { |
219 auto verbose = sc.find("verbose"); | |
220 | |
221 if (verbose != sc.end()) | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
222 log::set_verbose(util::is_boolean(verbose->value())); |
348 | 223 } |
224 | |
225 /* | |
372
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
226 * readAlias |
348 | 227 * ------------------------------------------------------------------- |
228 * | |
229 * Read aliases for irccdctl. | |
230 * | |
372
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
231 * [alias.<name>] |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
232 * cmd1 = ( "command", "arg1, "...", "argn" ) |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
233 * cmd2 = ( "command", "arg1, "...", "argn" ) |
348 | 234 */ |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
235 Alias readAlias(const ini::section &sc, const std::string &name) |
348 | 236 { |
372
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
237 Alias alias(name); |
348 | 238 |
372
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
239 /* |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
240 * Each defined option is a command that the user can call. The name is |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
241 * unused and serves as documentation purpose. |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
242 */ |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
243 for (const auto &option : sc) { |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
244 /* |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
245 * Iterate over the arguments which are usually a list and the first |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
246 * argument is a command name. |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
247 */ |
494
9fcdd3c9cd33
Irccd: switch to Boost.Format, closes #577
David Demelier <markand@malikania.fr>
parents:
489
diff
changeset
|
248 if (option.size() == 1 && option[0].empty()) |
9fcdd3c9cd33
Irccd: switch to Boost.Format, closes #577
David Demelier <markand@malikania.fr>
parents:
489
diff
changeset
|
249 throw std::runtime_error(util::sprintf("alias %s: missing command name in '%s'", name, option.key())); |
348 | 250 |
372
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
251 std::string command = option[0]; |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
252 std::vector<AliasArg> args(option.begin() + 1, option.end()); |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
253 |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
254 alias.emplace_back(std::move(command), std::move(args)); |
348 | 255 } |
372
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
256 |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
257 return alias; |
348 | 258 } |
259 | |
260 void read(const std::string &path) | |
0 | 261 { |
207
6635b9187d71
Irccd: switch to 4 spaces indent, #518
David Demelier <markand@malikania.fr>
parents:
95
diff
changeset
|
262 try { |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
263 ini::document doc = ini::read_file(path); |
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
264 ini::document::const_iterator it; |
0 | 265 |
348 | 266 if (!client && (it = doc.find("connect")) != doc.end()) |
267 readConnect(*it); | |
268 if ((it = doc.find("general")) != doc.end()) | |
269 readGeneral(*it); | |
372
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
270 |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
271 // [alias.*] sections. |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
272 for (const auto& sc : doc) { |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
273 if (sc.key().compare(0, 6, "alias.") == 0) { |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
274 auto name = sc.key().substr(6); |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
275 auto alias = readAlias(sc, name); |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
276 |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
277 aliases.emplace(std::move(name), std::move(alias)); |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
278 } |
6a7850696b86
Irccdctl: rework a bit how aliases are defined to allow better arguments parsing
David Demelier <markand@malikania.fr>
parents:
359
diff
changeset
|
279 } |
207
6635b9187d71
Irccd: switch to 4 spaces indent, #518
David Demelier <markand@malikania.fr>
parents:
95
diff
changeset
|
280 } catch (const std::exception &ex) { |
348 | 281 log::warning() << path << ": " << ex.what() << std::endl; |
207
6635b9187d71
Irccd: switch to 4 spaces indent, #518
David Demelier <markand@malikania.fr>
parents:
95
diff
changeset
|
282 } |
0 | 283 } |
348 | 284 |
285 /* | |
286 * Command line parsing. | |
287 * ------------------------------------------------------------------- | |
288 */ | |
289 | |
290 /* | |
291 * parseConnectIp | |
292 * ------------------------------------------------------------------ | |
293 * | |
294 * Parse internet connection from command line. | |
295 * | |
296 * -t ip | ipv6 | |
297 * -h host or ip | |
298 * -p port | |
299 */ | |
498
fbd80bfcf58d
Irccd: import new options, closes #713
David Demelier <markand@malikania.fr>
parents:
494
diff
changeset
|
300 void parseConnectIp(const option::result &options) |
348 | 301 { |
498
fbd80bfcf58d
Irccd: import new options, closes #713
David Demelier <markand@malikania.fr>
parents:
494
diff
changeset
|
302 option::result::const_iterator it; |
348 | 303 |
304 // Host (-h or --host). | |
305 std::string host; | |
306 | |
307 if ((it = options.find("-h")) == options.end() && (it = options.find("--host")) == options.end()) { | |
308 throw std::invalid_argument("missing host argument (-h or --host)"); | |
309 } | |
310 | |
311 host = it->second; | |
312 | |
313 // Port (-p or --port). | |
314 std::string port; | |
315 | |
316 if ((it = options.find("-p")) == options.end() && (it = options.find("--port")) == options.end()) { | |
317 throw std::invalid_argument("missing port argument (-p or --port)"); | |
318 } | |
319 | |
320 port = it->second; | |
321 | |
322 // Domain | |
323 int domain = AF_INET; | |
324 | |
325 if ((it = options.find("-t")) != options.end()) { | |
326 domain = it->second == "ipv6" ? AF_INET6 : AF_INET; | |
327 } else if ((it = options.find("--type")) != options.end()) { | |
328 domain = it->second == "ipv6" ? AF_INET6: AF_INET; | |
329 } | |
330 | |
331 address = net::resolveOne(host, port, domain, SOCK_STREAM); | |
332 client = std::make_unique<Client>(); | |
333 } | |
334 | |
335 /* | |
336 * parseConnectLocal | |
337 * ------------------------------------------------------------------ | |
338 * | |
339 * Parse local connection. | |
340 * | |
341 * -P file | |
342 */ | |
498
fbd80bfcf58d
Irccd: import new options, closes #713
David Demelier <markand@malikania.fr>
parents:
494
diff
changeset
|
343 void parseConnectLocal(const option::result &options) |
348 | 344 { |
345 #if !defined(IRCCD_SYSTEM_WINDOWS) | |
498
fbd80bfcf58d
Irccd: import new options, closes #713
David Demelier <markand@malikania.fr>
parents:
494
diff
changeset
|
346 option::result::const_iterator it; |
348 | 347 |
348 if ((it = options.find("-P")) == options.end() && (it = options.find("--path")) == options.end()) | |
349 throw std::invalid_argument("missing path parameter (-P or --path)"); | |
350 | |
351 address = net::local::create(it->second, false); | |
352 client = std::make_unique<Client>(); | |
353 #else | |
354 (void)options; | |
355 | |
356 throw std::invalid_argument("unix connection not supported on Windows"); | |
357 #endif | |
358 } | |
359 | |
360 /* | |
361 * parseConnect | |
362 * ------------------------------------------------------------------ | |
363 * | |
364 * Generic parsing of command line option for connection. | |
365 */ | |
498
fbd80bfcf58d
Irccd: import new options, closes #713
David Demelier <markand@malikania.fr>
parents:
494
diff
changeset
|
366 void parseConnect(const option::result &options) |
348 | 367 { |
368 assert(options.count("-t") > 0 || options.count("--type") > 0); | |
369 | |
370 auto it = options.find("-t"); | |
371 | |
372 if (it == options.end()) | |
373 it = options.find("--type"); | |
374 if (it->second == "ip" || it->second == "ipv6") | |
375 return parseConnectIp(options); | |
376 if (it->second == "unix") | |
377 return parseConnectLocal(options); | |
378 | |
379 throw std::invalid_argument("invalid type given: " + it->second); | |
380 } | |
381 | |
498
fbd80bfcf58d
Irccd: import new options, closes #713
David Demelier <markand@malikania.fr>
parents:
494
diff
changeset
|
382 option::result parse(int &argc, char **&argv) |
348 | 383 { |
384 // 1. Parse command line options. | |
498
fbd80bfcf58d
Irccd: import new options, closes #713
David Demelier <markand@malikania.fr>
parents:
494
diff
changeset
|
385 option::options def{ |
348 | 386 { "-c", true }, |
387 { "--config", true }, | |
388 { "-h", true }, | |
389 { "--help", false }, | |
390 { "--host", true }, | |
391 { "-p", true }, | |
392 { "--port", true }, | |
393 { "-P", true }, | |
394 { "--path", true }, | |
395 { "-t", true }, | |
396 { "--type", true }, | |
397 { "-v", false }, | |
398 { "--verbose", false } | |
399 }; | |
400 | |
498
fbd80bfcf58d
Irccd: import new options, closes #713
David Demelier <markand@malikania.fr>
parents:
494
diff
changeset
|
401 option::result result; |
348 | 402 |
403 try { | |
404 result = option::read(argc, argv, def); | |
405 | |
406 if (result.count("--help") != 0) { | |
407 usage(); | |
408 // NOTREACHED | |
409 } | |
410 | |
411 if (result.count("-v") != 0 || result.count("--verbose") != 0) { | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
412 log::set_verbose(true); |
348 | 413 } |
414 } catch (const std::exception &ex) { | |
494
9fcdd3c9cd33
Irccd: switch to Boost.Format, closes #577
David Demelier <markand@malikania.fr>
parents:
489
diff
changeset
|
415 log::warning() << "irccdctl: " << ex.what() << std::endl; |
348 | 416 usage(); |
417 } | |
418 | |
419 return result; | |
420 } | |
421 | |
422 void exec(std::vector<std::string>); | |
423 | |
424 void exec(const Alias &alias, std::vector<std::string> argsCopy) | |
425 { | |
426 std::vector<nlohmann::json> values; | |
427 | |
428 for (const AliasCommand &cmd : alias) { | |
429 std::vector<std::string> args(argsCopy); | |
430 std::vector<std::string> cmdArgs; | |
431 std::vector<std::string>::size_type toremove = 0; | |
432 | |
433 // 1. Append command name before. | |
434 cmdArgs.push_back(cmd.command()); | |
435 | |
436 for (const auto &arg : cmd.args()) { | |
437 if (arg.isPlaceholder()) { | |
494
9fcdd3c9cd33
Irccd: switch to Boost.Format, closes #577
David Demelier <markand@malikania.fr>
parents:
489
diff
changeset
|
438 if (args.size() < arg.index() + 1) |
9fcdd3c9cd33
Irccd: switch to Boost.Format, closes #577
David Demelier <markand@malikania.fr>
parents:
489
diff
changeset
|
439 throw std::invalid_argument(util::sprintf("missing argument for placeholder %d", arg.index())); |
348 | 440 |
441 cmdArgs.push_back(args[arg.index()]); | |
442 | |
443 if (arg.index() + 1 > toremove) { | |
444 toremove = arg.index() + 1; | |
445 } | |
446 } else { | |
447 cmdArgs.push_back(arg.value()); | |
448 } | |
449 } | |
450 | |
451 assert(toremove <= args.size()); | |
452 | |
453 // 2. Remove the arguments that been placed in placeholders. | |
454 args.erase(args.begin(), args.begin() + toremove); | |
455 | |
456 // 3. Now append the rest of arguments. | |
457 std::copy(args.begin(), args.end(), std::back_inserter(cmdArgs)); | |
458 | |
459 // 4. Finally try to execute. | |
460 exec(cmdArgs); | |
461 } | |
462 } | |
463 | |
464 void exec(std::vector<std::string> args) | |
465 { | |
466 assert(args.size() > 0); | |
467 | |
468 auto name = args[0]; | |
469 auto alias = aliases.find(name); | |
470 | |
471 // Remove name. | |
472 args.erase(args.begin()); | |
473 | |
474 if (alias != aliases.end()) { | |
475 exec(alias->second, args); | |
476 } else { | |
477 auto cmd = std::find_if(commands.begin(), commands.end(), [&] (auto &it) { | |
478 return it->name() == name; | |
479 }); | |
480 | |
481 if (cmd != commands.end()) { | |
482 (*cmd)->exec(*irccdctl, args); | |
483 } else { | |
484 throw std::invalid_argument("no alias or command named " + name); | |
485 } | |
486 } | |
487 } | |
488 | |
489 void init(int &argc, char **&argv) | |
490 { | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
491 sys::set_program_name("irccdctl"); |
348 | 492 net::init(); |
493 | |
494 --argc; | |
495 ++argv; | |
496 | |
497 commands.push_back(std::make_unique<cli::PluginConfigCli>()); | |
498 commands.push_back(std::make_unique<cli::PluginInfoCli>()); | |
499 commands.push_back(std::make_unique<cli::PluginListCli>()); | |
500 commands.push_back(std::make_unique<cli::PluginLoadCli>()); | |
501 commands.push_back(std::make_unique<cli::PluginReloadCli>()); | |
502 commands.push_back(std::make_unique<cli::PluginUnloadCli>()); | |
503 commands.push_back(std::make_unique<cli::ServerChannelMode>()); | |
504 commands.push_back(std::make_unique<cli::ServerChannelNoticeCli>()); | |
505 commands.push_back(std::make_unique<cli::ServerConnectCli>()); | |
506 commands.push_back(std::make_unique<cli::ServerDisconnectCli>()); | |
507 commands.push_back(std::make_unique<cli::ServerInfoCli>()); | |
508 commands.push_back(std::make_unique<cli::ServerInviteCli>()); | |
509 commands.push_back(std::make_unique<cli::ServerJoinCli>()); | |
510 commands.push_back(std::make_unique<cli::ServerKickCli>()); | |
511 commands.push_back(std::make_unique<cli::ServerListCli>()); | |
512 commands.push_back(std::make_unique<cli::ServerMeCli>()); | |
513 commands.push_back(std::make_unique<cli::ServerMessageCli>()); | |
514 commands.push_back(std::make_unique<cli::ServerModeCli>()); | |
515 commands.push_back(std::make_unique<cli::ServerNickCli>()); | |
516 commands.push_back(std::make_unique<cli::ServerNoticeCli>()); | |
517 commands.push_back(std::make_unique<cli::ServerPartCli>()); | |
518 commands.push_back(std::make_unique<cli::ServerReconnectCli>()); | |
355
ace71e2b8e2a
Irccd: add server-topic support again
David Demelier <markand@malikania.fr>
parents:
354
diff
changeset
|
519 commands.push_back(std::make_unique<cli::ServerTopicCli>()); |
452
2170aa0e38aa
Irccdctl: implement rule-add
David Demelier <markand@malikania.fr>
parents:
451
diff
changeset
|
520 commands.push_back(std::make_unique<cli::RuleAddCli>()); |
453
acb2d4990249
Irccdctl: implement rule-edit
David Demelier <markand@malikania.fr>
parents:
452
diff
changeset
|
521 commands.push_back(std::make_unique<cli::RuleEditCli>()); |
448
9be4f8a5cf1a
Irccdctl: implement rule-list
David Demelier <markand@malikania.fr>
parents:
428
diff
changeset
|
522 commands.push_back(std::make_unique<cli::RuleListCli>()); |
449
9968eac538e6
Irccdctl: implement rule-info
David Demelier <markand@malikania.fr>
parents:
448
diff
changeset
|
523 commands.push_back(std::make_unique<cli::RuleInfoCli>()); |
451
1fdedd2977d2
Irccdctl: implement rule-move
David Demelier <markand@malikania.fr>
parents:
450
diff
changeset
|
524 commands.push_back(std::make_unique<cli::RuleMoveCli>()); |
450
c8c68d4bf555
Irccdctl: implement rule-remove
David Demelier <markand@malikania.fr>
parents:
449
diff
changeset
|
525 commands.push_back(std::make_unique<cli::RuleRemoveCli>()); |
359
6d598fea2689
Irccdctl: put back watch missing, closes #591
David Demelier <markand@malikania.fr>
parents:
357
diff
changeset
|
526 commands.push_back(std::make_unique<cli::WatchCli>()); |
348 | 527 } |
528 | |
529 } // !namespace | |
530 | |
531 int main(int argc, char **argv) | |
532 { | |
533 init(argc, argv); | |
534 | |
535 // 1. Read command line arguments. | |
536 auto result = parse(argc, argv); | |
537 | |
538 /* | |
539 * 2. Open optional config by command line or by searching it | |
540 * | |
541 * The connection to irccd is searched in the following order : | |
542 * | |
543 * 1. From the command line if specified | |
544 * 2. From the configuration file specified by -c | |
545 * 3. From the configuration file searched through directories | |
546 */ | |
547 try { | |
548 if (result.count("-t") > 0 || result.count("--type") > 0) | |
549 parseConnect(result); | |
550 | |
551 auto it = result.find("-c"); | |
552 | |
553 if (it != result.end() || (it = result.find("--config")) != result.end()) | |
554 read(it->second); | |
555 else { | |
486
0b156b82b8c1
Irccd: rework of paths, closes #611
David Demelier <markand@malikania.fr>
parents:
477
diff
changeset
|
556 for (const auto& path : sys::config_filenames("irccdctl.conf")) { |
477
bf201d09ee6a
Irccd: use boost::filesystem (fs::exists), #594
David Demelier <markand@malikania.fr>
parents:
453
diff
changeset
|
557 boost::system::error_code ec; |
348 | 558 |
477
bf201d09ee6a
Irccd: use boost::filesystem (fs::exists), #594
David Demelier <markand@malikania.fr>
parents:
453
diff
changeset
|
559 if (boost::filesystem::exists(path, ec) && !ec) { |
348 | 560 read(path); |
561 break; | |
562 } | |
563 } | |
564 } | |
565 } catch (const std::exception &ex) { | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
566 log::warning() << sys::program_name() << ": " << ex.what() << std::endl; |
348 | 567 std::exit(1); |
568 } | |
569 | |
570 if (argc <= 0) { | |
571 usage(); | |
572 // NOTREACHED | |
573 } | |
574 if (std::strcmp(argv[0], "help") == 0) { | |
352
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
575 if (argc >= 2) |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
576 help(argv[1]); |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
577 else |
aab46e17cb27
Irccdctl: show help usage about command
David Demelier <markand@malikania.fr>
parents:
348
diff
changeset
|
578 help(); |
348 | 579 // NOTREACHED |
580 } | |
581 | |
582 if (!client) { | |
494
9fcdd3c9cd33
Irccd: switch to Boost.Format, closes #577
David Demelier <markand@malikania.fr>
parents:
489
diff
changeset
|
583 log::warning("irccdctl: no connection specified"); |
348 | 584 std::exit(1); |
585 } | |
586 | |
587 irccdctl = std::make_unique<Irccdctl>(std::move(client)); | |
588 irccdctl->client().onDisconnect.connect([&] (auto reason) { | |
589 log::warning() << "connection lost to irccd: " << reason << std::endl; | |
590 }); | |
591 irccdctl->client().onConnect.connect([&] (auto info) { | |
592 log::info() << "connected to irccd " | |
593 << info.major << "." | |
594 << info.minor << "." | |
595 << info.patch << std::endl; | |
596 }); | |
597 irccdctl->client().connect(address); | |
598 | |
599 // Build a vector of arguments. | |
600 std::vector<std::string> args; | |
601 | |
602 for (int i = 0; i < argc; ++i) | |
603 args.push_back(argv[i]); | |
604 | |
605 try { | |
606 exec(args); | |
607 } catch (const std::exception &ex) { | |
488
7e273b7f4f92
Irccd: new coding style, closes #576
David Demelier <markand@malikania.fr>
parents:
486
diff
changeset
|
608 std::cerr << sys::program_name() << ": unrecoverable error: " << ex.what() << std::endl; |
348 | 609 } |
610 } |