e8922eed2390d80b1ac5c98a3117f9752ecbfa5c
[platform/core/security/vasum.git] / cli / command-line-interface.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   Definition of CommandLineInterface class
23  */
24
25 #include "config.hpp"
26 #include "command-line-interface.hpp"
27 #include "vasum-client.h"
28 #include "utils/c-array.hpp"
29
30 #include <map>
31 #include <stdexcept>
32 #include <functional>
33 #include <ostream>
34 #include <iostream>
35 #include <sstream>
36 #include <iomanip>
37 #include <algorithm>
38 #include <vector>
39 #include <cassert>
40 #include <fcntl.h>
41 #include <linux/if_link.h>
42 #include <arpa/inet.h>
43 #include <unistd.h>
44 #include <string.h>
45
46 using namespace std;
47
48 namespace vasum {
49 namespace cli {
50
51 VsmClient CommandLineInterface::client = NULL;
52
53 namespace {
54
55 std::string zoneStateToString(const VsmZoneState& state)
56 {
57     std::string name;
58     switch (state) {
59         case STOPPED: name = "STOPPED"; break;
60         case STARTING: name = "STARTING"; break;
61         case RUNNING: name = "RUNNING"; break;
62         case STOPPING: name = "STOPPING"; break;
63         case ABORTING: name = "ABORTING"; break;
64         case FREEZING: name = "FREEZING"; break;
65         case FROZEN: name = "FROZEN"; break;
66         case THAWED: name = "THAWED"; break;
67         case LOCKED: name = "LOCKED"; break;
68         case MAX_STATE: name = "MAX_STATE"; break;
69         case ACTIVATING: name = "ACTIVATING"; break;
70         default: name = "MAX_STATE (ERROR)";
71     }
72
73     return name;
74 }
75
76 std::string netdevTypeToString(const VsmNetdevType& netdevType)
77 {
78     std::string type;
79     switch (netdevType) {
80         case VSMNETDEV_VETH: type = "VETH"; break;
81         case VSMNETDEV_PHYS: type = "PHYS"; break;
82         case VSMNETDEV_MACVLAN: type = "MACVLAN"; break;
83     }
84     return type;
85 }
86
87 std::string netdevToString(const VsmNetdev& netdev)
88 {
89     std::string out = std::string("Name: ") + vsm_netdev_get_name(netdev)
90         + std::string("\nType: ") + netdevTypeToString(vsm_netdev_get_type(netdev));
91     return out;
92 }
93
94 typedef vector<vector<string>> Table;
95
96 ostream& operator<<(ostream& out, const Table& table)
97 {
98     vector<size_t> sizes;
99     for (const auto& row : table) {
100         if (sizes.size() < row.size()) {
101             sizes.resize(row.size());
102         }
103         for (size_t i = 0; i < row.size(); ++i) {
104             sizes[i] = max(sizes[i], row[i].length());
105         }
106     }
107
108     for (const auto& row : table) {
109         for (size_t i = 0; i < row.size(); ++i) {
110             out << left << setw(sizes[i]+2) << row[i];
111         }
112         out << "\n";
113     }
114
115     return out;
116 }
117
118 enum macvlan_mode macvlanFromString(const std::string& mode) {
119     if (mode == "private") {
120         return MACVLAN_MODE_PRIVATE;
121     }
122     if (mode == "vepa") {
123         return MACVLAN_MODE_VEPA;
124     }
125     if (mode == "bridge") {
126         return MACVLAN_MODE_BRIDGE;
127     }
128     if (mode == "passthru") {
129         return MACVLAN_MODE_PASSTHRU;
130     }
131     throw runtime_error("Unsupported macvlan mode");
132 }
133
134 void buildZoneList(std::vector<std::string>& list)
135 {
136     using namespace std::placeholders;
137     VsmArrayString ids;
138
139     CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids));
140     for (VsmString* id = ids; *id; ++id) {
141         list.push_back(*id);
142     }
143     vsm_array_string_free(ids);
144 }
145
146 } // namespace
147
148 const std::vector<std::string> CommandLineInterface::buildCompletionList(const Args& a) const
149 {
150     std::vector<std::string> v;
151
152     if (a.size() > mArgsSpec.size() + 1) {
153         return v;
154     }
155
156     ArgSpec as = mArgsSpec[a.size() - 2];
157     string::size_type s = 0U;
158     string::size_type e = s;
159     while (e != string::npos) {
160         e = as.format.find('|', s);
161         std::string ss = as.format.substr(s, e - s);
162         s = e + 1;
163
164         if (ss == "{ZONE}") {
165             buildZoneList(v);
166         }
167         else if (ss == "{NETDEV}") {
168             //TODO: get list of available interfaces
169             v.push_back("lo");
170             v.push_back("eth0");
171         }
172         else if (ss.length() > 0) {
173             v.push_back(ss);
174         }
175     }
176
177     return v;
178 }
179
180 void CommandLineInterface::connect()
181 {
182     VsmStatus status;
183     if (CommandLineInterface::client != nullptr) {
184         return;
185     }
186
187     CommandLineInterface::client = vsm_client_create();
188     if (CommandLineInterface::client == nullptr) {
189         throw runtime_error("Can't create client");
190     }
191
192     status = vsm_connect(client);
193     if (VSMCLIENT_SUCCESS != status) {
194         string msg = vsm_get_status_message(CommandLineInterface::client);
195         vsm_client_free(CommandLineInterface::client);
196         CommandLineInterface::client = nullptr;
197         throw runtime_error(msg);
198     }
199 }
200
201 void CommandLineInterface::disconnect()
202 {
203     string msg;
204     VsmStatus status;
205
206     if (CommandLineInterface::client == nullptr) {
207         return ;
208     }
209
210     status = vsm_disconnect(CommandLineInterface::client);
211     if (VSMCLIENT_SUCCESS != status) {
212         msg = vsm_get_status_message(CommandLineInterface::client);
213     }
214
215     vsm_client_free(CommandLineInterface::client);
216     CommandLineInterface::client = nullptr;
217
218     if (VSMCLIENT_SUCCESS != status) {
219         throw runtime_error(msg);
220     }
221 }
222
223 void CommandLineInterface::executeCallback(const function<VsmStatus(VsmClient)>& fun)
224 {
225     CommandLineInterface::connect();
226
227     VsmStatus status = fun(CommandLineInterface::client);
228     if (VSMCLIENT_SUCCESS != status) {
229         throw runtime_error(vsm_get_status_message(CommandLineInterface::client));
230     }
231 }
232
233 const std::string& CommandLineInterface::getName() const
234 {
235     return mName;
236 }
237
238 const std::string& CommandLineInterface::getDescription() const
239 {
240     return mDescription;
241 }
242
243 void CommandLineInterface::printUsage(std::ostream& out) const
244 {
245     out << mName;
246     for (const auto& args : mArgsSpec) {
247         out << " " << args.name;
248     }
249
250     out << "\n\n"
251         << "\tDescription\n"
252         << "\t\t" << mDescription << "\n";
253
254     if (!mArgsSpec.empty()) {
255         out << "\n\tOptions\n";
256         for (const auto& args : mArgsSpec) {
257             out << "\t\t" << args.name << " -- " << args.description << "\n";
258         }
259     }
260     out << "\n";
261 }
262
263 bool CommandLineInterface::isAvailable(unsigned int mode) const
264 {
265     return (mAvailability & mode) == mode;
266 }
267
268 void CommandLineInterface::execute(const Args& argv) const
269 {
270     mExecutorCallback(argv);
271 }
272
273
274 void lock_queue(const Args& /*argv*/)
275 {
276     CommandLineInterface::executeCallback(vsm_lock_queue);
277 }
278
279 void unlock_queue(const Args& /*argv*/)
280 {
281     CommandLineInterface::executeCallback(vsm_unlock_queue);
282 }
283
284 void set_active_zone(const Args& argv)
285 {
286     using namespace std::placeholders;
287
288     if (argv.size() < 2) {
289         throw runtime_error("Not enough parameters");
290     }
291
292     CommandLineInterface::executeCallback(bind(vsm_set_active_zone, _1, argv[1].c_str()));
293 }
294
295 void create_zone(const Args& argv)
296 {
297     using namespace std::placeholders;
298
299     if (argv.size() < 2) {
300         throw runtime_error("Not enough parameters");
301     }
302
303     if (argv.size() >= 3 && !argv[2].empty()) {
304         CommandLineInterface::executeCallback(bind(vsm_create_zone, _1, argv[1].c_str(), argv[2].c_str()));
305     } else {
306         CommandLineInterface::executeCallback(bind(vsm_create_zone, _1, argv[1].c_str(), nullptr));
307     }
308 }
309
310 void destroy_zone(const Args& argv)
311 {
312     using namespace std::placeholders;
313
314     if (argv.size() < 2) {
315         throw runtime_error("Not enough parameters");
316     }
317
318     CommandLineInterface::executeCallback(bind(vsm_destroy_zone, _1, argv[1].c_str(), 1));
319 }
320
321 void shutdown_zone(const Args& argv)
322 {
323     using namespace std::placeholders;
324
325     if (argv.size() < 2) {
326         throw runtime_error("Not enough parameters");
327     }
328
329     CommandLineInterface::executeCallback(bind(vsm_shutdown_zone, _1, argv[1].c_str()));
330 }
331
332 void start_zone(const Args& argv)
333 {
334     using namespace std::placeholders;
335
336     if (argv.size() < 2) {
337         throw runtime_error("Not enough parameters");
338     }
339
340     CommandLineInterface::executeCallback(bind(vsm_start_zone, _1, argv[1].c_str()));
341 }
342
343 void console_zone(const Args& argv)
344 {
345     using namespace std::placeholders;
346
347     if (argv.size() < 2) {
348         throw runtime_error("Not enough parameters");
349     }
350
351     VsmZone zone;
352     CommandLineInterface::executeCallback(bind(vsm_lookup_zone_by_id, _1, argv[1].c_str(), &zone));
353
354     if (zoneStateToString(vsm_zone_get_state(zone)) != "RUNNING") {
355         vsm_zone_free(zone);
356         throw runtime_error("Zone '" + argv[1] + "' is not running");
357     }
358
359     std::string zonesPath = vsm_zone_get_rootfs(zone);
360     std::string zoneRootFs = argv[1] + "/rootfs";
361     zonesPath.erase(zonesPath.length()- zoneRootFs.length(), zoneRootFs.length());
362
363     vsm_zone_free(zone);
364
365     utils::CStringArrayBuilder args;
366     args.add("lxc-console")
367         .add("-t 0")
368         .add("-n").add(argv[1].c_str())
369         .add("-P").add(zonesPath.c_str());
370
371     if (!execv("/usr/bin/lxc-console", const_cast<char* const*>(args.c_array()))) {
372         throw runtime_error("Could not log into zone");
373     }
374 }
375
376 void lock_zone(const Args& argv)
377 {
378     using namespace std::placeholders;
379
380     if (argv.size() < 2) {
381         throw runtime_error("Not enough parameters");
382     }
383
384     CommandLineInterface::executeCallback(bind(vsm_lock_zone, _1, argv[1].c_str()));
385 }
386
387 void unlock_zone(const Args& argv)
388 {
389     using namespace std::placeholders;
390
391     if (argv.size() < 2) {
392         throw runtime_error("Not enough parameters");
393     }
394
395     CommandLineInterface::executeCallback(bind(vsm_unlock_zone, _1, argv[1].c_str()));
396 }
397
398 void get_zones_status(const Args& argv)
399 {
400     using namespace std::placeholders;
401
402     VsmArrayString ids;
403     VsmString activeId;
404     Table table;
405
406     if (argv.size() < 2) {
407         CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids));
408     }
409     else {
410         ids = reinterpret_cast<char**>(calloc(argv.size(), sizeof(char*)));
411         for (unsigned i = 1; i<argv.size(); ++i) {
412             ids[i - 1] = ::strdup(argv[i].c_str());
413         }
414     }
415
416
417     CommandLineInterface::executeCallback(bind(vsm_get_active_zone_id, _1, &activeId));
418     table.push_back({"Active", "Id", "State", "Terminal", "Root"});
419     for (VsmString* id = ids; *id; ++id) {
420         VsmZone zone;
421         CommandLineInterface::executeCallback(bind(vsm_lookup_zone_by_id, _1, *id, &zone));
422         assert(string(vsm_zone_get_id(zone)) == string(*id));
423         table.push_back({string(vsm_zone_get_id(zone)) == string(activeId) ? "YES" : "NO",
424                          vsm_zone_get_id(zone),
425                          zoneStateToString(vsm_zone_get_state(zone)),
426                          to_string(vsm_zone_get_terminal(zone)),
427                          vsm_zone_get_rootfs(zone)});
428         vsm_zone_free(zone);
429     }
430     vsm_string_free(activeId);
431     vsm_array_string_free(ids);
432     cout << table << endl;
433 }
434
435 void get_zone_ids(const Args& /*argv*/)
436 {
437     using namespace std::placeholders;
438
439     VsmArrayString ids;
440     CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids));
441     string delim;
442     for (VsmString* id = ids; *id; ++id) {
443         cout << delim << *id;
444         delim = ", ";
445     }
446     cout << endl;
447     vsm_array_string_free(ids);
448 }
449
450 void get_active_zone(const Args& /*argv*/)
451 {
452     using namespace std::placeholders;
453
454     VsmString id;
455     CommandLineInterface::executeCallback(bind(vsm_get_active_zone_id, _1, &id));
456     cout << id << endl;
457     vsm_string_free(id);
458 }
459
460 void grant_device(const Args& argv)
461 {
462     using namespace std::placeholders;
463
464     if (argv.size() < 3) {
465         throw runtime_error("Not enough parameters");
466     }
467
468     uint32_t flags = O_RDWR;
469     CommandLineInterface::executeCallback(bind(vsm_grant_device, _1, argv[1].c_str(), argv[2].c_str(), flags));
470 }
471
472 void revoke_device(const Args& argv)
473 {
474     using namespace std::placeholders;
475
476     if (argv.size() < 3) {
477         throw runtime_error("Not enough parameters");
478     }
479
480     CommandLineInterface::executeCallback(bind(vsm_revoke_device, _1, argv[1].c_str(), argv[2].c_str()));
481 }
482
483 void create_netdev(const Args& argv)
484 {
485     using namespace std::placeholders;
486
487     if (argv.size() < 2) {
488         throw runtime_error("Not enough parameters");
489     }
490
491     std::string nettype = argv[2];
492     if (nettype == "phys") {
493         if (argv.size() < 4) {
494             throw runtime_error("Not enough parameters");
495         }
496         CommandLineInterface::executeCallback(bind(vsm_create_netdev_phys,
497                   _1,
498                   argv[1].c_str(),
499                   argv[3].c_str()));
500     }
501     else if (nettype == "veth") {
502         if (argv.size() < 5) {
503             throw runtime_error("Not enough parameters");
504         }
505         CommandLineInterface::executeCallback(bind(vsm_create_netdev_veth,
506                   _1,
507                   argv[1].c_str(),
508                   argv[3].c_str(),
509                   argv[4].c_str()));
510     }
511     else if (nettype == "macvlan") {
512         if (argv.size() < 6) {
513             throw runtime_error("Not enough parameters");
514         }
515         CommandLineInterface::executeCallback(bind(vsm_create_netdev_macvlan,
516                   _1,
517                   argv[1].c_str(),
518                   argv[3].c_str(),
519                   argv[4].c_str(),
520                   macvlanFromString(argv[5].c_str())));
521     }
522     else
523         throw runtime_error("Wrong nettype option " + nettype);
524 }
525
526 void destroy_netdev(const Args& argv)
527 {
528     using namespace std::placeholders;
529
530     if (argv.size() < 3) {
531         throw runtime_error("Not enough parameters");
532     }
533     CommandLineInterface::executeCallback(bind(vsm_destroy_netdev,
534                   _1,
535                   argv[1].c_str(),
536                   argv[2].c_str()));
537 }
538
539 void netdev_list(const Args& argv)
540 {
541     using namespace std::placeholders;
542
543     if (argv.size() < 2) {
544         throw runtime_error("Not enough parameters");
545     }
546     if (argv.size() < 3) {
547         VsmArrayString ids;
548         CommandLineInterface::executeCallback(bind(vsm_zone_get_netdevs,
549                   _1,
550                   argv[1].c_str(),
551                   &ids));
552         string delim;
553         for (VsmString* id = ids; *id; ++id) {
554             cout << delim << *id;
555             delim = ", ";
556         }
557         if (delim.empty()) {
558             cout << "There is no network device in zone";
559         }
560         cout << endl;
561         vsm_array_string_free(ids);
562     }
563     else {
564         VsmNetdev netdev = NULL;
565         in_addr ipv4;
566         in6_addr ipv6;
567         char buf[INET_ADDRSTRLEN|INET6_ADDRSTRLEN];
568         CommandLineInterface::executeCallback(bind(vsm_lookup_netdev_by_name,
569                   _1,
570                   argv[1].c_str(),
571                   argv[2].c_str(),
572                   &netdev));
573         cout << netdevToString(netdev) << endl;
574         vsm_netdev_free(netdev);
575
576         CommandLineInterface::executeCallback(bind(vsm_netdev_get_ipv4_addr,
577                   _1,
578                   argv[1].c_str(),
579                   argv[2].c_str(),
580                   &ipv4));
581         if (inet_ntop(AF_INET, &ipv4, buf, INET_ADDRSTRLEN) == NULL) {
582             throw runtime_error("Wrong address received");
583         }
584         cout << buf << endl;
585
586         CommandLineInterface::executeCallback(bind(vsm_netdev_get_ipv6_addr,
587                   _1,
588                   argv[1].c_str(),
589                   argv[2].c_str(),
590                   &ipv6));
591         if (inet_ntop(AF_INET6, &ipv6, buf, INET6_ADDRSTRLEN) == NULL) {
592             throw runtime_error("Wrong address received");
593         }
594         cout << buf << endl;
595     }
596 }
597
598 void netdev_add_ip_addr(const Args& argv)
599 {
600     using namespace std::placeholders;
601     if (argv.size() < 5) {
602         throw runtime_error("Not enough parameters");
603     }
604     if (argv[3].find(':') == std::string::npos) {
605         in_addr addr;
606         if (inet_pton(AF_INET, argv[3].c_str(), &addr) != 1) {
607             throw runtime_error("Wrong address format");
608         };
609         CommandLineInterface::executeCallback(bind(vsm_netdev_set_ipv4_addr,
610                   _1,
611                   argv[1].c_str(),
612                   argv[2].c_str(),
613                   &addr,
614                   stoi(argv[4].c_str())));
615     }
616     else {
617         in6_addr addr;
618         if (inet_pton(AF_INET6, argv[3].c_str(), &addr) != 1) {
619             throw runtime_error("Wrong address format");
620         };
621         CommandLineInterface::executeCallback(bind(vsm_netdev_set_ipv6_addr,
622                   _1,
623                   argv[1].c_str(),
624                   argv[2].c_str(),
625                   &addr,
626                   stoi(argv[4].c_str())));
627     }
628 }
629
630 void netdev_del_ip_addr(const Args& argv)
631 {
632     using namespace std::placeholders;
633     if (argv.size() < 5) {
634         throw runtime_error("Not enough parameters");
635     }
636     if (argv[3].find(':') == std::string::npos) {
637         in_addr addr;
638         if (inet_pton(AF_INET, argv[3].c_str(), &addr) != 1) {
639             throw runtime_error("Wrong address format");
640         };
641         CommandLineInterface::executeCallback(bind(vsm_netdev_del_ipv4_addr,
642                   _1,
643                   argv[1].c_str(),
644                   argv[2].c_str(),
645                   &addr,
646                   stoi(argv[4].c_str())));
647     }
648     else {
649         in6_addr addr;
650         if (inet_pton(AF_INET6, argv[3].c_str(), &addr) != 1) {
651             throw runtime_error("Wrong address format");
652         };
653         CommandLineInterface::executeCallback(bind(vsm_netdev_del_ipv6_addr,
654                   _1,
655                   argv[1].c_str(),
656                   argv[2].c_str(),
657                   &addr,
658                   stoi(argv[4].c_str())));
659     }
660 }
661
662 void netdev_up(const Args& argv)
663 {
664     using namespace std::placeholders;
665
666     if (argv.size() < 3) {
667         throw runtime_error("Not enough parameters");
668     }
669     CommandLineInterface::executeCallback(bind(vsm_netdev_up,
670                   _1,
671                   argv[1].c_str(),
672                   argv[2].c_str()));
673 }
674
675 void netdev_down(const Args& argv)
676 {
677     using namespace std::placeholders;
678
679     if (argv.size() < 3) {
680         throw runtime_error("Not enough parameters");
681     }
682     CommandLineInterface::executeCallback(bind(vsm_netdev_down,
683                   _1,
684                   argv[1].c_str(),
685                   argv[2].c_str()));
686 }
687
688 void clean_up_zones_root(const Args& /* argv */)
689 {
690     using namespace std::placeholders;
691
692     CommandLineInterface::executeCallback(bind(vsm_clean_up_zones_root, _1));
693 }
694
695 } // namespace cli
696 } // namespace vasum