summaryrefslogtreecommitdiff
path: root/cpp/src/IcePatch2/Client.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/IcePatch2/Client.cpp')
-rw-r--r--cpp/src/IcePatch2/Client.cpp403
1 files changed, 403 insertions, 0 deletions
diff --git a/cpp/src/IcePatch2/Client.cpp b/cpp/src/IcePatch2/Client.cpp
new file mode 100644
index 00000000000..2d8f9d9352c
--- /dev/null
+++ b/cpp/src/IcePatch2/Client.cpp
@@ -0,0 +1,403 @@
+// **********************************************************************
+//
+// Copyright (c) 2004 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include <Ice/Application.h>
+#include <IcePatch2/FileServerI.h>
+#include <IcePatch2/Util.h>
+#include <fstream>
+
+using namespace std;
+using namespace Ice;
+using namespace IcePatch2;
+
+namespace IcePatch2
+{
+
+class Client : public Application
+{
+public:
+
+ void usage();
+ virtual int run(int, char*[]);
+
+private:
+
+ void usage(const std::string&);
+};
+
+};
+
+int
+IcePatch2::Client::run(int argc, char* argv[])
+{
+ bool thorough = false;
+ bool dry = false;
+ string dataDir;
+
+ for(int i = 1; i < argc; ++i)
+ {
+ if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
+ {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ else if(strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0)
+ {
+ cout << ICE_STRING_VERSION << endl;
+ return EXIT_FAILURE;
+ }
+ else if(strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--thorough") == 0)
+ {
+ thorough = true;
+ }
+ else if(strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dry") == 0)
+ {
+ dry = true;
+ }
+ else if(argv[i][0] == '-')
+ {
+ cerr << argv[0] << ": unknown option `" << argv[i] << "'" << endl;
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ if(dataDir.empty())
+ {
+ dataDir = argv[i];
+ }
+ else
+ {
+ cerr << argv[0] << ": too many arguments" << endl;
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ PropertiesPtr properties = communicator()->getProperties();
+
+ if(dataDir.empty())
+ {
+ string dataDir = properties->getProperty("IcePatch2.Directory");
+ if(dataDir.empty())
+ {
+ cerr << argv[0] << ": no data directory specified" << endl;
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ Int chunk = properties->getPropertyAsIntWithDefault("IcePatch2.ChunkSize", 100000);
+ if(chunk < 1)
+ {
+ chunk = 1;
+ }
+
+ try
+ {
+ FileInfoSeq infoSeq;
+
+#ifdef _WIN32
+ char cwd[_MAX_PATH];
+ if(_getcwd(cwd, _MAX_PATH) == NULL)
+#else
+ char cwd[PATH_MAX];
+ if(getcwd(cwd, PATH_MAX) == NULL)
+#endif
+ {
+ throw string("cannot get the current directory: ") + strerror(errno);
+ }
+
+ dataDir = normalize(string(cwd) + '/' + dataDir);
+
+ if(chdir(dataDir.c_str()) == -1)
+ {
+ throw "cannot change directory to `" + dataDir + "': " + strerror(errno);
+ }
+
+ if(thorough)
+ {
+ getFileInfoSeq(".", infoSeq, false, false);
+ }
+ else
+ {
+ loadFileInfoSeq(dataDir + ".sum", infoSeq);
+ }
+
+ sort(infoSeq.begin(), infoSeq.end(), FileInfoCompare());
+
+ const char* endpointsProperty = "IcePatch2.Endpoints";
+ const string endpoints = properties->getProperty(endpointsProperty);
+ if(endpoints.empty())
+ {
+ cerr << argv[0] << ": property `" << endpointsProperty << "' is not set" << endl;
+ return EXIT_FAILURE;
+ }
+
+ const char* idProperty = "IcePatch2.Identity";
+ const Identity id = stringToIdentity(properties->getPropertyWithDefault(idProperty, "IcePatch2/server"));
+
+ ObjectPrx fileServerBase = communicator()->stringToProxy(identityToString(id) + ':' + endpoints);
+ FileServerPrx fileServer = FileServerPrx::checkedCast(fileServerBase);
+ if(!fileServer)
+ {
+ cerr << argv[0] << ": proxy `" << identityToString(id) << ':' << endpoints << "' is not a file server."
+ << endl;
+ return EXIT_FAILURE;
+ }
+
+ FileTree0 tree0;
+ getFileTree0(infoSeq, tree0);
+
+ FileInfoSeq removeFiles;
+ FileInfoSeq updateFiles;
+
+ if(tree0.checksum != fileServer->getChecksum0())
+ {
+ ByteSeqSeq checksum1Seq = fileServer->getChecksum1Seq();
+ if(checksum1Seq.size() != 256)
+ {
+ cerr << argv[0] << ": server returned illegal value" << endl;
+ return EXIT_FAILURE;
+ }
+
+ for(int node0 = 0; node0 < 256; ++node0)
+ {
+ if(tree0.nodes[node0].checksum != checksum1Seq[node0])
+ {
+ ByteSeqSeq checksum2Seq = fileServer->getChecksum2Seq(node0);
+ if(checksum2Seq.size() != 256)
+ {
+ cerr << argv[0] << ": server returned illegal value" << endl;
+ return EXIT_FAILURE;
+ }
+
+ for(int node1 = 0; node1 < 256; ++node1)
+ {
+ if(tree0.nodes[node0].nodes[node1].checksum != checksum2Seq[node1])
+ {
+ FileInfoSeq fileSeq = fileServer->getFileInfoSeq(node0, node1);
+ sort(fileSeq.begin(), fileSeq.end(), FileInfoCompare());
+
+ set_difference(tree0.nodes[node0].nodes[node1].files.begin(),
+ tree0.nodes[node0].nodes[node1].files.end(),
+ fileSeq.begin(),
+ fileSeq.end(),
+ back_inserter(removeFiles),
+ FileInfoCompare());
+
+ set_difference(fileSeq.begin(),
+ fileSeq.end(),
+ tree0.nodes[node0].nodes[node1].files.begin(),
+ tree0.nodes[node0].nodes[node1].files.end(),
+ back_inserter(updateFiles),
+ FileInfoCompare());
+ }
+ }
+ }
+ }
+ }
+
+ sort(removeFiles.begin(), removeFiles.end(), FileInfoCompare());
+ sort(updateFiles.begin(), updateFiles.end(), FileInfoCompare());
+
+ //
+ // We remove the summary file, so that if something goes wrong
+ // during patching, the next run has to be done with the
+ // thorough option. Otherwise there could be inconsistencies
+ // between the summary file and the real content.
+ //
+ if(!dry)
+ {
+ removeRecursive(dataDir + ".sum");
+ }
+
+ FileInfoSeq::const_iterator p;
+
+ p = removeFiles.begin();
+ while(p != removeFiles.end())
+ {
+ cout << "remove: " << p->path << endl;
+
+ if(!dry)
+ {
+ try
+ {
+ removeRecursive(p->path);
+ }
+ catch(...)
+ {
+ }
+ }
+
+ string dir = p->path + '/';
+
+ do
+ {
+ ++p;
+ }
+ while(p->path.compare(0, dir.size(), dir) == 0);
+ }
+
+ Long total = 0;
+ Long updated = 0;
+
+ for(p = updateFiles.begin(); p != updateFiles.end(); ++p)
+ {
+ if(p->size > 0) // Regular, non-empty file?
+ {
+ total += p->size;
+ }
+ }
+
+ for(p = updateFiles.begin(); p != updateFiles.end(); ++p)
+ {
+ cout << "update: " << p->path << ' ' << flush;
+
+ if(p->size < 0) // Directory?
+ {
+ if(!dry)
+ {
+ createDirectoryRecursive(p->path);
+ }
+ }
+ else // Regular file.
+ {
+ if(!dry)
+ {
+ string dir = getDirname(p->path);
+ if(!dir.empty())
+ {
+ createDirectoryRecursive(dir);
+ }
+ }
+
+ Int pos = 0;
+ string progress;
+
+ while(pos < p->size)
+ {
+ ByteSeq bytes;
+
+ try
+ {
+ bytes = fileServer->getFileCompressed(p->path, pos, chunk);
+ }
+ catch(const FileAccessException& ex)
+ {
+ cerr << argv[0] << ": server error for `" << p->path << "':" << ex.reason << endl;
+ return EXIT_FAILURE;
+ }
+
+ if(bytes.empty())
+ {
+ cerr << argv[0] << ": size mismatch for `" << p->path << "'" << endl;
+ return EXIT_FAILURE;
+ }
+
+ pos += bytes.size();
+ updated += bytes.size();
+
+ for(unsigned int i = 0; i < progress.size(); ++i)
+ {
+ cout << '\b';
+ }
+ ostringstream s;
+ s << pos << '/' << p->size << " (" << updated << '/' << total << ')';
+ progress = s.str();
+ cout << progress << flush;
+ }
+
+/*
+ if(!dry)
+ {
+ try
+ {
+ removeRecursive(p->path);
+ }
+ catch(...)
+ {
+ }
+
+ ofstream os(p->path.c_str());
+ if(!os)
+ {
+ throw "cannot open `" + p->path + "' for writing: " + strerror(errno);
+ }
+ }
+*/
+ }
+
+ cout << endl;
+ }
+
+ //
+ // After a complete and successful patch, we write a new
+ // summary file.
+ //
+ if(!dry)
+ {
+ FileInfoSeq newInfoSeq1;
+ newInfoSeq1.reserve(infoSeq.size());
+
+ set_difference(infoSeq.begin(),
+ infoSeq.end(),
+ removeFiles.begin(),
+ removeFiles.end(),
+ back_inserter(newInfoSeq1),
+ FileInfoCompare());
+
+ FileInfoSeq newInfoSeq2;
+ newInfoSeq2.reserve(newInfoSeq1.size() + updateFiles.size());
+
+ set_union(newInfoSeq1.begin(),
+ newInfoSeq1.end(),
+ updateFiles.begin(),
+ updateFiles.end(),
+ back_inserter(newInfoSeq2),
+ FileInfoCompare());
+
+ saveFileInfoSeq(dataDir + ".sum", newInfoSeq2);
+ }
+ }
+ catch(const string& ex)
+ {
+ cerr << argv[0] << ": " << ex << endl;
+ return EXIT_FAILURE;
+ }
+ catch(const char* ex)
+ {
+ cerr << argv[0] << ": " << ex << endl;
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+void
+IcePatch2::Client::usage(const string& appName)
+{
+ string options =
+ "Options:\n"
+ "-h, --help Show this message.\n"
+ "-v, --version Display the Ice version.\n"
+ "-t, --thorough Recalculate all checksums.\n"
+ "-d, --dry Don't update, do a dry run only.";
+
+ cerr << "Usage: " << appName << " [options] [DIR]" << endl;
+ cerr << options << endl;
+}
+
+int
+main(int argc, char* argv[])
+{
+ Client app;
+ return app.main(argc, argv);
+}