blob: 6f739503d9c98a103d7aad62a4a73c7ed06d4275 [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)
Alexander Afanasyev95f9f552013-02-26 23:05:20 -080048 , m_executor (1)
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080049 , 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"
Alexander Afanasyev95f9f552013-02-26 23:05:20 -080056 m_PREFIX_INFO = Name ("/localhost")(m_userName)("chronoshare")(m_sharedFolderName)("info");
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080057
58 // <PREFIX_CMD> = /localhost/<user's-device-name>/"chronoshare"/"cmd"
Alexander Afanasyev95f9f552013-02-26 23:05:20 -080059 m_PREFIX_CMD = Name ("/localhost")(m_userName)("chronoshare")(m_sharedFolderName)("cmd");
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080060
Alexander Afanasyev95f9f552013-02-26 23:05:20 -080061 m_executor.start ();
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080062
63 registerPrefixes ();
64}
65
66StateServer::~StateServer()
67{
Alexander Afanasyev95f9f552013-02-26 23:05:20 -080068 m_executor.shutdown ();
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080069
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
Alexander Afanasyev95f9f552013-02-26 23:05:20 -080080 m_ccnx->setInterestFilter (Name (m_PREFIX_INFO)("actions")("folder"), bind(&StateServer::info_actions_folder, this, _1));
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080081
Alexander Afanasyev95f9f552013-02-26 23:05:20 -080082 // <PREFIX_INFO>/"filestate"/"all"/<nonce>/<segment>
83 m_ccnx->setInterestFilter (Name (m_PREFIX_INFO)("filestate")("folder"), bind(&StateServer::info_filestate_folder, this, _1));
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080084
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{
Alexander Afanasyev95f9f552013-02-26 23:05:20 -080092 m_ccnx->clearInterestFilter (Name (m_PREFIX_INFO)("actions")("folder"));
93 m_ccnx->clearInterestFilter (Name (m_PREFIX_INFO)("filestate")("folder"));
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080094 m_ccnx->clearInterestFilter (Name (m_PREFIX_CMD) ("restore")("file"));
95}
96
Alexander Afanasyev95f9f552013-02-26 23:05:20 -080097// void
98// StateServer::info_actions_all (const Name &interest)
99// {
100// _LOG_DEBUG (">> info_actions_all: " << interest);
101// m_executor.execute (bind (&StateServer::info_actions_all_Execute, this, interest));
102// }
103
104// void
105// StateServer::info_actions_all_Execute (const Name &interest)
106// {
107// // <PREFIX_INFO>/"actions"/"all"/<nonce>/<offset> get list of all actions
108
109// try
110// {
111// int offset = interest.getCompFromBackAsInt (0);
112
113// // LookupActionsInFolderRecursively
114// /// @todo !!! add security checking
115// m_ccnx->publishData (interest, "FAIL: Not implemented", 1);
116// }
117// catch (Ccnx::NameException &ne)
118// {
119// // ignore any unexpected interests and errors
120// _LOG_ERROR (*boost::get_error_info<Ccnx::error_info_str>(ne));
121// }
122// }
123
124void debugAction (const Ccnx::Name &name, sqlite3_int64 seq_no, const ActionItem &action)
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800125{
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800126 std::cout << name << ", " << seq_no << ", " << action.filename () << std::endl;
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800127}
128
129void
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800130StateServer::info_actions_folder (const Name &interest)
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800131{
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800132 if (interest.size () - m_PREFIX_INFO.size () != 4 &&
133 interest.size () - m_PREFIX_INFO.size () != 5)
134 {
135 _LOG_DEBUG ("Invalid interest: " << interest);
136 return;
137 }
138
139 _LOG_DEBUG (">> info_actions_all: " << interest);
140 m_executor.execute (bind (&StateServer::info_actions_folder_Execute, this, interest));
141}
142
143void
144StateServer::info_actions_folder_Execute (const Name &interest)
145{
146 // <PREFIX_INFO>/"actions"/"folder"/<folder>/<nonce>/<offset> get list of all actions
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800147
148 try
149 {
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800150 int offset = interest.getCompFromBackAsInt (0);
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800151
152 /// @todo !!! add security checking
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800153
154 string folder;
155 if (interest.size () - m_PREFIX_INFO.size () == 5)
156 folder = interest.getCompFromBackAsString (2);
157 else // == 4
158 folder = "";
159
160 m_actionLog->LookupActionsInFolderRecursively (debugAction, folder, offset*100, 100);
161
162 // m_ccnx->publishData (interest, "FAIL: Not implemented", 1);
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800163 }
164 catch (Ccnx::NameException &ne)
165 {
166 // ignore any unexpected interests and errors
167 _LOG_ERROR (*boost::get_error_info<Ccnx::error_info_str>(ne));
168 }
169}
170
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800171void debugFileState (const FileItem &file)
172{
173 std::cout << file.filename () << std::endl;
174}
175
176void
177StateServer::info_filestate_folder (const Ccnx::Name &interest)
178{
179 if (interest.size () - m_PREFIX_INFO.size () != 4 &&
180 interest.size () - m_PREFIX_INFO.size () != 5)
181 {
182 _LOG_DEBUG ("Invalid interest: " << interest << ", " << interest.size () - m_PREFIX_INFO.size ());
183 return;
184 }
185
186 _LOG_DEBUG (">> info_filestate_folder: " << interest);
187 m_executor.execute (bind (&StateServer::info_filestate_folder_Execute, this, interest));
188}
189
190
191void
192StateServer::info_filestate_folder_Execute (const Ccnx::Name &interest)
193{
194 // <PREFIX_INFO>/"filestate"/"folder"/<one-component-relative-folder-name>/<nonce>/<offset>
195 try
196 {
197 int offset = interest.getCompFromBackAsInt (0);
198
199 // /// @todo !!! add security checking
200
201 string folder;
202 if (interest.size () - m_PREFIX_INFO.size () == 5)
203 folder = interest.getCompFromBackAsString (2);
204 else // == 4
205 folder = "";
206
207 FileStatePtr fileState = m_actionLog->GetFileState ();
208 fileState->LookupFilesInFolderRecursively (debugFileState, folder, offset*100, 100);
209
210 // m_ccnx->publishData (interest, "FAIL: Not implemented", 1);
211 }
212 catch (Ccnx::NameException &ne)
213 {
214 // ignore any unexpected interests and errors
215 _LOG_ERROR (*boost::get_error_info<Ccnx::error_info_str>(ne));
216 }
217}
218
219
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800220void
221StateServer::cmd_restore_file (const Ccnx::Name &interest)
222{
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800223 if (interest.size () - m_PREFIX_CMD.size () != 4 &&
224 interest.size () - m_PREFIX_CMD.size () != 5)
225 {
226 _LOG_DEBUG ("Invalid interest: " << interest);
227 return;
228 }
229
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800230 _LOG_DEBUG (">> cmd_restore_file: " << interest);
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800231 m_executor.execute (bind (&StateServer::cmd_restore_file_Execute, this, interest));
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800232}
233
234void
235StateServer::cmd_restore_file_Execute (const Ccnx::Name &interest)
236{
237 // <PREFIX_CMD>/"restore"/"file"/<one-component-relative-file-name>/<version>/<file-hash>
238
239 /// @todo !!! add security checking
240
241 try
242 {
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800243 FileItemPtr file;
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800244
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800245 if (interest.size () - m_PREFIX_CMD.size () == 5)
246 {
247 Hash hash (head(interest.getCompFromBack (0)), interest.getCompFromBack (0).size());
248 int64_t version = interest.getCompFromBackAsInt (1);
249 string filename = interest.getCompFromBackAsString (2); // should be safe even with full relative path
250
251 file = m_actionLog->LookupAction (filename, version, hash);
252 if (!file)
253 {
254 _LOG_ERROR ("Requested file is not found: [" << filename << "] version [" << version << "] hash [" << hash.shortHash () << "]");
255 }
256 }
257 else
258 {
259 int64_t version = interest.getCompFromBackAsInt (0);
260 string filename = interest.getCompFromBackAsString (1); // should be safe even with full relative path
261
262 file = m_actionLog->LookupAction (filename, version, Hash (0,0));
263 if (!file)
264 {
265 _LOG_ERROR ("Requested file is not found: [" << filename << "] version [" << version << "]");
266 }
267 }
268
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800269 if (!file)
270 {
271 m_ccnx->publishData (interest, "FAIL: Requested file is not found", 1);
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800272 return;
273 }
274
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800275 Hash hash = Hash (file->file_hash ().c_str (), file->file_hash ().size ());
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800276
277 ///////////////////
278 // now the magic //
279 ///////////////////
280
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800281 boost::filesystem::path filePath = m_rootDir / file->filename ();
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800282 Name deviceName (file->device_name ().c_str (), file->device_name ().size ());
283
284 try
285 {
286 if (filesystem::exists (filePath) &&
287 filesystem::last_write_time (filePath) == file->mtime () &&
288 filesystem::status (filePath).permissions () == static_cast<filesystem::perms> (file->mode ()) &&
289 *Hash::FromFileContent (filePath) == hash)
290 {
291 m_ccnx->publishData (interest, "OK: File already exists", 1);
292 _LOG_DEBUG ("Asking to assemble a file, but file already exists on a filesystem");
293 return;
294 }
295 }
296 catch (filesystem::filesystem_error &error)
297 {
298 m_ccnx->publishData (interest, "FAIL: File operation failed", 1);
299 _LOG_ERROR ("File operations failed on [" << filePath << "] (ignoring)");
300 }
301
302 _LOG_TRACE ("Restoring file [" << filePath << "]");
303 if (m_objectManager.objectsToLocalFile (deviceName, hash, filePath))
304 {
305 last_write_time (filePath, file->mtime ());
306 permissions (filePath, static_cast<filesystem::perms> (file->mode ()));
307 m_ccnx->publishData (interest, "OK", 1);
308 }
309 else
310 {
311 m_ccnx->publishData (interest, "FAIL: Unknown error while restoring file", 1);
312 }
313 }
314 catch (Ccnx::NameException &ne)
315 {
316 // ignore any unexpected interests and errors
317 _LOG_ERROR(*boost::get_error_info<Ccnx::error_info_str>(ne));
318 }
319}