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
17#include "recursive_file_copy.h"
18
20#include <basis/guards.h>
23#include <filesystem/filename.h>
28#include <octopus/entity_defs.h>
30#include <octopus/octopus.h>
33#include <timely/time_control.h>
34
35using namespace application;
36using namespace basis;
37using namespace filesystem;
38using namespace loggers;
39using namespace structures;
40using namespace textual;
41using namespace timely;
42
43namespace 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
69
70const char *recursive_file_copy::outcome_name(const outcome &to_name)
71{ return common::outcome_name(to_name); }
72
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;
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) {
125LOG("spewing list of what IS there...");
126LOG(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;
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) {
158LOG("spewing list of what IS there...");
159LOG(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;
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
215 BASE_LOG(astring("finished transfer from \"") + source_dir
216 + "\" to \"" + target_dir + "\"");
217 break;
218 }
219
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
#define LOG(s)
#define BASE_LOG(s)
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
static const char * outcome_name(const outcome &to_name)
Returns a string representation of the outcome "to_name".
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".
static basis::astring default_separator()
returns the default separator character for this OS.
Definition filename.cpp:93
basis::astring text_form() const
Base objects used by the file transfer tentacle to schedule transfers.
@ BUILD_TARGET_TREE
asks the target side to build the directory tree from the source.
@ CONCLUDE_TRANSFER_MARKER
this infoton marks the end of the transfer process.
@ PLACE_FILE_CHUNKS
the destination side requests a new set of chunks.
@ TREE_COMPARISON
the destination root will be compared with the source root.
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.
@ COMPARE_CONTENT_SAMPLE
samples parts of file for comparison.
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.
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
static const char * outcome_name(const basis::outcome &to_name)
static basis::outcome copy_hierarchy(int transfer_mode, const basis::astring &source_dir, const basis::astring &target_dir, const structures::string_array &includes, const basis::astring &source_start=basis::astring::empty_string())
copies a directory hierarchy starting at "source_dir" into "target_dir".
An array of strings with some additional helpful methods.
char * copy(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:54
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.
const int HOUR_ms
Number of milliseconds in an hour.
A platform independent way to obtain the timestamp of a file.
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>
#define FAKE_HOSTNAME
#define RETURN_ERROR_RFC(msg, err)