Ejemplo n.º 1
0
int main(int argc, char** argv) {
    CString sConfig;
    CString sDataDir = "";

    thread_setup();

    seedPRNG();
    CDebug::SetStdoutIsTTY(isatty(1));

    int iArg, iOptIndex = -1;
    bool bMakeConf = false;
    bool bMakePass = false;
    bool bAllowRoot = false;
    bool bForeground = false;
#ifdef ALWAYS_RUN_IN_FOREGROUND
    bForeground = true;
#endif
#ifdef HAVE_LIBSSL
    bool bMakePem = false;
#endif
    CZNC::CreateInstance();

    while ((iArg = getopt_long(argc, argv, "hvnrcspd:Df", g_LongOpts,
                               &iOptIndex)) != -1) {
        switch (iArg) {
            case 'h':
                GenerateHelp(argv[0]);
                return 0;
            case 'v':
                cout << CZNC::GetTag() << endl;
                cout << CZNC::GetCompileOptionsString() << endl;
                return 0;
            case 'n':
                CDebug::SetStdoutIsTTY(false);
                break;
            case 'r':
                bAllowRoot = true;
                break;
            case 'c':
                bMakeConf = true;
                break;
            case 's':
                bMakePass = true;
                break;
            case 'p':
#ifdef HAVE_LIBSSL
                bMakePem = true;
                break;
#else
                CUtils::PrintError("ZNC is compiled without SSL support.");
                return 1;
#endif /* HAVE_LIBSSL */
            case 'd':
                sDataDir = CString(optarg);
                break;
            case 'f':
                bForeground = true;
                break;
            case 'D':
                bForeground = true;
                CDebug::SetDebug(true);
                break;
            case '?':
            default:
                GenerateHelp(argv[0]);
                return 1;
        }
    }

    if (optind < argc) {
        CUtils::PrintError("Unrecognized command line arguments.");
        CUtils::PrintError("Did you mean to run `/znc " +
                           CString(argv[optind]) + "' in IRC client instead?");
        CUtils::PrintError("Hint: `/znc " + CString(argv[optind]) +
                           "' is an alias for `/msg *status " +
                           CString(argv[optind]) + "'");
        return 1;
    }

    CZNC* pZNC = &CZNC::Get();
    pZNC->InitDirs(((argc) ? argv[0] : ""), sDataDir);

#ifdef HAVE_LIBSSL
    if (bMakePem) {
        pZNC->WritePemFile();

        CZNC::DestroyInstance();
        return 0;
    }
#endif /* HAVE_LIBSSL */

    if (bMakePass) {
        CString sSalt;
        CUtils::PrintMessage("Type your new password.");
        CString sHash = CUtils::GetSaltedHashPass(sSalt);
        CUtils::PrintMessage("Kill ZNC process, if it's running.");
        CUtils::PrintMessage(
            "Then replace password in the <User> section of your config with "
            "this:");
        // Not PrintMessage(), to remove [**] from the beginning, to ease
        // copypasting
        std::cout << "<Pass password>" << std::endl;
        std::cout << "\tMethod = " << CUtils::sDefaultHash << std::endl;
        std::cout << "\tHash = " << sHash << std::endl;
        std::cout << "\tSalt = " << sSalt << std::endl;
        std::cout << "</Pass>" << std::endl;
        CUtils::PrintMessage(
            "After that start ZNC again, and you should be able to login with "
            "the new password.");

        CZNC::DestroyInstance();
        return 0;
    }

    {
        set<CModInfo> ssGlobalMods;
        set<CModInfo> ssUserMods;
        set<CModInfo> ssNetworkMods;
        CUtils::PrintAction("Checking for list of available modules");
        pZNC->GetModules().GetAvailableMods(ssGlobalMods,
                                            CModInfo::GlobalModule);
        pZNC->GetModules().GetAvailableMods(ssUserMods, CModInfo::UserModule);
        pZNC->GetModules().GetAvailableMods(ssNetworkMods,
                                            CModInfo::NetworkModule);
        if (ssGlobalMods.empty() && ssUserMods.empty() &&
            ssNetworkMods.empty()) {
            CUtils::PrintStatus(false, "");
            CUtils::PrintError(
                "No modules found. Perhaps you didn't install ZNC properly?");
            CUtils::PrintError(
                "Read http://wiki.znc.in/Installation for instructions.");
            if (!CUtils::GetBoolInput(
                    "Do you really want to run ZNC without any modules?",
                    false)) {
                CZNC::DestroyInstance();
                return 1;
            }
        }
        CUtils::PrintStatus(true, "");
    }

    if (isRoot()) {
        CUtils::PrintError(
            "You are running ZNC as root! Don't do that! There are not many "
            "valid");
        CUtils::PrintError(
            "reasons for this and it can, in theory, cause great damage!");
        if (!bAllowRoot) {
            CZNC::DestroyInstance();
            return 1;
        }
        CUtils::PrintError("You have been warned.");
        CUtils::PrintError(
            "Hit CTRL+C now if you don't want to run ZNC as root.");
        CUtils::PrintError("ZNC will start in 30 seconds.");
        sleep(30);
    }

    if (bMakeConf) {
        if (!pZNC->WriteNewConfig(sConfig)) {
            CZNC::DestroyInstance();
            return 0;
        }
        /* Fall through to normal bootup */
    }

    CString sConfigError;
    if (!pZNC->ParseConfig(sConfig, sConfigError)) {
        CUtils::PrintError("Unrecoverable config error.");
        CZNC::DestroyInstance();
        return 1;
    }

    if (!pZNC->OnBoot()) {
        CUtils::PrintError("Exiting due to module boot errors.");
        CZNC::DestroyInstance();
        return 1;
    }

    if (bForeground) {
        int iPid = getpid();
        CUtils::PrintMessage("Staying open for debugging [pid: " +
                             CString(iPid) + "]");

        pZNC->WritePidFile(iPid);
        CUtils::PrintMessage(CZNC::GetTag());
    } else {
        CUtils::PrintAction("Forking into the background");

        int iPid = fork();

        if (iPid == -1) {
            CUtils::PrintStatus(false, strerror(errno));
            CZNC::DestroyInstance();
            return 1;
        }

        if (iPid > 0) {
            // We are the parent. We are done and will go to bed.
            CUtils::PrintStatus(true, "[pid: " + CString(iPid) + "]");

            pZNC->WritePidFile(iPid);
            CUtils::PrintMessage(CZNC::GetTag());
            /* Don't destroy pZNC here or it will delete the pid file. */
            return 0;
        }

        /* fcntl() locks don't necessarily propagate to forked()
         *   children.  Reacquire the lock here.  Use the blocking
         *   call to avoid race condition with parent exiting.
         */
        if (!pZNC->WaitForChildLock()) {
            CUtils::PrintError(
                "Child was unable to obtain lock on config file.");
            CZNC::DestroyInstance();
            return 1;
        }

        // Redirect std in/out/err to /dev/null
        close(0);
        open("/dev/null", O_RDONLY);
        close(1);
        open("/dev/null", O_WRONLY);
        close(2);
        open("/dev/null", O_WRONLY);

        CDebug::SetStdoutIsTTY(false);

        // We are the child. There is no way we can be a process group
        // leader, thus setsid() must succeed.
        setsid();
        // Now we are in our own process group and session (no
        // controlling terminal). We are independent!
    }

    // Handle all signals in separate thread
    std::unique_ptr<CSignalHandler> SignalHandler(new CSignalHandler(pZNC));

    int iRet = 0;

    try {
        pZNC->Loop();
    } catch (const CException& e) {
        switch (e.GetType()) {
            case CException::EX_Shutdown:
                iRet = 0;
                break;
            case CException::EX_Restart: {
                // strdup() because GCC is stupid
                char* args[] = {
                    strdup(argv[0]),                    strdup("--datadir"),
                    strdup(pZNC->GetZNCPath().c_str()), nullptr,
                    nullptr,                            nullptr,
                    nullptr};
                int pos = 3;
                if (CDebug::Debug())
                    args[pos++] = strdup("--debug");
                else if (bForeground)
                    args[pos++] = strdup("--foreground");
                if (!CDebug::StdoutIsTTY()) args[pos++] = strdup("--no-color");
                if (bAllowRoot) args[pos++] = strdup("--allow-root");
                // The above code adds 3 entries to args tops
                // which means the array should be big enough

                SignalHandler.reset();
                CZNC::DestroyInstance();
                execvp(args[0], args);
                CUtils::PrintError("Unable to restart ZNC [" +
                                   CString(strerror(errno)) + "]");
            } /* Fall through */
            default:
                iRet = 1;
        }
    }

    SignalHandler.reset();
    CZNC::DestroyInstance();

    CUtils::PrintMessage("Exiting");

    return iRet;
}
Ejemplo n.º 2
0
int main(int argc, char** argv) {
	CString sConfig;
	CString sDataDir = "";

	seedPRNG();
	// Win32 doesn't support shell escape codes, so we do this.
	CUtils::SetStdoutIsTTY(false);

	CString sConsoleTitle = "ZNC " + CZNC::GetVersion();
	SetConsoleTitle(sConsoleTitle.c_str());

	// register Ctrl handler
	if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleCtrlHandler, TRUE) == NULL)
	{
		CUtils::PrintMessage("Couldn't register console Ctrl handler function!");
	}

	// this prevents open()/read() and friends from stripping \r
	// from files... simply adding _O_BINARY to the modes doesn't seem
	// to be enough for some reason...
	// if we don't do this, Template.cpp will break
	// because it uses string.size() for file pos
	// calculations.
	_set_fmode(_O_BINARY);

