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; }
int main(int argc, char** argv) { CString sConfig; CString sDataDir = ""; seedPRNG(); CUtils::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; while ((iArg = getopt_long(argc, argv, "hvnrcspd:Df", g_LongOpts, &iOptIndex)) != -1) { #else while ((iArg = getopt_long(argc, argv, "hvnrcsd:Df", 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 'n': CUtils::SetStdoutIsTTY(false); break; case 'r': bAllowRoot = true; break; 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 'f': bForeground = true; break; case 'D': bForeground = true; CUtils::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; } #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 (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); CUtils::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 (CUtils::Debug()) args[pos++] = strdup("--debug"); else if (bForeground) args[pos++] = strdup("--foreground"); if (!CUtils::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; }