⛏️ index : haiku.git

/*
 * Copyright 2002-2025, Haiku Inc.
 * Distributed under the terms of the MIT license.
 *
 * Authors:
 *		FranΓ§ois Revol (mmu_man)
 *		Humdinger, humdinger@mailbox.org
 */


#include <Application.h>
#include <MessageRunner.h>
#include <Messenger.h>
#include <NetworkInterface.h>
#include <NetworkRoster.h>
#include <String.h>

#include <cstdio>
#include <cstdlib>


static int sExitValue = EXIT_FAILURE;
static const char* kProgramName = "waitfor";
static const char* kProgramSig = "application/x-vnd.haiku-waitfor";


#define INTERVAL 100000 // 0.1sec
#define CHECK 'chck'


enum command {
	NONE,
	APP_RUNNING,
	NETWORK,
	THREAD_STARTED,
	THREAD_ENDED
};


class WaitforApp : public BApplication {
public:
	WaitforApp();
	virtual ~WaitforApp();

	virtual void ReadyToRun();
	virtual void ArgvReceived(int32 argc, char** argv);
	virtual void MessageReceived(BMessage* message);

private:
	void _Usage();
	bool _CheckNetworkConnection();

	BMessageRunner* fRunner;
	BString fThreadName;
	BString fAppSig;
	command fOption;
};


WaitforApp::WaitforApp()
	:
	BApplication(kProgramSig),
	fThreadName(NULL),
	fAppSig(NULL),
	fOption(NONE)
{
	BMessage message(CHECK);
	fRunner = new BMessageRunner(this, &message, INTERVAL);
}


WaitforApp::~WaitforApp()
{
	stop_watching_network(this);
	delete fRunner;
}


void
WaitforApp::ArgvReceived(int32 argc, char** argv)
{
	if (argc == 2 && strcmp(argv[1], "-n") == 0) {
		fOption = NETWORK;
	} else if (argc == 2) {
		fOption = THREAD_STARTED;
		fThreadName = argv[1];
	} else if (argc == 3 && strcmp(argv[1], "-e") == 0) {
		fOption = THREAD_ENDED;
		fThreadName = argv[2];
	} else if (argc == 3 && strcmp(argv[1], "-m") == 0) {
		fOption = APP_RUNNING;
		fAppSig = argv[2];
	} else
		fOption = NONE;
}


void
WaitforApp::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case CHECK:
		{
			switch (fOption) {
				case APP_RUNNING:
				{
					BMessenger messenger(fAppSig);
					if (messenger.IsValid()) {
						sExitValue = EXIT_SUCCESS;
						PostMessage(B_QUIT_REQUESTED);
					}
					break;
				}
				case THREAD_STARTED:
				{
					if (find_thread(fThreadName) >= 0) {
						sExitValue = EXIT_SUCCESS;
						PostMessage(B_QUIT_REQUESTED);
					}
					break;
				}
				case THREAD_ENDED:
				{
					if (find_thread(fThreadName) < 0) {
						sExitValue = EXIT_SUCCESS;
						PostMessage(B_QUIT_REQUESTED);
					}
					break;
				}
				case NONE:
				{
					_Usage();
					PostMessage(B_QUIT_REQUESTED);
					break;
				}
				default:
					break;
			}
			break;
		}
		case B_NETWORK_MONITOR:
		{
			if (_CheckNetworkConnection())
				PostMessage(B_QUIT_REQUESTED);
			break;
		}
		default:
		{
			BApplication::MessageReceived(message);
			break;
		}
	}
}


void
WaitforApp::ReadyToRun()
{
	if (fOption == NETWORK) {
		if (_CheckNetworkConnection())
			PostMessage(B_QUIT_REQUESTED);
		else {
			start_watching_network(
				B_WATCH_NETWORK_INTERFACE_CHANGES | B_WATCH_NETWORK_LINK_CHANGES, this);
		}
	}
}


bool
WaitforApp::_CheckNetworkConnection()
{
	BNetworkRoster& roster = BNetworkRoster::Default();
	BNetworkInterface interface;
	uint32 cookie = 0;
	while (roster.GetNextInterface(&cookie, interface) == B_OK) {
		uint32 flags = interface.Flags();
		if ((flags & IFF_LOOPBACK) == 0 && (flags & (IFF_UP | IFF_LINK)) == (IFF_UP | IFF_LINK)) {
			sExitValue = EXIT_SUCCESS;
			return true;
		}
	}
	// No network connection detected
	return false;
}


void
WaitforApp::_Usage()
{
	BString usageText("Usage:\n"
					  "  %s <thread_name>\n"
					  "      wait until a thread with 'thread_name' has been started.\n\n"
					  "  %s -e <thread_name>\n"
					  "      wait until all threads with thread_name have ended.\n\n"
					  "  %s -m <app_signature>\n"
					  "      wait until the application specified by 'app_signature' is "
					  "ready to receive messages.\n\n"
					  "  %s -n\n"
					  "      wait until the network connection is up.\n");
	usageText.ReplaceAll("%s", kProgramName);
	fprintf(stderr, usageText);
}


int
main(int argc, char** argv)
{
	WaitforApp waitforApp;
	waitforApp.Run();

	return sExitValue;
}