blob: a9b6d8bdd5ebcf85f941e4c8047a730d232ca8d0 [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
37static const uint64_t DEFAULT_INTEREST_LIFETIME = 4000;
38static const uint64_t DEFAULT_FRESHNESS_PERIOD = 10000;
39static const uint64_t DEFAULT_CHECK_PERIOD = 1000;
40
41enum CommandType
42{
43 START,
44 CHECK,
45 STOP
46};
47
48class NdnRepoWatch : ndn::noncopyable
49{
50public:
51 class Error : public std::runtime_error
52 {
53 public:
54 explicit
55 Error(const std::string& what)
56 : std::runtime_error(what)
57 {
58 }
59 };
60
61 NdnRepoWatch()
62 : freshnessPeriod(DEFAULT_FRESHNESS_PERIOD)
63 , interestLifetime(DEFAULT_INTEREST_LIFETIME)
64 , hasTimeout(false)
65 , watchTimeout(0)
66 , hasMaxInterestNum(false)
67 , maxInterestNum(0)
68 , status(START)
69 , isVerbose(false)
70
71 , m_scheduler(m_face.getIoService())
72 , m_checkPeriod(DEFAULT_CHECK_PERIOD)
73 {
74 }
75
76 void
77 run();
78
79private:
80
81 void
82 startWatchCommand();
83
84 void
85 onWatchCommandResponse(const ndn::Interest& interest, ndn::Data& data);
86
87 void
88 onWatchCommandTimeout(const ndn::Interest& interest);
89
90 void
91 stopProcess();
92
93 void
94 signData(ndn::Data& data);
95
96 void
97 startCheckCommand();
98
99 void
100 onCheckCommandTimeout(const ndn::Interest& interest);
101
102 void
103 onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data);
104
105 void
106 onStopCommandTimeout(const ndn::Interest& interest);
107
108 ndn::Interest
109 generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
110 const RepoCommandParameter& commandParameter);
111
112public:
113 std::string identityForCommand;
114 milliseconds freshnessPeriod;
115 milliseconds interestLifetime;
116 bool hasTimeout;
117 milliseconds watchTimeout;
118 bool hasMaxInterestNum;
119 int64_t maxInterestNum;
120 CommandType status;
121 ndn::Name repoPrefix;
122 ndn::Name ndnName;
123 bool isVerbose;
124
125
126private:
127 ndn::Face m_face;
128 ndn::Scheduler m_scheduler;
129 milliseconds m_checkPeriod;
130
131 ndn::Name m_dataPrefix;
132 ndn::KeyChain m_keyChain;
133 typedef std::map<uint64_t, ndn::shared_ptr<ndn::Data> > DataContainer;
134};
135
136void
137NdnRepoWatch::run()
138{
139 m_dataPrefix = ndnName;
140 startWatchCommand();
141
142 if (hasTimeout)
143 m_scheduler.scheduleEvent(watchTimeout, ndn::bind(&NdnRepoWatch::stopProcess, this));
144
145 m_face.processEvents();
146}
147
148void
149NdnRepoWatch::startWatchCommand()
150{
151 RepoCommandParameter parameters;
152 parameters.setName(m_dataPrefix);
153
154 repoPrefix.append("watch");
155 if (status == START) {
156 if (hasMaxInterestNum) {
157 parameters.setMaxInterestNum(maxInterestNum);
158 }
159 if (hasTimeout) {
160 parameters.setWatchTimeout(watchTimeout);
161 }
162 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "start", parameters);
163 m_face.expressInterest(commandInterest,
164 ndn::bind(&NdnRepoWatch::onWatchCommandResponse, this, _1, _2),
165 ndn::bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
166 }
167 else if (status == STOP){
168 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "stop", parameters);
169 m_face.expressInterest(commandInterest,
170 ndn::bind(&NdnRepoWatch::onWatchCommandResponse, this, _1, _2),
171 ndn::bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
172 }
173 else if (status == CHECK){
174 ndn::Interest commandInterest = generateCommandInterest(repoPrefix, "check", parameters);
175 m_face.expressInterest(commandInterest,
176 ndn::bind(&NdnRepoWatch::onWatchCommandResponse, this, _1, _2),
177 ndn::bind(&NdnRepoWatch::onWatchCommandTimeout, this, _1));
178 }
179
180}
181
182void
183NdnRepoWatch::onWatchCommandResponse(const ndn::Interest& interest, ndn::Data& data)
184{
185 RepoCommandResponse response(data.getContent().blockFromValue());
186 int statusCode = response.getStatusCode();
187 if (statusCode >= 400) {
188 throw Error("Watch command failed with code " +
189 boost::lexical_cast<std::string>(statusCode));
190 }
191 else if (statusCode == 101) {
192 std::cerr << "Watching prefix is stopped!" <<std::endl;
193 m_face.getIoService().stop();
194 return;
195 }
196 else if (statusCode == 300) {
197 std::cerr << "Watching prefix is running!" <<std::endl;
198 m_scheduler.scheduleEvent(m_checkPeriod,
199 ndn::bind(&NdnRepoWatch::startCheckCommand, this));
200 return;
201 }
202 else if (statusCode == 100) {
203 std::cerr << "Watching prefix starts!" <<std::endl;
204 m_scheduler.scheduleEvent(m_checkPeriod,
205 ndn::bind(&NdnRepoWatch::startCheckCommand, this));
206 return;
207 }
208 else {
209 throw Error("Unrecognized Status Code " +
210 boost::lexical_cast<std::string>(statusCode));
211 }
212}
213
214void
215NdnRepoWatch::onWatchCommandTimeout(const ndn::Interest& interest)
216{
217 throw Error("command response timeout");
218}
219
220void
221NdnRepoWatch::stopProcess()
222{
223 m_face.getIoService().stop();
224}
225
226void
227NdnRepoWatch::startCheckCommand()
228{
229 repoPrefix.append("watch");
230 ndn::Interest checkInterest = generateCommandInterest(repoPrefix, "check",
231 RepoCommandParameter()
232 .setName(m_dataPrefix));
233 m_face.expressInterest(checkInterest,
234 ndn::bind(&NdnRepoWatch::onWatchCommandResponse, this, _1, _2),
235 ndn::bind(&NdnRepoWatch::onCheckCommandTimeout, this, _1));
236}
237
238void
239NdnRepoWatch::onCheckCommandTimeout(const ndn::Interest& interest)
240{
241 throw Error("check response timeout");
242}
243
244void
245NdnRepoWatch::onStopCommandResponse(const ndn::Interest& interest, ndn::Data& data)
246{
247 RepoCommandResponse response(data.getContent().blockFromValue());
248 int statusCode = response.getStatusCode();
249 if (statusCode != 101) {
250 throw Error("Watch stop command failed with code: " +
251 boost::lexical_cast<std::string>(statusCode));
252 }
253 else {
254 std::cerr << "Status code is 101. Watching prefix is stopped successfully!" << std::endl;
255 m_face.getIoService().stop();
256 return;
257 }
258}
259
260void
261NdnRepoWatch::onStopCommandTimeout(const ndn::Interest& interest)
262{
263 throw Error("stop response timeout");
264}
265
266ndn::Interest
267NdnRepoWatch::generateCommandInterest(const ndn::Name& commandPrefix, const std::string& command,
268 const RepoCommandParameter& commandParameter)
269{
270 ndn::Interest interest(ndn::Name(commandPrefix)
271 .append(command)
272 .append(commandParameter.wireEncode()));
273 interest.setInterestLifetime(interestLifetime);
274
275 if (identityForCommand.empty())
276 m_keyChain.sign(interest);
277 else {
278 m_keyChain.signByIdentity(interest, ndn::Name(identityForCommand));
279 }
280
281 return interest;
282}
283
284static void
285usage()
286{
287 fprintf(stderr,
288 "NdnRepoWatch [-I identity]"
289 " [-x freshness] [-l lifetime] [-w watchtimeout]"
290 " [-n maxinterestnum][-s stop] [-c check]repo-prefix ndn-name\n"
291 "\n"
292 " Write a file into a repo.\n"
293 " -I: specify identity used for signing commands\n"
294 " -x: FreshnessPeriod in milliseconds\n"
295 " -l: InterestLifetime in milliseconds for each command\n"
296 " -w: timeout in milliseconds for whole process (default unlimited)\n"
297 " -n: total number of interests to be sent for whole process (default unlimited)\n"
298 " -s: stop the whole process\n"
299 " -c: check the process\n"
300 " repo-prefix: repo command prefix\n"
301 " ndn-name: NDN Name prefix for written Data\n"
302 );
303 exit(1);
304}
305
306int
307main(int argc, char** argv)
308{
309 NdnRepoWatch app;
310 int opt;
311 while ((opt = getopt(argc, argv, "x:l:w:n:scI:")) != -1) {
312 switch (opt) {
313 case 'x':
314 try {
315 app.freshnessPeriod = milliseconds(boost::lexical_cast<uint64_t>(optarg));
316 }
317 catch (boost::bad_lexical_cast&) {
318 std::cerr << "-x option should be an integer.";
319 return 1;
320 }
321 break;
322 case 'l':
323 try {
324 app.interestLifetime = milliseconds(boost::lexical_cast<uint64_t>(optarg));
325 }
326 catch (boost::bad_lexical_cast&) {
327 std::cerr << "-l option should be an integer.";
328 return 1;
329 }
330 break;
331 case 'w':
332 app.hasTimeout = true;
333 try {
334 app.watchTimeout = milliseconds(boost::lexical_cast<int64_t>(optarg));
335 }
336 catch (boost::bad_lexical_cast&) {
337 std::cerr << "-w option should be an integer.";
338 return 1;
339 }
340 break;
341 case 'n':
342 app.hasMaxInterestNum = true;
343 try {
344 app.maxInterestNum = boost::lexical_cast<int64_t>(optarg);
345 }
346 catch (boost::bad_lexical_cast&) {
347 std::cerr << "-n option should be an integer.";
348 return 1;
349 }
350 break;
351 case 's':
352 app.status = STOP;
353 break;
354 case 'c':
355 app.status = CHECK;
356 break;
357 case 'I':
358 app.identityForCommand = std::string(optarg);
359 break;
360 case 'v':
361 app.isVerbose = true;
362 break;
363 case 'h':
364 usage();
365 break;
366 default:
367 break;
368 }
369 }
370
371 argc -= optind;
372 argv += optind;
373
374 if (argc != 2)
375 usage();
376
377 app.repoPrefix = Name(argv[0]);
378 app.ndnName = Name(argv[1]);
379
380 app.run();
381
382 return 0;
383}
384
385} // namespace repo
386
387int
388main(int argc, char** argv)
389{
390 try {
391 return repo::main(argc, argv);
392 }
393 catch (std::exception& e) {
394 std::cerr << "ERROR: " << e.what() << std::endl;
395 return 2;
396 }
397}