blob: 4d2d9c56874dfddd686e8ed3425410228489e933 [file] [log] [blame]
Weiqi Shi90d1e882014-08-01 21:26:21 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
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>
24#include <ndn-cxx/security/key-chain.hpp>
Junxiao Shi047a6fb2017-06-08 16:16:05 +000025#include <ndn-cxx/security/signing-helpers.hpp>
Weiqi Shi90d1e882014-08-01 21:26:21 -070026#include <ndn-cxx/util/scheduler.hpp>
27#include <fstream>
28#include <string>
29#include <stdlib.h>
30#include <stdint.h>
31
32#include <boost/lexical_cast.hpp>
33
34namespace repo {
35
36using namespace ndn::time;
37
Wentao Shanga8f3c402014-10-30 14:03:27 -070038using std::shared_ptr;
39using std::bind;
40using std::placeholders::_1;
41using std::placeholders::_2;
42
Weiqi Shi90d1e882014-08-01 21:26:21 -070043static const uint64_t DEFAULT_INTEREST_LIFETIME = 4000;
44static const uint64_t DEFAULT_FRESHNESS_PERIOD = 10000;
45static const uint64_t DEFAULT_CHECK_PERIOD = 1000;
46
47enum CommandType
48{
49 START,
50 CHECK,
51 STOP
52};
53
Alexander Afanasyev42290b22017-03-09 12:58:29 -080054class NdnRepoWatch : boost::noncopyable
Weiqi Shi90d1e882014-08-01 21:26:21 -070055{
56public:
57 class Error : public std::runtime_error
58 {
59 public:
60 explicit
61 Error(const std::string& what)
62 : std::runtime_error(what)
63 {
64 }
65 };
66
67 NdnRepoWatch()
68 : freshnessPeriod(DEFAULT_FRESHNESS_PERIOD)
69 , interestLifetime(DEFAULT_INTEREST_LIFETIME)
70 , hasTimeout(false)
71 , watchTimeout(0)
72 , hasMaxInterestNum(false)
73 , maxInterestNum(0)
74 , status(START)
75 , isVerbose(false)
76
77 , m_scheduler(m_face.getIoService())
78 , m_checkPeriod(DEFAULT_CHECK_PERIOD)
79 {
80 }
81
82 void
83 run();
84
85private:
86
87 void
88 startWatchCommand();
89
90 void
Alexander Afanasyev42290b22017-03-09 12:58:29 -080091 onWatchCommandResponse(const ndn::Interest& interest, const ndn::Data& data);
Weiqi Shi90d1e882014-08-01 21:26:21 -070092
93 void
94 onWatchCommandTimeout(const ndn::Interest& interest);
95
96 void
97 stopProcess();
98
99 void
100 signData(ndn::Data& data);
101
102 void
103 startCheckCommand();
104
105 void
106 onCheckCommandTimeout(const ndn::Interest& interest);
107
108 void
109 onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data);
110
111 void
112 onStopCommandTimeout(const ndn::Interest& interest);
113
114 ndn::Interest
115 generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
116 const RepoCommandParameter& commandParameter);
117
118public:
119 std::string identityForCommand;
120 milliseconds freshnessPeriod;
121 milliseconds interestLifetime;
122 bool hasTimeout;
123 milliseconds watchTimeout;
124 bool hasMaxInterestNum;
125 int64_t maxInterestNum;
126 CommandType status;
127 ndn::Name repoPrefix;
128 ndn::Name ndnName;
129 bool isVerbose;
130
131
132private:
133 ndn::Face m_face;
134 ndn::Scheduler m_scheduler;
135 milliseconds m_checkPeriod;
136
137 ndn::Name m_dataPrefix;
138 ndn::KeyChain m_keyChain;
Wentao Shanga8f3c402014-10-30 14:03:27 -0700139 typedef std::map<uint64_t, shared_ptr<ndn::Data> > DataContainer;
Weiqi Shi90d1e882014-08-01 21:26:21 -0700140};
141
142void
143NdnRepoWatch::run()
144{
145 m_dataPrefix = ndnName;
146 startWatchCommand();
147
148 if (hasTimeout)
Wentao Shanga8f3c402014-10-30 14:03:27 -0700149 m_scheduler.scheduleEvent(watchTimeout, bind(&NdnRepoWatch::stopProcess, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700150
151 m_face.processEvents();
152}
153
154void
155NdnRepoWatch::startWatchCommand()
156{
157 RepoCommandParameter parameters;
158 parameters.setName(m_dataPrefix);
159
160 repoPrefix.append("watch");
161 if (status == START) {
162 if (hasMaxInterestNum) {
163 parameters.setMaxInterestNum(maxInterestNum);
164 }
165 if (hasTimeout) {
166 parameters.setWatchTimeout(watchTimeout);
167 }
168 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "start", parameters);
169 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700170 bind(&NdnRepoWatch::onWatchCommandResponse, this,
171 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800172 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700173 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700174 }
175 else if (status == STOP){
176 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "stop", 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 == CHECK){
184 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "check", 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
192}
193
194void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800195NdnRepoWatch::onWatchCommandResponse(const ndn::Interest& interest, const ndn::Data& data)
Weiqi Shi90d1e882014-08-01 21:26:21 -0700196{
197 RepoCommandResponse response(data.getContent().blockFromValue());
198 int statusCode = response.getStatusCode();
199 if (statusCode >= 400) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800200 BOOST_THROW_EXCEPTION(Error("Watch command failed with code " +
201 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700202 }
203 else if (statusCode == 101) {
204 std::cerr << "Watching prefix is stopped!" <<std::endl;
205 m_face.getIoService().stop();
206 return;
207 }
208 else if (statusCode == 300) {
209 std::cerr << "Watching prefix is running!" <<std::endl;
210 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700211 bind(&NdnRepoWatch::startCheckCommand, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700212 return;
213 }
214 else if (statusCode == 100) {
215 std::cerr << "Watching prefix starts!" <<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 {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800221 BOOST_THROW_EXCEPTION(Error("Unrecognized Status Code " +
222 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700223 }
224}
225
226void
227NdnRepoWatch::onWatchCommandTimeout(const ndn::Interest& interest)
228{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800229 BOOST_THROW_EXCEPTION(Error("command response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700230}
231
232void
233NdnRepoWatch::stopProcess()
234{
235 m_face.getIoService().stop();
236}
237
238void
239NdnRepoWatch::startCheckCommand()
240{
241 repoPrefix.append("watch");
242 ndn::Interest checkInterest = generateCommandInterest(repoPrefix, "check",
243 RepoCommandParameter()
244 .setName(m_dataPrefix));
245 m_face.expressInterest(checkInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700246 bind(&NdnRepoWatch::onWatchCommandResponse, this, _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800247 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700248 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700249}
250
251void
252NdnRepoWatch::onCheckCommandTimeout(const ndn::Interest& interest)
253{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800254 BOOST_THROW_EXCEPTION(Error("check response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700255}
256
257void
258NdnRepoWatch::onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data)
259{
260 RepoCommandResponse response(data.getContent().blockFromValue());
261 int statusCode = response.getStatusCode();
262 if (statusCode != 101) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800263 BOOST_THROW_EXCEPTION(Error("Watch stop command failed with code: " +
264 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700265 }
266 else {
267 std::cerr << "Status code is 101. Watching prefix is stopped successfully!" << std::endl;
268 m_face.getIoService().stop();
269 return;
270 }
271}
272
273void
274NdnRepoWatch::onStopCommandTimeout(const ndn::Interest& interest)
275{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800276 BOOST_THROW_EXCEPTION(Error("stop response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700277}
278
279ndn::Interest
280NdnRepoWatch::generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
281 const RepoCommandParameter& commandParameter)
282{
283 ndn::Interest interest(ndn::Name(commandPrefix)
284 .append(command)
285 .append(commandParameter.wireEncode()));
286 interest.setInterestLifetime(interestLifetime);
287
288 if (identityForCommand.empty())
289 m_keyChain.sign(interest);
290 else {
Junxiao Shi047a6fb2017-06-08 16:16:05 +0000291 m_keyChain.sign(interest, ndn::signingByIdentity(identityForCommand));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700292 }
293
294 return interest;
295}
296
297static void
298usage()
299{
300 fprintf(stderr,
301 "NdnRepoWatch [-I identity]"
302 " [-x freshness] [-l lifetime] [-w watchtimeout]"
303 " [-n maxinterestnum][-s stop] [-c check]repo-prefix ndn-name\n"
304 "\n"
305 " Write a file into a repo.\n"
306 " -I: specify identity used for signing commands\n"
307 " -x: FreshnessPeriod in milliseconds\n"
308 " -l: InterestLifetime in milliseconds for each command\n"
309 " -w: timeout in milliseconds for whole process (default unlimited)\n"
310 " -n: total number of interests to be sent for whole process (default unlimited)\n"
311 " -s: stop the whole process\n"
312 " -c: check the process\n"
313 " repo-prefix: repo command prefix\n"
314 " ndn-name: NDN Name prefix for written Data\n"
315 );
316 exit(1);
317}
318
319int
320main(int argc, char** argv)
321{
322 NdnRepoWatch app;
323 int opt;
324 while ((opt = getopt(argc, argv, "x:l:w:n:scI:")) != -1) {
325 switch (opt) {
326 case 'x':
327 try {
328 app.freshnessPeriod = milliseconds(boost::lexical_cast<uint64_t>(optarg));
329 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800330 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700331 std::cerr << "-x option should be an integer.";
332 return 1;
333 }
334 break;
335 case 'l':
336 try {
337 app.interestLifetime = milliseconds(boost::lexical_cast<uint64_t>(optarg));
338 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800339 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700340 std::cerr << "-l option should be an integer.";
341 return 1;
342 }
343 break;
344 case 'w':
345 app.hasTimeout = true;
346 try {
347 app.watchTimeout = milliseconds(boost::lexical_cast<int64_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 << "-w option should be an integer.";
351 return 1;
352 }
353 break;
354 case 'n':
355 app.hasMaxInterestNum = true;
356 try {
357 app.maxInterestNum = 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 << "-n option should be an integer.";
361 return 1;
362 }
363 break;
364 case 's':
365 app.status = STOP;
366 break;
367 case 'c':
368 app.status = CHECK;
369 break;
370 case 'I':
371 app.identityForCommand = std::string(optarg);
372 break;
373 case 'v':
374 app.isVerbose = true;
375 break;
376 case 'h':
377 usage();
378 break;
379 default:
380 break;
381 }
382 }
383
384 argc -= optind;
385 argv += optind;
386
387 if (argc != 2)
388 usage();
389
390 app.repoPrefix = Name(argv[0]);
391 app.ndnName = Name(argv[1]);
392
393 app.run();
394
395 return 0;
396}
397
398} // namespace repo
399
400int
401main(int argc, char** argv)
402{
403 try {
404 return repo::main(argc, argv);
405 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800406 catch (const std::exception& e) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700407 std::cerr << "ERROR: " << e.what() << std::endl;
408 return 2;
409 }
410}