00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include <basis/byte_array.h>
00016 #include <basis/istring.h>
00017 #include <basis/portable.h>
00018 #include <cromp/cromp_client.h>
00019 #include <cromp/cromp_server.h>
00020 #include <mechanisms/time_stamp.h>
00021 #include <octopus/entity_defs.h>
00022 #include <octopus/tentacle.h>
00023 #include <opsystem/application_shell.h>
00024 #include <opsystem/command_line.h>
00025 #include <loggers/console_logger.h>
00026 #include <opsystem/directory_tree.h>
00027 #include <loggers/file_logger.h>
00028 #include <opsystem/filename_list.h>
00029 #include <data_struct/static_memory_gremlin.h>
00030 #include <sockets/address.h>
00031 #include <sockets/machine_uid.h>
00032 #include <sockets/tcpip_stack.h>
00033 #include <tentacles/file_transfer_tentacle.h>
00034
00035 #define LOG(a) CLASS_EMERGENCY_LOG(program_wide_logger(), a)
00036
00037 const int REPORTING_INTERVAL = 28 * SECOND_ms;
00038
00039 const int REFRESH_INTERVAL = 20 * MINUTE_ms;
00040
00041 const int COMPARATOR_PORT = 10809;
00042
00043
00044 const int MAX_CHUNK = 16 * KILOBYTE;
00045
00046
00048
00049 class find_missing : public application_shell
00050 {
00051 public:
00052 find_missing();
00053 ~find_missing();
00054
00055 virtual int execute();
00056
00057 IMPLEMENT_CLASS_NAME("find_missing");
00058
00059 int retrieve_info_from_server();
00060
00061
00062
00063 int print_instructions();
00064
00065
00066 private:
00067 bool _saw_clients;
00068 cromp_server *_server_side;
00069
00070 cromp_client *_client_side;
00071 bool _leave_when_no_clients;
00072 bool _encryption;
00073 istring _source;
00074 istring _target;
00075 bool _started_okay;
00076 };
00077
00079
00080 find_missing::find_missing()
00081 : application_shell("find_missing"),
00082 _saw_clients(false),
00083 _server_side(NIL),
00084 _client_side(NIL),
00085 _leave_when_no_clients(false),
00086 _encryption(false),
00087 _started_okay(false)
00088 {
00089 FUNCDEF("constructor");
00090 SET_DEFAULT_COMBO_LOGGER;
00091 LOG("");
00092 LOG("");
00093
00094 command_line args(__argc, __argv);
00095
00096 istring port_text;
00097 int port = COMPARATOR_PORT;
00098 if (args.get_value("port", port_text, false))
00099 port = port_text.convert(COMPARATOR_PORT);
00100 int posn = 0;
00101 if (args.find("exit", posn)) {
00102 LOG("seeing the 'exit without clients' flag set.");
00103 _leave_when_no_clients = true;
00104 }
00105
00106 int indy = 0;
00107 if (args.find("encrypt", indy, false)
00108 || (args.find('e', indy, false)) ) {
00109 LOG("enabling encryption!");
00110
00111 _encryption = true;
00112 }
00113
00114 bool server = true;
00115 indy = 0;
00116 if (args.find("client", indy, false)) {
00117 LOG("client role chosen.");
00118 server = false;
00119 } else {
00120 LOG("server role chosen.");
00121 }
00122
00123 internet_address addr;
00124 addr.port = port;
00125
00126
00127 istring hostname("local");
00128 istring host_temp;
00129 if (args.get_value("host", host_temp, false)) {
00130 LOG(istring("using host: ") + host_temp);
00131 hostname = host_temp;
00132 } else LOG(istring("using host: ") + hostname);
00133 strcpy(addr.hostname, hostname.s());
00134
00135 if (server) {
00136 istring key;
00137 if (!args.get_value("key", key, false)) {
00138 print_instructions();
00139 LOG("No keyword specified on command line.");
00140 return;
00141 }
00142 istring root;
00143 if (!args.get_value("root", root, false)) {
00144 print_instructions();
00145 LOG("No transfer root was specified on the command line.");
00146 return;
00147 }
00148
00149 LOG("starting comparison server");
00150 _server_side = new cromp_server(cromp_server::any_address(port));
00151 file_transfer_tentacle *new_tent = new file_transfer_tentacle
00152 (MAX_CHUNK, true);
00153 new_tent->add_correspondence(key, root, REFRESH_INTERVAL);
00154 _server_side->add_tentacle(new_tent);
00155 _server_side->enable_servers(_encryption);
00156 } else {
00157 LOG("starting comparison client");
00158 _client_side = new cromp_client(addr);
00159 if (_encryption) _client_side->enable_encryption();
00160
00161 outcome ret = _client_side->connect();
00162 if (ret != cromp_client::OKAY)
00163 non_continuable_error(class_name(), func, istring("failed to connect to "
00164 "the server: ") + cromp_client::outcome_name(ret));
00165
00166 file_transfer_tentacle *new_tent = new file_transfer_tentacle
00167 (MAX_CHUNK, true);
00168
00169 if (!args.get_value("source", _source, false)) {
00170 print_instructions();
00171 LOG("No source path was specified on the command line.");
00172 return;
00173 }
00174 if (!args.get_value("target", _target, false)) {
00175 print_instructions();
00176 LOG("No target path was specified on the command line.");
00177 return;
00178 }
00179
00180 string_array includes;
00181 outcome regis = new_tent->register_file_transfer
00182 (_client_side->entity(), _source, _target, includes);
00183 if (regis != cromp_client::OKAY)
00184 non_continuable_error(class_name(), func, "failed to register transfer");
00185
00186 _client_side->add_tentacle(new_tent);
00187 }
00188
00189 _started_okay = true;
00190
00191 }
00192
00193 find_missing::~find_missing()
00194 {
00195 WHACK(_client_side);
00196 WHACK(_server_side);
00197 }
00198
00199 int find_missing::print_instructions()
00200 {
00201 istring name = filename(__argv[0]).basename().raw();
00202 log(isprintf("%s usage:", name.s()));
00203 log("");
00204 log(isprintf("\
00205 This program can compare directory trees and report the files that are\n\
00206 missing on the client's side compared to what the server is offering.\n\
00207 The program can function as either the server side or the client side.\n\
00208 The available flags are:\n\
00209 \n\
00210 %s --client --host srvname --port P --source key_path --target cli_dest\n\
00211 \n\
00212 The client side needs to know the server host (srvname) and the port where\n\
00213 the server is listening for connections (P). The client will compare its\n\
00214 local path (cli_dest) with the server's keyed path (key_path). The key\n\
00215 path will begin with whatever keyword the server is offering, plus optional\n\
00216 additional path components to retrieve less than the whole tree being\n\
00217 served.\n\
00218 \n\
00219 \n\
00220 %s --server --host srvname --port P --key keyname --root srv_path\n\
00221 \n\
00222 The server side needs to know what address and port to listen on (srvname\n\
00223 and P). It will open a server there that provides a directory hierarchy\n\
00224 starting at the root specified (srv_path). The directory tree will be known\n\
00225 to clients as the key word (keyname), thus freeing the clients from needing\n\
00226 to know absolute paths on the server.\n\
00227 \n\
00228 ", name.s(), name.s()));
00229
00230 return 23;
00231 }
00232
00233 int find_missing::retrieve_info_from_server()
00234 {
00235 FUNCDEF("retrieve_info_from_server");
00236
00237 file_transfer_infoton initiate;
00238 initiate._request = true;
00239 initiate._command = file_transfer_infoton::TREE_COMPARISON;
00240 initiate._src_root = _source;
00241 initiate._dest_root = _target;
00242 directory_tree target_area(_target);
00243 target_area.calculate();
00244 string_set includes;
00245 initiate.package_tree_info(target_area, includes);
00246 octopus_request_id cmd_id;
00247 outcome start_ret = _client_side->submit(initiate, cmd_id);
00248 if (start_ret != tentacle::OKAY)
00249 non_continuable_error(class_name(), func, istring("failed to initiate "
00250 " the transfer: ") + cromp_client::outcome_name(start_ret));
00251
00252 infoton *start_reply_tmp = NIL;
00253
00254 outcome first_receipt = _client_side->acquire(start_reply_tmp, cmd_id);
00255 if (first_receipt != cromp_client::OKAY)
00256 non_continuable_error(class_name(), func, istring("failed to receive response: ")
00257 + cromp_client::outcome_name(start_ret));
00258 file_transfer_infoton *start_reply = dynamic_cast<file_transfer_infoton *>
00259 (start_reply_tmp);
00260 if (!start_reply)
00261 non_continuable_error(class_name(), func, "failed to cast starting infoton to "
00262 "proper type");
00263
00264 filename_list diffs;
00265 byte_array pack_copy = start_reply->_packed_data;
00266 if (!diffs.unpack(pack_copy))
00267 non_continuable_error(class_name(), func, "could not unpack filename list!");
00268 log("Differences found between local target and server's tree:");
00270 for (int i = 0; i < diffs.elements(); i++) {
00271 log(isprintf("%d: %s", i + 1, diffs[i]->raw().s()));
00272 }
00273
00274 return 0;
00275 }
00276
00277 int find_missing::execute()
00278 {
00279 FUNCDEF("execute");
00280
00281 if (!_started_okay) return 32;
00282
00283 time_stamp next_report(REPORTING_INTERVAL);
00284
00285 while (true) {
00286
00287
00288 if (_server_side && !_server_side->clients() && _leave_when_no_clients
00289 && _saw_clients) {
00290 LOG("exiting now");
00291 break;
00292 }
00293
00294 if (_client_side) return retrieve_info_from_server();
00295
00296 if (time_stamp() > next_report) {
00297 if (_server_side)
00298 LOG(isprintf("There are %d clients.", _server_side->clients()));
00299
00300 next_report.reset(REPORTING_INTERVAL);
00301 }
00302
00303 portable::sleep_ms(100);
00304 }
00305 return 0;
00306 }
00307
00309
00310 HOOPLE_MAIN(find_missing, )
00311