diff options
Diffstat (limited to 'lib')
31 files changed, 968 insertions, 750 deletions
diff --git a/lib/backupclient/BackupDaemonConfigVerify.cpp b/lib/backupclient/BackupDaemonConfigVerify.cpp index 865ee413..e28e26ff 100644 --- a/lib/backupclient/BackupDaemonConfigVerify.cpp +++ b/lib/backupclient/BackupDaemonConfigVerify.cpp @@ -8,10 +8,11 @@ // -------------------------------------------------------------------------- #include "Box.h" + +#include "BackupConstants.h" #include "BackupDaemonConfigVerify.h" -#include "Daemon.h" #include "BoxPortsAndFiles.h" -#include "BackupConstants.h" +#include "Daemon.h" #include "MemLeakFindOn.h" @@ -148,7 +149,9 @@ static const ConfigurationVerifyKey verifyrootkeys[] = ConfigTest_IsUint32), ConfigurationVerifyKey("CertificateFile", 0), ConfigurationVerifyKey("PrivateKeyFile", 0), - ConfigurationVerifyKey("TrustedCAsFile", ConfigTest_LastEntry), + ConfigurationVerifyKey("TrustedCAsFile", 0), + ConfigurationVerifyKey("SSLSecurityLevel", ConfigTest_IsInt | ConfigTest_LastEntry, + BOX_DEFAULT_SSL_SECURITY_LEVEL), }; const ConfigurationVerify BackupDaemonConfigVerify = diff --git a/lib/backupstore/BackupStoreCheck.cpp b/lib/backupstore/BackupStoreCheck.cpp index b53ebf6d..37e45b03 100644 --- a/lib/backupstore/BackupStoreCheck.cpp +++ b/lib/backupstore/BackupStoreCheck.cpp @@ -174,9 +174,17 @@ void BackupStoreCheck::Check() try { + // We should be able to load a reference to the old refcount database + // (read-only) at the same time that we have a reference to the new one + // (temporary) open but not yet committed. std::auto_ptr<BackupStoreRefCountDatabase> apOldRefs = BackupStoreRefCountDatabase::Load(account, false); - mNumberErrorsFound += mapNewRefs->ReportChangesTo(*apOldRefs); + + // If we have created a new lost+found directory (and thus allocated it a nonzero + // object ID) then it's not surprising that the previous refcount DB did not have + // a reference to this directory, and not an error, so ignore it. + mNumberErrorsFound += mapNewRefs->ReportChangesTo(*apOldRefs, + mLostAndFoundDirectoryID); // ignore_object_id } catch(BoxException &e) { diff --git a/lib/backupstore/BackupStoreCheck2.cpp b/lib/backupstore/BackupStoreCheck2.cpp index 13831a09..3775cc4a 100644 --- a/lib/backupstore/BackupStoreCheck2.cpp +++ b/lib/backupstore/BackupStoreCheck2.cpp @@ -429,7 +429,7 @@ int64_t BackupStoreCheck::GetLostAndFoundDirID() if(!mFixErrors) { // The result will never be used anyway if errors aren't being fixed - return 1; + return 0; } // Load up the root directory diff --git a/lib/backupstore/BackupStoreRefCountDatabase.cpp b/lib/backupstore/BackupStoreRefCountDatabase.cpp index b2ea1abd..86da0943 100644 --- a/lib/backupstore/BackupStoreRefCountDatabase.cpp +++ b/lib/backupstore/BackupStoreRefCountDatabase.cpp @@ -347,17 +347,22 @@ bool BackupStoreRefCountDatabase::RemoveReference(int64_t ObjectID) return (refcount > 0); } -int BackupStoreRefCountDatabase::ReportChangesTo(BackupStoreRefCountDatabase& rOldRefs) +int BackupStoreRefCountDatabase::ReportChangesTo(BackupStoreRefCountDatabase& rOldRefs, + int64_t ignore_object_id) { int ErrorCount = 0; int64_t MaxOldObjectId = rOldRefs.GetLastObjectIDUsed(); int64_t MaxNewObjectId = GetLastObjectIDUsed(); for (int64_t ObjectID = BACKUPSTORE_ROOT_DIRECTORY_ID; - ObjectID < std::max(MaxOldObjectId, MaxNewObjectId); + ObjectID <= std::max(MaxOldObjectId, MaxNewObjectId); ObjectID++) { - typedef BackupStoreRefCountDatabase::refcount_t refcount_t; + if(ObjectID == ignore_object_id) + { + continue; + } + refcount_t OldRefs = (ObjectID <= MaxOldObjectId) ? rOldRefs.GetRefCount(ObjectID) : 0; refcount_t NewRefs = (ObjectID <= MaxNewObjectId) ? diff --git a/lib/backupstore/BackupStoreRefCountDatabase.h b/lib/backupstore/BackupStoreRefCountDatabase.h index 915653a4..6c16516e 100644 --- a/lib/backupstore/BackupStoreRefCountDatabase.h +++ b/lib/backupstore/BackupStoreRefCountDatabase.h @@ -87,7 +87,8 @@ public: void AddReference(int64_t ObjectID); // RemoveReference returns false if refcount drops to zero bool RemoveReference(int64_t ObjectID); - int ReportChangesTo(BackupStoreRefCountDatabase& rOldRefs); + int ReportChangesTo(BackupStoreRefCountDatabase& rOldRefs, + int64_t ignore_object_id = 0); private: static std::string GetFilename(const BackupStoreAccountDatabase::Entry& diff --git a/lib/backupstore/StoreTestUtils.cpp b/lib/backupstore/StoreTestUtils.cpp index 2b773cb1..902552d5 100644 --- a/lib/backupstore/StoreTestUtils.cpp +++ b/lib/backupstore/StoreTestUtils.cpp @@ -54,7 +54,7 @@ bool delete_account() } std::vector<uint32_t> ExpectedRefCounts; -int bbstored_pid = 0, bbackupd_pid = 0; +int bbstored_pid = 0, bbackupd_pid = 0, s3simulator_pid = 0; void set_refcount(int64_t ObjectID, uint32_t RefCount) { @@ -266,11 +266,11 @@ bool check_reference_counts() return counts_ok; } -bool StartServer() +bool StartServer(const std::string& daemon_args) { - bbstored_pid = StartDaemon(bbstored_pid, - BBSTORED " " + bbstored_args + " testfiles/bbstored.conf", - "testfiles/bbstored.pid"); + const std::string& daemon_args_final(daemon_args.size() ? daemon_args : bbstored_args); + bbstored_pid = StartDaemon(bbstored_pid, BBSTORED " " + daemon_args_final + + " testfiles/bbstored.conf", "testfiles/bbstored.pid"); return bbstored_pid != 0; } @@ -282,11 +282,11 @@ bool StopServer(bool wait_for_process) return result; } -bool StartClient(const std::string& bbackupd_conf_file) +bool StartClient(const std::string& bbackupd_conf_file, const std::string& daemon_args) { - bbackupd_pid = StartDaemon(bbackupd_pid, - BBACKUPD " " + bbackupd_args + " " + bbackupd_conf_file, - "testfiles/bbackupd.pid"); + const std::string& daemon_args_final(daemon_args.size() ? daemon_args : bbackupd_args); + bbackupd_pid = StartDaemon(bbackupd_pid, BBACKUPD " " + daemon_args_final + " -c " + + bbackupd_conf_file, "testfiles/bbackupd.pid"); return bbackupd_pid != 0; } @@ -298,3 +298,40 @@ bool StopClient(bool wait_for_process) return result; } +bool StartSimulator() +{ + s3simulator_pid = StartDaemon(s3simulator_pid, + "../../bin/s3simulator/s3simulator " + bbstored_args + + " testfiles/s3simulator.conf", "testfiles/s3simulator.pid"); + return s3simulator_pid != 0; +} + +bool StopSimulator() +{ + bool result = StopDaemon(s3simulator_pid, "testfiles/s3simulator.pid", + "s3simulator.memleaks", true); + s3simulator_pid = 0; + return result; +} + +bool kill_running_daemons() +{ + bool success = true; + + if(FileExists("testfiles/bbstored.pid")) + { + TEST_THAT_OR(KillServer("testfiles/bbstored.pid", true), success = false); + } + + if(FileExists("testfiles/bbackupd.pid")) + { + TEST_THAT_OR(KillServer("testfiles/bbackupd.pid", true), success = false); + } + + if(FileExists("testfiles/s3simulator.pid")) + { + TEST_THAT_OR(KillServer("testfiles/s3simulator.pid", true), success = false); + } + + return success; +} diff --git a/lib/backupstore/StoreTestUtils.h b/lib/backupstore/StoreTestUtils.h index b3faebb5..794fbd44 100644 --- a/lib/backupstore/StoreTestUtils.h +++ b/lib/backupstore/StoreTestUtils.h @@ -66,17 +66,24 @@ bool run_housekeeping_and_check_account(); bool check_reference_counts(); //! Starts the bbstored test server running, which must not already be running. -bool StartServer(); +bool StartServer(const std::string& daemon_args = ""); //! Stops the currently running bbstored test server. bool StopServer(bool wait_for_process = false); //! Starts the bbackupd client running, which must not already be running. -bool StartClient(const std::string& bbackupd_conf_file = "testfiles/bbackupd.conf"); +bool StartClient(const std::string& bbackupd_conf_file = "testfiles/bbackupd.conf", + const std::string& daemon_args = ""); //! Stops the currently running bbackupd client. bool StopClient(bool wait_for_process = false); +bool StartSimulator(); + +bool StopSimulator(); + +bool kill_running_daemons(); + //! Creates the standard test account, for example after delete_account(). bool create_account(int soft, int hard); diff --git a/lib/bbackupd/BackupDaemon.cpp b/lib/bbackupd/BackupDaemon.cpp index 996c1919..d75aa381 100644 --- a/lib/bbackupd/BackupDaemon.cpp +++ b/lib/bbackupd/BackupDaemon.cpp @@ -579,8 +579,9 @@ void BackupDaemon::InitCrypto() std::string certFile(conf.GetKeyValue("CertificateFile")); std::string keyFile(conf.GetKeyValue("PrivateKeyFile")); std::string caFile(conf.GetKeyValue("TrustedCAsFile")); + int ssl_security_level(conf.GetKeyValueInt("SSLSecurityLevel")); mTlsContext.Initialise(false /* as client */, certFile.c_str(), - keyFile.c_str(), caFile.c_str()); + keyFile.c_str(), caFile.c_str(), ssl_security_level); // Set up the keys for various things BackupClientCryptoKeys_Setup(conf.GetKeyValue("KeysFile")); diff --git a/lib/common/BoxException.h b/lib/common/BoxException.h index 361f04e8..e3a2268a 100644 --- a/lib/common/BoxException.h +++ b/lib/common/BoxException.h @@ -34,6 +34,9 @@ public: private: }; +#define EXCEPTION_IS_TYPE(exception_obj, type, subtype) \ + (exception_obj.GetType() == type::ExceptionType && \ + exception_obj.GetSubType() == type::subtype) #endif // BOXEXCEPTION__H diff --git a/lib/common/BoxPortsAndFiles.h.in b/lib/common/BoxPortsAndFiles.h.in index 8978cd4c..f63e614b 100644 --- a/lib/common/BoxPortsAndFiles.h.in +++ b/lib/common/BoxPortsAndFiles.h.in @@ -22,7 +22,7 @@ // default security level if SSLSecurityLevel is not specified: see // https://github.com/boxbackup/boxbackup/wiki/WeakSSLCertificates -const int BOX_DEFAULT_SSL_SECURITY_LEVEL = 1; +const int BOX_DEFAULT_SSL_SECURITY_LEVEL = -1; // configuration file paths #ifdef WIN32 diff --git a/lib/common/Configuration.cpp b/lib/common/Configuration.cpp index 8ce8d389..9860864a 100644 --- a/lib/common/Configuration.cpp +++ b/lib/common/Configuration.cpp @@ -470,6 +470,16 @@ int Configuration::GetKeyValueInt(const std::string& rKeyName) const } +int Configuration::GetKeyValueInt(const std::string& rKeyName, int default_value) const +{ + if(!KeyExists(rKeyName)) + { + return default_value; + } + return GetKeyValueInt(rKeyName); +} + + // -------------------------------------------------------------------------- // // Function @@ -778,8 +788,7 @@ bool Configuration::Verify(const ConfigurationVerify &rVerify, } else if(pvkey->HasDefaultValue()) { - mKeys[pvkey->Name()] = - pvkey->DefaultValue(); + mKeys[pvkey->Name()] = pvkey->DefaultValue(); } } @@ -922,5 +931,3 @@ bool Configuration::Verify(const ConfigurationVerify &rVerify, return ok; } - - diff --git a/lib/common/Configuration.h b/lib/common/Configuration.h index e6498e80..f9b5eb77 100644 --- a/lib/common/Configuration.h +++ b/lib/common/Configuration.h @@ -122,6 +122,7 @@ public: bool KeyExists(const std::string& rKeyName) const; const std::string &GetKeyValue(const std::string& rKeyName) const; int GetKeyValueInt(const std::string& rKeyName) const; + int GetKeyValueInt(const std::string& rKeyName, int default_value) const; uint32_t GetKeyValueUint32(const std::string& rKeyName) const; bool GetKeyValueBool(const std::string& rKeyName) const; std::vector<std::string> GetKeyNames() const; diff --git a/lib/common/FileStream.h b/lib/common/FileStream.h index 1426d8a2..26442212 100644 --- a/lib/common/FileStream.h +++ b/lib/common/FileStream.h @@ -42,6 +42,7 @@ public: virtual pos_type BytesLeftToRead(); virtual void Write(const void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + using IOStream::Write; virtual pos_type GetPosition() const; virtual void Seek(IOStream::pos_type Offset, int SeekType); virtual void Close(); diff --git a/lib/common/Test.cpp b/lib/common/Test.cpp index 2c51cd61..3d1620a1 100644 --- a/lib/common/Test.cpp +++ b/lib/common/Test.cpp @@ -28,7 +28,7 @@ int num_tests_selected = 0; int num_failures = 0; -int old_failure_count = 0; +static int old_failure_count = 0; // do not expose! int first_fail_line; std::string original_working_dir; std::string first_fail_file; @@ -97,6 +97,8 @@ bool setUp(const char* function_name) if(StartsWith("TestDir", filename) || StartsWith("0_", filename) || filename == "accounts.txt" || + filename == "bbackupd-data" || + filename == "ca" || StartsWith("file", filename) || StartsWith("notifyran", filename) || StartsWith("notifyscript.tag", filename) || @@ -105,7 +107,9 @@ bool setUp(const char* function_name) filename == "syncallowscript.control" || StartsWith("syncallowscript.notifyran.", filename) || filename == "test2.downloaded" || - EndsWith("testfile", filename)) + EndsWith("testfile", filename) || + filename == "tmp" || + EndsWith(".qdbm", filename)) { std::string filepath = std::string("testfiles\\") + filename; @@ -201,7 +205,8 @@ bool setUp(const char* function_name) "testfiles/restore* testfiles/bbackupd-data " "testfiles/syncallowscript.control " "testfiles/syncallowscript.notifyran.* " - "testfiles/test2.downloaded" + "testfiles/test2.downloaded " + "testfiles/tmp " ) == 0); TEST_THAT_THROWONFAIL(system("touch testfiles/accounts.txt") == 0); #endif @@ -382,133 +387,6 @@ int ReadPidFile(const char *pidFile) return pid; } -int LaunchServer(const std::string& rCommandLine, const char *pidFile) -{ - BOX_INFO("Starting server: " << rCommandLine); - -#ifdef WIN32 - - PROCESS_INFORMATION procInfo; - - STARTUPINFO startInfo; - startInfo.cb = sizeof(startInfo); - startInfo.lpReserved = NULL; - startInfo.lpDesktop = NULL; - startInfo.lpTitle = NULL; - startInfo.dwFlags = 0; - startInfo.cbReserved2 = 0; - startInfo.lpReserved2 = NULL; - - std::string cmd = ConvertPaths(rCommandLine); - CHAR* tempCmd = strdup(cmd.c_str()); - - DWORD result = CreateProcess - ( - NULL, // lpApplicationName, naughty! - tempCmd, // lpCommandLine - NULL, // lpProcessAttributes - NULL, // lpThreadAttributes - false, // bInheritHandles - 0, // dwCreationFlags - NULL, // lpEnvironment - NULL, // lpCurrentDirectory - &startInfo, // lpStartupInfo - &procInfo // lpProcessInformation - ); - - free(tempCmd); - - TEST_THAT_OR(result != 0, - BOX_LOG_WIN_ERROR("Launch failed: " << rCommandLine); - return -1; - ); - - CloseHandle(procInfo.hProcess); - CloseHandle(procInfo.hThread); - - return WaitForServerStartup(pidFile, (int)procInfo.dwProcessId); - -#else // !WIN32 - - TEST_THAT_OR(RunCommand(rCommandLine) == 0, - TEST_FAIL_WITH_MESSAGE("Failed to start server: " << rCommandLine); - return -1; - ) - - return WaitForServerStartup(pidFile, 0); - -#endif // WIN32 -} - -int WaitForServerStartup(const char *pidFile, int pidIfKnown) -{ - #ifdef WIN32 - if (pidFile == NULL) - { - return pidIfKnown; - } - #else - // on other platforms there is no other way to get - // the PID, so a NULL pidFile doesn't make sense. - ASSERT(pidFile != NULL); - #endif - - // time for it to start up - BOX_TRACE("Waiting for server to start"); - - for (int i = 0; i < 15; i++) - { - if (TestFileNotEmpty(pidFile)) - { - break; - } - - if (pidIfKnown && !ServerIsAlive(pidIfKnown)) - { - break; - } - - ::sleep(1); - } - - // on Win32 we can check whether the process is alive - // without even checking the PID file - - if (pidIfKnown && !ServerIsAlive(pidIfKnown)) - { - TEST_FAIL_WITH_MESSAGE("Server died!"); - return -1; - } - - if (!TestFileNotEmpty(pidFile)) - { - TEST_FAIL_WITH_MESSAGE("Server didn't save PID file"); - return -1; - } - - BOX_TRACE("Server started"); - - // wait a second for the pid to be written to the file - ::sleep(1); - - // read pid file - int pid = ReadPidFile(pidFile); - - // On Win32 we can check whether the PID in the pidFile matches - // the one returned by the system, which it always should. - - if (pidIfKnown && pid != pidIfKnown) - { - BOX_ERROR("Server wrote wrong pid to file (" << pidFile << - "): expected " << pidIfKnown << " but found " << - pid); - TEST_FAIL_WITH_MESSAGE("Server wrote wrong pid to file"); - return -1; - } - - return pid; -} - void TestRemoteProcessMemLeaksFunc(const char *filename, const char* file, int line) { diff --git a/lib/common/Test.h b/lib/common/Test.h index 32f8220d..35443c29 100644 --- a/lib/common/Test.h +++ b/lib/common/Test.h @@ -34,10 +34,13 @@ #define TEST_RETURN_COMMAND(actual, expected, command) TEST_EQUAL_LINE((expected << 8), actual, command); #endif +#define DEFAULT_BBSTORED_CONFIG_FILE "testfiles/bbstored.conf" +#define DEFAULT_BBACKUPD_CONFIG_FILE "testfiles/bbackupd.conf" +#define DEFAULT_S3_CACHE_DIR "testfiles/bbackupd-cache" + extern int num_failures; extern int first_fail_line; extern int num_tests_selected; -extern int old_failure_count; extern std::string first_fail_file; extern std::string bbackupd_args, bbstored_args, bbackupquery_args, test_args; extern std::list<std::string> run_only_named_tests; @@ -217,6 +220,7 @@ int finish_test_suite(); bool TestFileExists(const char *Filename); bool TestDirExists(const char *Filename); +bool TestFileNotEmpty(const char *Filename); // -1 if doesn't exist int TestGetFileSize(const std::string& Filename); @@ -224,8 +228,6 @@ std::string ConvertPaths(const std::string& rOriginal); int RunCommand(const std::string& rCommandLine); bool ServerIsAlive(int pid); int ReadPidFile(const char *pidFile); -int LaunchServer(const std::string& rCommandLine, const char *pidFile); -int WaitForServerStartup(const char *pidFile, int pidIfKnown); #define TestRemoteProcessMemLeaks(filename) \ TestRemoteProcessMemLeaksFunc(filename, __FILE__, __LINE__) diff --git a/lib/server/ConnectionException.txt b/lib/server/ConnectionException.txt index 7dcaadeb..74390853 100644 --- a/lib/server/ConnectionException.txt +++ b/lib/server/ConnectionException.txt @@ -15,6 +15,7 @@ TLSNoPeerCertificate 36 TLSPeerCertificateInvalid 37 Check certification process TLSClosedWhenWriting 38 TLSHandshakeTimedOut 39 +TLSPeerWeakCertificate 40 The peer's certificate is too weak for the current SSL Security Level, see https://github.com/boxbackup/boxbackup/wiki/WeakSSLCertificates Protocol_Timeout 41 Probably a network issue between client and server. Protocol_ObjTooBig 42 Protocol_BadCommandRecieved 44 diff --git a/lib/server/Daemon.cpp b/lib/server/Daemon.cpp index d3c8441f..76141b6f 100644 --- a/lib/server/Daemon.cpp +++ b/lib/server/Daemon.cpp @@ -42,6 +42,7 @@ #include "autogen_ConnectionException.h" #include "autogen_ServerException.h" +#include "BoxPortsAndFiles.h" #include "Configuration.h" #include "Daemon.h" #include "FileModificationTime.h" @@ -52,6 +53,9 @@ #include "MemLeakFindOn.h" +const ConfigurationVerifyKey ssl_security_level_key("SSLSecurityLevel", + ConfigTest_IsInt | ConfigTest_LastEntry, BOX_DEFAULT_SSL_SECURITY_LEVEL); + Daemon *Daemon::spDaemon = 0; diff --git a/lib/server/Daemon.h b/lib/server/Daemon.h index b5384918..60ce4507 100644 --- a/lib/server/Daemon.h +++ b/lib/server/Daemon.h @@ -121,5 +121,7 @@ private: ConfigurationVerifyKey("LogFacility", 0), \ ConfigurationVerifyKey("User", ConfigTest_LastEntry) +extern const ConfigurationVerifyKey ssl_security_level_key; + #endif // DAEMON__H diff --git a/lib/server/ServerControl.cpp b/lib/server/ServerControl.cpp index f1a718df..b6cadab3 100644 --- a/lib/server/ServerControl.cpp +++ b/lib/server/ServerControl.cpp @@ -18,7 +18,9 @@ #include "BoxTime.h" #include "IOStreamGetLine.h" #include "ServerControl.h" +#include "SocketStream.h" #include "Test.h" +#include "autogen_ServerException.h" #ifdef WIN32 @@ -227,7 +229,7 @@ bool KillServer(int pid, bool WaitForProcess) return !ServerIsAlive(pid); } -bool KillServer(std::string pid_file, bool WaitForProcess) +bool KillServer(const std::string& pid_file, bool WaitForProcess) { FileStream fs(pid_file); IOStreamGetLine getline(fs); @@ -251,11 +253,174 @@ bool KillServer(std::string pid_file, bool WaitForProcess) return status; } -int StartDaemon(int current_pid, const std::string& cmd_line, const char* pid_file) +int LaunchServer(const std::string& rCommandLine, const char *pidFile, int port, + const std::string& socket_path) +{ + BOX_INFO("Starting server: " << rCommandLine); + +#ifdef WIN32 + + // Use a Windows "Job Object" as a container for all our child + // processes. The test runner will create this job object when + // it starts, and close the handle (killing any running daemons) + // when it exits. This is the best way to avoid daemons hanging + // around and causing subsequent tests to fail, and/or the test + // runner to hang waiting for a daemon that will never terminate. + + PROCESS_INFORMATION procInfo; + + STARTUPINFO startInfo; + startInfo.cb = sizeof(startInfo); + startInfo.lpReserved = NULL; + startInfo.lpDesktop = NULL; + startInfo.lpTitle = NULL; + startInfo.dwFlags = 0; + startInfo.cbReserved2 = 0; + startInfo.lpReserved2 = NULL; + + std::string cmd = ConvertPaths(rCommandLine); + CHAR* tempCmd = strdup(cmd.c_str()); + + DWORD result = CreateProcess + ( + NULL, // lpApplicationName, naughty! + tempCmd, // lpCommandLine + NULL, // lpProcessAttributes + NULL, // lpThreadAttributes + false, // bInheritHandles + 0, // dwCreationFlags + NULL, // lpEnvironment + NULL, // lpCurrentDirectory + &startInfo, // lpStartupInfo + &procInfo // lpProcessInformation + ); + + free(tempCmd); + + TEST_THAT_OR(result != 0, + BOX_LOG_WIN_ERROR("Failed to CreateProcess: " << rCommandLine); + return -1; + ); + + CloseHandle(procInfo.hProcess); + CloseHandle(procInfo.hThread); + + return WaitForServerStartup(pidFile, (int)procInfo.dwProcessId, port, socket_path); + +#else // !WIN32 + + TEST_THAT_OR(RunCommand(rCommandLine) == 0, + TEST_FAIL_WITH_MESSAGE("Failed to start server: " << rCommandLine); + return -1; + ) + + return WaitForServerStartup(pidFile, 0, port, socket_path); + +#endif // WIN32 +} + +int WaitForServerStartup(const char *pidFile, int pidIfKnown, int port, + const std::string& socket_path) +{ +#ifdef WIN32 + if(pidFile == NULL && port == 0 && socket_path == "") + { + return pidIfKnown; + } +#else + // On other platforms there is no other way to get the PID, so a NULL pidFile doesn't + // make sense. + ASSERT(pidFile != NULL); +#endif + + // time for it to start up + BOX_TRACE("Waiting for server to start"); + + for (int i = 150; i >= 0; i--) + { + if(i == 0) + { + // ran out of time waiting + TEST_FAIL_WITH_MESSAGE("Server didn't start within expected time"); + return -1; + } + + ShortSleep(MilliSecondsToBoxTime(100), false); + + if(!TestFileNotEmpty(pidFile)) + { + // Hasn't written a complete PID file yet, go round again + continue; + } + + // Once we know what PID the process has/had, we can check if it has died during or + // shortly after startup: + if (pidIfKnown && !ServerIsAlive(pidIfKnown)) + { + TEST_FAIL_WITH_MESSAGE("Server died!"); + return -1; + } + + if(port != 0 || socket_path != "") + { + try + { + if(port != 0) + { + SocketStream conn; + conn.Open(Socket::TypeINET, "localhost", port); + } + + if(socket_path != "") + { + SocketStream conn; + conn.Open(Socket::TypeUNIX, socket_path); + } + } + catch(ServerException &e) + { + if(EXCEPTION_IS_TYPE(e, ServerException, SocketOpenError)) + { + // not listening on port, go round again + continue; + } + else + { + // something bad happened, break + throw; + } + } + } + + // All tests that we can do have passed, looks good! + break; + } + + BOX_TRACE("Server started"); + + // read pid file + int pid = ReadPidFile(pidFile); + + // On Win32 we can check whether the PID in the pidFile matches + // the one returned by the system, which it always should. + if (pidIfKnown && pid != pidIfKnown) + { + BOX_ERROR("Server wrote wrong pid to file (" << pidFile << + "): expected " << pidIfKnown << " but found " << + pid); + TEST_FAIL_WITH_MESSAGE("Server wrote wrong pid to file"); + return -1; + } + + return pid; +} + +int StartDaemon(int current_pid, const std::string& cmd_line, const char* pid_file, int port, + const std::string& socket_path) { TEST_THAT_OR(current_pid == 0, return 0); - int new_pid = LaunchServer(cmd_line, pid_file); + int new_pid = LaunchServer(cmd_line, pid_file, port, socket_path); TEST_THAT_OR(new_pid != -1 && new_pid != 0, return 0); ::sleep(1); diff --git a/lib/server/ServerControl.h b/lib/server/ServerControl.h index be2464c1..28320491 100644 --- a/lib/server/ServerControl.h +++ b/lib/server/ServerControl.h @@ -4,11 +4,17 @@ #include "Test.h" bool HUPServer(int pid); -bool KillServer(int pid, bool WaitForProcess = false); -bool KillServer(std::string pid_file, bool WaitForProcess = false); -int StartDaemon(int current_pid, const std::string& cmd_line, const char* pid_file); -bool StopDaemon(int current_pid, const std::string& pid_file, - const std::string& memleaks_file, bool wait_for_process); +bool KillServer(int pid, bool wait_for_process = false); +bool KillServer(const std::string& pid_file, bool wait_for_process = false); +bool KillServerInternal(int pid); +int StartDaemon(int current_pid, const std::string& cmd_line, const char* pid_file, int port = 0, + const std::string& socket_path = ""); +bool StopDaemon(int current_pid, const std::string& pid_file, const std::string& memleaks_file, + bool wait_for_process); +int LaunchServer(const std::string& rCommandLine, const char *pidFile, int port = 0, + const std::string& socket_path = ""); +int WaitForServerStartup(const char *pidFile, int pidIfKnown, int port = 0, + const std::string& socket_path = ""); #ifdef WIN32 #include "WinNamedPipeStream.h" diff --git a/lib/server/ServerException.txt b/lib/server/ServerException.txt index 474b4067..02fb379c 100644 --- a/lib/server/ServerException.txt +++ b/lib/server/ServerException.txt @@ -29,6 +29,7 @@ TLSSetCiphersFailed 28 SSLLibraryInitialisationError 29 TLSNoSSLObject 31 TLSAlreadyHandshaked 35 +TLSServerWeakCertificate 36 Our SSL certificate is too weak for the current SSL Security Level, see https://github.com/boxbackup/boxbackup/wiki/WeakSSLCertificates SocketSetNonBlockingFailed 40 Protocol_BadUsage 43 Protocol_UnsuitableStreamTypeForSending 51 diff --git a/lib/server/ServerStream.h b/lib/server/ServerStream.h index 3f6eed7e..db8beaf2 100644 --- a/lib/server/ServerStream.h +++ b/lib/server/ServerStream.h @@ -306,7 +306,16 @@ public: #endif // !WIN32 // Just handle in this process SetProcessTitle("handling"); - HandleConnection(connection); + try + { + HandleConnection(connection); + } + catch(BoxException &e) + { + BOX_ERROR("Error in child handler, terminating connection: " + "exception " << e.what() << "(" << e.GetType() << "/" << + e.GetSubType() << ")"); + } SetProcessTitle("idle"); #ifndef WIN32 } diff --git a/lib/server/ServerTLS.h b/lib/server/ServerTLS.h index f748f4b2..6b53e860 100644 --- a/lib/server/ServerTLS.h +++ b/lib/server/ServerTLS.h @@ -10,6 +10,7 @@ #ifndef SERVERTLS__H #define SERVERTLS__H +#include "BoxPortsAndFiles.h" #include "ServerStream.h" #include "SocketStreamTLS.h" #include "SSLLib.h" @@ -52,8 +53,12 @@ public: std::string certFile(serverconf.GetKeyValue("CertificateFile")); std::string keyFile(serverconf.GetKeyValue("PrivateKeyFile")); std::string caFile(serverconf.GetKeyValue("TrustedCAsFile")); + + int ssl_security_level(serverconf.GetKeyValueInt("SSLSecurityLevel", + BOX_DEFAULT_SSL_SECURITY_LEVEL)); + mContext.Initialise(true /* as server */, certFile.c_str(), - keyFile.c_str(), caFile.c_str()); + keyFile.c_str(), caFile.c_str(), ssl_security_level); // Then do normal stream server stuff ServerStream<SocketStreamTLS, Port, ListenBacklog, @@ -75,6 +80,8 @@ private: ConfigurationVerifyKey("CertificateFile", ConfigTest_Exists), \ ConfigurationVerifyKey("PrivateKeyFile", ConfigTest_Exists), \ ConfigurationVerifyKey("TrustedCAsFile", ConfigTest_Exists), \ + ConfigurationVerifyKey("SSLSecurityLevel", ConfigTest_IsInt, \ + BOX_DEFAULT_SSL_SECURITY_LEVEL), \ SERVERSTREAM_VERIFY_SERVER_KEYS(DEFAULT_ADDRESSES) #endif // SERVERTLS__H diff --git a/lib/server/SocketStream.cpp b/lib/server/SocketStream.cpp index edb5e5b8..f8c6c85c 100644 --- a/lib/server/SocketStream.cpp +++ b/lib/server/SocketStream.cpp @@ -171,6 +171,10 @@ void SocketStream::Open(Socket::Type Type, const std::string& rName, int Port) Socket::NameLookupToSockAddr(addr, sockDomain, Type, rName, Port, addrLen); + std::ostringstream oss; + oss << rName << ":" << Port; + mPeerSocketDesc = oss.str(); + // Create the socket mSocketHandle = ::socket(sockDomain, SOCK_STREAM, 0 /* let OS choose protocol */); diff --git a/lib/server/SocketStream.h b/lib/server/SocketStream.h index fd57af8f..6b029387 100644 --- a/lib/server/SocketStream.h +++ b/lib/server/SocketStream.h @@ -114,6 +114,7 @@ private: protected: off_t mBytesRead; off_t mBytesWritten; + std::string mPeerSocketDesc; public: off_t GetBytesRead() const {return mBytesRead;} diff --git a/lib/server/SocketStreamTLS.cpp b/lib/server/SocketStreamTLS.cpp index e6299bfa..fc9652b7 100644 --- a/lib/server/SocketStreamTLS.cpp +++ b/lib/server/SocketStreamTLS.cpp @@ -10,8 +10,7 @@ #include "Box.h" #define TLS_CLASS_IMPLEMENTATION_CPP -#include <openssl/ssl.h> -#include <openssl/bio.h> + #include <errno.h> #include <fcntl.h> @@ -19,6 +18,10 @@ #include <poll.h> #endif +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/ssl.h> + #include "autogen_ConnectionException.h" #include "autogen_ServerException.h" #include "BoxTime.h" @@ -126,8 +129,8 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer) mpBIO = ::BIO_new(::BIO_s_socket()); if(mpBIO == 0) { - CryptoUtils::LogError("creating socket bio"); - THROW_EXCEPTION(ServerException, TLSAllocationFailed) + THROW_EXCEPTION_MESSAGE(ServerException, TLSAllocationFailed, + "Failed to create SSL BIO: " << CryptoUtils::LogError("creating socket bio")); } tOSSocketHandle socket = GetSocketHandle(); @@ -137,8 +140,8 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer) mpSSL = ::SSL_new(rContext.GetRawContext()); if(mpSSL == 0) { - CryptoUtils::LogError("creating SSL object"); - THROW_EXCEPTION(ServerException, TLSAllocationFailed) + THROW_EXCEPTION_MESSAGE(ServerException, TLSAllocationFailed, + "Failed to create SSL object: " << CryptoUtils::LogError("creating SSL object")); } // Make the socket non-blocking so timeouts on Read work @@ -203,15 +206,43 @@ void SocketStreamTLS::Handshake(const TLSContext &rContext, bool IsServer) default: // (and SSL_ERROR_ZERO_RETURN) // Error occured +#if HAVE_DECL_SSL_R_EE_KEY_TOO_SMALL + int err_reason = ERR_GET_REASON(ERR_peek_error()); + const char *file, *data; + int line, flags; + ERR_peek_error_line_data(&file, &line, &data, &flags); + long verify_result = SSL_get_verify_result(mpSSL); + + if(se == SSL_ERROR_SSL && verify_result == X509_V_ERR_CA_KEY_TOO_SMALL) + { + // Would be nice to use GetPeerCommonName() in these error messages, + // but since the certificate isn't trusted, that might be misleading, + // and it's not available to us anyway :( + + THROW_EXCEPTION_MESSAGE(ConnectionException, TLSPeerWeakCertificate, + (IsServer ? "Failed to accept connection from" : + "Failed to connect to") << " " << mPeerSocketDesc << + ": key too short for current security level"); + } + else if(se == SSL_ERROR_SSL && verify_result == X509_V_ERR_CA_MD_TOO_WEAK) + { + THROW_EXCEPTION_MESSAGE(ConnectionException, TLSPeerWeakCertificate, + (IsServer ? "Failed to accept connection from" : + "Failed to connect to") << " " << mPeerSocketDesc << + ": hash too weak for current security level"); + } + else +#endif // HAVE_DECL_SSL_R_EE_KEY_TOO_SMALL if(IsServer) { - CryptoUtils::LogError("accepting connection"); - THROW_EXCEPTION(ConnectionException, TLSHandshakeFailed) + THROW_EXCEPTION_MESSAGE(ConnectionException, TLSHandshakeFailed, + "Failed to accept connection: " << + CryptoUtils::LogError("accepting connection")); } else { - CryptoUtils::LogError("connecting"); - THROW_EXCEPTION(ConnectionException, TLSHandshakeFailed) + THROW_EXCEPTION_MESSAGE(ConnectionException, TLSHandshakeFailed, + "Failed to connect: " << CryptoUtils::LogError("connecting")); } } } diff --git a/lib/server/SocketStreamTLS.h b/lib/server/SocketStreamTLS.h index 3fda98c1..8a793966 100644 --- a/lib/server/SocketStreamTLS.h +++ b/lib/server/SocketStreamTLS.h @@ -34,10 +34,11 @@ public: SocketStreamTLS(); SocketStreamTLS(int socket); ~SocketStreamTLS(); + private: SocketStreamTLS(const SocketStreamTLS &rToCopy); -public: +public: void Open(const TLSContext &rContext, Socket::Type Type, const std::string& rName, int Port = 0); void Handshake(const TLSContext &rContext, bool IsServer = false); diff --git a/lib/server/TLSContext.cpp b/lib/server/TLSContext.cpp index d3f41f45..9c01452b 100644 --- a/lib/server/TLSContext.cpp +++ b/lib/server/TLSContext.cpp @@ -10,6 +10,7 @@ #include "Box.h" #define TLS_CLASS_IMPLEMENTATION_CPP +#include <openssl/err.h> #include <openssl/ssl.h> #include "autogen_ConnectionException.h" @@ -22,7 +23,7 @@ #include "MemLeakFindOn.h" #define MAX_VERIFICATION_DEPTH 2 -#define CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" +#define CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" // Macros to allow compatibility with OpenSSL 1.0 and 1.1 APIs. See // https://github.com/charybdis-ircd/charybdis/blob/release/3.5/libratbox/src/openssl_ratbox.h @@ -72,7 +73,8 @@ TLSContext::~TLSContext() // Created: 2003/08/06 // // -------------------------------------------------------------------------- -void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const char *PrivateKeyFile, const char *TrustedCAsFile) +void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const char *PrivateKeyFile, + const char *TrustedCAsFile, int SSLSecurityLevel) { if(mpContext != 0) { @@ -86,36 +88,64 @@ void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const c } #ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL - BOX_WARNING("This version of Box Backup overrides the system-wide SSLSecurityLevel for " - "backwards compatibility. Please upgrade as soon as possible. See " - "https://github.com/boxbackup/boxbackup/wiki/WeakSSLCertificates#workaround-2 " - "for details"); - SSL_CTX_set_security_level(mpContext, BOX_DEFAULT_SSL_SECURITY_LEVEL); + if(SSLSecurityLevel == -1) + { + BOX_WARNING("SSLSecurityLevel not set. Your connection may not be secure. " + "Please see https://github.com/boxbackup/boxbackup/wiki/WeakSSLCertificates " + "for details"); + SSLSecurityLevel = 1; // Default for now. Unsafe, but we warned the user. + // TODO: upgrade to level 2 soon. + } + + SSL_CTX_set_security_level(mpContext, SSLSecurityLevel); +#else + if(SSLSecurityLevel != BOX_DEFAULT_SSL_SECURITY_LEVEL) + { + BOX_WARNING("SSLSecurityLevel is set, but this Box Backup is not compiled with " + "OpenSSL 1.1 or higher, so will be ignored (compiled with " + OPENSSL_VERSION_TEXT ")"); + } #endif // Setup our identity if(::SSL_CTX_use_certificate_chain_file(mpContext, CertificatesFile) != 1) { - std::string msg = "loading certificates from "; - msg += CertificatesFile; - CryptoUtils::LogError(msg); - THROW_EXCEPTION(ServerException, TLSLoadCertificatesFailed) +#if HAVE_DECL_SSL_R_EE_KEY_TOO_SMALL + int err_reason = ERR_GET_REASON(ERR_peek_error()); + if(err_reason == SSL_R_EE_KEY_TOO_SMALL) + { + THROW_EXCEPTION_MESSAGE(ServerException, TLSServerWeakCertificate, + "Failed to load certificates from " << CertificatesFile << ": " + "key too short for current security level"); + } + else if(err_reason == SSL_R_CA_MD_TOO_WEAK) + { + THROW_EXCEPTION_MESSAGE(ServerException, TLSServerWeakCertificate, + "Failed to load certificates from " << CertificatesFile << ": " + "hash too weak for current security level"); + } + else +#endif // HAVE_DECL_SSL_R_EE_KEY_TOO_SMALL + { + THROW_EXCEPTION_MESSAGE(ServerException, TLSLoadCertificatesFailed, + "Failed to load certificates from " << CertificatesFile << ": " << + CryptoUtils::LogError("loading certificates")); + } } + if(::SSL_CTX_use_PrivateKey_file(mpContext, PrivateKeyFile, SSL_FILETYPE_PEM) != 1) { - std::string msg = "loading private key from "; - msg += PrivateKeyFile; - CryptoUtils::LogError(msg); - THROW_EXCEPTION(ServerException, TLSLoadPrivateKeyFailed) + THROW_EXCEPTION_MESSAGE(ServerException, TLSLoadPrivateKeyFailed, + "Failed to load private key from " << PrivateKeyFile << ": " << + CryptoUtils::LogError("loading private key")); } // Setup the identify of CAs we trust if(::SSL_CTX_load_verify_locations(mpContext, TrustedCAsFile, NULL) != 1) { - std::string msg = "loading CA cert from "; - msg += TrustedCAsFile; - CryptoUtils::LogError(msg); - THROW_EXCEPTION(ServerException, TLSLoadTrustedCAsFailed) + THROW_EXCEPTION_MESSAGE(ServerException, TLSLoadTrustedCAsFailed, + "Failed to load CA certificate from " << TrustedCAsFile << ": " << + CryptoUtils::LogError("loading CA cert")); } // Setup options to require these certificates @@ -126,8 +156,9 @@ void TLSContext::Initialise(bool AsServer, const char *CertificatesFile, const c // Setup allowed ciphers if(::SSL_CTX_set_cipher_list(mpContext, CIPHER_LIST) != 1) { - CryptoUtils::LogError("setting cipher list to " CIPHER_LIST); - THROW_EXCEPTION(ServerException, TLSSetCiphersFailed) + THROW_EXCEPTION_MESSAGE(ServerException, TLSSetCiphersFailed, + "Failed to set cipher list to " << CIPHER_LIST << ": " << + CryptoUtils::LogError("setting cipher list")); } } diff --git a/lib/server/TLSContext.h b/lib/server/TLSContext.h index f52f5457..6239d136 100644 --- a/lib/server/TLSContext.h +++ b/lib/server/TLSContext.h @@ -30,7 +30,8 @@ public: private: TLSContext(const TLSContext &); public: - void Initialise(bool AsServer, const char *CertificatesFile, const char *PrivateKeyFile, const char *TrustedCAsFile); + void Initialise(bool AsServer, const char *CertificatesFile, const char *PrivateKeyFile, + const char *TrustedCAsFile, int SSLSecurityLevel = -1); SSL_CTX *GetRawContext() const; private: diff --git a/lib/win32/getopt_long.cpp b/lib/win32/getopt_long.cpp index af2833a1..825de2a0 100755 --- a/lib/win32/getopt_long.cpp +++ b/lib/win32/getopt_long.cpp @@ -1,546 +1,546 @@ -/* $OpenBSD: getopt_long.c,v 1.20 2005/10/25 15:49:37 jmc Exp $ */
-/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
-// Adapted for Box Backup by Chris Wilson <chris+boxbackup@qwirx.com>
-
-/*
- * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Sponsored in part by the Defense Advanced Research Projects
- * Agency (DARPA) and Air Force Research Laboratory, Air Force
- * Materiel Command, USAF, under agreement number F39502-99-1-0512.
- */
-/*-
- * Copyright (c) 2000 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Dieter Baron and Thomas Klausner.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-// #include "Box.h"
-#include "emu.h"
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "box_getopt.h"
-
-#ifdef REPLACE_GETOPT // until end of file
-
-int opterr = 1; /* if error message should be printed */
-int optind = 1; /* index into parent argv vector */
-int optopt = '?'; /* character checked for validity */
-int optreset; /* reset getopt */
-char *optarg; /* argument associated with option */
-
-#define PRINT_ERROR ((opterr) && (*options != ':'))
-
-#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
-#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
-#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
-
-/* return values */
-#define BADCH (int)'?'
-#define BADARG ((*options == ':') ? (int)':' : (int)'?')
-#define INORDER (int)1
-
-#define EMSG ""
-
-static void warnx(const char* fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
-}
-
-static int getopt_internal(int, char * const *, const char *,
- const struct option *, int *, int);
-static int parse_long_options(char * const *, const char *,
- const struct option *, int *, int);
-static int gcd(int, int);
-static void permute_args(int, int, int, char * const *);
-
-static char *place = EMSG; /* option letter processing */
-
-/* XXX: set optreset to 1 rather than these two */
-static int nonopt_start = -1; /* first non option argument (for permute) */
-static int nonopt_end = -1; /* first option after non options (for permute) */
-
-/* Error messages */
-static const char recargchar[] = "option requires an argument -- %c";
-static const char recargstring[] = "option requires an argument -- %s";
-static const char ambig[] = "ambiguous option -- %.*s";
-static const char noarg[] = "option doesn't take an argument -- %.*s";
-static const char illoptchar[] = "unknown option -- %c";
-static const char illoptstring[] = "unknown option -- %s";
-
-/*
- * Compute the greatest common divisor of a and b.
- */
-static int
-gcd(int a, int b)
-{
- int c;
-
- c = a % b;
- while (c != 0) {
- a = b;
- b = c;
- c = a % b;
- }
-
- return (b);
-}
-
-/*
- * Exchange the block from nonopt_start to nonopt_end with the block
- * from nonopt_end to opt_end (keeping the same order of arguments
- * in each block).
- */
-static void
-permute_args(int panonopt_start, int panonopt_end, int opt_end,
- char * const *nargv)
-{
- int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
- char *swap;
-
- /*
- * compute lengths of blocks and number and size of cycles
- */
- nnonopts = panonopt_end - panonopt_start;
- nopts = opt_end - panonopt_end;
- ncycle = gcd(nnonopts, nopts);
- cyclelen = (opt_end - panonopt_start) / ncycle;
-
- for (i = 0; i < ncycle; i++) {
- cstart = panonopt_end+i;
- pos = cstart;
- for (j = 0; j < cyclelen; j++) {
- if (pos >= panonopt_end)
- pos -= nnonopts;
- else
- pos += nopts;
- swap = nargv[pos];
- /* LINTED const cast */
- ((char **) nargv)[pos] = nargv[cstart];
- /* LINTED const cast */
- ((char **)nargv)[cstart] = swap;
- }
- }
-}
-
-/*
- * parse_long_options --
- * Parse long options in argc/argv argument vector.
- * Returns -1 if short_too is set and the option does not match long_options.
- */
-static int
-parse_long_options(char * const *nargv, const char *options,
- const struct option *long_options, int *idx, int short_too)
-{
- char *current_argv, *has_equal;
- size_t current_argv_len;
- int i, match;
-
- current_argv = place;
- match = -1;
-
- optind++;
-
- if ((has_equal = strchr(current_argv, '=')) != NULL) {
- /* argument found (--option=arg) */
- current_argv_len = has_equal - current_argv;
- has_equal++;
- } else
- current_argv_len = strlen(current_argv);
-
- for (i = 0; long_options[i].name; i++) {
- /* find matching long option */
- if (strncmp(current_argv, long_options[i].name,
- current_argv_len))
- continue;
-
- if (strlen(long_options[i].name) == current_argv_len) {
- /* exact match */
- match = i;
- break;
- }
- /*
- * If this is a known short option, don't allow
- * a partial match of a single character.
- */
- if (short_too && current_argv_len == 1)
- continue;
-
- if (match == -1) /* partial match */
- match = i;
- else {
- /* ambiguous abbreviation */
- if (PRINT_ERROR)
- warnx(ambig, (int)current_argv_len,
- current_argv);
- optopt = 0;
- return (BADCH);
- }
- }
- if (match != -1) { /* option found */
- if (long_options[match].has_arg == no_argument
- && has_equal) {
- if (PRINT_ERROR)
- warnx(noarg, (int)current_argv_len,
- current_argv);
- /*
- * XXX: GNU sets optopt to val regardless of flag
- */
- if (long_options[match].flag == NULL)
- optopt = long_options[match].val;
- else
- optopt = 0;
- return (BADARG);
- }
- if (long_options[match].has_arg == required_argument ||
- long_options[match].has_arg == optional_argument) {
- if (has_equal)
- optarg = has_equal;
- else if (long_options[match].has_arg ==
- required_argument) {
- /*
- * optional argument doesn't use next nargv
- */
- optarg = nargv[optind++];
- }
- }
- if ((long_options[match].has_arg == required_argument)
- && (optarg == NULL)) {
- /*
- * Missing argument; leading ':' indicates no error
- * should be generated.
- */
- if (PRINT_ERROR)
- warnx(recargstring,
- current_argv);
- /*
- * XXX: GNU sets optopt to val regardless of flag
- */
- if (long_options[match].flag == NULL)
- optopt = long_options[match].val;
- else
- optopt = 0;
- --optind;
- return (BADARG);
- }
- } else { /* unknown option */
- if (short_too) {
- --optind;
- return (-1);
- }
- if (PRINT_ERROR)
- warnx(illoptstring, current_argv);
- optopt = 0;
- return (BADCH);
- }
- if (idx)
- *idx = match;
- if (long_options[match].flag) {
- *long_options[match].flag = long_options[match].val;
- return (0);
- } else
- return (long_options[match].val);
-}
-
-/*
- * getopt_internal --
- * Parse argc/argv argument vector. Called by user level routines.
- */
-static int
-getopt_internal(int nargc, char * const *nargv, const char *options,
- const struct option *long_options, int *idx, int flags)
-{
- const char * oli; /* option letter list index */
- int optchar, short_too;
- static int posixly_correct = -1;
-
- if (options == NULL)
- return (-1);
-
- /*
- * Disable GNU extensions if POSIXLY_CORRECT is set or options
- * string begins with a '+'.
- */
- if (posixly_correct == -1)
- posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
- if (posixly_correct || *options == '+')
- flags &= ~FLAG_PERMUTE;
- else if (*options == '-')
- flags |= FLAG_ALLARGS;
- if (*options == '+' || *options == '-')
- options++;
-
- /*
- * XXX Some GNU programs (like cvs) set optind to 0 instead of
- * XXX using optreset. Work around this braindamage.
- */
- if (optind == 0)
- optind = optreset = 1;
-
- optarg = NULL;
- if (optreset)
- nonopt_start = nonopt_end = -1;
-start:
- if (optreset || !*place) { /* update scanning pointer */
- optreset = 0;
- if (optind >= nargc) { /* end of argument vector */
- place = EMSG;
- if (nonopt_end != -1) {
- /* do permutation, if we have to */
- permute_args(nonopt_start, nonopt_end,
- optind, nargv);
- optind -= nonopt_end - nonopt_start;
- }
- else if (nonopt_start != -1) {
- /*
- * If we skipped non-options, set optind
- * to the first of them.
- */
- optind = nonopt_start;
- }
- nonopt_start = nonopt_end = -1;
- return (-1);
- }
- if (*(place = nargv[optind]) != '-' ||
- (place[1] == '\0' && strchr(options, '-') == NULL)) {
- place = EMSG; /* found non-option */
- if (flags & FLAG_ALLARGS) {
- /*
- * GNU extension:
- * return non-option as argument to option 1
- */
- optarg = nargv[optind++];
- return (INORDER);
- }
- if (!(flags & FLAG_PERMUTE)) {
- /*
- * If no permutation wanted, stop parsing
- * at first non-option.
- */
- return (-1);
- }
- /* do permutation */
- if (nonopt_start == -1)
- nonopt_start = optind;
- else if (nonopt_end != -1) {
- permute_args(nonopt_start, nonopt_end,
- optind, nargv);
- nonopt_start = optind -
- (nonopt_end - nonopt_start);
- nonopt_end = -1;
- }
- optind++;
- /* process next argument */
- goto start;
- }
- if (nonopt_start != -1 && nonopt_end == -1)
- nonopt_end = optind;
-
- /*
- * If we have "-" do nothing, if "--" we are done.
- */
- if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
- optind++;
- place = EMSG;
- /*
- * We found an option (--), so if we skipped
- * non-options, we have to permute.
- */
- if (nonopt_end != -1) {
- permute_args(nonopt_start, nonopt_end,
- optind, nargv);
- optind -= nonopt_end - nonopt_start;
- }
- nonopt_start = nonopt_end = -1;
- return (-1);
- }
- }
-
- /*
- * Check long options if:
- * 1) we were passed some
- * 2) the arg is not just "-"
- * 3) either the arg starts with -- we are getopt_long_only()
- */
- if (long_options != NULL && place != nargv[optind] &&
- (*place == '-' || (flags & FLAG_LONGONLY))) {
- short_too = 0;
- if (*place == '-')
- place++; /* --foo long option */
- else if (*place != ':' && strchr(options, *place) != NULL)
- short_too = 1; /* could be short option too */
-
- optchar = parse_long_options(nargv, options, long_options,
- idx, short_too);
- if (optchar != -1) {
- place = EMSG;
- return (optchar);
- }
- }
-
- if ((optchar = (int)*place++) == (int)':' ||
- optchar == (int)'-' && *place != '\0' ||
- (oli = strchr(options, optchar)) == NULL) {
- /*
- * If the user specified "-" and '-' isn't listed in
- * options, return -1 (non-option) as per POSIX.
- * Otherwise, it is an unknown option character (or ':').
- */
- if (optchar == (int)'-' && *place == '\0')
- return (-1);
- if (!*place)
- ++optind;
- if (PRINT_ERROR)
- warnx(illoptchar, optchar);
- optopt = optchar;
- return (BADCH);
- }
- if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
- /* -W long-option */
- if (*place) /* no space */
- /* NOTHING */;
- else if (++optind >= nargc) { /* no arg */
- place = EMSG;
- if (PRINT_ERROR)
- warnx(recargchar, optchar);
- optopt = optchar;
- return (BADARG);
- } else /* white space */
- place = nargv[optind];
- optchar = parse_long_options(nargv, options, long_options,
- idx, 0);
- place = EMSG;
- return (optchar);
- }
- if (*++oli != ':') { /* doesn't take argument */
- if (!*place)
- ++optind;
- } else { /* takes (optional) argument */
- optarg = NULL;
- if (*place) /* no white space */
- optarg = place;
- /* XXX: disable test for :: if PC? (GNU doesn't) */
- else if (oli[1] != ':') { /* arg not optional */
- if (++optind >= nargc) { /* no arg */
- place = EMSG;
- if (PRINT_ERROR)
- warnx(recargchar, optchar);
- optopt = optchar;
- return (BADARG);
- } else
- optarg = nargv[optind];
- } else if (!(flags & FLAG_PERMUTE)) {
- /*
- * If permutation is disabled, we can accept an
- * optional arg separated by whitespace so long
- * as it does not start with a dash (-).
- */
- if (optind + 1 < nargc && *nargv[optind + 1] != '-')
- optarg = nargv[++optind];
- }
- place = EMSG;
- ++optind;
- }
- /* dump back option letter */
- return (optchar);
-}
-
-/*
- * getopt --
- * Parse argc/argv argument vector.
- *
- * [eventually this will replace the BSD getopt]
- */
-int
-getopt(int nargc, char * const *nargv, const char *options)
-{
-
- /*
- * We don't pass FLAG_PERMUTE to getopt_internal() since
- * the BSD getopt(3) (unlike GNU) has never done this.
- *
- * Furthermore, since many privileged programs call getopt()
- * before dropping privileges it makes sense to keep things
- * as simple (and bug-free) as possible.
- */
- return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
-}
-
-/*
- * getopt_long --
- * Parse argc/argv argument vector.
- */
-int
-getopt_long(int nargc, char * const *nargv, const char *options,
- const struct option *long_options, int *idx)
-{
-
- return (getopt_internal(nargc, nargv, options, long_options, idx,
- FLAG_PERMUTE));
-}
-
-/*
- * getopt_long_only --
- * Parse argc/argv argument vector.
- */
-int
-getopt_long_only(int nargc, char * const *nargv, const char *options,
- const struct option *long_options, int *idx)
-{
-
- return (getopt_internal(nargc, nargv, options, long_options, idx,
- FLAG_PERMUTE|FLAG_LONGONLY));
-}
-
-#endif // REPLACE_GETOPT
+/* $OpenBSD: getopt_long.c,v 1.20 2005/10/25 15:49:37 jmc Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ +// Adapted for Box Backup by Chris Wilson <chris+boxbackup@qwirx.com> + +/* + * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +// #include "Box.h" +#include "emu.h" + +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "box_getopt.h" + +#ifdef REPLACE_GETOPT // until end of file + +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +static void warnx(const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, match; + + current_argv = place; + match = -1; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + const char * oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + */ + if (posixly_correct == -1) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + else if (*options == '-') + flags |= FLAG_ALLARGS; + if (*options == '+' || *options == '-') + options++; + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + optchar == (int)'-' && *place != '\0' || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + /* XXX: disable test for :: if PC? (GNU doesn't) */ + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } else if (!(flags & FLAG_PERMUTE)) { + /* + * If permutation is disabled, we can accept an + * optional arg separated by whitespace so long + * as it does not start with a dash (-). + */ + if (optind + 1 < nargc && *nargv[optind + 1] != '-') + optarg = nargv[++optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} + +#endif // REPLACE_GETOPT diff --git a/lib/win32/messages.rc b/lib/win32/messages.rc index 116522b7..0885a897 100755 --- a/lib/win32/messages.rc +++ b/lib/win32/messages.rc @@ -1,2 +1,2 @@ -LANGUAGE 0x9,0x1
-1 11 MSG00001.bin
+LANGUAGE 0x9,0x1 +1 11 MSG00001.bin |