blob: 4172d79498001cf33ee7c37acf2761817f241348 [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>
25#include <ndn-cxx/util/scheduler.hpp>
26#include <fstream>
27#include <string>
28#include <stdlib.h>
29#include <stdint.h>
30
31#include <boost/lexical_cast.hpp>
32
33namespace repo {
34
35using namespace ndn::time;
36
Wentao Shanga8f3c402014-10-30 14:03:27 -070037using std::shared_ptr;
38using std::bind;
39using std::placeholders::_1;
40using std::placeholders::_2;
41
Weiqi Shi90d1e882014-08-01 21:26:21 -070042static const uint64_t DEFAULT_INTEREST_LIFETIME = 4000;
43static const uint64_t DEFAULT_FRESHNESS_PERIOD = 10000;
44static const uint64_t DEFAULT_CHECK_PERIOD = 1000;
45
46enum CommandType
47{
48 START,
49 CHECK,
50 STOP
51};
52
Alexander Afanasyev42290b22017-03-09 12:58:29 -080053class NdnRepoWatch : boost::noncopyable
Weiqi Shi90d1e882014-08-01 21:26:21 -070054{
55public:
56 class Error : public std::runtime_error
57 {
58 public:
59 explicit
60 Error(const std::string& what)
61 : std::runtime_error(what)
62 {
63 }
64 };
65
66 NdnRepoWatch()
67 : freshnessPeriod(DEFAULT_FRESHNESS_PERIOD)
68 , interestLifetime(DEFAULT_INTEREST_LIFETIME)
69 , hasTimeout(false)
70 , watchTimeout(0)
71 , hasMaxInterestNum(false)
72 , maxInterestNum(0)
73 , status(START)
74 , isVerbose(false)
75
76 , m_scheduler(m_face.getIoService())
77 , m_checkPeriod(DEFAULT_CHECK_PERIOD)
78 {
79 }
80
81 void
82 run();
83
84private:
85
86 void
87 startWatchCommand();
88
89 void
Alexander Afanasyev42290b22017-03-09 12:58:29 -080090 onWatchCommandResponse(const ndn::Interest& interest, const ndn::Data& data);
Weiqi Shi90d1e882014-08-01 21:26:21 -070091
92 void
93 onWatchCommandTimeout(const ndn::Interest& interest);
94
95 void
96 stopProcess();
97
98 void
99 signData(ndn::Data& data);
100
101 void
102 startCheckCommand();
103
104 void
105 onCheckCommandTimeout(const ndn::Interest& interest);
106
107 void
108 onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data);
109
110 void
111 onStopCommandTimeout(const ndn::Interest& interest);
112
113 ndn::Interest
114 generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
115 const RepoCommandParameter& commandParameter);
116
117public:
118 std::string identityForCommand;
119 milliseconds freshnessPeriod;
120 milliseconds interestLifetime;
121 bool hasTimeout;
122 milliseconds watchTimeout;
123 bool hasMaxInterestNum;
124 int64_t maxInterestNum;
125 CommandType status;
126 ndn::Name repoPrefix;
127 ndn::Name ndnName;
128 bool isVerbose;
129
130
131private:
132 ndn::Face m_face;
133 ndn::Scheduler m_scheduler;
134 milliseconds m_checkPeriod;
135
136 ndn::Name m_dataPrefix;
137 ndn::KeyChain m_keyChain;
Wentao Shanga8f3c402014-10-30 14:03:27 -0700138 typedef std::map<uint64_t, shared_ptr<ndn::Data> > DataContainer;
Weiqi Shi90d1e882014-08-01 21:26:21 -0700139};
140
141void
142NdnRepoWatch::run()
143{
144 m_dataPrefix = ndnName;
145 startWatchCommand();
146
147 if (hasTimeout)
Wentao Shanga8f3c402014-10-30 14:03:27 -0700148 m_scheduler.scheduleEvent(watchTimeout, bind(&NdnRepoWatch::stopProcess, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700149
150 m_face.processEvents();
151}
152
153void
154NdnRepoWatch::startWatchCommand()
155{
156 RepoCommandParameter parameters;
157 parameters.setName(m_dataPrefix);
158
159 repoPrefix.append("watch");
160 if (status == START) {
161 if (hasMaxInterestNum) {
162 parameters.setMaxInterestNum(maxInterestNum);
163 }
164 if (hasTimeout) {
165 parameters.setWatchTimeout(watchTimeout);
166 }
167 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "start", parameters);
168 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700169 bind(&NdnRepoWatch::onWatchCommandResponse, this,
170 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800171 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700172 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700173 }
174 else if (status == STOP){
175 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "stop", parameters);
176 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700177 bind(&NdnRepoWatch::onWatchCommandResponse, this,
178 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800179 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700180 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700181 }
182 else if (status == CHECK){
183 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "check", parameters);
184 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700185 bind(&NdnRepoWatch::onWatchCommandResponse, this,
186 _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800187 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700188 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700189 }
190
191}
192
193void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800194NdnRepoWatch::onWatchCommandResponse(const ndn::Interest& interest, const ndn::Data& data)
Weiqi Shi90d1e882014-08-01 21:26:21 -0700195{
196 RepoCommandResponse response(data.getContent().blockFromValue());
197 int statusCode = response.getStatusCode();
198 if (statusCode >= 400) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800199 BOOST_THROW_EXCEPTION(Error("Watch command failed with code " +
200 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700201 }
202 else if (statusCode == 101) {
203 std::cerr << "Watching prefix is stopped!" <<std::endl;
204 m_face.getIoService().stop();
205 return;
206 }
207 else if (statusCode == 300) {
208 std::cerr << "Watching prefix is running!" <<std::endl;
209 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700210 bind(&NdnRepoWatch::startCheckCommand, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700211 return;
212 }
213 else if (statusCode == 100) {
214 std::cerr << "Watching prefix starts!" <<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 {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800220 BOOST_THROW_EXCEPTION(Error("Unrecognized Status Code " +
221 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700222 }
223}
224
225void
226NdnRepoWatch::onWatchCommandTimeout(const ndn::Interest& interest)
227{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800228 BOOST_THROW_EXCEPTION(Error("command response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700229}
230
231void
232NdnRepoWatch::stopProcess()
233{
234 m_face.getIoService().stop();
235}
236
237void
238NdnRepoWatch::startCheckCommand()
239{
240 repoPrefix.append("watch");
241 ndn::Interest checkInterest = generateCommandInterest(repoPrefix, "check",
242 RepoCommandParameter()
243 .setName(m_dataPrefix));
244 m_face.expressInterest(checkInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700245 bind(&NdnRepoWatch::onWatchCommandResponse, this, _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800246 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1), // Nack
Wentao Shanga8f3c402014-10-30 14:03:27 -0700247 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700248}
249
250void
251NdnRepoWatch::onCheckCommandTimeout(const ndn::Interest& interest)
252{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800253 BOOST_THROW_EXCEPTION(Error("check response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700254}
255
256void
257NdnRepoWatch::onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data)
258{
259 RepoCommandResponse response(data.getContent().blockFromValue());
260 int statusCode = response.getStatusCode();
261 if (statusCode != 101) {
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800262 BOOST_THROW_EXCEPTION(Error("Watch stop command failed with code: " +
263 boost::lexical_cast<std::string>(statusCode)));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700264 }
265 else {
266 std::cerr << "Status code is 101. Watching prefix is stopped successfully!" << std::endl;
267 m_face.getIoService().stop();
268 return;
269 }
270}
271
272void
273NdnRepoWatch::onStopCommandTimeout(const ndn::Interest& interest)
274{
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800275 BOOST_THROW_EXCEPTION(Error("stop response timeout"));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700276}
277
278ndn::Interest
279NdnRepoWatch::generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
280 const RepoCommandParameter& commandParameter)
281{
282 ndn::Interest interest(ndn::Name(commandPrefix)
283 .append(command)
284 .append(commandParameter.wireEncode()));
285 interest.setInterestLifetime(interestLifetime);
286
287 if (identityForCommand.empty())
288 m_keyChain.sign(interest);
289 else {
290 m_keyChain.signByIdentity(interest, ndn::Name(identityForCommand));
291 }
292
293 return interest;
294}
295
296static void
297usage()
298{
299 fprintf(stderr,
300 "NdnRepoWatch [-I identity]"
301 " [-x freshness] [-l lifetime] [-w watchtimeout]"
302 " [-n maxinterestnum][-s stop] [-c check]repo-prefix ndn-name\n"
303 "\n"
304 " Write a file into a repo.\n"
305 " -I: specify identity used for signing commands\n"
306 " -x: FreshnessPeriod in milliseconds\n"
307 " -l: InterestLifetime in milliseconds for each command\n"
308 " -w: timeout in milliseconds for whole process (default unlimited)\n"
309 " -n: total number of interests to be sent for whole process (default unlimited)\n"
310 " -s: stop the whole process\n"
311 " -c: check the process\n"
312 " repo-prefix: repo command prefix\n"
313 " ndn-name: NDN Name prefix for written Data\n"
314 );
315 exit(1);
316}
317
318int
319main(int argc, char** argv)
320{
321 NdnRepoWatch app;
322 int opt;
323 while ((opt = getopt(argc, argv, "x:l:w:n:scI:")) != -1) {
324 switch (opt) {
325 case 'x':
326 try {
327 app.freshnessPeriod = milliseconds(boost::lexical_cast<uint64_t>(optarg));
328 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800329 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700330 std::cerr << "-x option should be an integer.";
331 return 1;
332 }
333 break;
334 case 'l':
335 try {
336 app.interestLifetime = 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 << "-l option should be an integer.";
340 return 1;
341 }
342 break;
343 case 'w':
344 app.hasTimeout = true;
345 try {
346 app.watchTimeout = milliseconds(boost::lexical_cast<int64_t>(optarg));
347 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800348 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700349 std::cerr << "-w option should be an integer.";
350 return 1;
351 }
352 break;
353 case 'n':
354 app.hasMaxInterestNum = true;
355 try {
356 app.maxInterestNum = boost::lexical_cast<int64_t>(optarg);
357 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800358 catch (const boost::bad_lexical_cast&) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700359 std::cerr << "-n option should be an integer.";
360 return 1;
361 }
362 break;
363 case 's':
364 app.status = STOP;
365 break;
366 case 'c':
367 app.status = CHECK;
368 break;
369 case 'I':
370 app.identityForCommand = std::string(optarg);
371 break;
372 case 'v':
373 app.isVerbose = true;
374 break;
375 case 'h':
376 usage();
377 break;
378 default:
379 break;
380 }
381 }
382
383 argc -= optind;
384 argv += optind;
385
386 if (argc != 2)
387 usage();
388
389 app.repoPrefix = Name(argv[0]);
390 app.ndnName = Name(argv[1]);
391
392 app.run();
393
394 return 0;
395}
396
397} // namespace repo
398
399int
400main(int argc, char** argv)
401{
402 try {
403 return repo::main(argc, argv);
404 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800405 catch (const std::exception& e) {
Weiqi Shi90d1e882014-08-01 21:26:21 -0700406 std::cerr << "ERROR: " << e.what() << std::endl;
407 return 2;
408 }
409}