blob: 16be91557ca593b2b6c414bfde1a424b149ce5c9 [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
36#include <boost/lexical_cast.hpp>
37
38namespace repo {
39
40using namespace ndn::time;
41
Wentao Shanga8f3c402014-10-30 14:03:27 -070042using std::shared_ptr;
43using std::bind;
44using std::placeholders::_1;
45using std::placeholders::_2;
46
Weiqi Shi90d1e882014-08-01 21:26:21 -070047static const uint64_t DEFAULT_INTEREST_LIFETIME = 4000;
48static const uint64_t DEFAULT_FRESHNESS_PERIOD = 10000;
49static const uint64_t DEFAULT_CHECK_PERIOD = 1000;
50
51enum CommandType
52{
53 START,
54 CHECK,
55 STOP
56};
57
Alexander Afanasyev42290b22017-03-09 12:58:29 -080058class NdnRepoWatch : boost::noncopyable
Weiqi Shi90d1e882014-08-01 21:26:21 -070059{
60public:
61 class Error : public std::runtime_error
62 {
63 public:
64 explicit
65 Error(const std::string& what)
66 : std::runtime_error(what)
67 {
68 }
69 };
70
71 NdnRepoWatch()
72 : freshnessPeriod(DEFAULT_FRESHNESS_PERIOD)
73 , interestLifetime(DEFAULT_INTEREST_LIFETIME)
74 , hasTimeout(false)
75 , watchTimeout(0)
76 , hasMaxInterestNum(false)
77 , maxInterestNum(0)
78 , status(START)
79 , isVerbose(false)
Weiqi Shi90d1e882014-08-01 21:26:21 -070080 , m_scheduler(m_face.getIoService())
81 , m_checkPeriod(DEFAULT_CHECK_PERIOD)
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -050082 , m_cmdSigner(m_keyChain)
Weiqi Shi90d1e882014-08-01 21:26:21 -070083 {
84 }
85
86 void
87 run();
88
89private:
90
91 void
92 startWatchCommand();
93
94 void
Alexander Afanasyev42290b22017-03-09 12:58:29 -080095 onWatchCommandResponse(const ndn::Interest& interest, const ndn::Data& data);
Weiqi Shi90d1e882014-08-01 21:26:21 -070096
97 void
98 onWatchCommandTimeout(const ndn::Interest& interest);
99
100 void
101 stopProcess();
102
103 void
104 signData(ndn::Data& data);
105
106 void
107 startCheckCommand();
108
109 void
110 onCheckCommandTimeout(const ndn::Interest& interest);
111
112 void
113 onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data);
114
115 void
116 onStopCommandTimeout(const ndn::Interest& interest);
117
118 ndn::Interest
119 generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
120 const RepoCommandParameter& commandParameter);
121
122public:
123 std::string identityForCommand;
124 milliseconds freshnessPeriod;
125 milliseconds interestLifetime;
126 bool hasTimeout;
127 milliseconds watchTimeout;
128 bool hasMaxInterestNum;
129 int64_t maxInterestNum;
130 CommandType status;
131 ndn::Name repoPrefix;
132 ndn::Name ndnName;
133 bool isVerbose;
134
135
136private:
137 ndn::Face m_face;
138 ndn::Scheduler m_scheduler;
139 milliseconds m_checkPeriod;
140
141 ndn::Name m_dataPrefix;
142 ndn::KeyChain m_keyChain;
Wentao Shanga8f3c402014-10-30 14:03:27 -0700143 typedef std::map<uint64_t, shared_ptr<ndn::Data> > DataContainer;
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500144 ndn::security::CommandInterestSigner m_cmdSigner;
Weiqi Shi90d1e882014-08-01 21:26:21 -0700145};
146
147void
148NdnRepoWatch::run()
149{
150 m_dataPrefix = ndnName;
151 startWatchCommand();
152
153 if (hasTimeout)
Wentao Shanga8f3c402014-10-30 14:03:27 -0700154 m_scheduler.scheduleEvent(watchTimeout, bind(&NdnRepoWatch::stopProcess, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700155
156 m_face.processEvents();
157}
158
159void
160NdnRepoWatch::startWatchCommand()
161{
162 RepoCommandParameter parameters;
163 parameters.setName(m_dataPrefix);
164
165 repoPrefix.append("watch");
166 if (status == START) {
167 if (hasMaxInterestNum) {
168 parameters.setMaxInterestNum(maxInterestNum);
169 }
170 if (hasTimeout) {
171 parameters.setWatchTimeout(watchTimeout);
172 }
173 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "start", parameters);
174 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700175 bind(&NdnRepoWatch::onWatchCommandResponse, this,
176 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800177 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700178 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700179 }
180 else if (status == STOP){
181 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "stop", parameters);
182 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700183 bind(&NdnRepoWatch::onWatchCommandResponse, this,
184 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800185 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700186 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700187 }
188 else if (status == CHECK){
189 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "check", parameters);
190 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700191 bind(&NdnRepoWatch::onWatchCommandResponse, this,
192 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800193 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700194 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700195 }
196
197}
198
199void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800200NdnRepoWatch::onWatchCommandResponse(const ndn::Interest& interest, const ndn::Data& data)
Weiqi Shi90d1e882014-08-01 21:26:21 -0700201{
202 RepoCommandResponse response(data.getContent().blockFromValue());
203 int statusCode = response.getStatusCode();
204 if (statusCode >= 400) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800205 BOOST_THROW_EXCEPTION(Error("Watch command failed with code " +
206 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700207 }
208 else if (statusCode == 101) {
209 std::cerr << "Watching prefix is stopped!" <<std::endl;
210 m_face.getIoService().stop();
211 return;
212 }
213 else if (statusCode == 300) {
214 std::cerr << "Watching prefix is running!" <<std::endl;
215 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700216 bind(&NdnRepoWatch::startCheckCommand, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700217 return;
218 }
219 else if (statusCode == 100) {
220 std::cerr << "Watching prefix starts!" <<std::endl;
221 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700222 bind(&NdnRepoWatch::startCheckCommand, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700223 return;
224 }
225 else {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800226 BOOST_THROW_EXCEPTION(Error("Unrecognized Status Code " +
227 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700228 }
229}
230
231void
232NdnRepoWatch::onWatchCommandTimeout(const ndn::Interest& interest)
233{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800234 BOOST_THROW_EXCEPTION(Error("command response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700235}
236
237void
238NdnRepoWatch::stopProcess()
239{
240 m_face.getIoService().stop();
241}
242
243void
244NdnRepoWatch::startCheckCommand()
245{
246 repoPrefix.append("watch");
247 ndn::Interest checkInterest = generateCommandInterest(repoPrefix, "check",
248 RepoCommandParameter()
249 .setName(m_dataPrefix));
250 m_face.expressInterest(checkInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700251 bind(&NdnRepoWatch::onWatchCommandResponse, this, _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800252 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700253 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700254}
255
256void
257NdnRepoWatch::onCheckCommandTimeout(const ndn::Interest& interest)
258{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800259 BOOST_THROW_EXCEPTION(Error("check response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700260}
261
262void
263NdnRepoWatch::onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data)
264{
265 RepoCommandResponse response(data.getContent().blockFromValue());
266 int statusCode = response.getStatusCode();
267 if (statusCode != 101) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800268 BOOST_THROW_EXCEPTION(Error("Watch stop command failed with code: " +
269 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700270 }
271 else {
272 std::cerr << "Status code is 101. Watching prefix is stopped successfully!" << std::endl;
273 m_face.getIoService().stop();
274 return;
275 }
276}
277
278void
279NdnRepoWatch::onStopCommandTimeout(const ndn::Interest& interest)
280{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800281 BOOST_THROW_EXCEPTION(Error("stop response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700282}
283
284ndn::Interest
285NdnRepoWatch::generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
286 const RepoCommandParameter& commandParameter)
287{
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500288 Name cmd = commandPrefix;
289 cmd
290 .append(command)
291 .append(commandParameter.wireEncode());
292 ndn::Interest interest;
Weiqi Shi90d1e882014-08-01 21:26:21 -0700293
294 if (identityForCommand.empty())
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500295 interest = m_cmdSigner.makeCommandInterest(cmd);
Weiqi Shi90d1e882014-08-01 21:26:21 -0700296 else {
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500297 interest = m_cmdSigner.makeCommandInterest(cmd, ndn::signingByIdentity(identityForCommand));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700298 }
299
Alexander Afanasyev2e8147c2017-11-09 14:17:02 -0500300 interest.setInterestLifetime(interestLifetime);
Weiqi Shi90d1e882014-08-01 21:26:21 -0700301 return interest;
302}
303
304static void
305usage()
306{
307 fprintf(stderr,
308 "NdnRepoWatch [-I identity]"
309 " [-x freshness] [-l lifetime] [-w watchtimeout]"
310 " [-n maxinterestnum][-s stop] [-c check]repo-prefix ndn-name\n"
311 "\n"
312 " Write a file into a repo.\n"
313 " -I: specify identity used for signing commands\n"
314 " -x: FreshnessPeriod in milliseconds\n"
315 " -l: InterestLifetime in milliseconds for each command\n"
316 " -w: timeout in milliseconds for whole process (default unlimited)\n"
317 " -n: total number of interests to be sent for whole process (default unlimited)\n"
318 " -s: stop the whole process\n"
319 " -c: check the process\n"
320 " repo-prefix: repo command prefix\n"
321 " ndn-name: NDN Name prefix for written Data\n"
322 );
323 exit(1);
324}
325
326int
327main(int argc, char** argv)
328{
329 NdnRepoWatch app;
330 int opt;
331 while ((opt = getopt(argc, argv, "x:l:w:n:scI:")) != -1) {
332 switch (opt) {
333 case 'x':
334 try {
335 app.freshnessPeriod = milliseconds(boost::lexical_cast<uint64_t>(optarg));
336 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800337 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700338 std::cerr << "-x option should be an integer.";
339 return 1;
340 }
341 break;
342 case 'l':
343 try {
344 app.interestLifetime = milliseconds(boost::lexical_cast<uint64_t>(optarg));
345 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800346 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700347 std::cerr << "-l option should be an integer.";
348 return 1;
349 }
350 break;
351 case 'w':
352 app.hasTimeout = true;
353 try {
354 app.watchTimeout = milliseconds(boost::lexical_cast<int64_t>(optarg));
355 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800356 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700357 std::cerr << "-w option should be an integer.";
358 return 1;
359 }
360 break;
361 case 'n':
362 app.hasMaxInterestNum = true;
363 try {
364 app.maxInterestNum = boost::lexical_cast<int64_t>(optarg);
365 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800366 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700367 std::cerr << "-n option should be an integer.";
368 return 1;
369 }
370 break;
371 case 's':
372 app.status = STOP;
373 break;
374 case 'c':
375 app.status = CHECK;
376 break;
377 case 'I':
378 app.identityForCommand = std::string(optarg);
379 break;
380 case 'v':
381 app.isVerbose = true;
382 break;
383 case 'h':
384 usage();
385 break;
386 default:
387 break;
388 }
389 }
390
391 argc -= optind;
392 argv += optind;
393
394 if (argc != 2)
395 usage();
396
397 app.repoPrefix = Name(argv[0]);
398 app.ndnName = Name(argv[1]);
399
400 app.run();
401
402 return 0;
403}
404
405} // namespace repo
406
407int
408main(int argc, char** argv)
409{
410 try {
411 return repo::main(argc, argv);
412 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800413 catch (const std::exception& e) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700414 std::cerr << "ERROR: " << e.what() << std::endl;
415 return 2;
416 }
417}