gearmand.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
  2. *
  3. * Gearmand client and server library.
  4. *
  5. * Copyright (C) 2011-2012 Data Differential, http://datadifferential.com/
  6. * Copyright (C) 2008 Brian Aker, Eric Day
  7. * All rights reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions are
  11. * met:
  12. *
  13. * * Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. *
  16. * * Redistributions in binary form must reproduce the above
  17. * copyright notice, this list of conditions and the following disclaimer
  18. * in the documentation and/or other materials provided with the
  19. * distribution.
  20. *
  21. * * The names of its contributors may not be used to endorse or
  22. * promote products derived from this software without specific prior
  23. * written permission.
  24. *
  25. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  26. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  27. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  28. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  29. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  30. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  31. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  32. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  33. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  35. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. */
  38. #include "gear_config.h"
  39. #include <configmake.h>
  40. #include <cerrno>
  41. #include <cstdio>
  42. #include <cstdlib>
  43. #include <cstring>
  44. #include <fcntl.h>
  45. #include <fstream>
  46. #include <pwd.h>
  47. #include <signal.h>
  48. #include <sys/resource.h>
  49. #include <sys/stat.h>
  50. #include <sys/types.h>
  51. #include <syslog.h>
  52. #include <unistd.h>
  53. #ifdef TIME_WITH_SYS_TIME
  54. # include <sys/time.h>
  55. # include <time.h>
  56. #else
  57. # ifdef HAVE_SYS_TIME_H
  58. # include <sys/time.h>
  59. # else
  60. # include <time.h>
  61. # endif
  62. #endif
  63. #include <libgearman-server/gearmand.h>
  64. #include <libgearman-server/plugins.h>
  65. #include <libgearman-server/queue.hpp>
  66. #define GEARMAND_LOG_REOPEN_TIME 60
  67. #include "util/daemon.hpp"
  68. #include "util/pidfile.hpp"
  69. #include <boost/program_options.hpp>
  70. #include <boost/program_options/options_description.hpp>
  71. #include <boost/program_options/parsers.hpp>
  72. #include <boost/program_options/variables_map.hpp>
  73. #include <boost/token_functions.hpp>
  74. #include <boost/tokenizer.hpp>
  75. #include <iostream>
  76. #include "gearmand/error.hpp"
  77. #include "gearmand/log.hpp"
  78. using namespace datadifferential;
  79. using namespace gearmand;
  80. static bool _set_fdlimit(rlim_t fds);
  81. static bool _switch_user(const char *user);
  82. extern "C" {
  83. static bool _set_signals(void);
  84. }
  85. static void _log(const char *line, gearmand_verbose_t verbose, void *context);
  86. int main(int argc, char *argv[])
  87. {
  88. gearmand::error::init(argv[0]);
  89. int backlog;
  90. rlim_t fds= 0;
  91. uint32_t job_retries;
  92. uint32_t worker_wakeup;
  93. std::string host;
  94. std::string user;
  95. std::string log_file;
  96. std::string pid_file;
  97. std::string protocol;
  98. std::string queue_type;
  99. std::string verbose_string= "ERROR";
  100. std::string config_file;
  101. uint32_t threads;
  102. bool opt_exceptions;
  103. bool opt_round_robin;
  104. bool opt_daemon;
  105. bool opt_check_args;
  106. bool opt_syslog;
  107. boost::program_options::options_description general("General options");
  108. general.add_options()
  109. ("backlog,b", boost::program_options::value(&backlog)->default_value(32),
  110. "Number of backlog connections for listen.")
  111. ("daemon,d", boost::program_options::bool_switch(&opt_daemon)->default_value(false),
  112. "Daemon, detach and run in the background.")
  113. ("exceptions", boost::program_options::bool_switch(&opt_exceptions)->default_value(false),
  114. "Enable protocol exceptions by default.")
  115. ("file-descriptors,f", boost::program_options::value(&fds),
  116. "Number of file descriptors to allow for the process (total connections will be slightly less). Default is max allowed for user.")
  117. ("help,h", "Print this help menu.")
  118. ("job-retries,j", boost::program_options::value(&job_retries)->default_value(0),
  119. "Number of attempts to run the job before the job server removes it. This is helpful to ensure a bad job does not crash all available workers. Default is no limit.")
  120. ("log-file,l", boost::program_options::value(&log_file)->default_value(LOCALSTATEDIR"/log/gearmand.log"),
  121. "Log file to write errors and information to. If the log-file parameter is specified as 'stderr', then output will go to stderr. If 'none', then no logfile will be generated.")
  122. ("listen,L", boost::program_options::value(&host),
  123. "Address the server should listen on. Default is INADDR_ANY.")
  124. ("pid-file,P", boost::program_options::value(&pid_file)->default_value(GEARMAND_PID),
  125. "File to write process ID out to.")
  126. ("protocol,r", boost::program_options::value(&protocol),
  127. "Load protocol module.")
  128. ("round-robin,R", boost::program_options::bool_switch(&opt_round_robin)->default_value(false),
  129. "Assign work in round-robin order per worker connection. The default is to assign work in the order of functions added by the worker.")
  130. ("queue-type,q", boost::program_options::value(&queue_type)->default_value("builtin"),
  131. "Persistent queue type to use.")
  132. ("config-file", boost::program_options::value(&config_file)->default_value(GEARMAND_CONFIG),
  133. "Can be specified with '@name', too")
  134. ("syslog", boost::program_options::bool_switch(&opt_syslog)->default_value(false),
  135. "Use syslog.")
  136. ("threads,t", boost::program_options::value(&threads)->default_value(4),
  137. "Number of I/O threads to use. Default=4.")
  138. ("user,u", boost::program_options::value(&user),
  139. "Switch to given user after startup.")
  140. ("verbose", boost::program_options::value(&verbose_string)->default_value(verbose_string),
  141. "Set verbose level (FATAL, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG).")
  142. ("version,V", "Display the version of gearmand and exit.")
  143. ("worker-wakeup,w", boost::program_options::value(&worker_wakeup)->default_value(0),
  144. "Number of workers to wakeup for each job received. The default is to wakeup all available workers.")
  145. ;
  146. boost::program_options::options_description all("Allowed options");
  147. all.add(general);
  148. gearmand::protocol::HTTP http;
  149. all.add(http.command_line_options());
  150. gearmand::protocol::Gear gear;
  151. all.add(gear.command_line_options());
  152. gearmand::plugins::initialize(all);
  153. boost::program_options::positional_options_description positional;
  154. positional.add("provided", -1);
  155. // Now insert all options that we want to make visible to the user
  156. boost::program_options::options_description visible("Allowed options");
  157. visible.add(all);
  158. boost::program_options::options_description hidden("Hidden options");
  159. hidden.add_options()
  160. ("check-args", boost::program_options::bool_switch(&opt_check_args)->default_value(false),
  161. "Check command line and configuration file argments and then exit.");
  162. all.add(hidden);
  163. boost::program_options::variables_map vm;
  164. try {
  165. // Disable allow_guessing
  166. int style= boost::program_options::command_line_style::default_style ^ boost::program_options::command_line_style::allow_guessing;
  167. boost::program_options::parsed_options parsed= boost::program_options::command_line_parser(argc, argv)
  168. .options(all)
  169. .positional(positional)
  170. .style(style)
  171. .run();
  172. store(parsed, vm);
  173. notify(vm);
  174. if (config_file.empty() == false)
  175. {
  176. // Load the file and tokenize it
  177. std::ifstream ifs(config_file.c_str());
  178. if (ifs)
  179. {
  180. // Read the whole file into a string
  181. std::stringstream ss;
  182. ss << ifs.rdbuf();
  183. // Split the file content
  184. boost::char_separator<char> sep(" \n\r");
  185. std::string sstr= ss.str();
  186. boost::tokenizer<boost::char_separator<char> > tok(sstr, sep);
  187. std::vector<std::string> args;
  188. std::copy(tok.begin(), tok.end(), back_inserter(args));
  189. for (std::vector<std::string>::iterator iter= args.begin();
  190. iter != args.end();
  191. ++iter)
  192. {
  193. std::cerr << *iter << std::endl;
  194. }
  195. // Parse the file and store the options
  196. store(boost::program_options::command_line_parser(args).options(visible).run(), vm);
  197. }
  198. else if (config_file.compare(GEARMAND_CONFIG))
  199. {
  200. error::message("Could not open configuration file.");
  201. return EXIT_FAILURE;
  202. }
  203. }
  204. notify(vm);
  205. }
  206. catch(boost::program_options::validation_error &e)
  207. {
  208. error::message(e.what());
  209. return EXIT_FAILURE;
  210. }
  211. catch(std::exception &e)
  212. {
  213. if (e.what() and strncmp("-v", e.what(), 2) == 0)
  214. {
  215. error::message("Option -v has been deprecated, please use --verbose");
  216. }
  217. else
  218. {
  219. error::message(e.what());
  220. }
  221. return EXIT_FAILURE;
  222. }
  223. gearmand_verbose_t verbose= GEARMAND_VERBOSE_ERROR;
  224. if (gearmand_verbose_check(verbose_string.c_str(), verbose) == false)
  225. {
  226. error::message("Invalid value for --verbose supplied");
  227. return EXIT_FAILURE;
  228. }
  229. if (opt_check_args)
  230. {
  231. return EXIT_SUCCESS;
  232. }
  233. if (vm.count("help"))
  234. {
  235. std::cout << visible << std::endl;
  236. return EXIT_SUCCESS;
  237. }
  238. if (vm.count("version"))
  239. {
  240. std::cout << std::endl << "gearmand " << gearmand_version() << " - " << gearmand_bugreport() << std::endl;
  241. return EXIT_SUCCESS;
  242. }
  243. if (fds > 0 && _set_fdlimit(fds))
  244. {
  245. return EXIT_FAILURE;
  246. }
  247. if (not user.empty() and _switch_user(user.c_str()))
  248. {
  249. return EXIT_FAILURE;
  250. }
  251. if (opt_daemon)
  252. {
  253. util::daemonize(false, true);
  254. }
  255. if (_set_signals())
  256. {
  257. return EXIT_FAILURE;
  258. }
  259. util::Pidfile _pid_file(pid_file);
  260. if (_pid_file.create() == false and pid_file.compare(GEARMAND_PID))
  261. {
  262. error::perror(_pid_file.error_message().c_str());
  263. return EXIT_FAILURE;
  264. }
  265. gearmand::gearmand_log_info_st log_info(log_file, opt_syslog);
  266. if (log_info.initialized() == false)
  267. {
  268. return EXIT_FAILURE;
  269. }
  270. gearmand_st *_gearmand= gearmand_create(host.empty() ? NULL : host.c_str(),
  271. threads, backlog,
  272. static_cast<uint8_t>(job_retries),
  273. static_cast<uint8_t>(worker_wakeup),
  274. _log, &log_info, verbose,
  275. opt_round_robin, opt_exceptions);
  276. if (_gearmand == NULL)
  277. {
  278. error::message("Could not create gearmand library instance.");
  279. return EXIT_FAILURE;
  280. }
  281. if (queue_type.empty() == false)
  282. {
  283. gearmand_error_t rc;
  284. if ((rc= gearmand::queue::initialize(_gearmand, queue_type.c_str())) != GEARMAN_SUCCESS)
  285. {
  286. error::message("Error while initializing the queue", queue_type.c_str());
  287. gearmand_free(_gearmand);
  288. return EXIT_FAILURE;
  289. }
  290. }
  291. if (gear.start(_gearmand) != GEARMAN_SUCCESS)
  292. {
  293. error::message("Error while enabling Gear protocol module");
  294. gearmand_free(_gearmand);
  295. return EXIT_FAILURE;
  296. }
  297. if (protocol.compare("http") == 0)
  298. {
  299. if (http.start(_gearmand) != GEARMAN_SUCCESS)
  300. {
  301. error::message("Error while enabling protocol module", protocol.c_str());
  302. gearmand_free(_gearmand);
  303. return EXIT_FAILURE;
  304. }
  305. }
  306. else if (protocol.empty() == false)
  307. {
  308. error::message("Unknown protocol module", protocol.c_str());
  309. gearmand_free(_gearmand);
  310. return EXIT_FAILURE;
  311. }
  312. if (opt_daemon)
  313. {
  314. if (util::daemon_is_ready(true) == false)
  315. {
  316. return EXIT_FAILURE;
  317. }
  318. }
  319. gearmand_error_t ret= gearmand_run(_gearmand);
  320. gearmand_free(_gearmand);
  321. _gearmand= NULL;
  322. return (ret == GEARMAN_SUCCESS || ret == GEARMAN_SHUTDOWN) ? 0 : 1;
  323. }
  324. static bool _set_fdlimit(rlim_t fds)
  325. {
  326. struct rlimit rl;
  327. if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
  328. {
  329. error::perror("Could not get file descriptor limit");
  330. return true;
  331. }
  332. rl.rlim_cur= fds;
  333. if (rl.rlim_max < rl.rlim_cur)
  334. {
  335. rl.rlim_max= rl.rlim_cur;
  336. }
  337. if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
  338. {
  339. error::perror("Failed to set limit for the number of file "
  340. "descriptors. Try running as root or giving a "
  341. "smaller value to -f.");
  342. return true;
  343. }
  344. return false;
  345. }
  346. static bool _switch_user(const char *user)
  347. {
  348. if (getuid() == 0 or geteuid() == 0)
  349. {
  350. struct passwd *pw= getpwnam(user);
  351. if (not pw)
  352. {
  353. error::message("Could not find user", user);
  354. return EXIT_FAILURE;
  355. }
  356. if (setgid(pw->pw_gid) == -1 || setuid(pw->pw_uid) == -1)
  357. {
  358. error::message("Could not switch to user", user);
  359. return EXIT_FAILURE;
  360. }
  361. }
  362. else
  363. {
  364. error::message("Must be root to switch users.");
  365. return true;
  366. }
  367. return false;
  368. }
  369. static void _shutdown_handler(int signal_arg)
  370. {
  371. if (signal_arg == SIGUSR1)
  372. {
  373. gearmand_wakeup(Gearmand(), GEARMAND_WAKEUP_SHUTDOWN_GRACEFUL);
  374. }
  375. else
  376. {
  377. gearmand_wakeup(Gearmand(), GEARMAND_WAKEUP_SHUTDOWN);
  378. }
  379. }
  380. static void _reset_log_handler(int) // signal_arg
  381. {
  382. gearmand_log_info_st *log_info= static_cast<gearmand_log_info_st *>(Gearmand()->log_context);
  383. log_info->write(GEARMAND_VERBOSE_NOTICE, "SIGHUP, reopening log file");
  384. log_info->reset();
  385. }
  386. extern "C" {
  387. static bool _set_signals(void)
  388. {
  389. struct sigaction sa;
  390. memset(&sa, 0, sizeof(struct sigaction));
  391. sa.sa_handler= SIG_IGN;
  392. if (sigemptyset(&sa.sa_mask) == -1 or
  393. sigaction(SIGPIPE, &sa, 0) == -1)
  394. {
  395. error::perror("Could not set SIGPIPE handler.");
  396. return true;
  397. }
  398. sa.sa_handler= _shutdown_handler;
  399. if (sigaction(SIGTERM, &sa, 0) == -1)
  400. {
  401. error::perror("Could not set SIGTERM handler.");
  402. return true;
  403. }
  404. if (sigaction(SIGINT, &sa, 0) == -1)
  405. {
  406. error::perror("Could not set SIGINT handler.");
  407. return true;
  408. }
  409. if (sigaction(SIGUSR1, &sa, 0) == -1)
  410. {
  411. error::perror("Could not set SIGUSR1 handler.");
  412. return true;
  413. }
  414. sa.sa_handler= _reset_log_handler;
  415. if (sigaction(SIGHUP, &sa, 0) == -1)
  416. {
  417. error::perror("Could not set SIGHUP handler.");
  418. return true;
  419. }
  420. return false;
  421. }
  422. }
  423. static void _log(const char *mesg, gearmand_verbose_t verbose, void *context)
  424. {
  425. gearmand_log_info_st *log_info= static_cast<gearmand_log_info_st *>(context);
  426. log_info->write(verbose, mesg);
  427. }