Advertisement
Guest User

Untitled

a guest
Jun 16th, 2016
104
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.18 KB | None | 0 0
  1. /*
  2. * Copyright (C) 2004-2016 ZNC, see the NOTICE file for details.
  3. * Copyriht (C) 2016 WBNC
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17.  
  18. #include <znc/znc.h>
  19. #include <signal.h>
  20. #include <time.h>
  21. #include <thread>
  22.  
  23. #if defined(HAVE_LIBSSL) && defined(HAVE_PTHREAD)
  24. #include <znc/Threads.h>
  25. #include <openssl/crypto.h>
  26. #include <memory>
  27.  
  28. static std::vector<std::unique_ptr<CMutex>> lock_cs;
  29.  
  30. static void locking_callback(int mode, int type, const char* file, int line) {
  31. if (mode & CRYPTO_LOCK) {
  32. lock_cs[type]->lock();
  33. } else {
  34. lock_cs[type]->unlock();
  35. }
  36. }
  37.  
  38. static unsigned long thread_id_callback() {
  39. return (unsigned long)pthread_self();
  40. }
  41.  
  42. static CRYPTO_dynlock_value* dyn_create_callback(const char* file, int line) {
  43. return (CRYPTO_dynlock_value*)new CMutex;
  44. }
  45.  
  46. static void dyn_lock_callback(int mode, CRYPTO_dynlock_value* dlock,
  47. const char* file, int line) {
  48. CMutex* mtx = (CMutex*)dlock;
  49.  
  50. if (mode & CRYPTO_LOCK) {
  51. mtx->lock();
  52. } else {
  53. mtx->unlock();
  54. }
  55. }
  56.  
  57. static void dyn_destroy_callback(CRYPTO_dynlock_value* dlock, const char* file,
  58. int line) {
  59. CMutex* mtx = (CMutex*)dlock;
  60.  
  61. delete mtx;
  62. }
  63.  
  64. static void thread_setup() {
  65. lock_cs.resize(CRYPTO_num_locks());
  66.  
  67. for (std::unique_ptr<CMutex>& mtx : lock_cs)
  68. mtx = std::unique_ptr<CMutex>(new CMutex());
  69.  
  70. CRYPTO_set_id_callback(&thread_id_callback);
  71. CRYPTO_set_locking_callback(&locking_callback);
  72.  
  73. CRYPTO_set_dynlock_create_callback(&dyn_create_callback);
  74. CRYPTO_set_dynlock_lock_callback(&dyn_lock_callback);
  75. CRYPTO_set_dynlock_destroy_callback(&dyn_destroy_callback);
  76. }
  77.  
  78. #else
  79. #define thread_setup()
  80. #endif
  81.  
  82. using std::cout;
  83. using std::endl;
  84. using std::set;
  85.  
  86. #ifdef HAVE_GETOPT_LONG
  87. #include <getopt.h>
  88. #else
  89. #define no_argument 0
  90. #define required_argument 1
  91. #define optional_argument 2
  92.  
  93. struct option {
  94. const char* a;
  95. int opt;
  96. int* flag;
  97. int val;
  98. };
  99.  
  100. static inline int getopt_long(int argc, char* const argv[],
  101. const char* optstring, const struct option*,
  102. int*) {
  103. return getopt(argc, argv, optstring);
  104. }
  105. #endif
  106.  
  107. static const struct option g_LongOpts[] = {
  108. {"help", no_argument, nullptr, 'h'},
  109. {"version", no_argument, nullptr, 'v'},
  110. {"debug", no_argument, nullptr, 'D'},
  111. {"foreground", no_argument, nullptr, 'f'},
  112. {"no-color", no_argument, nullptr, 'n'},
  113. {"allow-root", no_argument, nullptr, 'r'},
  114. {"makeconf", no_argument, nullptr, 'c'},
  115. {"makepass", no_argument, nullptr, 's'},
  116. {"makepem", no_argument, nullptr, 'p'},
  117. {"datadir", required_argument, nullptr, 'd'},
  118. {nullptr, 0, nullptr, 0}};
  119.  
  120. static void GenerateHelp(const char* appname) {
  121. CUtils::PrintMessage("USAGE: " + CString(appname) + " [options]");
  122. CUtils::PrintMessage("Options are:");
  123. CUtils::PrintMessage(
  124. "\t-h, --help List available command line options (this page)");
  125. CUtils::PrintMessage(
  126. "\t-v, --version Output version information and exit");
  127. CUtils::PrintMessage("\t-f, --foreground Make ZNC foreground. Best used with screen/tmux.");
  128. CUtils::PrintMessage(
  129. "\t-D, --debug Output debugging information (Implies -f)");
  130. CUtils::PrintMessage(
  131. "\t-n, --no-color Don't use escape sequences in the output");
  132. CUtils::PrintMessage(
  133. "\t-r, --allow-root Don't complain if WZNC is run as root");
  134. CUtils::PrintMessage(
  135. "\t-c, --makeconf Interactively create a new config");
  136. CUtils::PrintMessage(
  137. "\t-s, --makepass Generates a password for use in config");
  138. #ifdef HAVE_LIBSSL
  139. CUtils::PrintMessage(
  140. "\t-p, --makepem Generates a pemfile for use with SSL");
  141. #endif /* HAVE_LIBSSL */
  142. CUtils::PrintMessage(
  143. "\t-d, --datadir Set a different WZNC config directory (default is ~/.znc.)");
  144. }
  145.  
  146. class CSignalHandler {
  147. public:
  148. CSignalHandler(CZNC* pZNC) {
  149. if (pipe(m_iPipe)) {
  150. DEBUG("Ouch, can't open pipe for signal handler: "
  151. << strerror(errno));
  152. exit(1);
  153. }
  154. pZNC->GetManager().MonitorFD(new CSignalHandlerMonitorFD(m_iPipe[0]));
  155. sigset_t signals;
  156. sigfillset(&signals);
  157. pthread_sigmask(SIG_SETMASK, &signals, nullptr);
  158. m_thread = std::thread([=]() { HandleSignals(pZNC); });
  159. }
  160. ~CSignalHandler() {
  161. pthread_cancel(m_thread.native_handle());
  162. m_thread.join();
  163. }
  164.  
  165. private:
  166. class CSignalHandlerMonitorFD : public CSMonitorFD {
  167. // This class just prevents the pipe buffer from filling by clearing it
  168. public:
  169. CSignalHandlerMonitorFD(int fd) { Add(fd, CSockManager::ECT_Read); }
  170.  
  171. bool FDsThatTriggered(
  172. const std::map<int, short>& miiReadyFds) override {
  173. for (const auto& it : miiReadyFds) {
  174. if (it.second) {
  175. int sig;
  176. read(it.first, &sig, sizeof(sig));
  177. }
  178. }
  179. return true;
  180. }
  181. };
  182.  
  183. void HandleSignals(CZNC* pZNC) {
  184. sigset_t signals;
  185. sigemptyset(&signals);
  186. sigaddset(&signals, SIGHUP);
  187. sigaddset(&signals, SIGUSR1);
  188. sigaddset(&signals, SIGINT);
  189. sigaddset(&signals, SIGQUIT);
  190. sigaddset(&signals, SIGTERM);
  191. sigaddset(&signals, SIGPIPE);
  192. // Handle only these signals specially; the rest will have their default
  193. // action, but in this thread
  194. pthread_sigmask(SIG_SETMASK, &signals, nullptr);
  195. while (true) {
  196. int sig;
  197. pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr);
  198. // This thread can be cancelled, but only during this function.
  199. // Such cancel will be the only way to finish this thread.
  200. if (sigwait(&signals, &sig) == -1) continue;
  201. pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr);
  202. // TODO probably move switch() to CSignalHandlerMonitorFD?
  203. switch (sig) {
  204. case SIGHUP:
  205. pZNC->SetConfigState(CZNC::ECONFIG_NEED_REHASH);
  206. break;
  207. case SIGUSR1:
  208. pZNC->SetConfigState(CZNC::ECONFIG_NEED_VERBOSE_WRITE);
  209. break;
  210. case SIGINT:
  211. case SIGQUIT:
  212. case SIGTERM:
  213. pZNC->SetConfigState(CZNC::ECONFIG_NEED_QUIT);
  214. // Reset handler to default by:
  215. // * not blocking it
  216. // * not waiting for it
  217. // So, if ^C is pressed, but for some reason it didn't work,
  218. // second ^C will kill the process for sure.
  219. sigdelset(&signals, sig);
  220. pthread_sigmask(SIG_SETMASK, &signals, nullptr);
  221. break;
  222. case SIGPIPE:
  223. default:
  224. break;
  225. }
  226. // This write() must succeed because POSIX guarantees that writes of
  227. // less than PIPE_BUF are atomic (and PIPE_BUF is at least 512).
  228. size_t w = write(m_iPipe[1], &sig, sizeof(sig));
  229. if (w != sizeof(sig)) {
  230. DEBUG(
  231. "Something bad happened during write() to a pipe for "
  232. "signal handler, wrote "
  233. << w << " bytes: " << strerror(errno));
  234. exit(1);
  235. }
  236. }
  237. }
  238.  
  239. std::thread m_thread;
  240.  
  241. // pipe for waking up the main thread
  242. int m_iPipe[2];
  243. };
  244.  
  245. static bool isRoot() {
  246. // User root? If one of these were root, we could switch the others to root,
  247. // too
  248. return (geteuid() == 0 || getuid() == 0);
  249. }
  250.  
  251. static void seedPRNG() {
  252. struct timeval tv;
  253. unsigned int seed;
  254.  
  255. // Try to find a seed which can't be as easily guessed as only time()
  256.  
  257. if (gettimeofday(&tv, nullptr) == 0) {
  258. seed = (unsigned int)tv.tv_sec;
  259.  
  260. // This is in [0:1e6], which means that roughly 20 bits are
  261. // actually used, let's try to shuffle the high bits.
  262. seed ^= uint32_t((tv.tv_usec << 10) | tv.tv_usec);
  263. } else
  264. seed = (unsigned int)time(nullptr);
  265.  
  266. seed ^= rand();
  267. seed ^= getpid();
  268.  
  269. srand(seed);
  270. }
  271.  
  272. int main(int argc, char** argv) {
  273. CString sConfig;
  274. CString sDataDir = "";
  275.  
  276. thread_setup();
  277.  
  278. seedPRNG();
  279. CDebug::SetStdoutIsTTY(isatty(1));
  280.  
  281. int iArg, iOptIndex = -1;
  282. bool bMakeConf = false;
  283. bool bMakePass = false;
  284. bool bAllowRoot = false;
  285. bool bForeground = false;
  286. #ifdef ALWAYS_RUN_IN_FOREGROUND
  287. bForeground = true;
  288. #endif
  289. #ifdef HAVE_LIBSSL
  290. bool bMakePem = false;
  291. #endif
  292. CZNC::CreateInstance();
  293.  
  294. while ((iArg = getopt_long(argc, argv, "hvnrcspd:Df", g_LongOpts,
  295. &iOptIndex)) != -1) {
  296. switch (iArg) {
  297. case 'h':
  298. GenerateHelp(argv[0]);
  299. return 0;
  300. case 'v':
  301. cout << CZNC::GetTag() << endl;
  302. cout << CZNC::GetCompileOptionsString() << endl;
  303. return 0;
  304. case 'n':
  305. CDebug::SetStdoutIsTTY(false);
  306. break;
  307. case 'r':
  308. bAllowRoot = true;
  309. break;
  310. case 'c':
  311. bMakeConf = true;
  312. break;
  313. case 's':
  314. bMakePass = true;
  315. break;
  316. case 'p':
  317. #ifdef HAVE_LIBSSL
  318. bMakePem = true;
  319. break;
  320. #else
  321. CUtils::PrintError("WZNC is compiled without SSL support.");
  322. return 1;
  323. #endif /* HAVE_LIBSSL */
  324. case 'd':
  325. sDataDir = CString(optarg);
  326. break;
  327. case 'f':
  328. bForeground = true;
  329. break;
  330. case 'D':
  331. bForeground = true;
  332. CDebug::SetDebug(true);
  333. break;
  334. case '?':
  335. default:
  336. GenerateHelp(argv[0]);
  337. return 1;
  338. }
  339. }
  340.  
  341. if (optind < argc) {
  342. CUtils::PrintError("Unrecognized command line arguments.");
  343. CUtils::PrintError("Did you mean to run `/znc " +
  344. CString(argv[optind]) + "' in IRC client instead?");
  345. CUtils::PrintError("Hint: `/znc " + CString(argv[optind]) +
  346. "' is an alias for `/msg *status " +
  347. CString(argv[optind]) + "'");
  348. return 1;
  349. }
  350.  
  351. CZNC* pZNC = &CZNC::Get();
  352. pZNC->InitDirs(((argc) ? argv[0] : ""), sDataDir);
  353.  
  354. #ifdef HAVE_LIBSSL
  355. if (bMakePem) {
  356. pZNC->WritePemFile();
  357.  
  358. CZNC::DestroyInstance();
  359. return 0;
  360. }
  361. #endif /* HAVE_LIBSSL */
  362.  
  363. if (bMakePass) {
  364. CString sSalt;
  365. CUtils::PrintMessage("Type your new password.");
  366. CString sHash = CUtils::GetSaltedHashPass(sSalt);
  367. CUtils::PrintMessage("Kill the WZNC process, if it's running.");
  368. CUtils::PrintMessage(
  369. "Then replace password in the <User> section of your config with "
  370. "this:");
  371. // Not PrintMessage(), to remove [**] from the beginning, to ease
  372. // copypasting
  373. std::cout << "<Pass password>" << std::endl;
  374. std::cout << "\tMethod = " << CUtils::sDefaultHash << std::endl;
  375. std::cout << "\tHash = " << sHash << std::endl;
  376. std::cout << "\tSalt = " << sSalt << std::endl;
  377. std::cout << "</Pass>" << std::endl;
  378. CUtils::PrintMessage(
  379. "After that, start WZNC again, and you should be able to login with "
  380. "the new password.");
  381.  
  382. CZNC::DestroyInstance();
  383. return 0;
  384. }
  385.  
  386. {
  387. set<CModInfo> ssGlobalMods;
  388. set<CModInfo> ssUserMods;
  389. set<CModInfo> ssNetworkMods;
  390. CUtils::PrintAction("Checking for list of available modules");
  391. pZNC->GetModules().GetAvailableMods(ssGlobalMods,
  392. CModInfo::GlobalModule);
  393. pZNC->GetModules().GetAvailableMods(ssUserMods, CModInfo::UserModule);
  394. pZNC->GetModules().GetAvailableMods(ssNetworkMods,
  395. CModInfo::NetworkModule);
  396. if (ssGlobalMods.empty() && ssUserMods.empty() &&
  397. ssNetworkMods.empty()) {
  398. CUtils::PrintStatus(false, "");
  399. CUtils::PrintError(
  400. "No modules found. Perhaps you didn't install WZNC properly?");
  401. CUtils::PrintError(
  402. "Read http://wiki.znc.in/Installation for instructions.");
  403. if (!CUtils::GetBoolInput(
  404. "Do you really want to run WZNC without any modules?",
  405. false)) {
  406. CZNC::DestroyInstance();
  407. return 1;
  408. }
  409. }
  410. CUtils::PrintStatus(true, "");
  411. }
  412.  
  413. if (isRoot()) {
  414. CUtils::PrintError(
  415. "You are running WZNC as root! Don't do that! There are not many "
  416. "valid");
  417. CUtils::PrintError(
  418. "reasons for this and it can, in theory, cause great damage!");
  419. if (!bAllowRoot) {
  420. CZNC::DestroyInstance();
  421. return 1;
  422. }
  423. CUtils::PrintError("You have been warned.");
  424. CUtils::PrintError(
  425. "Hit CTRL+C now if you don't want to run WZNC as root.");
  426. CUtils::PrintError("WZNC will start in 30 seconds.");
  427. sleep(30);
  428. }
  429.  
  430. if (bMakeConf) {
  431. if (!pZNC->WriteNewConfig(sConfig)) {
  432. CZNC::DestroyInstance();
  433. return 0;
  434. }
  435. /* Fall through to normal bootup */
  436. }
  437.  
  438. CString sConfigError;
  439. if (!pZNC->ParseConfig(sConfig, sConfigError)) {
  440. CUtils::PrintError("Unrecoverable config error.");
  441. CZNC::DestroyInstance();
  442. return 1;
  443. }
  444.  
  445. if (!pZNC->OnBoot()) {
  446. CUtils::PrintError("Exiting due to module boot errors.");
  447. CZNC::DestroyInstance();
  448. return 1;
  449. }
  450.  
  451. if (bForeground) {
  452. int iPid = getpid();
  453. CUtils::PrintMessage("Staying open for debugging [pid: " +
  454. CString(iPid) + "]");
  455.  
  456. pZNC->WritePidFile(iPid);
  457. CUtils::PrintMessage(CZNC::GetTag());
  458. } else {
  459. CUtils::PrintAction("Forking into the background");
  460.  
  461. int iPid = fork();
  462.  
  463. if (iPid == -1) {
  464. CUtils::PrintStatus(false, strerror(errno));
  465. CZNC::DestroyInstance();
  466. return 1;
  467. }
  468.  
  469. if (iPid > 0) {
  470. // We are the parent. We are done and will go to bed.
  471. CUtils::PrintStatus(true, "[pid: " + CString(iPid) + "]");
  472.  
  473. pZNC->WritePidFile(iPid);
  474. CUtils::PrintMessage(CZNC::GetTag());
  475. /* Don't destroy pZNC here or it will delete the pid file. */
  476. return 0;
  477. }
  478.  
  479. /* fcntl() locks don't necessarily propagate to forked()
  480. * children. Reacquire the lock here. Use the blocking
  481. * call to avoid race condition with parent exiting.
  482. */
  483. if (!pZNC->WaitForChildLock()) {
  484. CUtils::PrintError(
  485. "Child was unable to obtain lock on config file.");
  486. CZNC::DestroyInstance();
  487. return 1;
  488. }
  489.  
  490. // Redirect std in/out/err to /dev/null
  491. close(0);
  492. open("/dev/null", O_RDONLY);
  493. close(1);
  494. open("/dev/null", O_WRONLY);
  495. close(2);
  496. open("/dev/null", O_WRONLY);
  497.  
  498. CDebug::SetStdoutIsTTY(false);
  499.  
  500. // We are the child. There is no way we can be a process group
  501. // leader, thus setsid() must succeed.
  502. setsid();
  503. // Now we are in our own process group and session (no
  504. // controlling terminal). We are independent!
  505. }
  506.  
  507. // Handle all signals in separate thread
  508. std::unique_ptr<CSignalHandler> SignalHandler(new CSignalHandler(pZNC));
  509.  
  510. int iRet = 0;
  511.  
  512. try {
  513. pZNC->Loop();
  514. } catch (const CException& e) {
  515. switch (e.GetType()) {
  516. case CException::EX_Shutdown:
  517. iRet = 0;
  518. break;
  519. case CException::EX_Restart: {
  520. // strdup() because GCC is stupid
  521. char* args[] = {
  522. strdup(argv[0]), strdup("--datadir"),
  523. strdup(pZNC->GetZNCPath().c_str()), nullptr,
  524. nullptr, nullptr,
  525. nullptr};
  526. int pos = 3;
  527. if (CDebug::Debug())
  528. args[pos++] = strdup("--debug");
  529. else if (bForeground)
  530. args[pos++] = strdup("--foreground");
  531. if (!CDebug::StdoutIsTTY()) args[pos++] = strdup("--no-color");
  532. if (bAllowRoot) args[pos++] = strdup("--allow-root");
  533. // The above code adds 3 entries to args tops
  534. // which means the array should be big enough
  535.  
  536. SignalHandler.reset();
  537. CZNC::DestroyInstance();
  538. execvp(args[0], args);
  539. CUtils::PrintError("Error: Unable to restart WZNC [" +
  540. CString(strerror(errno)) + "]");
  541. } /* Fall through */
  542. default:
  543. iRet = 1;
  544. }
  545. }
  546.  
  547. SignalHandler.reset();
  548. CZNC::DestroyInstance();
  549.  
  550. CUtils::PrintMessage("Exiting");
  551.  
  552. return iRet;
  553. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement