blob: 0707c8786c8473f3bb39568802a40c1a26090129 [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/*
Alexander Afanasyev42290b22017-03-09 12:58:29 -08003 * Copyright (c) 2014-2017, 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>
Weiqi Shi90d1e882014-08-01 21:26:21 -070027#include <ndn-cxx/util/scheduler.hpp>
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040028
Weiqi Shi90d1e882014-08-01 21:26:21 -070029#include <stdint.h>
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040030#include <stdlib.h>
31
32#include <fstream>
33#include <iostream>
34#include <string>
Weiqi Shi90d1e882014-08-01 21:26:21 -070035
Eric Newberryb16edda2017-12-23 17:54:47 -070036#include <boost/asio/io_service.hpp>
Weiqi Shi90d1e882014-08-01 21:26:21 -070037#include <boost/lexical_cast.hpp>
38
39namespace repo {
40
41using namespace ndn::time;
42
Wentao Shanga8f3c402014-10-30 14:03:27 -070043using std::shared_ptr;
44using std::bind;
45using std::placeholders::_1;
46using std::placeholders::_2;
47
Weiqi Shi90d1e882014-08-01 21:26:21 -070048static const uint64_t DEFAULT_INTEREST_LIFETIME = 4000;
49static const uint64_t DEFAULT_FRESHNESS_PERIOD = 10000;
50static const uint64_t DEFAULT_CHECK_PERIOD = 1000;
51
52enum CommandType
53{
54 START,
55 CHECK,
56 STOP
57};
58
Alexander Afanasyev42290b22017-03-09 12:58:29 -080059class NdnRepoWatch : boost::noncopyable
Weiqi Shi90d1e882014-08-01 21:26:21 -070060{
61public:
62 class Error : public std::runtime_error
63 {
64 public:
65 explicit
66 Error(const std::string& what)
67 : std::runtime_error(what)
68 {
69 }
70 };
71
72 NdnRepoWatch()
73 : freshnessPeriod(DEFAULT_FRESHNESS_PERIOD)
74 , interestLifetime(DEFAULT_INTEREST_LIFETIME)
75 , hasTimeout(false)
76 , watchTimeout(0)
77 , hasMaxInterestNum(false)
78 , maxInterestNum(0)
79 , status(START)
80 , isVerbose(false)
Weiqi Shi90d1e882014-08-01 21:26:21 -070081 , m_scheduler(m_face.getIoService())
82 , m_checkPeriod(DEFAULT_CHECK_PERIOD)
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -050083 , m_cmdSigner(m_keyChain)
Weiqi Shi90d1e882014-08-01 21:26:21 -070084 {
85 }
86
87 void
88 run();
89
90private:
91
92 void
93 startWatchCommand();
94
95 void
Alexander Afanasyev42290b22017-03-09 12:58:29 -080096 onWatchCommandResponse(const ndn::Interest& interest, const ndn::Data& data);
Weiqi Shi90d1e882014-08-01 21:26:21 -070097
98 void
99 onWatchCommandTimeout(const ndn::Interest& interest);
100
101 void
102 stopProcess();
103
104 void
105 signData(ndn::Data& data);
106
107 void
108 startCheckCommand();
109
110 void
111 onCheckCommandTimeout(const ndn::Interest& interest);
112
113 void
114 onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data);
115
116 void
117 onStopCommandTimeout(const ndn::Interest& interest);
118
119 ndn::Interest
120 generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
121 const RepoCommandParameter& commandParameter);
122
123public:
124 std::string identityForCommand;
125 milliseconds freshnessPeriod;
126 milliseconds interestLifetime;
127 bool hasTimeout;
128 milliseconds watchTimeout;
129 bool hasMaxInterestNum;
130 int64_t maxInterestNum;
131 CommandType status;
132 ndn::Name repoPrefix;
133 ndn::Name ndnName;
134 bool isVerbose;
135
136
137private:
138 ndn::Face m_face;
139 ndn::Scheduler m_scheduler;
140 milliseconds m_checkPeriod;
141
142 ndn::Name m_dataPrefix;
143 ndn::KeyChain m_keyChain;
Wentao Shanga8f3c402014-10-30 14:03:27 -0700144 typedef std::map<uint64_t, shared_ptr<ndn::Data> > DataContainer;
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500145 ndn::security::CommandInterestSigner m_cmdSigner;
Weiqi Shi90d1e882014-08-01 21:26:21 -0700146};
147
148void
149NdnRepoWatch::run()
150{
151 m_dataPrefix = ndnName;
152 startWatchCommand();
153
154 if (hasTimeout)
Wentao Shanga8f3c402014-10-30 14:03:27 -0700155 m_scheduler.scheduleEvent(watchTimeout, bind(&NdnRepoWatch::stopProcess, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700156
157 m_face.processEvents();
158}
159
160void
161NdnRepoWatch::startWatchCommand()
162{
163 RepoCommandParameter parameters;
164 parameters.setName(m_dataPrefix);
165
166 repoPrefix.append("watch");
167 if (status == START) {
168 if (hasMaxInterestNum) {
169 parameters.setMaxInterestNum(maxInterestNum);
170 }
171 if (hasTimeout) {
172 parameters.setWatchTimeout(watchTimeout);
173 }
174 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "start", parameters);
175 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700176 bind(&NdnRepoWatch::onWatchCommandResponse, this,
177 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800178 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700179 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700180 }
181 else if (status == STOP){
182 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "stop", parameters);
183 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700184 bind(&NdnRepoWatch::onWatchCommandResponse, this,
185 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800186 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700187 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700188 }
189 else if (status == CHECK){
190 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "check", parameters);
191 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700192 bind(&NdnRepoWatch::onWatchCommandResponse, this,
193 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800194 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700195 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700196 }
197
198}
199
200void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800201NdnRepoWatch::onWatchCommandResponse(const ndn::Interest& interest, const ndn::Data& data)
Weiqi Shi90d1e882014-08-01 21:26:21 -0700202{
203 RepoCommandResponse response(data.getContent().blockFromValue());
204 int statusCode = response.getStatusCode();
205 if (statusCode >= 400) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800206 BOOST_THROW_EXCEPTION(Error("Watch command failed with code " +
207 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700208 }
209 else if (statusCode == 101) {
210 std::cerr << "Watching prefix is stopped!" <<std::endl;
211 m_face.getIoService().stop();
212 return;
213 }
214 else if (statusCode == 300) {
215 std::cerr << "Watching prefix is running!" <<std::endl;
216 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700217 bind(&NdnRepoWatch::startCheckCommand, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700218 return;
219 }
220 else if (statusCode == 100) {
221 std::cerr << "Watching prefix starts!" <<std::endl;
222 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700223 bind(&NdnRepoWatch::startCheckCommand, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700224 return;
225 }
226 else {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800227 BOOST_THROW_EXCEPTION(Error("Unrecognized Status Code " +
228 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700229 }
230}
231
232void
233NdnRepoWatch::onWatchCommandTimeout(const ndn::Interest& interest)
234{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800235 BOOST_THROW_EXCEPTION(Error("command response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700236}
237
238void
239NdnRepoWatch::stopProcess()
240{
241 m_face.getIoService().stop();
242}
243
244void
245NdnRepoWatch::startCheckCommand()
246{
247 repoPrefix.append("watch");
248 ndn::Interest checkInterest = generateCommandInterest(repoPrefix, "check",
249 RepoCommandParameter()
250 .setName(m_dataPrefix));
251 m_face.expressInterest(checkInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700252 bind(&NdnRepoWatch::onWatchCommandResponse, this, _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800253 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700254 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700255}
256
257void
258NdnRepoWatch::onCheckCommandTimeout(const ndn::Interest& interest)
259{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800260 BOOST_THROW_EXCEPTION(Error("check response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700261}
262
263void
264NdnRepoWatch::onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data)
265{
266 RepoCommandResponse response(data.getContent().blockFromValue());
267 int statusCode = response.getStatusCode();
268 if (statusCode != 101) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800269 BOOST_THROW_EXCEPTION(Error("Watch stop command failed with code: " +
270 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700271 }
272 else {
273 std::cerr << "Status code is 101. Watching prefix is stopped successfully!" << std::endl;
274 m_face.getIoService().stop();
275 return;
276 }
277}
278
279void
280NdnRepoWatch::onStopCommandTimeout(const ndn::Interest& interest)
281{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800282 BOOST_THROW_EXCEPTION(Error("stop response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700283}
284
285ndn::Interest
286NdnRepoWatch::generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
287 const RepoCommandParameter& commandParameter)
288{
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500289 Name cmd = commandPrefix;
290 cmd
291 .append(command)
292 .append(commandParameter.wireEncode());
293 ndn::Interest interest;
Weiqi Shi90d1e882014-08-01 21:26:21 -0700294
295 if (identityForCommand.empty())
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500296 interest = m_cmdSigner.makeCommandInterest(cmd);
Weiqi Shi90d1e882014-08-01 21:26:21 -0700297 else {
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500298 interest = m_cmdSigner.makeCommandInterest(cmd, ndn::signingByIdentity(identityForCommand));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700299 }
300
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500301 interest.setInterestLifetime(interestLifetime);
Weiqi Shi90d1e882014-08-01 21:26:21 -0700302 return interest;
303}
304
305static void
306usage()
307{
308 fprintf(stderr,
309 "NdnRepoWatch [-I identity]"
310 " [-x freshness] [-l lifetime] [-w watchtimeout]"
311 " [-n maxinterestnum][-s stop] [-c check]repo-prefix ndn-name\n"
312 "\n"
313 " Write a file into a repo.\n"
314 " -I: specify identity used for signing commands\n"
315 " -x: FreshnessPeriod in milliseconds\n"
316 " -l: InterestLifetime in milliseconds for each command\n"
317 " -w: timeout in milliseconds for whole process (default unlimited)\n"
318 " -n: total number of interests to be sent for whole process (default unlimited)\n"
319 " -s: stop the whole process\n"
320 " -c: check the process\n"
321 " repo-prefix: repo command prefix\n"
322 " ndn-name: NDN Name prefix for written Data\n"
323 );
324 exit(1);
325}
326
327int
328main(int argc, char** argv)
329{
330 NdnRepoWatch app;
331 int opt;
332 while ((opt = getopt(argc, argv, "x:l:w:n:scI:")) != -1) {
333 switch (opt) {
334 case 'x':
335 try {
336 app.freshnessPeriod = milliseconds(boost::lexical_cast<uint64_t>(optarg));
337 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800338 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700339 std::cerr << "-x option should be an integer.";
340 return 1;
341 }
342 break;
343 case 'l':
344 try {
345 app.interestLifetime = milliseconds(boost::lexical_cast<uint64_t>(optarg));
346 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800347 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700348 std::cerr << "-l option should be an integer.";
349 return 1;
350 }
351 break;
352 case 'w':
353 app.hasTimeout = true;
354 try {
355 app.watchTimeout = milliseconds(boost::lexical_cast<int64_t>(optarg));
356 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800357 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700358 std::cerr << "-w option should be an integer.";
359 return 1;
360 }
361 break;
362 case 'n':
363 app.hasMaxInterestNum = true;
364 try {
365 app.maxInterestNum = boost::lexical_cast<int64_t>(optarg);
366 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800367 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700368 std::cerr << "-n option should be an integer.";
369 return 1;
370 }
371 break;
372 case 's':
373 app.status = STOP;
374 break;
375 case 'c':
376 app.status = CHECK;
377 break;
378 case 'I':
379 app.identityForCommand = std::string(optarg);
380 break;
381 case 'v':
382 app.isVerbose = true;
383 break;
384 case 'h':
385 usage();
386 break;
387 default:
388 break;
389 }
390 }
391
392 argc -= optind;
393 argv += optind;
394
395 if (argc != 2)
396 usage();
397
398 app.repoPrefix = Name(argv[0]);
399 app.ndnName = Name(argv[1]);
400
401 app.run();
402
403 return 0;
404}
405
406} // namespace repo
407
408int
409main(int argc, char** argv)
410{
411 try {
412 return repo::main(argc, argv);
413 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800414 catch (const std::exception& e) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700415 std::cerr << "ERROR: " << e.what() << std::endl;
416 return 2;
417 }
418}