blob: c5128f369aebbd085da9daf326f818b7418f1d1c [file] [log] [blame]
Weiqi Shi90d1e882014-08-01 21:26:21 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014, Regents of the University of California.
4 *
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
17 * repo-ng, e.g., in COPYING.md file. if (not, see <http://www.gnu.org/licenses/>.
18 */
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
53class NdnRepoWatch : ndn::noncopyable
54{
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
90 onWatchCommandResponse(const ndn::Interest& interest, ndn::Data& data);
91
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),
171 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700172 }
173 else if (status == STOP){
174 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "stop", parameters);
175 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700176 bind(&NdnRepoWatch::onWatchCommandResponse, this,
177 _1, _2),
178 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700179 }
180 else if (status == CHECK){
181 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "check", parameters);
182 m_face.expressInterest(commandInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700183 bind(&NdnRepoWatch::onWatchCommandResponse, this,
184 _1, _2),
185 bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700186 }
187
188}
189
190void
191NdnRepoWatch::onWatchCommandResponse(const ndn::Interest& interest, ndn::Data& data)
192{
193 RepoCommandResponse response(data.getContent().blockFromValue());
194 int statusCode = response.getStatusCode();
195 if (statusCode >= 400) {
196 throw Error("Watch command failed with code " +
197 boost::lexical_cast<std::string>(statusCode));
198 }
199 else if (statusCode == 101) {
200 std::cerr << "Watching prefix is stopped!" <<std::endl;
201 m_face.getIoService().stop();
202 return;
203 }
204 else if (statusCode == 300) {
205 std::cerr << "Watching prefix is running!" <<std::endl;
206 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700207 bind(&NdnRepoWatch::startCheckCommand, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700208 return;
209 }
210 else if (statusCode == 100) {
211 std::cerr << "Watching prefix starts!" <<std::endl;
212 m_scheduler.scheduleEvent(m_checkPeriod,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700213 bind(&NdnRepoWatch::startCheckCommand, this));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700214 return;
215 }
216 else {
217 throw Error("Unrecognized Status Code " +
218 boost::lexical_cast<std::string>(statusCode));
219 }
220}
221
222void
223NdnRepoWatch::onWatchCommandTimeout(const ndn::Interest& interest)
224{
225 throw Error("command response timeout");
226}
227
228void
229NdnRepoWatch::stopProcess()
230{
231 m_face.getIoService().stop();
232}
233
234void
235NdnRepoWatch::startCheckCommand()
236{
237 repoPrefix.append("watch");
238 ndn::Interest checkInterest = generateCommandInterest(repoPrefix, "check",
239 RepoCommandParameter()
240 .setName(m_dataPrefix));
241 m_face.expressInterest(checkInterest,
Wentao Shanga8f3c402014-10-30 14:03:27 -0700242 bind(&NdnRepoWatch::onWatchCommandResponse, this, _1, _2),
243 bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1));
Weiqi Shi90d1e882014-08-01 21:26:21 -0700244}
245
246void
247NdnRepoWatch::onCheckCommandTimeout(const ndn::Interest& interest)
248{
249 throw Error("check response timeout");
250}
251
252void
253NdnRepoWatch::onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data)
254{
255 RepoCommandResponse response(data.getContent().blockFromValue());
256 int statusCode = response.getStatusCode();
257 if (statusCode != 101) {
258 throw Error("Watch stop command failed with code: " +
259 boost::lexical_cast<std::string>(statusCode));
260 }
261 else {
262 std::cerr << "Status code is 101. Watching prefix is stopped successfully!" << std::endl;
263 m_face.getIoService().stop();
264 return;
265 }
266}
267
268void
269NdnRepoWatch::onStopCommandTimeout(const ndn::Interest& interest)
270{
271 throw Error("stop response timeout");
272}
273
274ndn::Interest
275NdnRepoWatch::generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
276 const RepoCommandParameter& commandParameter)
277{
278 ndn::Interest interest(ndn::Name(commandPrefix)
279 .append(command)
280 .append(commandParameter.wireEncode()));
281 interest.setInterestLifetime(interestLifetime);
282
283 if (identityForCommand.empty())
284 m_keyChain.sign(interest);
285 else {
286 m_keyChain.signByIdentity(interest, ndn::Name(identityForCommand));
287 }
288
289 return interest;
290}
291
292static void
293usage()
294{
295 fprintf(stderr,
296 "NdnRepoWatch [-I identity]"
297 " [-x freshness] [-l lifetime] [-w watchtimeout]"
298 " [-n maxinterestnum][-s stop] [-c check]repo-prefix ndn-name\n"
299 "\n"
300 " Write a file into a repo.\n"
301 " -I: specify identity used for signing commands\n"
302 " -x: FreshnessPeriod in milliseconds\n"
303 " -l: InterestLifetime in milliseconds for each command\n"
304 " -w: timeout in milliseconds for whole process (default unlimited)\n"
305 " -n: total number of interests to be sent for whole process (default unlimited)\n"
306 " -s: stop the whole process\n"
307 " -c: check the process\n"
308 " repo-prefix: repo command prefix\n"
309 " ndn-name: NDN Name prefix for written Data\n"
310 );
311 exit(1);
312}
313
314int
315main(int argc, char** argv)
316{
317 NdnRepoWatch app;
318 int opt;
319 while ((opt = getopt(argc, argv, "x:l:w:n:scI:")) != -1) {
320 switch (opt) {
321 case 'x':
322 try {
323 app.freshnessPeriod = milliseconds(boost::lexical_cast<uint64_t>(optarg));
324 }
325 catch (boost::bad_lexical_cast&) {
326 std::cerr << "-x option should be an integer.";
327 return 1;
328 }
329 break;
330 case 'l':
331 try {
332 app.interestLifetime = milliseconds(boost::lexical_cast<uint64_t>(optarg));
333 }
334 catch (boost::bad_lexical_cast&) {
335 std::cerr << "-l option should be an integer.";
336 return 1;
337 }
338 break;
339 case 'w':
340 app.hasTimeout = true;
341 try {
342 app.watchTimeout = milliseconds(boost::lexical_cast<int64_t>(optarg));
343 }
344 catch (boost::bad_lexical_cast&) {
345 std::cerr << "-w option should be an integer.";
346 return 1;
347 }
348 break;
349 case 'n':
350 app.hasMaxInterestNum = true;
351 try {
352 app.maxInterestNum = boost::lexical_cast<int64_t>(optarg);
353 }
354 catch (boost::bad_lexical_cast&) {
355 std::cerr << "-n option should be an integer.";
356 return 1;
357 }
358 break;
359 case 's':
360 app.status = STOP;
361 break;
362 case 'c':
363 app.status = CHECK;
364 break;
365 case 'I':
366 app.identityForCommand = std::string(optarg);
367 break;
368 case 'v':
369 app.isVerbose = true;
370 break;
371 case 'h':
372 usage();
373 break;
374 default:
375 break;
376 }
377 }
378
379 argc -= optind;
380 argv += optind;
381
382 if (argc != 2)
383 usage();
384
385 app.repoPrefix = Name(argv[0]);
386 app.ndnName = Name(argv[1]);
387
388 app.run();
389
390 return 0;
391}
392
393} // namespace repo
394
395int
396main(int argc, char** argv)
397{
398 try {
399 return repo::main(argc, argv);
400 }
401 catch (std::exception& e) {
402 std::cerr << "ERROR: " << e.what() << std::endl;
403 return 2;
404 }
405}