blob: 98830d07be21e6b3cb418ffaa24703ce28b68074 [file] [log] [blame]
Alexander Afanasyev026eaf32013-02-23 16:37:14 -08001/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
2/*
3 * Copyright (c) 2013 University of California, Los Angeles
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
19 * Zhenkai Zhu <zhenkai@cs.ucla.edu>
20 */
21
22#include "state-server.h"
23#include "logging.h"
24#include <boost/make_shared.hpp>
25#include <utility>
26#include "task.h"
27#include "periodic-task.h"
28#include "simple-interval-generator.h"
29#include <boost/lexical_cast.hpp>
30
31INIT_LOGGER ("StateServer");
32
33using namespace Ccnx;
34using namespace std;
35using namespace boost;
36
37StateServer::StateServer(CcnxWrapperPtr ccnx, ActionLogPtr actionLog,
38 const boost::filesystem::path &rootDir,
39 const Ccnx::Name &userName, const std::string &sharedFolderName,
40 const std::string &appName,
41 ObjectManager &objectManager,
42 int freshness/* = -1*/)
43 : m_ccnx(ccnx)
44 , m_actionLog(actionLog)
45 , m_objectManager (objectManager)
46 , m_rootDir(rootDir)
47 , m_freshness(freshness)
48 , m_scheduler (new Scheduler())
49 , m_userName (userName)
50 , m_sharedFolderName (sharedFolderName)
51 , m_appName (appName)
52{
53 // may be later /localhost should be replaced with /%C1.M.S.localhost
54
55 // <PREFIX_INFO> = /localhost/<user's-device-name>/"chronoshare"/"info"
56 m_PREFIX_INFO = Name ("/localhost")(m_userName)("chronoshare")("info");
57
58 // <PREFIX_CMD> = /localhost/<user's-device-name>/"chronoshare"/"cmd"
59 m_PREFIX_CMD = Name ("/localhost")(m_userName)("chronoshare")("cmd");
60
61 m_scheduler->start ();
62
63 registerPrefixes ();
64}
65
66StateServer::~StateServer()
67{
68 m_scheduler->shutdown ();
69
70 deregisterPrefixes ();
71}
72
73void
74StateServer::registerPrefixes ()
75{
76 // currently supporting limited number of command.
77 // will be extended to support all planned commands later
78
79 // <PREFIX_INFO>/"actions"/"all"/<nonce>/<segment> get list of all actions
80 m_ccnx->setInterestFilter (Name (m_PREFIX_INFO)("actions")("all"), bind(&StateServer::info_actions_all, this, _1));
81
82 // // <PREFIX_INFO>/"filestate"/"all"/<nonce>/<segment>
83 // m_ccnx->setInterestFilter (Name (m_PREFIX_INFO)("filestate")("all"), bind(&StateServer::info_filestate_all, this, _1));
84
85 // <PREFIX_CMD>/"restore"/"file"/<one-component-relative-file-name>/<version>/<file-hash>
86 m_ccnx->setInterestFilter (Name (m_PREFIX_CMD)("restore")("file"), bind(&StateServer::cmd_restore_file, this, _1));
87}
88
89void
90StateServer::deregisterPrefixes ()
91{
92 m_ccnx->clearInterestFilter (Name (m_PREFIX_INFO)("actions")("all"));
93 m_ccnx->clearInterestFilter (Name (m_PREFIX_INFO)("filestate")("all"));
94 m_ccnx->clearInterestFilter (Name (m_PREFIX_CMD) ("restore")("file"));
95}
96
97void
98StateServer::info_actions_all (const Name &interest)
99{
100 _LOG_DEBUG (">> info_actions_all: " << interest);
101 m_scheduler->scheduleOneTimeTask (m_scheduler, 0, bind (&StateServer::info_actions_all_Execute, this, interest), boost::lexical_cast<string>(interest));
102}
103
104void
105StateServer::info_actions_all_Execute (const Name &interest)
106{
107 // <PREFIX_INFO>/"actions"/"all"/<nonce>/<segment> get list of all actions
108
109 try
110 {
111 int segment = interest.getCompFromBackAsInt (0);
112 if (segment != 0)
113 {
114 // ignore anything except segment 0, other stuff should be cached.. if not, then good luck
115 return;
116 }
117
118 /// @todo !!! add security checking
119 m_ccnx->publishData (interest, "FAIL: Not implemented", 1);
120 }
121 catch (Ccnx::NameException &ne)
122 {
123 // ignore any unexpected interests and errors
124 _LOG_ERROR (*boost::get_error_info<Ccnx::error_info_str>(ne));
125 }
126}
127
128void
129StateServer::cmd_restore_file (const Ccnx::Name &interest)
130{
131 _LOG_DEBUG (">> cmd_restore_file: " << interest);
132 m_scheduler->scheduleOneTimeTask (m_scheduler, 0, bind (&StateServer::cmd_restore_file_Execute, this, interest), boost::lexical_cast<string>(interest));
133}
134
135void
136StateServer::cmd_restore_file_Execute (const Ccnx::Name &interest)
137{
138 // <PREFIX_CMD>/"restore"/"file"/<one-component-relative-file-name>/<version>/<file-hash>
139
140 /// @todo !!! add security checking
141
142 try
143 {
144 Hash hash (head(interest.getCompFromBack (0)), interest.getCompFromBack (0).size());
145 int64_t version = interest.getCompFromBackAsInt (1);
146 string filename = interest.getCompFromBackAsString (2); // should be safe even with full relative path
147
148 FileItemPtr file = m_actionLog->LookupAction (filename, version, hash);
149 if (!file)
150 {
151 m_ccnx->publishData (interest, "FAIL: Requested file is not found", 1);
152 _LOG_ERROR ("Requested file is not found: [" << filename << "] version [" << version << "] hash [" << hash.shortHash () << "]");
153 return;
154 }
155
156 hash = Hash (file->file_hash ().c_str (), file->file_hash ().size ());
157
158 ///////////////////
159 // now the magic //
160 ///////////////////
161
162 boost::filesystem::path filePath = m_rootDir / filename;
163 Name deviceName (file->device_name ().c_str (), file->device_name ().size ());
164
165 try
166 {
167 if (filesystem::exists (filePath) &&
168 filesystem::last_write_time (filePath) == file->mtime () &&
169 filesystem::status (filePath).permissions () == static_cast<filesystem::perms> (file->mode ()) &&
170 *Hash::FromFileContent (filePath) == hash)
171 {
172 m_ccnx->publishData (interest, "OK: File already exists", 1);
173 _LOG_DEBUG ("Asking to assemble a file, but file already exists on a filesystem");
174 return;
175 }
176 }
177 catch (filesystem::filesystem_error &error)
178 {
179 m_ccnx->publishData (interest, "FAIL: File operation failed", 1);
180 _LOG_ERROR ("File operations failed on [" << filePath << "] (ignoring)");
181 }
182
183 _LOG_TRACE ("Restoring file [" << filePath << "]");
184 if (m_objectManager.objectsToLocalFile (deviceName, hash, filePath))
185 {
186 last_write_time (filePath, file->mtime ());
187 permissions (filePath, static_cast<filesystem::perms> (file->mode ()));
188 m_ccnx->publishData (interest, "OK", 1);
189 }
190 else
191 {
192 m_ccnx->publishData (interest, "FAIL: Unknown error while restoring file", 1);
193 }
194 }
195 catch (Ccnx::NameException &ne)
196 {
197 // ignore any unexpected interests and errors
198 _LOG_ERROR(*boost::get_error_info<Ccnx::error_info_str>(ne));
199 }
200}