blob: b844c00c15fb86faa514bbf37a01a947b7410076 [file] [log] [blame]
Weiqi Shi90d1e882014-08-01 21:26:21 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Alexander Afanasyevc0e26582017-08-13 21:16:49 -04002/*
weijia yuan82cf9142018-10-21 12:25:02 -07003 * Copyright (c) 2014-2018, Regents of the University of California.
Weiqi Shi90d1e882014-08-01 21:26:21 -07004 *
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/>.
Weiqi Shi90d1e882014-08-01 21:26:21 -070018 */
19
20#include "../src/repo-command-parameter.hpp"
21#include "../src/repo-command-response.hpp"
22
23#include <ndn-cxx/face.hpp>
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -050024#include <ndn-cxx/security/command-interest-signer.hpp>
Weiqi Shi90d1e882014-08-01 21:26:21 -070025#include <ndn-cxx/security/key-chain.hpp>
Junxiao Shi047a6fb2017-06-08 16:16:05 +000026#include <ndn-cxx/security/signing-helpers.hpp>
weijia yuan82cf9142018-10-21 12:25:02 -070027
28#include <boost/asio/io_service.hpp>
Weiqi Shi90d1e882014-08-01 21:26:21 -070029#include <ndn-cxx/util/scheduler.hpp>
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040030
Weiqi Shi90d1e882014-08-01 21:26:21 -070031#include <stdint.h>
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040032#include <stdlib.h>
33
34#include <fstream>
35#include <iostream>
36#include <string>
Weiqi Shi90d1e882014-08-01 21:26:21 -070037
Eric Newberryb16edda2017-12-23 17:54:47 -070038#include <boost/asio/io_service.hpp>
Weiqi Shi90d1e882014-08-01 21:26:21 -070039#include <boost/lexical_cast.hpp>
40
41namespace repo {
42
43using namespace ndn::time;
44
Wentao Shanga8f3c402014-10-30 14:03:27 -070045using std::shared_ptr;
46using std::bind;
47using std::placeholders::_1;
48using std::placeholders::_2;
49
Weiqi Shi90d1e882014-08-01 21:26:21 -070050static const uint64_t DEFAULT_INTEREST_LIFETIME = 4000;
51static const uint64_t DEFAULT_FRESHNESS_PERIOD = 10000;
52static const uint64_t DEFAULT_CHECK_PERIOD = 1000;
53
54enum CommandType
55{
56 START,
57 CHECK,
58 STOP
59};
60
Alexander Afanasyev42290b22017-03-09 12:58:29 -080061class NdnRepoWatch : boost::noncopyable
Weiqi Shi90d1e882014-08-01 21:26:21 -070062{
63public:
64 class Error : public std::runtime_error
65 {
66 public:
67 explicit
68 Error(const std::string& what)
69 : std::runtime_error(what)
70 {
71 }
72 };
73
74 NdnRepoWatch()
75 : freshnessPeriod(DEFAULT_FRESHNESS_PERIOD)
76 , interestLifetime(DEFAULT_INTEREST_LIFETIME)
77 , hasTimeout(false)
78 , watchTimeout(0)
79 , hasMaxInterestNum(false)
80 , maxInterestNum(0)
81 , status(START)
82 , isVerbose(false)
Weiqi Shi90d1e882014-08-01 21:26:21 -070083 , m_scheduler(m_face.getIoService())
84 , m_checkPeriod(DEFAULT_CHECK_PERIOD)
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -050085 , m_cmdSigner(m_keyChain)
Weiqi Shi90d1e882014-08-01 21:26:21 -070086 {
87 }
88
89 void
90 run();
91
92private:
93
94 void
95 startWatchCommand();
96
97 void
Alexander Afanasyev42290b22017-03-09 12:58:29 -080098 onWatchCommandResponse(const ndn::Interest& interest, const ndn::Data& data);
Weiqi Shi90d1e882014-08-01 21:26:21 -070099
100 void
101 onWatchCommandTimeout(const ndn::Interest& interest);
102
103 void
104 stopProcess();
105
106 void
107 signData(ndn::Data& data);
108
109 void
110 startCheckCommand();
111
112 void
113 onCheckCommandTimeout(const ndn::Interest& interest);
114
115 void
116 onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data);
117
118 void
119 onStopCommandTimeout(const ndn::Interest& interest);
120
121 ndn::Interest
122 generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
123 const RepoCommandParameter& commandParameter);
124
125public:
126 std::string identityForCommand;
127 milliseconds freshnessPeriod;
128 milliseconds interestLifetime;
129 bool hasTimeout;
130 milliseconds watchTimeout;
131 bool hasMaxInterestNum;
132 int64_t maxInterestNum;
133 CommandType status;
134 ndn::Name repoPrefix;
135 ndn::Name ndnName;
136 bool isVerbose;
137
138
139private:
140 ndn::Face m_face;
141 ndn::Scheduler m_scheduler;
142 milliseconds m_checkPeriod;
143
144 ndn::Name m_dataPrefix;
145 ndn::KeyChain m_keyChain;
Wentao Shanga8f3c402014-10-30 14:03:27 -0700146 typedef std::map<uint64_t, shared_ptr<ndn::Data> > DataContainer;
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500147 ndn::security::CommandInterestSigner m_cmdSigner;
Weiqi Shi90d1e882014-08-01 21:26:21 -0700148};
149
150void
151NdnRepoWatch::run()
152{
153 m_dataPrefix = ndnName;
154 startWatchCommand();
155
156 if (hasTimeout)
Wentao Shanga8f3c402014-10-30 14:03:27 -0700157 m_scheduler.scheduleEvent(watchTimeout, bind(&NdnRepoWatch::stopProcess, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700158
159 m_face.processEvents();
160}
161
162void
163NdnRepoWatch::startWatchCommand()
164{
165 RepoCommandParameter parameters;
166 parameters.setName(m_dataPrefix);
167
168 repoPrefix.append("watch");
169 if (status == START) {
170 if (hasMaxInterestNum) {
171 parameters.setMaxInterestNum(maxInterestNum);
172 }
173 if (hasTimeout) {
174 parameters.setWatchTimeout(watchTimeout);
175 }
176 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "start", parameters);
177 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700178 bind(&NdnRepoWatch::onWatchCommandResponse, this,
179 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800180 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700181 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700182 }
183 else if (status == STOP){
184 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "stop", parameters);
185 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700186 bind(&NdnRepoWatch::onWatchCommandResponse, this,
187 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800188 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700189 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700190 }
191 else if (status == CHECK){
192 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "check", parameters);
193 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700194 bind(&NdnRepoWatch::onWatchCommandResponse, this,
195 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800196 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700197 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700198 }
199
200}
201
202void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800203NdnRepoWatch::onWatchCommandResponse(const ndn::Interest& interest, const ndn::Data& data)
Weiqi Shi90d1e882014-08-01 21:26:21 -0700204{
205 RepoCommandResponse response(data.getContent().blockFromValue());
weijia yuan82cf9142018-10-21 12:25:02 -0700206 int statusCode = response.getCode();
Weiqi Shi90d1e882014-08-01 21:26:21 -0700207 if (statusCode >= 400) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800208 BOOST_THROW_EXCEPTION(Error("Watch command failed with code " +
209 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700210 }
211 else if (statusCode == 101) {
212 std::cerr << "Watching prefix is stopped!" <<std::endl;
213 m_face.getIoService().stop();
214 return;
215 }
216 else if (statusCode == 300) {
217 std::cerr << "Watching prefix is running!" <<std::endl;
218 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700219 bind(&NdnRepoWatch::startCheckCommand, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700220 return;
221 }
222 else if (statusCode == 100) {
223 std::cerr << "Watching prefix starts!" <<std::endl;
224 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700225 bind(&NdnRepoWatch::startCheckCommand, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700226 return;
227 }
228 else {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800229 BOOST_THROW_EXCEPTION(Error("Unrecognized Status Code " +
230 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700231 }
232}
233
234void
235NdnRepoWatch::onWatchCommandTimeout(const ndn::Interest& interest)
236{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800237 BOOST_THROW_EXCEPTION(Error("command response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700238}
239
240void
241NdnRepoWatch::stopProcess()
242{
243 m_face.getIoService().stop();
244}
245
246void
247NdnRepoWatch::startCheckCommand()
248{
249 repoPrefix.append("watch");
250 ndn::Interest checkInterest = generateCommandInterest(repoPrefix, "check",
251 RepoCommandParameter()
252 .setName(m_dataPrefix));
253 m_face.expressInterest(checkInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700254 bind(&NdnRepoWatch::onWatchCommandResponse, this, _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800255 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700256 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700257}
258
259void
260NdnRepoWatch::onCheckCommandTimeout(const ndn::Interest& interest)
261{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800262 BOOST_THROW_EXCEPTION(Error("check response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700263}
264
265void
266NdnRepoWatch::onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data)
267{
268 RepoCommandResponse response(data.getContent().blockFromValue());
weijia yuan82cf9142018-10-21 12:25:02 -0700269 int statusCode = response.getCode();
Weiqi Shi90d1e882014-08-01 21:26:21 -0700270 if (statusCode != 101) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800271 BOOST_THROW_EXCEPTION(Error("Watch stop command failed with code: " +
272 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700273 }
274 else {
275 std::cerr << "Status code is 101. Watching prefix is stopped successfully!" << std::endl;
276 m_face.getIoService().stop();
277 return;
278 }
279}
280
281void
282NdnRepoWatch::onStopCommandTimeout(const ndn::Interest& interest)
283{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800284 BOOST_THROW_EXCEPTION(Error("stop response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700285}
286
287ndn::Interest
288NdnRepoWatch::generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
289 const RepoCommandParameter& commandParameter)
290{
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500291 Name cmd = commandPrefix;
292 cmd
293 .append(command)
294 .append(commandParameter.wireEncode());
295 ndn::Interest interest;
Weiqi Shi90d1e882014-08-01 21:26:21 -0700296
297 if (identityForCommand.empty())
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500298 interest = m_cmdSigner.makeCommandInterest(cmd);
Weiqi Shi90d1e882014-08-01 21:26:21 -0700299 else {
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500300 interest = m_cmdSigner.makeCommandInterest(cmd, ndn::signingByIdentity(identityForCommand));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700301 }
302
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500303 interest.setInterestLifetime(interestLifetime);
Weiqi Shi90d1e882014-08-01 21:26:21 -0700304 return interest;
305}
306
307static void
308usage()
309{
310 fprintf(stderr,
311 "NdnRepoWatch [-I identity]"
312 " [-x freshness] [-l lifetime] [-w watchtimeout]"
313 " [-n maxinterestnum][-s stop] [-c check]repo-prefix ndn-name\n"
314 "\n"
315 " Write a file into a repo.\n"
316 " -I: specify identity used for signing commands\n"
317 " -x: FreshnessPeriod in milliseconds\n"
318 " -l: InterestLifetime in milliseconds for each command\n"
319 " -w: timeout in milliseconds for whole process (default unlimited)\n"
320 " -n: total number of interests to be sent for whole process (default unlimited)\n"
321 " -s: stop the whole process\n"
322 " -c: check the process\n"
323 " repo-prefix: repo command prefix\n"
324 " ndn-name: NDN Name prefix for written Data\n"
325 );
326 exit(1);
327}
328
329int
330main(int argc, char** argv)
331{
332 NdnRepoWatch app;
333 int opt;
334 while ((opt = getopt(argc, argv, "x:l:w:n:scI:")) != -1) {
335 switch (opt) {
336 case 'x':
337 try {
338 app.freshnessPeriod = milliseconds(boost::lexical_cast<uint64_t>(optarg));
339 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800340 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700341 std::cerr << "-x option should be an integer.";
342 return 1;
343 }
344 break;
345 case 'l':
346 try {
347 app.interestLifetime = milliseconds(boost::lexical_cast<uint64_t>(optarg));
348 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800349 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700350 std::cerr << "-l option should be an integer.";
351 return 1;
352 }
353 break;
354 case 'w':
355 app.hasTimeout = true;
356 try {
357 app.watchTimeout = milliseconds(boost::lexical_cast<int64_t>(optarg));
358 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800359 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700360 std::cerr << "-w option should be an integer.";
361 return 1;
362 }
363 break;
364 case 'n':
365 app.hasMaxInterestNum = true;
366 try {
367 app.maxInterestNum = boost::lexical_cast<int64_t>(optarg);
368 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800369 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700370 std::cerr << "-n option should be an integer.";
371 return 1;
372 }
373 break;
374 case 's':
375 app.status = STOP;
376 break;
377 case 'c':
378 app.status = CHECK;
379 break;
380 case 'I':
381 app.identityForCommand = std::string(optarg);
382 break;
383 case 'v':
384 app.isVerbose = true;
385 break;
386 case 'h':
387 usage();
388 break;
389 default:
390 break;
391 }
392 }
393
394 argc -= optind;
395 argv += optind;
396
397 if (argc != 2)
398 usage();
399
400 app.repoPrefix = Name(argv[0]);
401 app.ndnName = Name(argv[1]);
402
403 app.run();
404
405 return 0;
406}
407
408} // namespace repo
409
410int
411main(int argc, char** argv)
412{
413 try {
414 return repo::main(argc, argv);
415 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800416 catch (const std::exception& e) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700417 std::cerr << "ERROR: " << e.what() << std::endl;
418 return 2;
419 }
420}