blob: 63a26caf5255524668eb5a59909f6cf9fbe6dde7 [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>
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>
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040027
Weiqi Shi90d1e882014-08-01 21:26:21 -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>
Weiqi Shi90d1e882014-08-01 21:26:21 -070034
35#include <boost/lexical_cast.hpp>
36
37namespace repo {
38
39using namespace ndn::time;
40
Wentao Shanga8f3c402014-10-30 14:03:27 -070041using std::shared_ptr;
42using std::bind;
43using std::placeholders::_1;
44using std::placeholders::_2;
45
Weiqi Shi90d1e882014-08-01 21:26:21 -070046static const uint64_t DEFAULT_INTEREST_LIFETIME = 4000;
47static const uint64_t DEFAULT_FRESHNESS_PERIOD = 10000;
48static const uint64_t DEFAULT_CHECK_PERIOD = 1000;
49
50enum CommandType
51{
52 START,
53 CHECK,
54 STOP
55};
56
Alexander Afanasyev42290b22017-03-09 12:58:29 -080057class NdnRepoWatch : boost::noncopyable
Weiqi Shi90d1e882014-08-01 21:26:21 -070058{
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 NdnRepoWatch()
71 : freshnessPeriod(DEFAULT_FRESHNESS_PERIOD)
72 , interestLifetime(DEFAULT_INTEREST_LIFETIME)
73 , hasTimeout(false)
74 , watchTimeout(0)
75 , hasMaxInterestNum(false)
76 , maxInterestNum(0)
77 , status(START)
78 , isVerbose(false)
79
80 , m_scheduler(m_face.getIoService())
81 , m_checkPeriod(DEFAULT_CHECK_PERIOD)
82 {
83 }
84
85 void
86 run();
87
88private:
89
90 void
91 startWatchCommand();
92
93 void
Alexander Afanasyev42290b22017-03-09 12:58:29 -080094 onWatchCommandResponse(const ndn::Interest& interest, const ndn::Data& data);
Weiqi Shi90d1e882014-08-01 21:26:21 -070095
96 void
97 onWatchCommandTimeout(const ndn::Interest& interest);
98
99 void
100 stopProcess();
101
102 void
103 signData(ndn::Data& data);
104
105 void
106 startCheckCommand();
107
108 void
109 onCheckCommandTimeout(const ndn::Interest& interest);
110
111 void
112 onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data);
113
114 void
115 onStopCommandTimeout(const ndn::Interest& interest);
116
117 ndn::Interest
118 generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
119 const RepoCommandParameter& commandParameter);
120
121public:
122 std::string identityForCommand;
123 milliseconds freshnessPeriod;
124 milliseconds interestLifetime;
125 bool hasTimeout;
126 milliseconds watchTimeout;
127 bool hasMaxInterestNum;
128 int64_t maxInterestNum;
129 CommandType status;
130 ndn::Name repoPrefix;
131 ndn::Name ndnName;
132 bool isVerbose;
133
134
135private:
136 ndn::Face m_face;
137 ndn::Scheduler m_scheduler;
138 milliseconds m_checkPeriod;
139
140 ndn::Name m_dataPrefix;
141 ndn::KeyChain m_keyChain;
Wentao Shanga8f3c402014-10-30 14:03:27 -0700142 typedef std::map<uint64_t, shared_ptr<ndn::Data> > DataContainer;
Weiqi Shi90d1e882014-08-01 21:26:21 -0700143};
144
145void
146NdnRepoWatch::run()
147{
148 m_dataPrefix = ndnName;
149 startWatchCommand();
150
151 if (hasTimeout)
Wentao Shanga8f3c402014-10-30 14:03:27 -0700152 m_scheduler.scheduleEvent(watchTimeout, bind(&NdnRepoWatch::stopProcess, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700153
154 m_face.processEvents();
155}
156
157void
158NdnRepoWatch::startWatchCommand()
159{
160 RepoCommandParameter parameters;
161 parameters.setName(m_dataPrefix);
162
163 repoPrefix.append("watch");
164 if (status == START) {
165 if (hasMaxInterestNum) {
166 parameters.setMaxInterestNum(maxInterestNum);
167 }
168 if (hasTimeout) {
169 parameters.setWatchTimeout(watchTimeout);
170 }
171 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "start", parameters);
172 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700173 bind(&NdnRepoWatch::onWatchCommandResponse, this,
174 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800175 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700176 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700177 }
178 else if (status == STOP){
179 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "stop", parameters);
180 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700181 bind(&NdnRepoWatch::onWatchCommandResponse, this,
182 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800183 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700184 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700185 }
186 else if (status == CHECK){
187 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "check", parameters);
188 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700189 bind(&NdnRepoWatch::onWatchCommandResponse, this,
190 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800191 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700192 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700193 }
194
195}
196
197void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800198NdnRepoWatch::onWatchCommandResponse(const ndn::Interest& interest, const ndn::Data& data)
Weiqi Shi90d1e882014-08-01 21:26:21 -0700199{
200 RepoCommandResponse response(data.getContent().blockFromValue());
201 int statusCode = response.getStatusCode();
202 if (statusCode >= 400) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800203 BOOST_THROW_EXCEPTION(Error("Watch command failed with code " +
204 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700205 }
206 else if (statusCode == 101) {
207 std::cerr << "Watching prefix is stopped!" <<std::endl;
208 m_face.getIoService().stop();
209 return;
210 }
211 else if (statusCode == 300) {
212 std::cerr << "Watching prefix is running!" <<std::endl;
213 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700214 bind(&NdnRepoWatch::startCheckCommand, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700215 return;
216 }
217 else if (statusCode == 100) {
218 std::cerr << "Watching prefix starts!" <<std::endl;
219 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700220 bind(&NdnRepoWatch::startCheckCommand, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700221 return;
222 }
223 else {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800224 BOOST_THROW_EXCEPTION(Error("Unrecognized Status Code " +
225 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700226 }
227}
228
229void
230NdnRepoWatch::onWatchCommandTimeout(const ndn::Interest& interest)
231{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800232 BOOST_THROW_EXCEPTION(Error("command response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700233}
234
235void
236NdnRepoWatch::stopProcess()
237{
238 m_face.getIoService().stop();
239}
240
241void
242NdnRepoWatch::startCheckCommand()
243{
244 repoPrefix.append("watch");
245 ndn::Interest checkInterest = generateCommandInterest(repoPrefix, "check",
246 RepoCommandParameter()
247 .setName(m_dataPrefix));
248 m_face.expressInterest(checkInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700249 bind(&NdnRepoWatch::onWatchCommandResponse, this, _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800250 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700251 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700252}
253
254void
255NdnRepoWatch::onCheckCommandTimeout(const ndn::Interest& interest)
256{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800257 BOOST_THROW_EXCEPTION(Error("check response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700258}
259
260void
261NdnRepoWatch::onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data)
262{
263 RepoCommandResponse response(data.getContent().blockFromValue());
264 int statusCode = response.getStatusCode();
265 if (statusCode != 101) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800266 BOOST_THROW_EXCEPTION(Error("Watch stop command failed with code: " +
267 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700268 }
269 else {
270 std::cerr << "Status code is 101. Watching prefix is stopped successfully!" << std::endl;
271 m_face.getIoService().stop();
272 return;
273 }
274}
275
276void
277NdnRepoWatch::onStopCommandTimeout(const ndn::Interest& interest)
278{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800279 BOOST_THROW_EXCEPTION(Error("stop response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700280}
281
282ndn::Interest
283NdnRepoWatch::generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
284 const RepoCommandParameter& commandParameter)
285{
286 ndn::Interest interest(ndn::Name(commandPrefix)
287 .append(command)
288 .append(commandParameter.wireEncode()));
289 interest.setInterestLifetime(interestLifetime);
290
291 if (identityForCommand.empty())
292 m_keyChain.sign(interest);
293 else {
Junxiao Shi047a6fb2017-06-08 16:16:05 +0000294 m_keyChain.sign(interest, ndn::signingByIdentity(identityForCommand));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700295 }
296
297 return interest;
298}
299
300static void
301usage()
302{
303 fprintf(stderr,
304 "NdnRepoWatch [-I identity]"
305 " [-x freshness] [-l lifetime] [-w watchtimeout]"
306 " [-n maxinterestnum][-s stop] [-c check]repo-prefix ndn-name\n"
307 "\n"
308 " Write a file into a repo.\n"
309 " -I: specify identity used for signing commands\n"
310 " -x: FreshnessPeriod in milliseconds\n"
311 " -l: InterestLifetime in milliseconds for each command\n"
312 " -w: timeout in milliseconds for whole process (default unlimited)\n"
313 " -n: total number of interests to be sent for whole process (default unlimited)\n"
314 " -s: stop the whole process\n"
315 " -c: check the process\n"
316 " repo-prefix: repo command prefix\n"
317 " ndn-name: NDN Name prefix for written Data\n"
318 );
319 exit(1);
320}
321
322int
323main(int argc, char** argv)
324{
325 NdnRepoWatch app;
326 int opt;
327 while ((opt = getopt(argc, argv, "x:l:w:n:scI:")) != -1) {
328 switch (opt) {
329 case 'x':
330 try {
331 app.freshnessPeriod = milliseconds(boost::lexical_cast<uint64_t>(optarg));
332 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800333 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700334 std::cerr << "-x option should be an integer.";
335 return 1;
336 }
337 break;
338 case 'l':
339 try {
340 app.interestLifetime = milliseconds(boost::lexical_cast<uint64_t>(optarg));
341 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800342 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700343 std::cerr << "-l option should be an integer.";
344 return 1;
345 }
346 break;
347 case 'w':
348 app.hasTimeout = true;
349 try {
350 app.watchTimeout = milliseconds(boost::lexical_cast<int64_t>(optarg));
351 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800352 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700353 std::cerr << "-w option should be an integer.";
354 return 1;
355 }
356 break;
357 case 'n':
358 app.hasMaxInterestNum = true;
359 try {
360 app.maxInterestNum = boost::lexical_cast<int64_t>(optarg);
361 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800362 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700363 std::cerr << "-n option should be an integer.";
364 return 1;
365 }
366 break;
367 case 's':
368 app.status = STOP;
369 break;
370 case 'c':
371 app.status = CHECK;
372 break;
373 case 'I':
374 app.identityForCommand = std::string(optarg);
375 break;
376 case 'v':
377 app.isVerbose = true;
378 break;
379 case 'h':
380 usage();
381 break;
382 default:
383 break;
384 }
385 }
386
387 argc -= optind;
388 argv += optind;
389
390 if (argc != 2)
391 usage();
392
393 app.repoPrefix = Name(argv[0]);
394 app.ndnName = Name(argv[1]);
395
396 app.run();
397
398 return 0;
399}
400
401} // namespace repo
402
403int
404main(int argc, char** argv)
405{
406 try {
407 return repo::main(argc, argv);
408 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800409 catch (const std::exception& e) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700410 std::cerr << "ERROR: " << e.what() << std::endl;
411 return 2;
412 }
413}