blob: 908a885133962116968a97d215b20fb58652e26d [file] [log] [blame]
Shuo Chenccfbe242014-04-29 23:57:51 +08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Alexander Afanasyevc0e26582017-08-13 21:16:49 -04002/*
Alexander Afanasyev42290b22017-03-09 12:58:29 -08003 * Copyright (c) 2014-2017, Regents of the University of California.
Shuo Chenccfbe242014-04-29 23:57:51 +08004 *
5 * This file is part of NDN repo-ng (Next generation of NDN repository).
6 * See AUTHORS.md for complete list of repo-ng authors and contributors.
7 *
8 * repo-ng is free software: you can redistribute it and/or modify it under the terms
9 * of the GNU General Public License as published by the Free Software Foundation,
10 * either version 3 of the License, or (at your option) any later version.
11 *
12 * repo-ng is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
Alexander Afanasyev42290b22017-03-09 12:58:29 -080017 * repo-ng, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
Shuo Chenccfbe242014-04-29 23:57:51 +080018 */
19
Alexander Afanasyev39d98072014-05-04 12:46:29 -070020#include "../src/repo-command-parameter.hpp"
21#include "../src/repo-command-response.hpp"
Shuo Chenccfbe242014-04-29 23:57:51 +080022
23#include <ndn-cxx/face.hpp>
24#include <ndn-cxx/security/key-chain.hpp>
Junxiao Shi959c5b92016-08-23 01:05:27 +000025#include <ndn-cxx/security/signing-helpers.hpp>
Wentao Shang91fb4f22014-05-20 10:55:22 -070026#include <ndn-cxx/util/scheduler.hpp>
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040027
Wentao Shang91fb4f22014-05-20 10:55:22 -070028#include <stdint.h>
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040029#include <stdlib.h>
30
31#include <fstream>
32#include <iostream>
33#include <string>
34
Shuo Chenccfbe242014-04-29 23:57:51 +080035#include <boost/filesystem.hpp>
Wentao Shang91fb4f22014-05-20 10:55:22 -070036#include <boost/lexical_cast.hpp>
37#include <boost/asio.hpp>
Shuo Chenccfbe242014-04-29 23:57:51 +080038#include <boost/iostreams/operations.hpp>
39#include <boost/iostreams/read.hpp>
40
41namespace repo {
42
43using namespace ndn::time;
44
Wentao Shanga8f3c402014-10-30 14:03:27 -070045using std::shared_ptr;
46using std::make_shared;
47using std::bind;
48using std::placeholders::_1;
49using std::placeholders::_2;
50
Shuo Chenccfbe242014-04-29 23:57:51 +080051static const uint64_t DEFAULT_BLOCK_SIZE = 1000;
52static const uint64_t DEFAULT_INTEREST_LIFETIME = 4000;
53static const uint64_t DEFAULT_FRESHNESS_PERIOD = 10000;
54static const uint64_t DEFAULT_CHECK_PERIOD = 1000;
Weiqi Shi5822e342014-08-21 20:05:30 -070055static const size_t PRE_SIGN_DATA_COUNT = 11;
Shuo Chenccfbe242014-04-29 23:57:51 +080056
Alexander Afanasyev42290b22017-03-09 12:58:29 -080057class NdnPutFile : boost::noncopyable
Shuo Chenccfbe242014-04-29 23:57:51 +080058{
59public:
60 class Error : public std::runtime_error
61 {
62 public:
63 explicit
64 Error(const std::string& what)
65 : std::runtime_error(what)
66 {
67 }
68 };
69
70 NdnPutFile()
71 : isUnversioned(false)
72 , isSingle(false)
73 , useDigestSha256(false)
74 , freshnessPeriod(DEFAULT_FRESHNESS_PERIOD)
75 , interestLifetime(DEFAULT_INTEREST_LIFETIME)
76 , hasTimeout(false)
77 , timeout(0)
78 , insertStream(0)
79 , isVerbose(false)
Shuo Chenccfbe242014-04-29 23:57:51 +080080 , m_scheduler(m_face.getIoService())
81 , m_timestampVersion(toUnixTimestamp(system_clock::now()).count())
82 , m_processId(0)
83 , m_checkPeriod(DEFAULT_CHECK_PERIOD)
84 , m_currentSegmentNo(0)
85 , m_isFinished(false)
86 {
87 }
88
89 void
90 run();
91
92private:
93 void
94 prepareNextData(uint64_t referenceSegmentNo);
95
96 void
97 startInsertCommand();
98
99 void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800100 onInsertCommandResponse(const ndn::Interest& interest, const ndn::Data& data);
Shuo Chenccfbe242014-04-29 23:57:51 +0800101
102 void
103 onInsertCommandTimeout(const ndn::Interest& interest);
104
105 void
106 onInterest(const ndn::Name& prefix, const ndn::Interest& interest);
107
108 void
109 onSingleInterest(const ndn::Name& prefix, const ndn::Interest& interest);
110
111 void
Wentao Shang91fb4f22014-05-20 10:55:22 -0700112 onRegisterSuccess(const ndn::Name& prefix);
113
114 void
Shuo Chenccfbe242014-04-29 23:57:51 +0800115 onRegisterFailed(const ndn::Name& prefix, const std::string& reason);
116
117 void
118 stopProcess();
119
120 void
121 signData(ndn::Data& data);
122
123 void
124 startCheckCommand();
125
126 void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800127 onCheckCommandResponse(const ndn::Interest& interest, const ndn::Data& data);
Shuo Chenccfbe242014-04-29 23:57:51 +0800128
129 void
130 onCheckCommandTimeout(const ndn::Interest& interest);
131
132 ndn::Interest
133 generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
134 const RepoCommandParameter& commandParameter);
135
136public:
137 bool isUnversioned;
138 bool isSingle;
139 bool useDigestSha256;
140 std::string identityForData;
141 std::string identityForCommand;
142 milliseconds freshnessPeriod;
143 milliseconds interestLifetime;
144 bool hasTimeout;
145 milliseconds timeout;
146 ndn::Name repoPrefix;
147 ndn::Name ndnName;
148 std::istream* insertStream;
149 bool isVerbose;
150
151private:
152 ndn::Face m_face;
153 ndn::Scheduler m_scheduler;
154 ndn::KeyChain m_keyChain;
Shuo Chenccfbe242014-04-29 23:57:51 +0800155 uint64_t m_timestampVersion;
156 uint64_t m_processId;
157 milliseconds m_checkPeriod;
158 size_t m_currentSegmentNo;
159 bool m_isFinished;
160 ndn::Name m_dataPrefix;
161
Wentao Shanga8f3c402014-10-30 14:03:27 -0700162 typedef std::map<uint64_t, shared_ptr<ndn::Data> > DataContainer;
Shuo Chenccfbe242014-04-29 23:57:51 +0800163 DataContainer m_data;
164};
165
166void
167NdnPutFile::prepareNextData(uint64_t referenceSegmentNo)
168{
169 // make sure m_data has [referenceSegmentNo, referenceSegmentNo + PRE_SIGN_DATA_COUNT] Data
170 if (m_isFinished)
171 return;
172
173 size_t nDataToPrepare = PRE_SIGN_DATA_COUNT;
174
175 if (!m_data.empty()) {
176 uint64_t maxSegmentNo = m_data.rbegin()->first;
177
178 if (maxSegmentNo - referenceSegmentNo >= nDataToPrepare) {
179 // nothing to prepare
180 return;
181 }
182
183 nDataToPrepare -= maxSegmentNo - referenceSegmentNo;
184 }
185
186 for (size_t i = 0; i < nDataToPrepare && !m_isFinished; ++i) {
187 uint8_t buffer[DEFAULT_BLOCK_SIZE];
188
189 std::streamsize readSize =
190 boost::iostreams::read(*insertStream, reinterpret_cast<char*>(buffer), DEFAULT_BLOCK_SIZE);
191
192 if (readSize <= 0) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800193 BOOST_THROW_EXCEPTION(Error("Error reading from the input stream"));
Shuo Chenccfbe242014-04-29 23:57:51 +0800194 }
195
Wentao Shanga8f3c402014-10-30 14:03:27 -0700196 shared_ptr<ndn::Data> data =
197 make_shared<ndn::Data>(Name(m_dataPrefix)
Shuo Chenccfbe242014-04-29 23:57:51 +0800198 .appendSegment(m_currentSegmentNo));
199
200 if (insertStream->peek() == std::istream::traits_type::eof()) {
Weiqi Shi5822e342014-08-21 20:05:30 -0700201 data->setFinalBlockId(ndn::name::Component::fromSegment(m_currentSegmentNo));
Shuo Chenccfbe242014-04-29 23:57:51 +0800202 m_isFinished = true;
203 }
204
205 data->setContent(buffer, readSize);
206 data->setFreshnessPeriod(freshnessPeriod);
207 signData(*data);
208
209 m_data.insert(std::make_pair(m_currentSegmentNo, data));
210
211 ++m_currentSegmentNo;
212 }
213}
214
215void
216NdnPutFile::run()
217{
218 m_dataPrefix = ndnName;
219 if (!isUnversioned)
220 m_dataPrefix.appendVersion(m_timestampVersion);
221
222 if (isVerbose)
223 std::cerr << "setInterestFilter for " << m_dataPrefix << std::endl;
224 m_face.setInterestFilter(m_dataPrefix,
225 isSingle ?
Wentao Shanga8f3c402014-10-30 14:03:27 -0700226 bind(&NdnPutFile::onSingleInterest, this, _1, _2)
227 :
228 bind(&NdnPutFile::onInterest, this, _1, _2),
229 bind(&NdnPutFile::onRegisterSuccess, this, _1),
230 bind(&NdnPutFile::onRegisterFailed, this, _1, _2));
Shuo Chenccfbe242014-04-29 23:57:51 +0800231
Weiqi Shi5822e342014-08-21 20:05:30 -0700232
Shuo Chenccfbe242014-04-29 23:57:51 +0800233 if (hasTimeout)
Wentao Shanga8f3c402014-10-30 14:03:27 -0700234 m_scheduler.scheduleEvent(timeout, bind(&NdnPutFile::stopProcess, this));
Shuo Chenccfbe242014-04-29 23:57:51 +0800235
236 m_face.processEvents();
237}
238
239void
Wentao Shang91fb4f22014-05-20 10:55:22 -0700240NdnPutFile::onRegisterSuccess(const Name& prefix)
241{
242 startInsertCommand();
243}
244
245void
Shuo Chenccfbe242014-04-29 23:57:51 +0800246NdnPutFile::startInsertCommand()
247{
248 RepoCommandParameter parameters;
249 parameters.setName(m_dataPrefix);
250 if (!isSingle) {
251 parameters.setStartBlockId(0);
252 }
253
254 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "insert", parameters);
255 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700256 bind(&NdnPutFile::onInsertCommandResponse, this, _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800257 bind(&NdnPutFile::onInsertCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700258 bind(&NdnPutFile::onInsertCommandTimeout, this, _1));
Shuo Chenccfbe242014-04-29 23:57:51 +0800259}
260
261void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800262NdnPutFile::onInsertCommandResponse(const ndn::Interest& interest, const ndn::Data& data)
Shuo Chenccfbe242014-04-29 23:57:51 +0800263{
264 RepoCommandResponse response(data.getContent().blockFromValue());
265 int statusCode = response.getStatusCode();
266 if (statusCode >= 400) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800267 BOOST_THROW_EXCEPTION(Error("insert command failed with code " +
268 boost::lexical_cast<std::string>(statusCode)));
Shuo Chenccfbe242014-04-29 23:57:51 +0800269 }
270 m_processId = response.getProcessId();
271
272 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700273 bind(&NdnPutFile::startCheckCommand, this));
Shuo Chenccfbe242014-04-29 23:57:51 +0800274}
275
276void
277NdnPutFile::onInsertCommandTimeout(const ndn::Interest& interest)
278{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800279 BOOST_THROW_EXCEPTION(Error("command response timeout"));
Shuo Chenccfbe242014-04-29 23:57:51 +0800280}
281
282void
283NdnPutFile::onInterest(const ndn::Name& prefix, const ndn::Interest& interest)
284{
285 if (interest.getName().size() != prefix.size() + 1) {
286 if (isVerbose) {
287 std::cerr << "Error processing incoming interest " << interest << ": "
288 << "Unrecognized Interest" << std::endl;
289 }
290 return;
291 }
292
293 uint64_t segmentNo;
294 try {
295 ndn::Name::Component segmentComponent = interest.getName().get(prefix.size());
296 segmentNo = segmentComponent.toSegment();
297 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800298 catch (const tlv::Error& e) {
Shuo Chenccfbe242014-04-29 23:57:51 +0800299 if (isVerbose) {
300 std::cerr << "Error processing incoming interest " << interest << ": "
301 << e.what() << std::endl;
302 }
303 return;
304 }
305
306 prepareNextData(segmentNo);
307
308 DataContainer::iterator item = m_data.find(segmentNo);
309 if (item == m_data.end()) {
310 if (isVerbose) {
311 std::cerr << "Requested segment [" << segmentNo << "] does not exist" << std::endl;
312 }
313 return;
314 }
315
Weiqi Shi5822e342014-08-21 20:05:30 -0700316 if (m_isFinished) {
317 uint64_t final = m_currentSegmentNo - 1;
318 item->second->setFinalBlockId(ndn::name::Component::fromSegment(final));
319
320 }
Shuo Chenccfbe242014-04-29 23:57:51 +0800321 m_face.put(*item->second);
322}
323
324void
325NdnPutFile::onSingleInterest(const ndn::Name& prefix, const ndn::Interest& interest)
326{
327 BOOST_ASSERT(prefix == m_dataPrefix);
328
329 if (prefix != interest.getName()) {
330 if (isVerbose) {
331 std::cerr << "Received unexpected interest " << interest << std::endl;
332 }
333 return;
334 }
335
336 uint8_t buffer[DEFAULT_BLOCK_SIZE];
337 std::streamsize readSize =
338 boost::iostreams::read(*insertStream, reinterpret_cast<char*>(buffer), DEFAULT_BLOCK_SIZE);
339
340 if (readSize <= 0) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800341 BOOST_THROW_EXCEPTION(Error("Error reading from the input stream"));
Shuo Chenccfbe242014-04-29 23:57:51 +0800342 }
343
344 if (insertStream->peek() != std::istream::traits_type::eof()) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800345 BOOST_THROW_EXCEPTION(Error("Input data does not fit into one Data packet"));
Shuo Chenccfbe242014-04-29 23:57:51 +0800346 }
347
Wentao Shanga8f3c402014-10-30 14:03:27 -0700348 shared_ptr<ndn::Data> data = make_shared<ndn::Data>(m_dataPrefix);
Weiqi Shi5822e342014-08-21 20:05:30 -0700349 data->setContent(buffer, readSize);
350 data->setFreshnessPeriod(freshnessPeriod);
351 signData(*data);
352 m_face.put(*data);
Shuo Chenccfbe242014-04-29 23:57:51 +0800353
354 m_isFinished = true;
355}
356
357void
358NdnPutFile::onRegisterFailed(const ndn::Name& prefix, const std::string& reason)
359{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800360 BOOST_THROW_EXCEPTION(Error("onRegisterFailed: " + reason));
Shuo Chenccfbe242014-04-29 23:57:51 +0800361}
362
363void
364NdnPutFile::stopProcess()
365{
366 m_face.getIoService().stop();
367}
368
369void
370NdnPutFile::signData(ndn::Data& data)
371{
372 if (useDigestSha256) {
Junxiao Shi047a6fb2017-06-08 16:16:05 +0000373 m_keyChain.sign(data, ndn::signingWithSha256());
Shuo Chenccfbe242014-04-29 23:57:51 +0800374 }
Junxiao Shi047a6fb2017-06-08 16:16:05 +0000375 else if (identityForData.empty())
376 m_keyChain.sign(data);
Shuo Chenccfbe242014-04-29 23:57:51 +0800377 else {
Junxiao Shi047a6fb2017-06-08 16:16:05 +0000378 m_keyChain.sign(data, ndn::signingByIdentity(identityForData));
Shuo Chenccfbe242014-04-29 23:57:51 +0800379 }
380}
381
382void
383NdnPutFile::startCheckCommand()
384{
385 ndn::Interest checkInterest = generateCommandInterest(repoPrefix, "insert check",
386 RepoCommandParameter()
387 .setProcessId(m_processId));
388 m_face.expressInterest(checkInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700389 bind(&NdnPutFile::onCheckCommandResponse, this, _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800390 bind(&NdnPutFile::onCheckCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700391 bind(&NdnPutFile::onCheckCommandTimeout, this, _1));
Shuo Chenccfbe242014-04-29 23:57:51 +0800392}
393
394void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800395NdnPutFile::onCheckCommandResponse(const ndn::Interest& interest, const ndn::Data& data)
Shuo Chenccfbe242014-04-29 23:57:51 +0800396{
397 RepoCommandResponse response(data.getContent().blockFromValue());
398 int statusCode = response.getStatusCode();
399 if (statusCode >= 400) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800400 BOOST_THROW_EXCEPTION(Error("Insert check command failed with code: " +
401 boost::lexical_cast<std::string>(statusCode)));
Shuo Chenccfbe242014-04-29 23:57:51 +0800402 }
403
404 if (m_isFinished) {
405 uint64_t insertCount = response.getInsertNum();
406
407 if (isSingle) {
408 if (insertCount == 1) {
409 m_face.getIoService().stop();
410 return;
411 }
412 }
413 // Technically, the check should not infer, but directly has signal from repo that
414 // write operation has been finished
415
416 if (insertCount == m_currentSegmentNo) {
417 m_face.getIoService().stop();
418 return;
419 }
420 }
421
422 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700423 bind(&NdnPutFile::startCheckCommand, this));
Shuo Chenccfbe242014-04-29 23:57:51 +0800424}
425
426void
427NdnPutFile::onCheckCommandTimeout(const ndn::Interest& interest)
428{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800429 BOOST_THROW_EXCEPTION(Error("check response timeout"));
Shuo Chenccfbe242014-04-29 23:57:51 +0800430}
431
432ndn::Interest
433NdnPutFile::generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
434 const RepoCommandParameter& commandParameter)
435{
436 ndn::Interest interest(ndn::Name(commandPrefix)
437 .append(command)
438 .append(commandParameter.wireEncode()));
439 interest.setInterestLifetime(interestLifetime);
440
441 if (identityForCommand.empty())
Junxiao Shi959c5b92016-08-23 01:05:27 +0000442 m_keyChain.sign(interest);
Shuo Chenccfbe242014-04-29 23:57:51 +0800443 else {
Junxiao Shi959c5b92016-08-23 01:05:27 +0000444 m_keyChain.sign(interest, ndn::signingByIdentity(identityForCommand));
Shuo Chenccfbe242014-04-29 23:57:51 +0800445 }
446
447 return interest;
448}
449
450static void
451usage()
452{
453 fprintf(stderr,
454 "ndnputfile [-u] [-s] [-D] [-d] [-i identity] [-I identity]"
455 " [-x freshness] [-l lifetime] [-w timeout] repo-prefix ndn-name filename\n"
456 "\n"
457 " Write a file into a repo.\n"
458 " -u: unversioned: do not add a version component\n"
459 " -s: single: do not add version or segment component, implies -u\n"
460 " -D: use DigestSha256 signing method instead of SignatureSha256WithRsa\n"
461 " -i: specify identity used for signing Data\n"
462 " -I: specify identity used for signing commands\n"
463 " -x: FreshnessPeriod in milliseconds\n"
464 " -l: InterestLifetime in milliseconds for each command\n"
465 " -w: timeout in milliseconds for whole process (default unlimited)\n"
466 " -v: be verbose\n"
467 " repo-prefix: repo command prefix\n"
468 " ndn-name: NDN Name prefix for written Data\n"
469 " filename: local file name; \"-\" reads from stdin\n"
470 );
471 exit(1);
472}
473
474int
475main(int argc, char** argv)
476{
477 NdnPutFile ndnPutFile;
478 int opt;
479 while ((opt = getopt(argc, argv, "usDi:I:x:l:w:vh")) != -1) {
480 switch (opt) {
481 case 'u':
482 ndnPutFile.isUnversioned = true;
483 break;
484 case 's':
485 ndnPutFile.isSingle = true;
486 break;
487 case 'D':
488 ndnPutFile.useDigestSha256 = true;
489 break;
490 case 'i':
491 ndnPutFile.identityForData = std::string(optarg);
492 break;
493 case 'I':
494 ndnPutFile.identityForCommand = std::string(optarg);
495 break;
496 case 'x':
497 try {
498 ndnPutFile.freshnessPeriod = milliseconds(boost::lexical_cast<uint64_t>(optarg));
499 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800500 catch (const boost::bad_lexical_cast&) {
Shuo Chenccfbe242014-04-29 23:57:51 +0800501 std::cerr << "-x option should be an integer.";
502 return 1;
503 }
504 break;
505 case 'l':
506 try {
507 ndnPutFile.interestLifetime = milliseconds(boost::lexical_cast<uint64_t>(optarg));
508 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800509 catch (const boost::bad_lexical_cast&) {
Shuo Chenccfbe242014-04-29 23:57:51 +0800510 std::cerr << "-l option should be an integer.";
511 return 1;
512 }
513 break;
514 case 'w':
515 ndnPutFile.hasTimeout = true;
516 try {
517 ndnPutFile.timeout = milliseconds(boost::lexical_cast<uint64_t>(optarg));
518 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800519 catch (const boost::bad_lexical_cast&) {
Shuo Chenccfbe242014-04-29 23:57:51 +0800520 std::cerr << "-w option should be an integer.";
521 return 1;
522 }
523 break;
524 case 'v':
525 ndnPutFile.isVerbose = true;
526 break;
527 case 'h':
528 usage();
529 break;
530 default:
531 break;
532 }
533 }
534
535 argc -= optind;
536 argv += optind;
537
538 if (argc != 3)
539 usage();
540
541 ndnPutFile.repoPrefix = Name(argv[0]);
542 ndnPutFile.ndnName = Name(argv[1]);
543 if (strcmp(argv[2], "-") == 0) {
544
545 ndnPutFile.insertStream = &std::cin;
546 ndnPutFile.run();
547 }
548 else {
549 std::ifstream inputFileStream(argv[2], std::ios::in | std::ios::binary);
550 if (!inputFileStream.is_open()) {
551 std::cerr << "ERROR: cannot open " << argv[2] << std::endl;
552 return 1;
553 }
554
555 ndnPutFile.insertStream = &inputFileStream;
556 ndnPutFile.run();
557 }
558
559 // ndnPutFile MUST NOT be used anymore because .insertStream is a dangling pointer
560
561 return 0;
562}
563
564} // namespace repo
565
566int
567main(int argc, char** argv)
568{
569 try {
570 return repo::main(argc, argv);
571 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800572 catch (const std::exception& e) {
Shuo Chenccfbe242014-04-29 23:57:51 +0800573 std::cerr << "ERROR: " << e.what() << std::endl;
574 return 2;
575 }
576}