blob: f4e596598c494455c822b096a3fd74fce3330bce [file] [log] [blame]
Vince Lehmanc439d662015-04-27 10:56:00 -05001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Saurab Dulal7526cee2018-01-31 18:14:10 +00003 * Copyright (c) 2014-2019, The University of Memphis,
Vince Lehmanc439d662015-04-27 10:56:00 -05004 * Regents of the University of California,
5 * Arizona Board of Regents.
6 *
7 * This file is part of NLSR (Named-data Link State Routing).
8 * See AUTHORS.md for complete list of NLSR authors and contributors.
9 *
10 * NLSR is free software: you can redistribute it and/or modify it under the terms
11 * of the GNU General Public License as published by the Free Software Foundation,
12 * either version 3 of the License, or (at your option) any later version.
13 *
14 * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
15 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 * PURPOSE. See the GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * NLSR, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
20 **/
21
22#include "nlsrc.hpp"
23
24#include "version.hpp"
laqinfan35731852017-08-08 06:17:39 -050025#include "src/publisher/dataset-interest-handler.hpp"
Vince Lehmanc439d662015-04-27 10:56:00 -050026
27#include <ndn-cxx/face.hpp>
28#include <ndn-cxx/data.hpp>
29#include <ndn-cxx/interest.hpp>
30#include <ndn-cxx/encoding/block.hpp>
Junxiao Shi3e5120c2016-09-10 16:58:34 +000031#include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
32#include <ndn-cxx/mgmt/nfd/control-response.hpp>
Vince Lehmanc439d662015-04-27 10:56:00 -050033#include <ndn-cxx/util/segment-fetcher.hpp>
Muktadir Chowdhuryf04f9892017-08-20 20:42:56 -050034#include <ndn-cxx/security/key-chain.hpp>
35#include <ndn-cxx/security/command-interest-signer.hpp>
Vince Lehmanc439d662015-04-27 10:56:00 -050036
37#include <iostream>
38
39namespace nlsrc {
40
41const ndn::Name Nlsrc::LOCALHOST_PREFIX = ndn::Name("/localhost/nlsr");
42const ndn::Name Nlsrc::LSDB_PREFIX = ndn::Name(Nlsrc::LOCALHOST_PREFIX).append("lsdb");
43const ndn::Name Nlsrc::NAME_UPDATE_PREFIX = ndn::Name(Nlsrc::LOCALHOST_PREFIX).append("prefix-update");
44
laqinfan35731852017-08-08 06:17:39 -050045const ndn::Name Nlsrc::RT_PREFIX = ndn::Name(Nlsrc::LOCALHOST_PREFIX).append("routing-table");
46
Vince Lehmanc439d662015-04-27 10:56:00 -050047const uint32_t Nlsrc::ERROR_CODE_TIMEOUT = 10060;
48const uint32_t Nlsrc::RESPONSE_CODE_SUCCESS = 200;
Saurab Dulal7526cee2018-01-31 18:14:10 +000049const uint32_t Nlsrc::RESPONSE_CODE_SAVE_OR_DELETE = 205;
Vince Lehmanc439d662015-04-27 10:56:00 -050050
51Nlsrc::Nlsrc(ndn::Face& face)
52 : m_face(face)
53{
54}
55
56void
57Nlsrc::printUsage()
58{
59 std::cout << "Usage:\n" << programName << " [-h] [-V] COMMAND [<Command Options>]\n"
60 " -h print usage and exit\n"
61 " -V print version and exit\n"
62 "\n"
63 " COMMAND can be one of the following:\n"
laqinfan35731852017-08-08 06:17:39 -050064 " lsdb\n"
65 " display NLSR lsdb status\n"
66 " routing\n"
67 " display routing table status\n"
Vince Lehmanc439d662015-04-27 10:56:00 -050068 " status\n"
laqinfan35731852017-08-08 06:17:39 -050069 " display all NLSR status (lsdb & routingtable)\n"
Vince Lehmanc439d662015-04-27 10:56:00 -050070 " advertise name\n"
71 " advertise a name prefix through NLSR\n"
Saurab Dulal7526cee2018-01-31 18:14:10 +000072 " advertise name save\n"
73 " advertise and save the name prefix to the conf file\n"
Vince Lehmanc439d662015-04-27 10:56:00 -050074 " withdraw name\n"
Saurab Dulal7526cee2018-01-31 18:14:10 +000075 " remove a name prefix advertised through NLSR\n"
76 " withdraw name delete\n"
77 " withdraw and delete the name prefix from the conf file"
Vince Lehmanc439d662015-04-27 10:56:00 -050078 << std::endl;
79}
80
81void
laqinfan35731852017-08-08 06:17:39 -050082Nlsrc::getStatus(const std::string& command)
Vince Lehmanc439d662015-04-27 10:56:00 -050083{
laqinfan35731852017-08-08 06:17:39 -050084 if (command == "lsdb") {
85 m_fetchSteps.push_back(std::bind(&Nlsrc::fetchAdjacencyLsas, this));
86 m_fetchSteps.push_back(std::bind(&Nlsrc::fetchCoordinateLsas, this));
87 m_fetchSteps.push_back(std::bind(&Nlsrc::fetchNameLsas, this));
88 m_fetchSteps.push_back(std::bind(&Nlsrc::printLsdb, this));
89 }
90 else if (command == "routing") {
91 m_fetchSteps.push_back(std::bind(&Nlsrc::fetchRtables, this));
92 m_fetchSteps.push_back(std::bind(&Nlsrc::printRT, this));
93 }
94 else if(command == "status") {
95 m_fetchSteps.push_back(std::bind(&Nlsrc::fetchAdjacencyLsas, this));
96 m_fetchSteps.push_back(std::bind(&Nlsrc::fetchCoordinateLsas, this));
97 m_fetchSteps.push_back(std::bind(&Nlsrc::fetchNameLsas, this));
98 m_fetchSteps.push_back(std::bind(&Nlsrc::fetchRtables, this));
99 m_fetchSteps.push_back(std::bind(&Nlsrc::printAll, this));
100 }
Vince Lehmanc439d662015-04-27 10:56:00 -0500101 runNextStep();
102}
103
104bool
105Nlsrc::dispatch(const std::string& command)
106{
107 if (command == "advertise") {
Saurab Dulal7526cee2018-01-31 18:14:10 +0000108 if (nOptions < 0) {
Vince Lehmanc439d662015-04-27 10:56:00 -0500109 return false;
110 }
Saurab Dulal7526cee2018-01-31 18:14:10 +0000111 else if (nOptions == 1) {
112 std::string saveFlag = commandLineArguments[0];
113 if (saveFlag != "save") {
114 return false;
115 }
116 }
Vince Lehmanc439d662015-04-27 10:56:00 -0500117
118 advertiseName();
119 return true;
120 }
121 else if (command == "withdraw") {
Saurab Dulal7526cee2018-01-31 18:14:10 +0000122 if (nOptions < 0) {
Vince Lehmanc439d662015-04-27 10:56:00 -0500123 return false;
124 }
Saurab Dulal7526cee2018-01-31 18:14:10 +0000125 else if (nOptions == 1) {
126 std::string saveFlag = commandLineArguments[0];
127 if (saveFlag != "delete") {
128 return false;
129 }
130 }
Vince Lehmanc439d662015-04-27 10:56:00 -0500131
132 withdrawName();
133 return true;
134 }
Saurab Dulal7526cee2018-01-31 18:14:10 +0000135 else if ((command == "lsdb") || (command == "routing") || (command == "status")) {
136 if (nOptions != -1) {
Vince Lehmanc439d662015-04-27 10:56:00 -0500137 return false;
138 }
laqinfan35731852017-08-08 06:17:39 -0500139 commandString = command;
Vince Lehmanc439d662015-04-27 10:56:00 -0500140
laqinfan35731852017-08-08 06:17:39 -0500141 getStatus(command);
Vince Lehmanc439d662015-04-27 10:56:00 -0500142 return true;
143 }
144
145 return false;
146}
147
148void
149Nlsrc::runNextStep()
150{
151 if (m_fetchSteps.empty()) {
152 return;
153 }
154
155 std::function<void()> nextStep = m_fetchSteps.front();
156 m_fetchSteps.pop_front();
157
158 nextStep();
159}
160
161void
162Nlsrc::advertiseName()
163{
Saurab Dulal7526cee2018-01-31 18:14:10 +0000164 ndn::Name name = commandLineArguments[-1];
Vince Lehmanc439d662015-04-27 10:56:00 -0500165
Saurab Dulal7526cee2018-01-31 18:14:10 +0000166 bool saveFlag = false;
167 std::string info = "(Advertise: " + name.toUri() + ")";
168 if (commandLineArguments[0]) {
169 saveFlag = true;
170 info = "(Save: " + name.toUri() + ")";
171 }
172 ndn::Name::Component verb("advertise");
173 sendNamePrefixUpdate(name, verb, info, saveFlag);
Vince Lehmanc439d662015-04-27 10:56:00 -0500174}
175
176void
177Nlsrc::withdrawName()
178{
Saurab Dulal7526cee2018-01-31 18:14:10 +0000179 ndn::Name name = commandLineArguments[-1];
Vince Lehmanc439d662015-04-27 10:56:00 -0500180
Saurab Dulal7526cee2018-01-31 18:14:10 +0000181 bool deleteFlag = false;
182 std::string info = "(Withdraw: " + name.toUri() + ")";
183 if (commandLineArguments[0]) {
184 deleteFlag = true;
185 info = "(Delete: " + name.toUri() + ")";
186 }
187 ndn::Name::Component verb("withdraw");
188 sendNamePrefixUpdate(name, verb, info, deleteFlag);
Vince Lehmanc439d662015-04-27 10:56:00 -0500189}
190
191void
192Nlsrc::sendNamePrefixUpdate(const ndn::Name& name,
193 const ndn::Name::Component& verb,
Saurab Dulal7526cee2018-01-31 18:14:10 +0000194 const std::string& info,
195 bool flag)
Vince Lehmanc439d662015-04-27 10:56:00 -0500196{
197 ndn::nfd::ControlParameters parameters;
198 parameters.setName(name);
Saurab Dulal7526cee2018-01-31 18:14:10 +0000199 if (flag) {
200 parameters.setFlags(1);
201 }
Vince Lehmanc439d662015-04-27 10:56:00 -0500202
203 ndn::Name commandName = NAME_UPDATE_PREFIX;
204 commandName.append(verb);
Muktadir Chowdhuryf04f9892017-08-20 20:42:56 -0500205 commandName.append(parameters.wireEncode());
Vince Lehmanc439d662015-04-27 10:56:00 -0500206
Muktadir Chowdhuryf04f9892017-08-20 20:42:56 -0500207 ndn::security::CommandInterestSigner cis(m_keyChain);
Vince Lehmanc439d662015-04-27 10:56:00 -0500208
Muktadir Chowdhuryf04f9892017-08-20 20:42:56 -0500209 ndn::Interest commandInterest =
210 cis.makeCommandInterest(commandName,
211 ndn::security::signingByIdentity(m_keyChain.getPib().
212 getDefaultIdentity()));
213
214 commandInterest.setMustBeFresh(true);
215
216 m_face.expressInterest(commandInterest,
Vince Lehmanc439d662015-04-27 10:56:00 -0500217 std::bind(&Nlsrc::onControlResponse, this, info, _2),
Alexander Afanasyev1de901f2017-03-09 12:43:57 -0800218 std::bind(&Nlsrc::onTimeout, this, ERROR_CODE_TIMEOUT, "Nack"),
Vince Lehmanc439d662015-04-27 10:56:00 -0500219 std::bind(&Nlsrc::onTimeout, this, ERROR_CODE_TIMEOUT, "Timeout"));
220}
221
222void
223Nlsrc::onControlResponse(const std::string& info, const ndn::Data& data)
224{
Vince Lehmand33e5bc2015-06-22 15:27:50 -0500225 if (data.getMetaInfo().getType() == ndn::tlv::ContentType_Nack) {
226 std::cerr << "ERROR: Run-time advertise/withdraw disabled" << std::endl;
227 return;
228 }
229
Vince Lehmanc439d662015-04-27 10:56:00 -0500230 ndn::nfd::ControlResponse response;
231
232 try {
233 response.wireDecode(data.getContent().blockFromValue());
234 }
235 catch (const std::exception& e) {
236 std::cerr << "ERROR: Control response decoding error" << std::endl;
237 return;
238 }
239
240 uint32_t code = response.getCode();
241
Saurab Dulal7526cee2018-01-31 18:14:10 +0000242 if (code != RESPONSE_CODE_SUCCESS && code != RESPONSE_CODE_SAVE_OR_DELETE) {
243
244 std::cerr << response.getText() << std::endl;
Vince Lehmanc439d662015-04-27 10:56:00 -0500245 std::cerr << "Name prefix update error (code: " << code << ")" << std::endl;
246 return;
247 }
248
249 std::cout << "Applied Name prefix update successfully: " << info << std::endl;
250}
251
252void
253Nlsrc::fetchAdjacencyLsas()
254{
Nick Gordon114537f2017-08-09 14:51:37 -0500255 fetchFromLsdb<nlsr::tlv::AdjacencyLsa>(nlsr::dataset::ADJACENCY_COMPONENT,
Vince Lehmanc439d662015-04-27 10:56:00 -0500256 std::bind(&Nlsrc::recordAdjacencyLsa, this, _1));
257}
258
259void
260Nlsrc::fetchCoordinateLsas()
261{
Nick Gordon114537f2017-08-09 14:51:37 -0500262 fetchFromLsdb<nlsr::tlv::CoordinateLsa>(nlsr::dataset::COORDINATE_COMPONENT,
Vince Lehmanc439d662015-04-27 10:56:00 -0500263 std::bind(&Nlsrc::recordCoordinateLsa, this, _1));
264}
265
266void
267Nlsrc::fetchNameLsas()
268{
Nick Gordon114537f2017-08-09 14:51:37 -0500269 fetchFromLsdb<nlsr::tlv::NameLsa>(nlsr::dataset::NAME_COMPONENT,
Vince Lehmanc439d662015-04-27 10:56:00 -0500270 std::bind(&Nlsrc::recordNameLsa, this, _1));
271}
272
laqinfan35731852017-08-08 06:17:39 -0500273void
274Nlsrc::fetchRtables()
275{
laqinfana073e2e2018-01-15 21:17:24 +0000276 fetchFromRt<nlsr::tlv::RoutingTableStatus>(
277 [this] (const nlsr::tlv::RoutingTableStatus& rts) {
laqinfan35731852017-08-08 06:17:39 -0500278 recordRtable(rts);
279 });
280}
281
Vince Lehmanc439d662015-04-27 10:56:00 -0500282template <class T>
283void
284Nlsrc::fetchFromLsdb(const ndn::Name::Component& datasetType,
285 const std::function<void(const T&)>& recordLsa)
286{
287 ndn::Name command = LSDB_PREFIX;
288 command.append(datasetType);
289
290 ndn::Interest interest(command);
291
Ashlesh Gawande05cb7282018-08-30 14:39:41 -0500292 auto fetcher = ndn::util::SegmentFetcher::start(m_face, interest, m_validator);
293 fetcher->onComplete.connect(std::bind(&Nlsrc::onFetchSuccess<T>, this, _1, recordLsa));
294 fetcher->onError.connect(std::bind(&Nlsrc::onTimeout, this, _1, _2));
Vince Lehmanc439d662015-04-27 10:56:00 -0500295}
296
297template <class T>
298void
laqinfan35731852017-08-08 06:17:39 -0500299Nlsrc::fetchFromRt(const std::function<void(const T&)>& recordDataset)
300{
301 ndn::Name command = RT_PREFIX;
302
303 ndn::Interest interest(command);
304
Ashlesh Gawande05cb7282018-08-30 14:39:41 -0500305 auto fetcher = ndn::util::SegmentFetcher::start(m_face, interest, m_validator);
306 fetcher->onComplete.connect(std::bind(&Nlsrc::onFetchSuccess<T>, this, _1, recordDataset));
307 fetcher->onError.connect(std::bind(&Nlsrc::onTimeout, this, _1, _2));
laqinfan35731852017-08-08 06:17:39 -0500308}
309
310template <class T>
311void
Vince Lehmanc439d662015-04-27 10:56:00 -0500312Nlsrc::onFetchSuccess(const ndn::ConstBufferPtr& data,
laqinfan35731852017-08-08 06:17:39 -0500313 const std::function<void(const T&)>& recordDataset)
Vince Lehmanc439d662015-04-27 10:56:00 -0500314{
315 ndn::Block block;
316 size_t offset = 0;
317
318 while (offset < data->size()) {
319 bool isOk = false;
320 std::tie(isOk, block) = ndn::Block::fromBuffer(data, offset);
321
322 if (!isOk) {
323 std::cerr << "ERROR: cannot decode LSA TLV" << std::endl;
324 break;
325 }
326
327 offset += block.size();
328
laqinfan35731852017-08-08 06:17:39 -0500329 T data(block);
330 recordDataset(data);
Vince Lehmanc439d662015-04-27 10:56:00 -0500331 }
332
333 runNextStep();
334}
335
336void
337Nlsrc::onTimeout(uint32_t errorCode, const std::string& error)
338{
339 std::cerr << "Request timed out (code: " << errorCode
340 << ", error: " << error << ")" << std::endl;
341}
342
343std::string
344Nlsrc::getLsaInfoString(const nlsr::tlv::LsaInfo& info)
345{
346 std::ostringstream os;
347 os << " info=" << info;
348
349 return os.str();
350}
351
352void
353Nlsrc::recordAdjacencyLsa(const nlsr::tlv::AdjacencyLsa& lsa)
354{
laqinfan35731852017-08-08 06:17:39 -0500355 Router& router = getRouterLsdb(lsa.getLsaInfo());
Vince Lehmanc439d662015-04-27 10:56:00 -0500356
357 std::ostringstream os;
358 os << " AdjacencyLsa:" << std::endl;
359
360 os << getLsaInfoString(lsa.getLsaInfo()) << std::endl;
361
362 for (const auto& adjacency : lsa.getAdjacencies()) {
363 os << " adjacency=" << adjacency << std::endl;
364 }
365
366 router.adjacencyLsaString = os.str();
367}
368
369void
370Nlsrc::recordCoordinateLsa(const nlsr::tlv::CoordinateLsa& lsa)
371{
laqinfan35731852017-08-08 06:17:39 -0500372 Router& router = getRouterLsdb(lsa.getLsaInfo());
Vince Lehmanc439d662015-04-27 10:56:00 -0500373
374 std::ostringstream os;
375 os << " Coordinate LSA:" << std::endl;
376
377 os << getLsaInfoString(lsa.getLsaInfo()) << std::endl;
378
Muktadir R Chowdhuryb00dc2a2016-11-05 10:48:58 -0600379 int i = 0;
380 for (auto const& value: lsa.getHyperbolicAngle()) {
381 os << " Hyp Angle " << i++ << ": "<< value << " ";
382 }
383 os << "\n radius=" << lsa.getHyperbolicRadius() << std::endl;
Vince Lehmanc439d662015-04-27 10:56:00 -0500384
385 router.coordinateLsaString = os.str();
386}
387
388void
389Nlsrc::recordNameLsa(const nlsr::tlv::NameLsa& lsa)
390{
laqinfan35731852017-08-08 06:17:39 -0500391 Router& router = getRouterLsdb(lsa.getLsaInfo());
Vince Lehmanc439d662015-04-27 10:56:00 -0500392
393 std::ostringstream os;
394 os << " Name LSA:" << std::endl;
395
396 os << getLsaInfoString(lsa.getLsaInfo()) << std::endl;
397
398 for (const auto& name : lsa.getNames()) {
399 os << " name=" << name << std::endl;
400 }
401
402 router.nameLsaString = os.str();
403}
404
405void
laqinfana073e2e2018-01-15 21:17:24 +0000406Nlsrc::recordRtable(const nlsr::tlv::RoutingTableStatus& rts)
laqinfan35731852017-08-08 06:17:39 -0500407{
laqinfan35731852017-08-08 06:17:39 -0500408 std::ostringstream os;
laqinfan7c13ba52018-01-15 21:17:24 +0000409
410 ndn::Name firstDes;
laqinfana073e2e2018-01-15 21:17:24 +0000411 for (const auto& rt : rts.getRoutingtable()) {
laqinfan7c13ba52018-01-15 21:17:24 +0000412 if (firstDes.empty()) {
413 firstDes = rt.getDestination().getName();
414 os << rt << std::endl;
415 continue;
416 }
417
418 if (firstDes == rt.getDestination().getName()) {
419 os << "\n------Dry-run Hyperbolic Routing Tables:------- \n " << std::endl;
420 }
laqinfana073e2e2018-01-15 21:17:24 +0000421 os << rt << std::endl;
laqinfan35731852017-08-08 06:17:39 -0500422 }
laqinfan7c13ba52018-01-15 21:17:24 +0000423
laqinfana073e2e2018-01-15 21:17:24 +0000424 m_rtString = os.str();
laqinfan35731852017-08-08 06:17:39 -0500425}
426
427void
Vince Lehmanc439d662015-04-27 10:56:00 -0500428Nlsrc::printLsdb()
429{
Vince Lehmanc439d662015-04-27 10:56:00 -0500430 std::cout << "LSDB:" << std::endl;
431
432 for (const auto& item : m_routers) {
433 std::cout << " OriginRouter: " << item.first << std::endl;
434 std::cout << std::endl;
435
436 const Router& router = item.second;
437
438 if (!router.adjacencyLsaString.empty()) {
439 std::cout << router.adjacencyLsaString << std::endl;
440 }
441
442 if (!router.coordinateLsaString.empty()) {
443 std::cout << router.coordinateLsaString << std::endl;
444 }
445
446 if (!router.nameLsaString.empty()) {
447 std::cout << router.nameLsaString << std::endl;
448 }
449 }
450}
451
laqinfan35731852017-08-08 06:17:39 -0500452void
453Nlsrc::printRT()
454{
laqinfana073e2e2018-01-15 21:17:24 +0000455 if (!m_rtString.empty()) {
456 std::cout << "Routing Table" << std::endl;
457 std::cout << m_rtString << std::endl;
458 }
459 else {
460 std::cout << "Routing Table is not calculated yet" << std::endl;
laqinfan35731852017-08-08 06:17:39 -0500461 }
462}
463
464void
465Nlsrc::printAll()
466{
467 std::cout << "NLSR Status" << std::endl;
468 printLsdb();
469 printRT();
470}
471
Vince Lehmanc439d662015-04-27 10:56:00 -0500472Nlsrc::Router&
laqinfan35731852017-08-08 06:17:39 -0500473Nlsrc::getRouterLsdb(const nlsr::tlv::LsaInfo& info)
Vince Lehmanc439d662015-04-27 10:56:00 -0500474{
475 const ndn::Name& originRouterName = info.getOriginRouter();
476
477 const auto& pair =
Alexander Afanasyevf9f39102015-12-01 17:43:40 -0800478 m_routers.insert(std::make_pair(originRouterName, Router()));
Vince Lehmanc439d662015-04-27 10:56:00 -0500479
480 return pair.first->second;
481}
482
483} // namespace nlsrc
484
485////////////////////////////////////////////////////////////////////////////////
486////////////////////////////////////////////////////////////////////////////////
487
488int
489main(int argc, char** argv)
490{
491 ndn::Face face;
492 nlsrc::Nlsrc nlsrc(face);
493
494 nlsrc.programName = argv[0];
495
496 if (argc < 2) {
497 nlsrc.printUsage();
498 return 0;
499 }
500
501 int opt;
502 while ((opt = ::getopt(argc, argv, "hV")) != -1) {
503 switch (opt) {
504 case 'h':
505 nlsrc.printUsage();
506 return 0;
507 case 'V':
508 std::cout << NLSR_VERSION_BUILD_STRING << std::endl;
509 return 0;
510 default:
511 nlsrc.printUsage();
512 return 1;
513 }
514 }
515
516 if (argc == ::optind) {
517 nlsrc.printUsage();
518 return 1;
519 }
520
521 try {
Saurab Dulal7526cee2018-01-31 18:14:10 +0000522 ::optind = 3; // Set ::optind to the command's index
Vince Lehmanc439d662015-04-27 10:56:00 -0500523
524 nlsrc.commandLineArguments = argv + ::optind;
525 nlsrc.nOptions = argc - ::optind;
526
527 // argv[1] points to the command, so pass it to the dispatch
528 bool isOk = nlsrc.dispatch(argv[1]);
529 if (!isOk) {
530 nlsrc.printUsage();
531 return 1;
532 }
533
534 face.processEvents();
535 }
536 catch (const std::exception& e) {
537 std::cerr << "ERROR: " << e.what() << std::endl;
538 return 2;
539 }
540 return 0;
Alexander Afanasyevf9f39102015-12-01 17:43:40 -0800541}