#ifdef HAVE_LIBSSL
	CRYPTO_malloc_init();
#endif

	// make sure the stuff in ZNC.dll matches this exe's version... bad crashes otherwise.
	if(CZNC::GetCoreVersion() != MODVERSION)
	{
		CUtils::PrintError("The version number in ZNC.dll doesn't match. Aborting.");
		return 1;
	}

	// process command line arguments

	int iArg, iOptIndex = -1;
	bool bMakeConf = false;
	bool bMakePass = false;
	bool bAllowRoot = false;

#ifdef HAVE_LIBSSL
	bool bMakePem = false;

	while ((iArg = getopt_long(argc, argv, "hvcspd:D", g_LongOpts, &iOptIndex)) != -1) {
#else
	while ((iArg = getopt_long(argc, argv, "hvcsd:D", g_LongOpts, &iOptIndex)) != -1) {
#endif /* HAVE_LIBSSL */
		switch (iArg) {
		case 'h':
			GenerateHelp(argv[0]);
			return 0;
		case 'v':
			cout << CZNC::GetTag() << endl;
			return 0;
		case 'c':
			bMakeConf = true;
			break;
		case 's':
			bMakePass = true;
			break;
#ifdef HAVE_LIBSSL
		case 'p':
			bMakePem = true;
			break;
#endif /* HAVE_LIBSSL */
		case 'd':
			sDataDir = CString(optarg);
			break;
		case 'D':
			CUtils::SetDebug(true);
			break;
		case '?':
		default:
			GenerateHelp(argv[0]);
			return 1;
		}
	}

	if (optind < argc) {
		sConfig = argv[optind];
	}

	if (!InitCsocket()) {
		CUtils::PrintError("Failed to initialize Csocket!");
		exit(-1);
	}

	CZNC* pZNC = &CZNC::Get();
	pZNC->InitDirs(((argc) ? argv[0] : ""), sDataDir);

#ifdef HAVE_LIBSSL
	if (bMakePem) {
		pZNC->WritePemFile();

		delete pZNC;
		return 0;
	}
#endif /* HAVE_LIBSSL */

	if (bMakePass) {
		CString sSalt;
		CString sHash = CUtils::GetSaltedHashPass(sSalt);
		CUtils::PrintMessage("Use this in the <User> section of your config:");
		CUtils::PrintMessage("Pass = "******"#" + sHash + "#" + sSalt + "#");

		delete pZNC;
		return 0;
	}

#ifndef RUN_FROM_SOURCE
	if (CFile::Exists(pZNC->GetCurPath() + "/znc-uninstalled.pc")) {
		CUtils::PrintError("It looks like you are running znc without installing it first.");
		CUtils::PrintError("Recompile with --enable-run-from-source if you intend to do that.");
	}
#endif

	if (bMakeConf) {
		if (!pZNC->WriteNewConfig(sConfig)) {
			delete pZNC;
			return 0;
		}
		/* Fall through to normal bootup */
	}

	if (!pZNC->ParseConfig(sConfig)) {
		if(argc < 2)
		{
			CUtils::PrintMessage("\n");
			CUtils::PrintMessage("Press any key to continue...");
			_getch();
		}
		else
		{
			CUtils::PrintError("Unrecoverable config error.");
		}
		delete pZNC;
		return 1;
	}

	if (!pZNC->OnBoot()) {
		CUtils::PrintError("Exiting due to module boot errors.");
		delete pZNC;
		return 1;
	}

	// removed: checks for isRoot, bForeground, forking and signal handlers

	int iRet = 0;

	CUtils::PrintMessage("\n\n***************************************************");
	CUtils::PrintMessage("** ZNC is now running. Do not close this window. **");
	CUtils::PrintMessage("***************************************************\n\n");

	try {
		pZNC->Loop(&g_bMainLoop);
		CUtils::PrintMessage("Terminating ...");	
	} catch (CException e) {
		switch (e.GetType()) {
			case CException::EX_Shutdown:
				iRet = 0;
				CUtils::PrintMessage("************** Shutting down ZNC... **************");
				break;
			case CException::EX_Restart: {
				// strdup() because GCC is stupid
				char *args[] = {
					strdup(argv[0]),
					strdup("--datadir"),
					strdup(pZNC->GetZNCPath().c_str()),
					NULL,
					NULL,
					NULL,
					NULL,
					NULL
				};
				int pos = 3;
				if (CUtils::Debug())
					args[pos++] = strdup("--debug");
#if 0
				else if (bForeground)
					args[pos++] = strdup("--foreground");
				if (!CUtils::StdoutIsTTY())
					args[pos++] = strdup("--no-color");
				if (bAllowRoot)
					args[pos++] = strdup("--allow-root");
#endif
				args[pos++] = strdup(pZNC->GetConfigFile().c_str());
				// The above code adds 4 entries to args tops
				// which means the array should be big enough

				CUtils::PrintMessage("************** Restarting ZNC... **************");
				delete pZNC; /* stuff screws up real bad if we don't close all sockets etc. */
				pZNC = NULL;

				execvp(args[0], args);
				CUtils::PrintError("Unable to restart znc [" + CString(strerror(errno)) + "]");
			} /* Fall through */
			default:
				iRet = 1;
		}
	}

	delete pZNC;

#ifdef _DEBUG
	::_getch();
#endif

	return iRet;
}
Ejemplo n.º 3
0
int main(int argc, char** argv) {
	CString sConfig;
	CString sDataDir = "";

	seedPRNG();
	CDebug::SetStdoutIsTTY(isatty(1));

	int iArg, iOptIndex = -1;
	bool bMakeConf = false;
	bool bMakePass = false;
	bool bAllowRoot = false;
	bool bForeground = false;
#ifdef ALWAYS_RUN_IN_FOREGROUND
	bForeground = true;
#endif
#ifdef HAVE_LIBSSL
	bool bMakePem = false;
#endif

	while ((iArg = getopt_long(argc, argv, "hvnrcspd:Df", g_LongOpts, &iOptIndex)) != -1) {
		switch (iArg) {
		case 'h':
			GenerateHelp(argv[0]);
			return 0;
		case 'v':
			cout << CZNC::GetTag() << endl;
			cout << CZNC::GetCompileOptionsString() << endl;
			return 0;
		case 'n':
			CDebug::SetStdoutIsTTY(false);
			break;
		case 'r':
			bAllowRoot = true;
			break;
		case 'c':
			bMakeConf = true;
			break;
		case 's':
			bMakePass = true;
			break;
		case 'p':
#ifdef HAVE_LIBSSL
			bMakePem = true;
			break;
#else
			CUtils::PrintError("ZNC is compiled without SSL support.");
			return 1;
#endif /* HAVE_LIBSSL */
		case 'd':
			sDataDir = CString(optarg);
			break;
		case 'f':
			bForeground = true;
			break;
		case 'D':
			bForeground = true;
			CDebug::SetDebug(true);
			break;
		case '?':
		default:
			GenerateHelp(argv[0]);
			return 1;
		}
	}

	if (optind < argc) {
		CUtils::PrintError("Specifying a config file as an argument isn't supported anymore.");
		CUtils::PrintError("Use --datadir instead.");
		return 1;
	}

	CZNC* pZNC = &CZNC::Get();
	pZNC->InitDirs(((argc) ? argv[0] : ""), sDataDir);

#ifdef HAVE_LIBSSL
	if (bMakePem) {
		pZNC->WritePemFile();

		delete pZNC;
		return 0;
	}
#endif /* HAVE_LIBSSL */

	if (bMakePass) {
		CString sSalt;
		CString sHash = CUtils::GetSaltedHashPass(sSalt);
		CUtils::PrintMessage("Use this in the <User> section of your config:");
		CUtils::PrintMessage("Pass = "******"#" + sHash + "#" + sSalt + "#");

		delete pZNC;
		return 0;
	}

	{
		set<CModInfo> ssGlobalMods;
		set<CModInfo> ssUserMods;
		set<CModInfo> ssNetworkMods;
		CUtils::PrintAction("Checking for list of available modules");
		pZNC->GetModules().GetAvailableMods(ssGlobalMods, CModInfo::GlobalModule);
		pZNC->GetModules().GetAvailableMods(ssUserMods, CModInfo::UserModule);
		pZNC->GetModules().GetAvailableMods(ssNetworkMods, CModInfo::NetworkModule);
		if (ssGlobalMods.empty() && ssUserMods.empty() && ssNetworkMods.empty()) {
			CUtils::PrintStatus(false, "");
			CUtils::PrintError("No modules found. Perhaps you didn't install ZNC properly?");
			CUtils::PrintError("Read http://wiki.znc.in/Installation for instructions.");
			if (!CUtils::GetBoolInput("Do you really want to run ZNC without any modules?", false)) {
				delete pZNC;
				return 1;
			}
		}
		CUtils::PrintStatus(true, "");
	}

	if (isRoot()) {
		CUtils::PrintError("You are running ZNC as root! Don't do that! There are not many valid");
		CUtils::PrintError("reasons for this and it can, in theory, cause great damage!");
		if (!bAllowRoot) {
			delete pZNC;
			return 1;
		}
		CUtils::PrintError("You have been warned.");
		CUtils::PrintError("Hit CTRL+C now if you don't want to run ZNC as root.");
		CUtils::PrintError("ZNC will start in 30 seconds.");
		sleep(30);
	}

	if (bMakeConf) {
		if (!pZNC->WriteNewConfig(sConfig)) {
			delete pZNC;
			return 0;
		}
		/* Fall through to normal bootup */
	}

	if (!pZNC->ParseConfig(sConfig)) {
		CUtils::PrintError("Unrecoverable config error.");
		delete pZNC;
		return 1;
	}

	if (!pZNC->OnBoot()) {
		CUtils::PrintError("Exiting due to module boot errors.");
		delete pZNC;
		return 1;
	}

	if (bForeground) {
		int iPid = getpid();
		CUtils::PrintMessage("Staying open for debugging [pid: " + CString(iPid) + "]");

		pZNC->WritePidFile(iPid);
		CUtils::PrintMessage(CZNC::GetTag());
	} else {
		CUtils::PrintAction("Forking into the background");

		int iPid = fork();

		if (iPid == -1) {
			CUtils::PrintStatus(false, strerror(errno));
			delete pZNC;
			return 1;
		}

		if (iPid > 0) {
			// We are the parent. We are done and will go to bed.
			CUtils::PrintStatus(true, "[pid: " + CString(iPid) + "]");

			pZNC->WritePidFile(iPid);
			CUtils::PrintMessage(CZNC::GetTag());
			/* Don't destroy pZNC here or it will delete the pid file. */
			return 0;
		}

		/* fcntl() locks don't necessarily propagate to forked()
		 *   children.  Reacquire the lock here.  Use the blocking
		 *   call to avoid race condition with parent exiting.
		 */
		if (!pZNC->WaitForChildLock()) {
			CUtils::PrintError("Child was unable to obtain lock on config file.");
			delete pZNC;
			return 1;
		}

		// Redirect std in/out/err to /dev/null
		close(0); open("/dev/null", O_RDONLY);
		close(1); open("/dev/null", O_WRONLY);
		close(2); open("/dev/null", O_WRONLY);

		CDebug::SetStdoutIsTTY(false);

		// We are the child. There is no way we can be a process group
		// leader, thus setsid() must succeed.
		setsid();
		// Now we are in our own process group and session (no
		// controlling terminal). We are independent!
	}

	struct sigaction sa;
	sa.sa_flags = 0;
	sigemptyset(&sa.sa_mask);

	sa.sa_handler = SIG_IGN;
	sigaction(SIGPIPE, &sa, (struct sigaction*) NULL);

	sa.sa_handler = signalHandler;
	sigaction(SIGHUP,  &sa, (struct sigaction*) NULL);
	sigaction(SIGUSR1, &sa, (struct sigaction*) NULL);

	// Once this signal is caught, the signal handler is reset
	// to SIG_DFL. This avoids endless loop with signals.
	sa.sa_flags = SA_RESETHAND;
	sa.sa_handler = die;
	sigaction(SIGINT,  &sa, (struct sigaction*) NULL);
	sigaction(SIGQUIT, &sa, (struct sigaction*) NULL);
	sigaction(SIGTERM, &sa, (struct sigaction*) NULL);

	int iRet = 0;

	try {
		pZNC->Loop();
	} catch (CException e) {
		switch (e.GetType()) {
			case CException::EX_Shutdown:
				iRet = 0;
				break;
			case CException::EX_Restart: {
				// strdup() because GCC is stupid
				char *args[] = {
					strdup(argv[0]),
					strdup("--datadir"),
					strdup(pZNC->GetZNCPath().c_str()),
					NULL,
					NULL,
					NULL,
					NULL
				};
				int pos = 3;
				if (CDebug::Debug())
					args[pos++] = strdup("--debug");
				else if (bForeground)
					args[pos++] = strdup("--foreground");
				if (!CDebug::StdoutIsTTY())
					args[pos++] = strdup("--no-color");
				if (bAllowRoot)
					args[pos++] = strdup("--allow-root");
				// The above code adds 3 entries to args tops
				// which means the array should be big enough

				delete pZNC;
				execvp(args[0], args);
				CUtils::PrintError("Unable to restart ZNC [" + CString(strerror(errno)) + "]");
			} /* Fall through */
			default:
				iRet = 1;
		}
	}

	delete pZNC;

	return iRet;
}