feisty meow concerns codebase  2.140
recursive_file_copy.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : recursive_file_copy *
4 * Author : Chris Koeritz *
5 * *
6 *******************************************************************************
7 * Copyright (c) 2005-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
14 
15 #include "file_transfer_infoton.h"
16 #include "file_transfer_tentacle.h"
17 #include "recursive_file_copy.h"
18 
20 #include <basis/guards.h>
21 #include <filesystem/directory.h>
23 #include <filesystem/filename.h>
26 #include <filesystem/huge_file.h>
28 #include <octopus/entity_defs.h>
30 #include <octopus/octopus.h>
33 #include <timely/time_control.h>
34 
35 using namespace application;
36 using namespace basis;
37 using namespace filesystem;
38 using namespace loggers;
39 using namespace structures;
40 using namespace textual;
41 using namespace timely;
42 
43 namespace octopi {
44 
45 #define DEBUG_RECURSIVE_FILE_COPY
46  // uncomment for noisier debugging.
47 
48 #define FAKE_HOSTNAME "internal_fake_host"
49 
50 #undef LOG
51 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
52 #undef BASE_LOG
53 #define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger::get(), s)
54 
55 #define RETURN_ERROR_RFC(msg, err) { \
56  LOG(msg); \
57  return err; \
58 }
59 
61  // maximum size for each transfer chunk.
62 
64  // how long do we allow the scanned file lists to stay relevant.
65  // we allow it a long time, since this is a copy and not an active
66  // synchronization.
67 
68 recursive_file_copy::~recursive_file_copy() {}
69 
70 const char *recursive_file_copy::outcome_name(const outcome &to_name)
71 { return common::outcome_name(to_name); }
72 
73 outcome recursive_file_copy::copy_hierarchy(int transfer_mode,
74  const astring &source_dir, const astring &target_dir,
75  const string_array &includes, const astring &source_start)
76 {
77  FUNCDEF("copy_hierarchy");
78 
79 /*
80  string_array includes;
81  if (_global_argc >= 5) {
82  for (int i = 4; i < _global_argc; i++) {
83  includes += _global_argv[i];
84  }
85  }
86 */
87 
88  const astring transfer_name = "snootums";
89 
90  astring source_root = transfer_name;
91  if (source_start.t()) {
92  source_root += filename::default_separator() + source_start;
93  }
94 
95  octopus ring_leader(FAKE_HOSTNAME, 10 * MEGABYTE);
98  ring_leader.add_tentacle(tran);
99 
100  outcome add_ret = tran->add_correspondence(transfer_name, source_dir,
102  if (add_ret != tentacle::OKAY)
103  RETURN_ERROR_RFC("failed to add the correspondence", NOT_FOUND);
104 
105 //hmmm: this kind of object creation should be packaged in helper functions.
107  initiate->_request = true;
108  initiate->_command = file_transfer_infoton::BUILD_TARGET_TREE;
109  initiate->_src_root = source_root;
110  initiate->_dest_root = target_dir;
111 
112  // make a directory snapshot with just directories, no files.
113  directory_tree target_area_just_dirs(source_dir, "*", true);
114  initiate->package_tree_info(target_area_just_dirs, includes);
115 
116  octopus_entity ent = ring_leader.issue_identity();
117  octopus_request_id req_id(ent, 1);
118  outcome start_ret = ring_leader.evaluate(initiate, req_id);
119  if (start_ret != tentacle::OKAY)
120  RETURN_ERROR_RFC("failed to build target tree", NONE_READY);
121 
122  file_transfer_infoton *reply_from_init
123  = (file_transfer_infoton *)ring_leader.acquire_specific_result(req_id);
124  if (!reply_from_init) {
125 LOG("spewing list of what IS there...");
126 LOG(ring_leader.responses().text_form());
127  RETURN_ERROR_RFC("no response to request to build target tree", NONE_READY);
128  }
129 
130  if (reply_from_init->_success != OKAY) {
131  RETURN_ERROR_RFC("failed to get good result from building target tree", reply_from_init->_success);
132  }
133 
134 //now repeating a lot of above to get tree compare going.
135 
136 //hmmm: this kind of object creation should be packaged in helper functions.
137  file_transfer_infoton *comparison_req = new file_transfer_infoton;
138  comparison_req->_request = true;
139  comparison_req->_command = file_transfer_infoton::TREE_COMPARISON;
140  comparison_req->_src_root = source_root;
141  comparison_req->_dest_root = target_dir;
142  // make a directory snapshot with just directories, no files.
143  directory_tree target_area(target_dir);
144 
145 //hmmm: simple asset counting debugging in calculate would be nice too.
146  target_area.calculate( !(transfer_mode & file_transfer_tentacle::COMPARE_CONTENT_SAMPLE) );
147 
148  comparison_req->package_tree_info(target_area, includes);
149 
150  ent = ring_leader.issue_identity();
151  req_id = octopus_request_id(ent, 1);
152  start_ret = ring_leader.evaluate(comparison_req, req_id);
153  if (start_ret != tentacle::OKAY)
154  RETURN_ERROR_RFC("failed to build target tree", NONE_READY);
155 
156  reply_from_init = (file_transfer_infoton *)ring_leader.acquire_specific_result(req_id);
157  if (!reply_from_init) {
158 LOG("spewing list of what IS there...");
159 LOG(ring_leader.responses().text_form());
160  RETURN_ERROR_RFC("no response to request to build target tree", NONE_READY);
161  }
162 
163 
164 //resuming
165 
166 
167  filename_list diffs;
168  byte_array pack_copy = reply_from_init->_packed_data;
169  if (!diffs.unpack(pack_copy)) {
170  RETURN_ERROR_RFC("could not unpack filename list!", GARBAGE);
171  }
172 // LOG(astring("got list of diffs:\n") + diffs.text_form());
173 
174  octopus client_spider(FAKE_HOSTNAME, 10 * MEGABYTE);
177  tran2->register_file_transfer(ent, source_root, target_dir, includes);
178  client_spider.add_tentacle(tran2);
179 
180  octopus_request_id resp_id(ent, 2);
181  outcome ini_resp_ret = client_spider.evaluate(reply_from_init, resp_id);
182  if (ini_resp_ret != tentacle::OKAY)
183  RETURN_ERROR_RFC("failed to process the start response!", FAILURE);
184 
185  infoton *junk = client_spider.acquire_specific_result(resp_id);
186  if (junk)
187  RETURN_ERROR_RFC("got a response we shouldn't have!", FAILURE);
188 
189  astring current_file; // what file is in progress right now?
190 
191  int iter = 0;
192  while (true) {
193 #ifdef DEBUG_RECURSIVE_FILE_COPY
194  LOG(a_sprintf("ongoing chunk %d", ++iter));
195 #endif
196 
197  // keep going until we find a broken reply.
199  ongoing->_request = true;
200  ongoing->_command = file_transfer_infoton::PLACE_FILE_CHUNKS;
201  ongoing->_src_root = source_root;
202  ongoing->_dest_root = target_dir;
203 
204  octopus_request_id chunk_id(ent, iter + 10);
205  outcome place_ret = ring_leader.evaluate(ongoing, chunk_id);
206  if (place_ret != tentacle::OKAY)
207  RETURN_ERROR_RFC("failed to run ongoing transfer", FAILURE);
208 
209  file_transfer_infoton *reply = (file_transfer_infoton *)ring_leader
210  .acquire_specific_result(chunk_id);
211  if (!reply)
212  RETURN_ERROR_RFC("failed to get ongoing transfer reply", NONE_READY);
213 
214  if (reply->_command == file_transfer_infoton::CONCLUDE_TRANSFER_MARKER) {
215  BASE_LOG(astring("finished transfer from \"") + source_dir
216  + "\" to \"" + target_dir + "\"");
217  break;
218  }
219 
220  byte_array copy = reply->_packed_data;
221  while (copy.length()) {
222  file_time empty;
223  file_transfer_header head(empty);
224  if (!head.unpack(copy))
225  RETURN_ERROR_RFC("failed to unpack header", GARBAGE);
226  if (copy.length() < head._length)
227  RETURN_ERROR_RFC("not enough length in array", GARBAGE);
228  if (head._length > 0)
229  copy.zap(0, head._length - 1);
230 
231 //hmmm: this needs better formatting, and should not repeat the same file name even
232 // if it's in multiple chunks.
234  }
235  if (copy.length())
236  RETURN_ERROR_RFC("still had data in array", GARBAGE);
237 
238  octopus_request_id resp_id(ent, iter + 11);
239  outcome resp_ret = client_spider.evaluate(reply, resp_id);
240  if (resp_ret != tentacle::OKAY)
241  RETURN_ERROR_RFC("failed to process the transfer reply!", FAILURE);
242  }
243 
244  return OKAY;
245 }
246 
247 } //namespace.
248 
249 
a_sprintf is a specialization of astring that provides printf style support.
Definition: astring.h:440
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
bool t() const
t() is a shortcut for the string being "true", as in non-empty.
Definition: astring.h:97
A very common template for a dynamic array of bytes.
Definition: byte_array.h:36
Outcomes describe the state of completion for an operation.
Definition: outcome.h:31
An object that traverses directory trees and provides a view of all files.
bool calculate(bool just_size)
visits each file in the directory_tree and calculates its attributes.
describes one portion of an ongoing file transfer.
int _length
the length of the transferred piece.
virtual bool unpack(basis::byte_array &packed_form)
Restores the packable from the "packed_form".
basis::astring readable_text_form() const
a nicer formatting of the information.
virtual bool unpack(basis::byte_array &packed_form)
Restores the packable from the "packed_form".
basis::astring text_form() const
Base objects used by the file transfer tentacle to schedule transfers.
basis::abyte _command
one of the commands above.
basis::outcome _success
reports what kind of result occurred.
basis::astring _src_root
the top-level directory of the source.
bool _request
if it's not a request, then it's a response.
basis::astring _dest_root
the top-level directory of the destination.
void package_tree_info(const filesystem::directory_tree &tree, const structures::string_array &includes)
prepares the packed data from the "tree" and "includes" list.
basis::byte_array _packed_data
the packed headers and file chunks.
Manages the transferrence of directory trees from one place to another.
basis::outcome add_correspondence(const basis::astring &source_mapping, const basis::astring &source_root, int refresh_interval)
adds a file transfer correspondence.
basis::outcome register_file_transfer(const octopus_entity &ent, const basis::astring &src_root, const basis::astring &dest_root, const structures::string_array &include)
records a transfer that is going to commence.
An infoton is an individual request parcel with accompanying information.
Definition: infoton.h:32
Provides a way of identifying users of an octopus object.
Definition: entity_defs.h:35
Identifies requests made on an octopus by users.
Definition: entity_defs.h:114
Octopus is a design pattern for generalized request processing systems.
Definition: octopus.h:47
entity_data_bin & responses()
allows external access to our set of results.
Definition: octopus.cpp:178
basis::outcome add_tentacle(tentacle *to_add, bool filter=false)
hooks a tentacle in to provide processing of one type of infoton.
Definition: octopus.cpp:253
basis::outcome evaluate(infoton *request, const octopus_request_id &item_id, bool now=false)
tries to process the "request" using the current set of tentacles.
Definition: octopus.cpp:351
octopus_entity issue_identity()
creates an entity identifier that is unique for this octopus.
Definition: octopus.cpp:527
infoton * acquire_specific_result(const octopus_request_id &original_id)
supports seeking the result for a specific request.
Definition: octopus.cpp:187
An array of strings with some additional helpful methods.
Definition: string_array.h:32
char * copy(register char *str)
Definition: makedep.cpp:554
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
Implements an application lock to ensure only one is running at once.
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
const int MEGABYTE
Number of bytes in a megabyte.
Definition: definitions.h:135
const int HOUR_ms
Number of milliseconds in an hour.
Definition: definitions.h:122
A platform independent way to obtain the timestamp of a file.
Definition: byte_filer.cpp:37
A logger that sends to the console screen using the standard output device.
const int MAX_CHUNK_RFC_COPY_HIER
const int EXPECTED_MAXIMUM_TRANSFER_TIME
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
#include <time.h>
Definition: earth_time.cpp:37
#define FAKE_HOSTNAME
#define LOG(s)
#define RETURN_ERROR_RFC(msg, err)
#define BASE_LOG(s)