int main(int argc, char * const * argv)
{
	// Start memory leak testing
	MEMLEAKFINDER_START

	Logging::SetProgramName(BOX_MODULE);

	struct option longopts[] = 
	{
		{ "bbackupd-args",	required_argument, NULL, 'c' },
		{ "bbstored-args",	required_argument, NULL, 's' },
		{ "test-daemon-args",	required_argument, NULL, 'd' },
		{ "execute-only",	required_argument, NULL, 'e' },
		{ "help",		no_argument, NULL,       'h' },
		{ NULL,			0,                 NULL,  0  }
	};

	int c;
	std::string options("c:d:e:hs:");
	options += Logging::OptionParser::GetOptionString();
	Logging::OptionParser LogLevel;

	while ((c = getopt_long(argc, argv, options.c_str(), longopts, NULL))
		!= -1)
	{
		switch(c)
		{
			case 'c':
			{
				if (bbackupd_args.length() > 0)
				{
					bbackupd_args += " ";
				}
				bbackupd_args += optarg;
			}
			break;

			case 'd':
			{
				if (test_args.length() > 0)
				{
					test_args += " ";
				}
				test_args += optarg;
			}
			break;

			case 'e':
			{
				run_only_named_tests.push_back(optarg);
			}
			break;

			case 'h':
			{
				return Usage(argv[0]);
			}
			break;

			case 's':
			{
				bbstored_args += " ";
				bbstored_args += optarg;
			}
			break;

			default:
			{
				int ret = LogLevel.ProcessOption(c);
				if(ret != 0)
				{
					fprintf(stderr, "Unknown option code "
						"'%c'\n", c);
					exit(2);
				}
			}
		}
	}

	Logging::FilterSyslog(Log::NOTHING);
	Logging::FilterConsole(LogLevel.GetCurrentLevel());

	argc -= optind - 1;
	argv += optind - 1;

	// If there is more than one argument, then the test is doing something advanced, so leave it alone
	bool fulltestmode = (argc == 1);

	if(fulltestmode)
	{
		// banner
		BOX_NOTICE("Running test TEST_NAME in " MODE_TEXT " mode...");

		// Count open file descriptors for a very crude "files left open" test
		Logging::GetSyslog().Shutdown();

		// On NetBSD, gethostbyname() appears to open a kqueue socket
		// and it's not clear how to close it again. So let's just do
		// it once, before counting fds for the first time, so that it's
		// already open and doesn't count as a leak.
		::gethostbyname("nonexistent");

		check_filedes(false);

		#ifdef WIN32
			// Under win32 we must initialise the Winsock library
			// before using sockets

			WSADATA info;
			TEST_THAT(WSAStartup(0x0101, &info) != SOCKET_ERROR)
		#endif
	}

	try
	{
		#ifdef BOX_MEMORY_LEAK_TESTING
		memleakfinder_init();
		#endif

		Timers::Init();
		int returncode = test(argc, (const char **)argv);
		Timers::Cleanup(false);

		fflush(stdout);
		fflush(stderr);
		
		// check for memory leaks, if enabled
		#ifdef BOX_MEMORY_LEAK_TESTING
			if(memleakfinder_numleaks() != 0)
			{
				num_failures++;
				printf("FAILURE: Memory leaks detected in test code\n");
				printf("==== MEMORY LEAKS =================================\n");
				memleakfinder_reportleaks();
				printf("===================================================\n");
			}
		#endif
		
		if(fulltestmode)
		{
			Logging::GetSyslog().Shutdown();

			bool filesleftopen = !checkfilesleftopen();

			fflush(stdout);
			fflush(stderr);
		
			if(filesleftopen)
			{
				num_failures++;
				printf("IMPLICIT TEST FAILED: Something left files open\n");
			}
			if(num_failures > 0)
			{
				printf("FAILED: %d tests failed (first at "
					"%s:%d)\n", num_failures, 
					first_fail_file.c_str(),
					first_fail_line);
			}
			else
			{
				printf("PASSED\n");
			}
		}
		
		return returncode;
	}
	catch(std::exception &e)
	{
		printf("FAILED: Exception caught: %s\n", e.what());
		return 1;
	}
	catch(...)
	{
		printf("FAILED: Unknown exception caught\n");
		return 1;
	}
	if(fulltestmode)
	{
		if(!checkfilesleftopen())
		{
			printf("WARNING: Files were left open\n");
		}
	}
}
Ejemplo n.º 2
0
int main(int argc, const char *argv[])
{
    int returnCode = 0;

    MAINHELPER_SETUP_MEMORY_LEAK_EXIT_REPORT("bbackupctl.memleaks",
            "bbackupctl")

    MAINHELPER_START

    Logging::SetProgramName("bbackupctl");

    // Filename for configuration file?
    std::string configFilename = BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE;

    // See if there's another entry on the command line
    int c;
    std::string options("c:");
    options += Logging::OptionParser::GetOptionString();
    Logging::OptionParser LogLevel;

    while((c = getopt(argc, (char * const *)argv, options.c_str())) != -1)
    {
        switch(c)
        {
        case 'c':
            // store argument
            configFilename = optarg;
            break;

        default:
            int ret = LogLevel.ProcessOption(c);
            if(ret != 0)
            {
                PrintUsageAndExit(ret);
            }
        }
    }
    // Adjust arguments
    argc -= optind;
    argv += optind;

    // Check there's a command
    if(argc != 1)
    {
        PrintUsageAndExit(2);
    }

    Logging::FilterConsole(LogLevel.GetCurrentLevel());

    // Read in the configuration file
    BOX_INFO("Using configuration file " << configFilename);

    std::string errs;
    std::auto_ptr<Configuration> config(
        Configuration::LoadAndVerify
        (configFilename, &BackupDaemonConfigVerify, errs));

    if(config.get() == 0 || !errs.empty())
    {
        BOX_ERROR("Invalid configuration file: " << errs);
        return 1;
    }
    // Easier coding
    const Configuration &conf(*config);

    // Check there's a socket defined in the config file
    if(!conf.KeyExists("CommandSocket"))
    {
        BOX_ERROR("Daemon isn't using a control socket, "
                  "could not execute command.\n"
                  "Add a CommandSocket declaration to the "
                  "bbackupd.conf file.");
        return 1;
    }

    // Connect to socket

#ifndef WIN32
    SocketStream connection;
#else /* WIN32 */
    WinNamedPipeStream connection;
#endif /* ! WIN32 */

    try
    {
#ifdef WIN32
        std::string socket = conf.GetKeyValue("CommandSocket");
        connection.Connect(socket);
#else
        connection.Open(Socket::TypeUNIX, conf.GetKeyValue("CommandSocket").c_str());
#endif
    }
    catch(...)
    {
        BOX_ERROR("Failed to connect to daemon control socket.\n"
                  "Possible causes:\n"
                  "  * Daemon not running\n"
                  "  * Daemon busy syncing with store server\n"
                  "  * Another bbackupctl process is communicating with the daemon\n"
                  "  * Daemon is waiting to recover from an error"
                 );

        return 1;
    }

    // For receiving data
    IOStreamGetLine getLine(connection);

    // Wait for the configuration summary
    std::string configSummary;
    if(!getLine.GetLine(configSummary, false, PROTOCOL_DEFAULT_TIMEOUT))
    {
        BOX_ERROR("Failed to receive configuration summary "
                  "from daemon");
        return 1;
    }

    // Was the connection rejected by the server?
    if(getLine.IsEOF())
    {
        BOX_ERROR("Server rejected the connection. Are you running "
                  "bbackupctl as the same user as the daemon?");
        return 1;
    }

    // Decode it
    int autoBackup, updateStoreInterval, minimumFileAge, maxUploadWait;
    if(::sscanf(configSummary.c_str(), "bbackupd: %d %d %d %d", &autoBackup,
                &updateStoreInterval, &minimumFileAge, &maxUploadWait) != 4)
    {
        BOX_ERROR("Config summary didn't decode.");
        return 1;
    }
    // Print summary?
    BOX_TRACE("Daemon configuration summary:\n"
              "  AutomaticBackup = " <<
              (autoBackup?"true":"false") << "\n"
              "  UpdateStoreInterval = " << updateStoreInterval <<
              " seconds\n"
              "  MinimumFileAge = " << minimumFileAge << " seconds\n"
              "  MaxUploadWait = " << maxUploadWait << " seconds");

    std::string stateLine;
    if(!getLine.GetLine(stateLine, false, PROTOCOL_DEFAULT_TIMEOUT) || getLine.IsEOF())
    {
        BOX_ERROR("Failed to receive state line from daemon");
        return 1;
    }

    // Decode it
    int currentState;
    if(::sscanf(stateLine.c_str(), "state %d", &currentState) != 1)
    {
        BOX_ERROR("Received invalid state line from daemon");
        return 1;
    }

    BOX_TRACE("Current state: " <<
              BackupDaemon::GetStateName(currentState));

    Command command = Default;
    std::string commandName(argv[0]);

    if(commandName == "wait-for-sync")
    {
        command = WaitForSyncStart;
    }
    else if(commandName == "wait-for-end")
    {
        command = WaitForSyncEnd;
    }
    else if(commandName == "sync-and-wait")
    {
        command = SyncAndWaitForEnd;
    }
    else if(commandName == "status")
    {
        BOX_NOTICE("state " <<
                   BackupDaemon::GetStateName(currentState));
        command = NoCommand;
    }

    switch(command)
    {
    case WaitForSyncStart:
    case WaitForSyncEnd:
    {
        // Check that it's in automatic mode,
        // because otherwise it'll never start

        if(!autoBackup)
        {
            BOX_ERROR("Daemon is not in automatic mode, "
                      "sync will never start!");
            return 1;
        }
    }
    break;

    case SyncAndWaitForEnd:
    {
        // send a sync command
        commandName = "force-sync";
        std::string cmd = commandName + "\n";
        connection.Write(cmd, PROTOCOL_DEFAULT_TIMEOUT);
        connection.WriteAllBuffered();

        if(currentState != 0)
        {
            BOX_INFO("Waiting for current sync/error state "
                     "to finish...");
        }
    }
    break;

    default:
    {
        // Normal case, just send the command given, plus a
        // quit command.
        std::string cmd = commandName + "\n";
        connection.Write(cmd, PROTOCOL_DEFAULT_TIMEOUT);
    }
    // fall through

    case NoCommand:
    {
        // Normal case, just send the command given plus a
        // quit command.
        std::string cmd = "quit\n";
        connection.Write(cmd, PROTOCOL_DEFAULT_TIMEOUT);
    }
    }

    // Read the response
    std::string line;
    bool syncIsRunning = false;
    bool finished = false;

    while(command != NoCommand && !finished && !getLine.IsEOF() &&
            getLine.GetLine(line, false, PROTOCOL_DEFAULT_TIMEOUT))
    {
        BOX_TRACE("Received line: " << line);

        if(line.substr(0, 6) == "state ")
        {
            std::string state_str = line.substr(6);
            int state_num;
            if(sscanf(state_str.c_str(), "%d", &state_num) == 1)
            {
                BOX_INFO("Daemon state changed to: " <<
                         BackupDaemon::GetStateName(state_num));
            }
            else
            {
                BOX_WARNING("Failed to parse line: " << line);
            }
        }

        switch(command)
        {
        case WaitForSyncStart:
        {
            // Need to wait for the state change...
            if(line == "start-sync")
            {
                // And we're done
                finished = true;
            }
        }
        break;

        case WaitForSyncEnd:
        case SyncAndWaitForEnd:
        {
            if(line == "start-sync")
            {
                BOX_TRACE("Sync started...");
                syncIsRunning = true;
            }
            else if(line == "finish-sync")
            {
                if (syncIsRunning)
                {
                    // And we're done
                    BOX_TRACE("Sync finished.");
                    finished = true;
                }
                else
                {
                    BOX_TRACE("Previous sync finished.");
                }
                // daemon must still be busy
            }
        }
        break;

        default:
        {
            // Is this an OK or error line?
            if(line == "ok")
            {
                BOX_TRACE("Control command "
                          "sent: " <<
                          commandName);
                finished = true;
            }
            else if(line == "error")
            {
                BOX_ERROR("Control command failed: " <<
                          commandName << ". Check "
                          "command spelling.");
                returnCode = 1;
                finished = true;
            }
        }
        }
    }

    // Send a quit command to finish nicely
    connection.Write("quit\n", 5, PROTOCOL_DEFAULT_TIMEOUT);

    MAINHELPER_END

#if defined WIN32 && ! defined BOX_RELEASE_BUILD
    closelog();
#endif

    return returnCode;
}