feisty meow concerns codebase  2.140
find_missing.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : find_missing *
4 * Author : Chris Koeritz *
5 * *
6 *******************************************************************************
7 * Copyright (c) 2002-$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 <basis/byte_array.h>
16 #include <basis/astring.h>
17 
19 //#include <application/command_line.h>
20 #include <basis/astring.h>
21 #include <cromp/cromp_client.h>
22 #include <cromp/cromp_server.h>
26 #include <loggers/combo_logger.h>
27 //#include <loggers/program_wide_logger.h>
28 #include <octopus/entity_defs.h>
29 #include <octopus/tentacle.h>
31 #include <sockets/machine_uid.h>
32 #include <sockets/tcpip_stack.h>
35 #include <timely/time_control.h>
36 #include <timely/time_stamp.h>
37 
38 using namespace application;
39 using namespace basis;
40 using namespace cromp;
41 using namespace filesystem;
42 using namespace loggers;
43 using namespace octopi;
44 using namespace sockets;
45 using namespace structures;
46 using namespace textual;
47 using namespace timely;
48 
49 #undef BASE_LOG
50 #define BASE_LOG(a) EMERGENCY_LOG(program_wide_logger::get(), astring(a))
51 #undef LOG
52 #define LOG(a) CLASS_EMERGENCY_LOG(program_wide_logger::get(), astring(a))
53 
54 const int REPORTING_INTERVAL = 28 * SECOND_ms; // how often to squawk.
55 
56 const int REFRESH_INTERVAL = 20 * MINUTE_ms; // how often we check tree.
57 
58 const int COMPARATOR_PORT = 10809;
59  // simple port grabbed randomly for the default.
60 
61 const int MAX_CHUNK = 16 * KILOBYTE;
62  // chunk size doesn't matter here; not transferring.
63 
65 
66 class find_missing : public application_shell
67 {
68 public:
69  find_missing();
70  ~find_missing();
71 
72  virtual int execute();
73 
74  DEFINE_CLASS_NAME("find_missing");
75 
76  int retrieve_info_from_server();
77  // for a client side comparison, this finds out which files are
78  // different and reports them.
79 
80  int print_instructions();
81  // shows the instructions for this program.
82 
83 private:
84  bool _saw_clients; // true if we ever got a connection.
85  cromp_server *_server_side;
86  // provides connection and transmission services for servers.
87  cromp_client *_client_side; // client side connection.
88  bool _leave_when_no_clients; // true if we should just do one run.
89  bool _encryption; // true if we're encrypting.
90  astring _source; // the source path which a client will ask the server for.
91  astring _target; // the target path where files are stored for the client.
92  bool _started_okay; // got through the command line checking.
93 };
94 
96 
97 find_missing::find_missing()
99  _saw_clients(false),
100  _server_side(NULL_POINTER),
101  _client_side(NULL_POINTER),
102  _leave_when_no_clients(false),
103  _encryption(false),
104  _started_okay(false)
105 {
106  FUNCDEF("constructor");
108  LOG("");
109  LOG("");
110 
112  // check for a port on the command line.
113  astring port_text;
114  int port = COMPARATOR_PORT;
115  if (args.get_value("port", port_text, false))
116  port = port_text.convert(COMPARATOR_PORT);
117  int posn = 0;
118  if (args.find("exit", posn)) {
119  LOG("seeing the 'exit without clients' flag set.");
120  _leave_when_no_clients = true;
121  }
122 
123  int indy = 0;
124  if (args.find("encrypt", indy, false)
125  || (args.find('e', indy, false)) ) {
126 LOG("enabling encryption!");
127  // they're saying that we should encrypt the communication.
128  _encryption = true;
129  }
130 
131  bool server = true;
132  indy = 0;
133  if (args.find("client", indy, false)) {
134 LOG("client role chosen.");
135  server = false;
136  } else {
137 LOG("server role chosen.");
138  }
139 
140  internet_address addr;
141  addr.port = port;
142 
143  // check for a hostname on the command line.
144  astring hostname("local");
145  astring host_temp;
146  if (args.get_value("host", host_temp, false)) {
147  LOG(astring("using host: ") + host_temp);
148  hostname = host_temp;
149  } else LOG(astring("using host: ") + hostname);
150  strcpy(addr.hostname, hostname.s());
151 
152  if (server) {
153  astring key;
154  if (!args.get_value("key", key, false)) {
156  LOG("No keyword specified on command line.");
157  return;
158  }
159  astring root;
160  if (!args.get_value("root", root, false)) {
162  LOG("No transfer root was specified on the command line.");
163  return;
164  }
165 
166  LOG("starting comparison server");
167  _server_side = new cromp_server(cromp_server::any_address(port));
169  (file_transfer_tentacle::transfer_modes)(file_transfer_tentacle::ONLY_REPORT_DIFFS
170  | file_transfer_tentacle::COMPARE_SIZE_AND_TIME
171  | file_transfer_tentacle::COMPARE_CONTENT_SAMPLE));
172  new_tent->add_correspondence(key, root, REFRESH_INTERVAL);
173  _server_side->add_tentacle(new_tent);
174  _server_side->enable_servers(_encryption);
175  } else {
176  LOG("starting comparison client");
177  _client_side = new cromp_client(addr);
178  if (_encryption) _client_side->enable_encryption();
179 
180  outcome ret = _client_side->connect();
181  if (ret != cromp_client::OKAY)
182  non_continuable_error(class_name(), func, astring("failed to connect to "
183  "the server: ") + cromp_client::outcome_name(ret));
184 
186  (file_transfer_tentacle::transfer_modes)(file_transfer_tentacle::ONLY_REPORT_DIFFS
187  | file_transfer_tentacle::COMPARE_SIZE_AND_TIME
188  | file_transfer_tentacle::COMPARE_CONTENT_SAMPLE));
189  if (!args.get_value("source", _source, false)) {
191  LOG("No source path was specified on the command line.");
192  return;
193  }
194  if (!args.get_value("target", _target, false)) {
196  LOG("No target path was specified on the command line.");
197  return;
198  }
199 
200  string_array includes;
201  outcome regis = new_tent->register_file_transfer
202  (_client_side->entity(), _source, _target, includes);
203  if (regis != cromp_client::OKAY)
204  non_continuable_error(class_name(), func, "failed to register transfer");
205 
206  _client_side->add_tentacle(new_tent);
207  }
208 
209  _started_okay = true;
210 
211 }
212 
213 find_missing::~find_missing()
214 {
215  WHACK(_client_side);
216  WHACK(_server_side);
217 }
218 
220 {
221  astring name = filename(_global_argv[0]).basename().raw();
222  BASE_LOG(a_sprintf("%s usage:", name.s()));
223  BASE_LOG("");
225 This program can compare directory trees and report the files that are\n\
226 missing on the client's side compared to what the server is offering.\n\
227 The program can function as either the server side or the client side.\n\
228 The available flags are:\n\
229 \n\
230 %s --client --host srvname --port P --source key_path --target cli_dest\n\
231 \n\
232 The client side needs to know the server host (srvname) and the port where\n\
233 the server is listening for connections (P). The client will compare its\n\
234 local path (cli_dest) with the server's keyed path (key_path). The key\n\
235 path will begin with whatever keyword the server is offering, plus optional\n\
236 additional path components to retrieve less than the whole tree being\n\
237 served.\n\
238 \n\
239 \n\
240 %s --server --host srvname --port P --key keyname --root srv_path\n\
241 \n\
242 The server side needs to know what address and port to listen on (srvname\n\
243 and P). It will open a server there that provides a directory hierarchy\n\
244 starting at the root specified (srv_path). The directory tree will be known\n\
245 to clients as the key word (keyname), thus freeing the clients from needing\n\
246 to know absolute paths on the server.\n\
247 \n\
248 ", name.s(), name.s()));
249 
250  return 23;
251 }
252 
253 int find_missing::retrieve_info_from_server()
254 {
255  FUNCDEF("retrieve_info_from_server");
256  // prepare a client request
257  file_transfer_infoton initiate;
258  initiate._request = true;
259  initiate._command = file_transfer_infoton::TREE_COMPARISON;
260  initiate._src_root = _source;
261  initiate._dest_root = _target;
262  directory_tree target_area(_target);
263  target_area.calculate(false);
264  string_set includes;
265  initiate.package_tree_info(target_area, includes);
266  octopus_request_id cmd_id;
267  outcome start_ret = _client_side->submit(initiate, cmd_id);
268  if (start_ret != tentacle::OKAY)
269  non_continuable_error(class_name(), func, astring("failed to initiate "
270  " the transfer: ") + cromp_client::outcome_name(start_ret));
271 
272  infoton *start_reply_tmp = NULL_POINTER;
273 //hmmm: set timeout appropriate to the speed of the connection!
274  outcome first_receipt = _client_side->acquire(start_reply_tmp, cmd_id);
275  if (first_receipt != cromp_client::OKAY)
276  non_continuable_error(class_name(), func, astring("failed to receive response: ")
277  + cromp_client::outcome_name(start_ret));
278  file_transfer_infoton *start_reply = dynamic_cast<file_transfer_infoton *>
279  (start_reply_tmp);
280  if (!start_reply)
281  non_continuable_error(class_name(), func, "failed to cast starting infoton to "
282  "proper type");
283 
284  filename_list diffs;
285  byte_array pack_copy = start_reply->_packed_data;
286  if (!diffs.unpack(pack_copy))
287  non_continuable_error(class_name(), func, "could not unpack filename list!");
288  BASE_LOG("Differences found between local target and server's tree:");
290  for (int i = 0; i < diffs.elements(); i++) {
291  BASE_LOG(a_sprintf("%d: %s", i + 1, diffs[i]->raw().s()));
292  }
293 
294  return 0;
295 }
296 
297 int find_missing::execute()
298 {
299  FUNCDEF("execute");
300 
301  if (!_started_okay) return 32;
302 
303  time_stamp next_report(REPORTING_INTERVAL);
304 
305  while (true) {
306  // make sure we didn't see our exit condition.
307 
308  if (_server_side && !_server_side->clients() && _leave_when_no_clients
309  && _saw_clients) {
310  LOG("exiting now");
311  break;
312  }
313 
314  if (_client_side) return retrieve_info_from_server();
315 
316  if (time_stamp() > next_report) {
317  if (_server_side)
318  LOG(a_sprintf("There are %d clients.", _server_side->clients()));
319 //report about client side also.
320  next_report.reset(REPORTING_INTERVAL);
321  }
322 
323  time_control::sleep_ms(100);
324  }
325  return 0;
326 }
327 
329 
330 HOOPLE_MAIN(find_missing, )
331 
int print_instructions(bool good, const astring &program_name)
Definition: checker.cpp:45
The application_shell is a base object for console programs.
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
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition: astring.h:113
int convert(int default_value) const
Converts the string into a corresponding integer.
Definition: astring.cpp:757
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.
virtual bool unpack(basis::byte_array &packed_form)
Restores the packable from the "packed_form".
Provides operations commonly needed on file names.
Definition: filename.h:64
const basis::astring & raw() const
returns the astring that we're holding onto for the path.
Definition: filename.cpp:97
filename basename() const
returns the base of the filename; no directory.
Definition: filename.cpp:385
Base objects used by the file transfer tentacle to schedule transfers.
basis::abyte _command
one of the commands above.
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
Identifies requests made on an octopus by users.
Definition: entity_defs.h:114
this type of address describes a destination out on the internet.
char hostname[MAXIMUM_HOSTNAME_LENGTH]
int elements() const
the maximum number of elements currently allowed in this amorph.
Definition: amorph.h:66
An array of strings with some additional helpful methods.
Definition: string_array.h:32
A simple object that wraps a templated set of strings.
Definition: set.h:168
Represents a point in time relative to the operating system startup time.
Definition: time_stamp.h:38
#define SETUP_COMBO_LOGGER
a macro that retasks the program-wide logger as a combo_logger.
Definition: combo_logger.h:49
#define non_continuable_error(c, f, i)
an extra piece of information used, if available, in bounds_halt below.
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition: enhance_cpp.h:45
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
const int COMPARATOR_PORT
const int MAX_CHUNK
#define BASE_LOG(a)
#define LOG(a)
const int REPORTING_INTERVAL
const int REFRESH_INTERVAL
Provides macros that implement the 'main' program of an application.
#define HOOPLE_MAIN(obj_name, obj_args)
options that should work for most unix and linux apps.
Definition: hoople_main.h:61
Implements an application lock to ensure only one is running at once.
char ** _global_argv
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition: functions.h:121
const int SECOND_ms
Number of milliseconds in a second.
Definition: definitions.h:120
const int MINUTE_ms
Number of milliseconds in a minute.
Definition: definitions.h:121
const int KILOBYTE
Number of bytes in a kilobyte.
Definition: definitions.h:134
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.
Provides access to the operating system's socket methods.
Definition: base_address.h:26
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
#include <time.h>
Definition: earth_time.cpp:37