Anope IRC Services  Version 2.0
init.cpp
Go to the documentation of this file.
1 /* Initalization and related routines.
2  *
3  * (C) 2003-2014 Anope Team
4  * Contact us at team@anope.org
5  *
6  * Please read COPYING and README for further details.
7  *
8  * Based on the original code of Epona by Lara.
9  * Based on the original code of Services by Andy Church.
10  *
11  */
12 
13 #include "services.h"
14 #include "config.h"
15 #include "users.h"
16 #include "protocol.h"
17 #include "bots.h"
18 #include "xline.h"
19 #include "socketengine.h"
20 #include "servers.h"
21 #include "language.h"
22 
23 #ifndef _WIN32
24 #include <sys/wait.h>
25 #include <sys/stat.h>
26 
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <pwd.h>
30 #include <grp.h>
31 #endif
32 
34 
35 /* Vector of pairs of command line arguments and their params */
36 static std::vector<std::pair<Anope::string, Anope::string> > CommandLineArguments;
37 
43 static void ParseCommandLineArguments(int ac, char **av)
44 {
45  for (int i = 1; i < ac; ++i)
46  {
47  Anope::string option = av[i];
48  Anope::string param;
49  while (!option.empty() && option[0] == '-')
50  option.erase(option.begin());
51  size_t t = option.find('=');
52  if (t != Anope::string::npos)
53  {
54  param = option.substr(t + 1);
55  option.erase(t);
56  }
57 
58  if (option.empty())
59  continue;
60 
61  CommandLineArguments.push_back(std::make_pair(option, param));
62  }
63 }
64 
71 static bool GetCommandLineArgument(const Anope::string &name, char shortname, Anope::string &param)
72 {
73  param.clear();
74 
75  for (std::vector<std::pair<Anope::string, Anope::string> >::iterator it = CommandLineArguments.begin(), it_end = CommandLineArguments.end(); it != it_end; ++it)
76  {
77  if (it->first.equals_ci(name) || (it->first.length() == 1 && it->first[0] == shortname))
78  {
79  param = it->second;
80  return true;
81  }
82  }
83 
84  return false;
85 }
86 
92 static bool GetCommandLineArgument(const Anope::string &name, char shortname = 0)
93 {
94  Anope::string Unused;
95  return GetCommandLineArgument(name, shortname, Unused);
96 }
97 
99 {
100  return isatty(fileno(stdout)) && isatty(fileno(stdin)) && isatty(fileno(stderr));
101 }
102 
104 {
105 #ifndef _WIN32
106  kill(getppid(), SIGUSR2);
107 
108  freopen("/dev/null", "r", stdin);
109  freopen("/dev/null", "w", stdout);
110  freopen("/dev/null", "w", stderr);
111 
112  setpgid(0, 0);
113 #else
114  FreeConsole();
115 #endif
116 }
117 
119 {
120  switch (Signal)
121  {
122  case SIGHUP:
123  {
125 
126  try
127  {
128  Configuration::Conf *new_config = new Configuration::Conf();
130  Config = new_config;
131  Config->Post(old);
132  delete old;
133  }
134  catch (const ConfigException &ex)
135  {
136  Log() << "Error reloading configuration file: " << ex.GetReason();
137  }
138  break;
139  }
140  case SIGTERM:
141  case SIGINT:
142 #ifndef _WIN32
143  Log() << "Received " << strsignal(Signal) << " signal (" << Signal << "), exiting.";
144  Anope::QuitReason = Anope::string("Services terminating via signal ") + strsignal(Signal) + " (" + stringify(Signal) + ")";
145 #else
146  Log() << "Received signal " << Signal << ", exiting.";
147  Anope::QuitReason = Anope::string("Services terminating via signal ") + stringify(Signal);
148 #endif
149  Anope::Quitting = true;
151  break;
152  }
153 
154  Signal = 0;
155 }
156 
157 #ifndef _WIN32
158 static void parent_signal_handler(int signal)
159 {
160  if (signal == SIGUSR2)
161  {
162  Anope::Quitting = true;
163  }
164  else if (signal == SIGCHLD)
165  {
166  Anope::ReturnValue = -1;
167  Anope::Quitting = true;
168  int status = 0;
169  wait(&status);
170  if (WIFEXITED(status))
171  Anope::ReturnValue = WEXITSTATUS(status);
172  }
173 }
174 #endif
175 
176 static void SignalHandler(int sig)
177 {
178  Anope::Signal = sig;
179 }
180 
181 static void InitSignals()
182 {
183  struct sigaction sa;
184 
185  sa.sa_flags = 0;
186  sigemptyset(&sa.sa_mask);
187 
189 
190  sigaction(SIGHUP, &sa, NULL);
191 
192  sigaction(SIGTERM, &sa, NULL);
193  sigaction(SIGINT, &sa, NULL);
194 
195  sa.sa_handler = SIG_IGN;
196 
197 #ifndef _WIN32
198  sigaction(SIGCHLD, &sa, NULL);
199 #endif
200  sigaction(SIGPIPE, &sa, NULL);
201 }
202 
203 /* Remove our PID file. Done at exit. */
204 
205 static void remove_pidfile()
206 {
207  remove(Config->GetBlock("serverinfo")->Get<const Anope::string>("pid").c_str());
208 }
209 
210 /* Create our PID file and write the PID to it. */
211 
212 static void write_pidfile()
213 {
214  FILE *pidfile = fopen(Config->GetBlock("serverinfo")->Get<const Anope::string>("pid").c_str(), "w");
215  if (pidfile)
216  {
217 #ifdef _WIN32
218  fprintf(pidfile, "%d\n", static_cast<int>(GetCurrentProcessId()));
219 #else
220  fprintf(pidfile, "%d\n", static_cast<int>(getpid()));
221 #endif
222  fclose(pidfile);
223  atexit(remove_pidfile);
224  }
225  else
226  throw CoreException("Can not write to PID file " + Config->GetBlock("serverinfo")->Get<const Anope::string>("pid"));
227 }
228 
229 static void setuidgid()
230 {
231 #ifndef _WIN32
232  Configuration::Block *options = Config->GetBlock("options");
233  uid_t uid = -1;
234  gid_t gid = -1;
235 
236  if (!options->Get<const Anope::string>("user").empty())
237  {
238  errno = 0;
239  struct passwd *u = getpwnam(options->Get<const Anope::string>("user").c_str());
240  if (u == NULL)
241  Log() << "Unable to setuid to " << options->Get<const Anope::string>("user") << ": " << Anope::LastError();
242  else
243  uid = u->pw_uid;
244  }
245  if (!options->Get<const Anope::string>("group").empty())
246  {
247  errno = 0;
248  struct group *g = getgrnam(options->Get<const Anope::string>("group").c_str());
249  if (g == NULL)
250  Log() << "Unable to setgid to " << options->Get<const Anope::string>("group") << ": " << Anope::LastError();
251  else
252  gid = g->gr_gid;
253  }
254 
255  for (unsigned i = 0; i < Config->LogInfos.size(); ++i)
256  {
257  LogInfo& li = Config->LogInfos[i];
258 
259  for (unsigned j = 0; j < li.logfiles.size(); ++j)
260  {
261  LogFile* lf = li.logfiles[j];
262 
263  chown(lf->filename.c_str(), uid, gid);
264  }
265  }
266 
267  if (static_cast<int>(gid) != -1)
268  {
269  if (setgid(gid) == -1)
270  Log() << "Unable to setgid to " << options->Get<const Anope::string>("group") << ": " << Anope::LastError();
271  else
272  Log() << "Successfully set group to " << options->Get<const Anope::string>("group");
273  }
274  if (static_cast<int>(uid) != -1)
275  {
276  if (setuid(uid) == -1)
277  Log() << "Unable to setuid to " << options->Get<const Anope::string>("user") << ": " << Anope::LastError();
278  else
279  Log() << "Successfully set user to " << options->Get<const Anope::string>("user");
280  }
281 #endif
282 }
283 
284 void Anope::Init(int ac, char **av)
285 {
286  /* Set file creation mask and group ID. */
287 #if defined(DEFUMASK) && HAVE_UMASK
288  umask(DEFUMASK);
289 #endif
290 
292 
293  /* Parse command line arguments */
295 
296  if (GetCommandLineArgument("version", 'v'))
297  {
298  Log(LOG_TERMINAL) << "Anope-" << Anope::Version() << " -- " << Anope::VersionBuildString();
299  throw CoreException();
300  }
301 
302  if (GetCommandLineArgument("help", 'h'))
303  {
304  Log(LOG_TERMINAL) << "Anope-" << Anope::Version() << " -- " << Anope::VersionBuildString();
305  Log(LOG_TERMINAL) << "Anope IRC Services (http://www.anope.org)";
306  Log(LOG_TERMINAL) << "Usage ./" << Anope::ServicesBin << " [options] ...";
307  Log(LOG_TERMINAL) << "-c, --config=filename.conf";
308  Log(LOG_TERMINAL) << " --confdir=conf file direcory";
309  Log(LOG_TERMINAL) << " --dbdir=database directory";
310  Log(LOG_TERMINAL) << "-d, --debug[=level]";
311  Log(LOG_TERMINAL) << "-h, --help";
312  Log(LOG_TERMINAL) << " --localedir=locale directory";
313  Log(LOG_TERMINAL) << " --logdir=logs directory";
314  Log(LOG_TERMINAL) << " --modulesdir=modules directory";
315  Log(LOG_TERMINAL) << "-e, --noexpire";
316  Log(LOG_TERMINAL) << "-n, --nofork";
317  Log(LOG_TERMINAL) << " --nothird";
318  Log(LOG_TERMINAL) << " --protocoldebug";
319  Log(LOG_TERMINAL) << "-r, --readonly";
320  Log(LOG_TERMINAL) << "-s, --support";
321  Log(LOG_TERMINAL) << "-v, --version";
322  Log(LOG_TERMINAL) << "";
323  Log(LOG_TERMINAL) << "Further support is available from http://www.anope.org";
324  Log(LOG_TERMINAL) << "Or visit us on IRC at irc.anope.org #anope";
325  throw CoreException();
326  }
327 
328  if (GetCommandLineArgument("nofork", 'n'))
329  Anope::NoFork = true;
330 
331  if (GetCommandLineArgument("support", 's'))
332  {
333  Anope::NoFork = Anope::NoThird = true;
334  ++Anope::Debug;
335  }
336 
337  if (GetCommandLineArgument("readonly", 'r'))
338  Anope::ReadOnly = true;
339 
340  if (GetCommandLineArgument("nothird"))
341  Anope::NoThird = true;
342 
343  if (GetCommandLineArgument("noexpire", 'e'))
344  Anope::NoExpire = true;
345 
346  if (GetCommandLineArgument("protocoldebug"))
347  Anope::ProtocolDebug = true;
348 
349  Anope::string arg;
350  if (GetCommandLineArgument("debug", 'd', arg))
351  {
352  if (!arg.empty())
353  {
354  int level = arg.is_number_only() ? convertTo<int>(arg) : -1;
355  if (level > 0)
356  Anope::Debug = level;
357  else
358  throw CoreException("Invalid option given to --debug");
359  }
360  else
361  ++Anope::Debug;
362  }
363 
364  if (GetCommandLineArgument("config", 'c', arg))
365  {
366  if (arg.empty())
367  throw CoreException("The --config option requires a file name");
368  ServicesConf = Configuration::File(arg, false);
369  }
370 
371  if (GetCommandLineArgument("confdir", 0, arg))
372  {
373  if (arg.empty())
374  throw CoreException("The --confdir option requires a path");
375  Anope::ConfigDir = arg;
376  }
377 
378  if (GetCommandLineArgument("dbdir", 0, arg))
379  {
380  if (arg.empty())
381  throw CoreException("The --dbdir option requires a path");
382  Anope::DataDir = arg;
383  }
384 
385  if (GetCommandLineArgument("localedir", 0, arg))
386  {
387  if (arg.empty())
388  throw CoreException("The --localedir option requires a path");
389  Anope::LocaleDir = arg;
390  }
391 
392  if (GetCommandLineArgument("modulesdir", 0, arg))
393  {
394  if (arg.empty())
395  throw CoreException("The --modulesdir option requires a path");
396  Anope::ModuleDir = arg;
397  }
398 
399  if (GetCommandLineArgument("logdir", 0, arg))
400  {
401  if (arg.empty())
402  throw CoreException("The --logdir option requires a path");
403  Anope::LogDir = arg;
404  }
405 
406  /* Chdir to Services data directory. */
407  if (chdir(Anope::ServicesDir.c_str()) < 0)
408  {
409  throw CoreException("Unable to chdir to " + Anope::ServicesDir + ": " + Anope::LastError());
410  }
411 
412  Log(LOG_TERMINAL) << "Anope " << Anope::Version() << ", " << Anope::VersionBuildString();
413 
414 #ifdef _WIN32
415  if (!SupportedWindowsVersion())
416  throw CoreException(GetWindowsVersion() + " is not a supported version of Windows");
417 #else
418  /* If we're root, issue a warning now */
419  if (!getuid() && !getgid())
420  {
421  std::cerr << "WARNING: You are currently running Anope as the root superuser. Anope does not" << std::endl;
422  std::cerr << " require root privileges to run, and it is discouraged that you run Anope" << std::endl;
423  std::cerr << " as the root superuser." << std::endl;
424  sleep(3);
425  }
426 #endif
427 
428 #ifdef _WIN32
429  Log(LOG_TERMINAL) << "Using configuration file " << Anope::ConfigDir << "\\" << ServicesConf.GetName();
430 #else
431  Log(LOG_TERMINAL) << "Using configuration file " << Anope::ConfigDir << "/" << ServicesConf.GetName();
432 
433  /* Fork to background */
434  if (!Anope::NoFork && Anope::AtTerm())
435  {
436  /* Install these before fork() - it is possible for the child to
437  * connect and kill() the parent before it is able to install the
438  * handler.
439  */
440  struct sigaction sa, old_sigusr2, old_sigchld;
441 
442  sa.sa_flags = 0;
443  sigemptyset(&sa.sa_mask);
445 
446  sigaction(SIGUSR2, &sa, &old_sigusr2);
447  sigaction(SIGCHLD, &sa, &old_sigchld);
448 
449  int i = fork();
450  if (i > 0)
451  {
452  sigset_t mask;
453 
454  sigemptyset(&mask);
455  sigsuspend(&mask);
456 
457  exit(Anope::ReturnValue);
458  }
459  else if (i == -1)
460  {
461  Log() << "Error, unable to fork: " << Anope::LastError();
462  Anope::NoFork = true;
463  }
464 
465  /* Child doesn't need these */
466  sigaction(SIGUSR2, &old_sigusr2, NULL);
467  sigaction(SIGCHLD, &old_sigchld, NULL);
468  }
469 
470 #endif
471 
472  /* Initialize the socket engine. Note that some engines can not survive a fork(), so this must be here. */
474 
475  /* Read configuration file; exit if there are problems. */
476  try
477  {
478  Config = new Configuration::Conf();
479  }
480  catch (const ConfigException &ex)
481  {
482  Log(LOG_TERMINAL) << ex.GetReason();
483  Log(LOG_TERMINAL) << "*** Support resources: Read through the services.conf self-contained";
484  Log(LOG_TERMINAL) << "*** documentation. Read the documentation files found in the 'docs'";
485  Log(LOG_TERMINAL) << "*** folder. Visit our portal located at http://www.anope.org/. Join";
486  Log(LOG_TERMINAL) << "*** our support channel on /server irc.anope.org channel #anope.";
487  throw CoreException("Configuration file failed to validate");
488  }
489 
490  /* Create me */
491  Configuration::Block *block = Config->GetBlock("serverinfo");
492  Me = new Server(NULL, block->Get<const Anope::string>("name"), 0, block->Get<const Anope::string>("description"), block->Get<const Anope::string>("id"));
493  for (botinfo_map::const_iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it)
494  {
495  it->second->server = Me;
496  ++Me->users;
497  }
498 
499  /* Announce ourselves to the logfile. */
500  Log() << "Anope " << Anope::Version() << " starting up" << (Anope::Debug || Anope::ReadOnly ? " (options:" : "") << (Anope::Debug ? " debug" : "") << (Anope::ReadOnly ? " readonly" : "") << (Anope::Debug || Anope::ReadOnly ? ")" : "");
501 
502  InitSignals();
503 
504  /* Initialize multi-language support */
506 
507  /* Initialize random number generator */
508  block = Config->GetBlock("options");
509  srand(block->Get<unsigned>("seed") ^ time(NULL));
510 
511  /* load modules */
512  Log() << "Loading modules...";
513  for (int i = 0; i < Config->CountBlock("module"); ++i)
514  ModuleManager::LoadModule(Config->GetBlock("module", i)->Get<const Anope::string>("name"), NULL);
515 
516  setuidgid();
517 
519  if (protocol == NULL)
520  throw CoreException("You must load a protocol module!");
521 
522  /* Write our PID to the PID file. */
523  write_pidfile();
524 
525  Log() << "Using IRCd protocol " << protocol->name;
526 
527  /* Auto assign sid if applicable */
528  if (IRCD->RequiresID)
529  {
531  if (Me->GetSID() == Me->GetName())
532  Me->SetSID(sid);
533  for (botinfo_map::iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it)
534  it->second->GenerateUID();
535  }
536 
537  /* Load up databases */
538  Log() << "Loading databases...";
539  EventReturn MOD_RESULT;
540  FOREACH_RESULT(OnLoadDatabase, MOD_RESULT, ());
541  static_cast<void>(MOD_RESULT);
542  Log() << "Databases loaded";
543 
544  for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it)
545  it->second->Sync();
546 
548 }
549 
CoreExport bool ReadOnly
Definition: main.cpp:28
CoreExport Anope::string ModuleDir
Definition: init.cpp:33
Definition: servers.h:42
Anope::string name
Definition: modules.h:221
static void SignalHandler(int sig)
Definition: init.cpp:176
sig_atomic_t Signal
Definition: main.cpp:33
void clear()
Definition: anope.h:187
static void write_pidfile()
Definition: init.cpp:212
CoreExport bool NoThird
Definition: main.cpp:28
void Init(int ac, char **av)
Definition: init.cpp:284
static void Init()
CoreExport string Version()
Definition: misc.cpp:625
CoreExport const string LastError()
Definition: misc.cpp:606
const Anope::string & GetSID() const
Definition: servers.cpp:203
#define SIGPIPE
Definition: sigaction.h:17
int ReturnValue
Definition: main.cpp:32
void CheckTypes()
Definition: serialize.cpp:37
int sa_mask
Definition: sigaction.h:24
bool AtTerm()
Definition: init.cpp:98
static void ParseCommandLineArguments(int ac, char **av)
Definition: init.cpp:43
static void parent_signal_handler(int signal)
Definition: init.cpp:158
#define SIGHUP
Definition: sigaction.h:14
int sa_flags
Definition: sigaction.h:23
void SetSID(const Anope::string &sid)
Definition: servers.cpp:195
std::vector< LogFile * > logfiles
Definition: logger.h:119
#define FOREACH_RESULT(ename, ret, args)
Definition: modules.h:95
bool RequiresID
Definition: protocol.h:69
virtual Anope::string SID_Retrieve()
Definition: protocol.cpp:76
void RegisterTypes()
Definition: serialize.cpp:30
iterator erase(const iterator &i)
Definition: anope.h:155
string substr(size_type pos=0, size_type n=npos) const
Definition: anope.h:277
void Fork()
Definition: init.cpp:103
unsigned users
Definition: servers.h:84
static void remove_pidfile()
Definition: init.cpp:205
CoreExport bool Quitting
Definition: main.cpp:34
Definition: Config.cs:26
CoreExport Anope::string ServicesDir
Definition: main.cpp:29
static const size_type npos
Definition: anope.h:44
const Anope::string & GetName() const
Definition: config.cpp:647
CoreExport Serialize::Checker< botinfo_map > BotListByNick
void InitLanguages()
Definition: language.cpp:26
int sigaction(int sig, struct sigaction *action, struct sigaction *old)
Definition: sigaction.cpp:15
bool empty() const
Definition: anope.h:126
Definition: logger.h:42
CoreExport IRCDProto * IRCD
Definition: protocol.cpp:23
std::basic_string< char, ci_char_traits, std::allocator< char > > string
Definition: hashcomp.h:133
EventReturn
Definition: modules.h:129
CoreExport Anope::string ServicesBin
Definition: main.cpp:30
CoreExport channel_map ChannelList
Definition: channels.cpp:29
CoreExport bool ProtocolDebug
Definition: main.cpp:28
CoreExport Anope::string LogDir
Definition: init.cpp:33
static Module * FindFirstOf(ModType type)
Anope::string filename
Definition: logger.h:44
CoreExport string VersionBuildString()
Definition: misc.cpp:639
CoreExport Anope::string ConfigDir
Definition: init.cpp:33
Anope::string stringify(const T &x)
Definition: anope.h:710
CoreExport Anope::string DataDir
Definition: init.cpp:33
Configuration::File ServicesConf
static void InitSignals()
Definition: init.cpp:181
CoreExport bool NoExpire
Definition: main.cpp:28
const Anope::string & GetName() const
Definition: servers.cpp:175
void HandleSignal()
Definition: init.cpp:118
static ModuleReturn LoadModule(const Anope::string &modname, User *u)
CoreExport int Debug
Definition: main.cpp:27
Anope::string name
Definition: access.cpp:22
CoreExport Anope::string LocaleDir
Definition: init.cpp:33
CoreExport Server * Me
Definition: servers.cpp:24
virtual const Anope::string & GetReason() const
Definition: anope.h:672
CoreExport bool NoFork
Definition: main.cpp:28
static bool GetCommandLineArgument(const Anope::string &name, char shortname, Anope::string &param)
Definition: init.cpp:71
const char * c_str() const
Definition: anope.h:117
Definition: logger.h:53
T Get(const Anope::string &tag)
Definition: config.h:44
CoreExport Configuration::Conf * Config
Definition: config.cpp:24
static std::vector< std::pair< Anope::string, Anope::string > > CommandLineArguments
Definition: init.cpp:36
static void setuidgid()
Definition: init.cpp:229
CoreExport void SaveDatabases()
Definition: main.cpp:67
iterator begin()
Definition: anope.h:282
bool is_number_only() const
Definition: anope.h:217
size_type find(const string &_str, size_type pos=0) const
Definition: anope.h:192
CoreExport Anope::string QuitReason
Definition: main.cpp:36
void(* sa_handler)(int)
Definition: sigaction.h:22
#define sigemptyset(x)
Definition: sigaction.h:11