vsm: update network related help, formating changes
[platform/core/security/vasum.git] / cli / main.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Mateusz Malicki <m.malicki2@samsung.com>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License
17  */
18
19 /**
20  * @file
21  * @author  Mateusz Malicki (m.malicki2@samsung.com)
22  * @brief   Declaration of CommandLineInterface class
23  */
24
25 #include "command-line-interface.hpp"
26
27 #include <cstdlib>
28 #include <map>
29 #include <stdexcept>
30 #include <string>
31 #include <iostream>
32 #include <algorithm>
33 #include <istream>
34 #include <sstream>
35 #include <fstream>
36 #include <iterator>
37 #include <iomanip>
38 #include <boost/algorithm/string/predicate.hpp>
39 #include <boost/filesystem.hpp>
40
41 #include <readline/readline.h>
42 #include <readline/history.h>
43
44 using namespace vasum::cli;
45
46 namespace fs =  boost::filesystem;
47
48 namespace {
49
50 static int interactiveMode = 0;
51 std::vector<CommandLineInterface> commands = {
52     {
53         create_zone,
54         "create",
55         "Create and add zone",
56         MODE_COMMAND_LINE | MODE_INTERACTIVE,
57         {{"zone_id", "zone name", ""},
58          {"[zone_tname]", "optional zone template name", ""}}
59     },
60     {
61         destroy_zone,
62         "destroy",
63         "Destroy zone",
64         MODE_COMMAND_LINE | MODE_INTERACTIVE,
65         {{"zone_id", "zone name", "{ZONE}"}}
66     },
67     {
68         start_zone,
69         "start",
70         "Start zone",
71         MODE_COMMAND_LINE | MODE_INTERACTIVE,
72         {{"zone_id", "zone name", "{ZONE}"}}
73     },
74     {
75         console_zone,
76         "console",
77         "Attach to zone text console",
78         MODE_COMMAND_LINE | MODE_INTERACTIVE,
79         {{"zone_id", "zone name", "{ZONE}"}}
80     },
81     {
82         shutdown_zone,
83         "shutdown",
84         "Shutdown zone",
85         MODE_COMMAND_LINE | MODE_INTERACTIVE,
86         {{"zone_id", "zone name", "{ZONE}"}}
87     },
88     {
89         lock_zone,
90         "suspend",
91         "Suspend (lock) zone",
92         MODE_COMMAND_LINE | MODE_INTERACTIVE,
93         {{"zone_id", "zone name", "{ZONE}"}}
94     },
95     {
96         unlock_zone,
97         "resume",
98         "Resume (unlock) zone",
99         MODE_COMMAND_LINE | MODE_INTERACTIVE,
100         {{"zone_id", "zone name", "{ZONE}"}}
101     },
102     {
103         set_active_zone,
104         "set-active",
105         "Set active (foreground) zone",
106         MODE_COMMAND_LINE | MODE_INTERACTIVE,
107         {{"zone_id", "zone name", "{ZONE}"}}
108     },
109     {
110         get_active_zone,
111         "get-active",
112         "Get active (foreground) zone",
113         MODE_COMMAND_LINE | MODE_INTERACTIVE,
114         {}
115     },
116     {
117         get_zone_ids,
118         "list",
119         "Get available zone ids",
120         MODE_COMMAND_LINE | MODE_INTERACTIVE,
121         {}
122     },
123     {
124         get_zones_status,
125         "status",
126         "List status for one or all zones (id, state, terminal, root path)",
127         MODE_COMMAND_LINE | MODE_INTERACTIVE,
128         {{"[zone_id]", "zone name", "{ZONE}"}}
129     },
130     {
131         clean_up_zones_root,
132         "clean",
133         "Clean up zones root directory",
134         MODE_COMMAND_LINE | MODE_INTERACTIVE,
135         {}
136     },
137     {
138         grant_device,
139         "device-grant",
140         "Grants access to the given device",
141         MODE_COMMAND_LINE | MODE_INTERACTIVE,
142         {{"zone_id", "zone name", "{ZONE}"},
143          {"device", "device name", ""}}
144     },
145     {
146         revoke_device,
147         "device-revoke",
148         "Revokes access to the given device",
149         MODE_COMMAND_LINE | MODE_INTERACTIVE,
150         {{"zone_id", "zone name", "{ZONE}"},
151          {"device", "device name", ""}}
152     },
153     {
154         create_netdev,
155         "net-create",
156         "Create network virtualization for the zone",
157         MODE_COMMAND_LINE | MODE_INTERACTIVE,
158         {
159          {"zone_id", "zone name", "{ZONE}"},
160          {"netdevtype", "interface type (veth, macvlan, phys)\n"
161             "   veth - create new zone iface and bridge to host\n"
162             "macvlan - create new zone slave iface briged to master with specified mode\n"
163             "   phys - move existing iface from host to zone (no way to move it back)",
164             "veth|macvlan|phys"
165          },
166          {"zone_netdev", "interface name (eth0)", "eth0|eth1"},
167          {"host_netdev", "bridge name (virbr0)", "virbr0|virbr1"},
168          {"mode", "macvlan mode (private, vepa, bridge, passthru)\n"
169              " private - bridge but no comunicate with otheri vlan\n"
170              "    vepa - ethernet switch\n"
171              "  bridge - light weight to other vlan\n"
172              "passthru - only one vlan device",
173              "private|vepa|bridge|passthru"}}
174     },
175     {
176         destroy_netdev,
177         "net-destroy",
178         "Destroy netdev in zone",
179         MODE_COMMAND_LINE | MODE_INTERACTIVE,
180         {{"zone_id", "zone name", "{ZONE}"},
181          {"netdev", "interface name (eth0)", "{NETDEV}"}}
182     },
183     {
184         netdev_list,
185         "net-list",
186         "List network devices in the zone",
187         MODE_COMMAND_LINE | MODE_INTERACTIVE,
188         {{"zone_id", "zone name", "{ZONE}"},
189          {"[netdev]", "interface name (eth0)", "{NETDEV}"}}
190     },
191     {
192         netdev_up,
193         "net-up",
194         "Setup a network device in the zone up",
195         MODE_COMMAND_LINE | MODE_INTERACTIVE,
196         {{"zone_id", "zone name", "{ZONE}"},
197          {"netdev", "interface name (eth0)", "{NETDEV}"}}
198     },
199     {
200         netdev_down,
201         "net-down",
202         "Setup a network device in the zone down",
203         MODE_COMMAND_LINE | MODE_INTERACTIVE,
204         {{"zone_id", "zone name", "{ZONE}"},
205          {"netdev", "interface name (eth0)", "{NETDEV}"}}
206     },
207     {
208         netdev_add_ip_addr,
209         "net-ip-add",
210         "Add ip/mask address to network interface",
211         MODE_COMMAND_LINE | MODE_INTERACTIVE,
212         {{"zone_id", "zone name", "{ZONE}"},
213          {"netdev", "interface name (eth0)", "{NETDEV}"},
214          {"ip", "address IPv4 or IPv6", ""},
215          {"prefix", "mask length in bits", "24"}}
216     },
217     {
218         netdev_del_ip_addr,
219         "net-ip-del",
220         "Del ip/mask address from network interface",
221         MODE_COMMAND_LINE | MODE_INTERACTIVE,
222         {{"zone_id", "zone name", "{ZONE}"},
223          {"netdev", "interface name (eth0)", "{NETDEV}"},
224          {"ip", "address IPv4 or IPv6", ""},
225          {"prefix", "mask length in bits", "24"}}
226     },
227     {
228         lock_queue,
229         "qlock",
230         "Exclusively lock the command queue",
231         MODE_INTERACTIVE,
232         {}
233     },
234     {
235         unlock_queue,
236         "qunlock",
237         "Unlock the queue",
238         MODE_INTERACTIVE,
239         {}
240     },
241 };
242
243 std::map<std::string,const CommandLineInterface> commandMap;
244
245 // wrappers for CommandLineInterface
246
247 void printUsage(std::ostream& out, const std::string& name, unsigned int mode)
248 {
249     const std::vector<std::string> addLineBefore = {"device-grant", "net-create", "qlock"};
250     std::string n;
251     if (!name.empty()) {
252         n = name + " ";
253     }
254
255     out << "Usage: " << n << "[-h|help|-f <filename>|[<command> [-h|help|<args>]]]\n\n";
256     if (mode == MODE_COMMAND_LINE) {
257         out << "Description:\n"
258             << "\tCommand line tool to manage vasum containers.\n"
259             << "\tCalled without parameters enters interactive mode.\n"
260             << "Options:\n"
261             << "\t-h,help        print this help\n"
262             << "\t-f <filename>  read and execute commands from file\n\n";
263     }
264     out << "command can be one of the following:\n";
265
266     for (const auto& command : commands) {
267         if (command.isAvailable(mode)) {
268             if (std::find(addLineBefore.begin(), addLineBefore.end(), command.getName()) != addLineBefore.end()) {
269                 out << std::endl;
270             }
271             out << "   " << std::setw(20) << std::left << command.getName();
272             const std::string& d = command.getDescription();
273             std::stringstream ss(d);
274             std::string item;
275             std::getline(ss, item);
276             out << item << std::endl;
277         }
278     }
279
280     out << "\nType '" << n << "command help' to read about a specific one.\n";
281 }
282
283 int connect()
284 {
285     try {
286         CommandLineInterface::connect();
287     } catch (const std::runtime_error& ex) {
288         std::cerr << ex.what() << std::endl;
289         return EXIT_FAILURE;
290     }
291
292     return EXIT_SUCCESS;
293 }
294
295 int disconnect()
296 {
297     try {
298         CommandLineInterface::disconnect();
299     } catch (const std::runtime_error& ex) {
300         std::cerr << ex.what() << std::endl;
301         return EXIT_FAILURE;
302     }
303
304     return EXIT_SUCCESS;
305 }
306
307 int executeCommand(const Args& argv, int mode)
308 {
309     auto pair = commandMap.find(argv[0]);
310     if (pair == commandMap.end()) {
311         return EXIT_FAILURE;
312     }
313
314     const CommandLineInterface& command = pair->second;
315     if (!command.isAvailable(mode)) {
316         std::cerr << "Command not available in this mode" << std::endl;
317         return EXIT_FAILURE;
318     }
319
320     if (argv.size() > 1 && (argv[1] == "-h" || argv[1] == "help")) {
321         command.printUsage(std::cout);
322         return EXIT_SUCCESS;
323     }
324
325     try {
326         command.execute(argv);
327     } catch (const std::runtime_error& ex) {
328         std::cerr << ex.what() << std::endl;
329         return EXIT_FAILURE;
330     }
331
332     return EXIT_SUCCESS;
333 }
334
335 // readline completion support
336 const std::vector<std::string> buildComplList(const Args& argv);
337 char *completion_generator(const char* text, int state)
338 {
339     static std::vector<std::string> list;
340     static unsigned index = 0;
341     if (state == 0) {
342         list.clear();
343         index = 0;
344
345         char *ln = rl_line_buffer;
346         std::istringstream iss(ln);
347         Args argv{std::istream_iterator<std::string>{iss},
348                   std::istream_iterator<std::string>{}};
349
350         size_t len = strlen(text);
351         if (len == 0 && argv.size() > 0) {
352             argv.push_back("");
353         }
354
355         const std::vector<std::string>& l = buildComplList(argv);
356         for (const auto &i : l) {
357             if (strncmp(text, i.c_str(), len) == 0) {
358                 list.push_back(i);
359             }
360         }
361     }
362     if (index < list.size()) {
363         return ::strdup(list[index++].c_str());
364     }
365
366     return NULL;
367 }
368
369 char** completion(const char* text, int /*start*/, int /*end*/)
370 {
371     ::rl_attempted_completion_over = 1; //disable default completion
372     return ::rl_completion_matches(text, &completion_generator);
373 }
374
375 static bool readline_from(const std::string& prompt, std::istream& stream, std::string& ln)
376 {
377     if (interactiveMode) {
378         char *cmd = ::readline(prompt.c_str());
379         if (cmd == NULL) {
380             return false;
381         }
382         ln = cmd;
383         free(cmd);
384     } else {
385         std::getline(stream, ln);
386         if (!stream.good()) {
387             return false;
388         }
389     }
390
391     if (interactiveMode && !ln.empty()) {
392         ::add_history(ln.c_str());
393     }
394
395     return true;
396 }
397
398 static int processStream(std::istream& stream)
399 {
400     if (connect() != EXIT_SUCCESS) {
401         return EXIT_FAILURE;
402     }
403
404     int rc = EXIT_FAILURE;
405     std::string ln;
406     while (readline_from("vsm> ", stream, ln)) {
407         if (ln.empty() || ln[0] == '#') { //skip empty line or comment
408              continue;
409         }
410
411         std::istringstream iss(ln);
412         Args argv{std::istream_iterator<std::string>{iss},
413                   std::istream_iterator<std::string>{}};
414
415         if (commandMap.count(argv[0]) == 0) {
416             printUsage(std::cout, "", MODE_INTERACTIVE);
417             continue;
418         }
419
420         rc = executeCommand(argv, MODE_INTERACTIVE);
421         if (rc == EXIT_FAILURE && !interactiveMode) {
422             break;
423         }
424     }
425
426     return rc;
427 }
428
429 static int processFile(const std::string& fn)
430 {
431     std::ifstream stream(fn);
432     if (!stream.good()) {
433         //TODO: Turn on exceptions on stream (and in the entire file)
434         std::cerr << "Can't open file " << fn << std::endl;
435         return EXIT_FAILURE;
436     }
437
438     return processStream(stream);
439 }
440
441 void printList(const std::vector<std::string>& list)
442 {
443     for (const auto& i : list) {
444         std::cout << i << std::endl;
445     }
446 }
447
448 const std::vector<std::string> buildComplList(const Args& argv)
449 {
450     if (argv.size() < 2) {
451         std::vector<std::string> list;
452         for (const auto& command : commands) {
453             if (command.isAvailable(MODE_COMMAND_LINE)) {
454                 list.push_back(command.getName());
455             }
456         }
457         return list;
458     } else {
459         std::string cmd = argv[0];
460         if (commandMap.find(cmd) != commandMap.end()) {
461             return commandMap[cmd].buildCompletionList(argv);
462         }
463         return std::vector<std::string>();
464     }
465 }
466
467 int bashComplMode(int argc, const char *argv[])
468 {
469     int rc = EXIT_FAILURE;
470     try {
471         Args args(argv, argv + argc);
472         printList(buildComplList(args));
473         rc = EXIT_SUCCESS;
474     } catch (const std::runtime_error& ex) {
475     }
476
477     return rc;
478 }
479
480 int cliMode(const int argc, const char** argv)
481 {
482     if (std::string(argv[1]) == "-h" || std::string(argv[1]) == "help") {
483         printUsage(std::cout, argv[0], MODE_COMMAND_LINE);
484         return EXIT_SUCCESS;
485     }
486
487     if (commandMap.find(argv[1]) == commandMap.end()) {
488         printUsage(std::cout, argv[0], MODE_COMMAND_LINE);
489         return EXIT_FAILURE;
490     }
491
492     // pass all the arguments excluding argv[0] - the executable name
493     Args commandArgs(argv + 1, argv + argc);
494     int rc = executeCommand(commandArgs, MODE_COMMAND_LINE);
495
496     return rc;
497 }
498
499 fs::path getHomePath() {
500     const char *h = ::getenv("HOME");
501     return fs::path(h ? h : "");
502 }
503
504
505 } // namespace
506
507
508 int main(const int argc, const char *argv[])
509 {
510     for (const auto& command : commands) {
511         commandMap.insert(std::pair<std::string,const CommandLineInterface>(command.getName(),command));
512     }
513
514     int rc = EXIT_FAILURE;
515     if (argc > 1) {
516
517         //process arguments
518         if (std::string(argv[1]) == "--bash-completion") {
519             rc = bashComplMode(argc - 2, argv + 2);
520         } else if (std::string(argv[1]) == "-f") {
521             if (argc < 3) {
522                 std::cerr << "Filename expected" << std::endl;
523                 rc = EXIT_FAILURE;
524             }
525             else {
526                 rc = processFile(std::string(argv[2]));
527             }
528         } else {
529             rc = cliMode(argc, argv);
530         }
531
532     } else {
533         fs::path historyfile(".vsm_history");
534
535         if (isatty(0) == 1) {
536             fs::path home = getHomePath();
537             if (!home.empty()) {
538                 historyfile = home / historyfile;
539             }
540             ::read_history(historyfile.c_str());
541
542             interactiveMode = 1;
543             ::rl_attempted_completion_function = completion;
544         }
545
546         rc = processStream(std::cin);
547
548         if (interactiveMode) {
549             ::write_history(historyfile.c_str());
550             std::cout << std::endl; // finish prompt line
551         }
552     }
553
554     disconnect();
555     return rc;
556 }