~singpolyma/biboumi

ref: f370ce3a80ef77f58a13b0328aa09c7becbf4f0e biboumi/src/main.cpp -rw-r--r-- 7.6 KiB
f370ce3aStephen Paul Weber Add CI 8 months ago
                                                                                
d600a284 Florent Le Coz
d9d30dd7 Florent Le Coz
f0d9273d Florent Le Coz
3afb63a6 Florent Le Coz
ea0b2f2b Florent Le Coz
4c1b9abe louiz’
3afb63a6 Florent Le Coz
5b560078 louiz’
b86547dc Florent Le Coz
4c1b9abe louiz’
5402a256 louiz’
9167cdf1 Florent Le Coz
d872c2b4 louiz’
3afb63a6 Florent Le Coz
ae02e58b louiz’
8fd27466 Florent Le Coz
ae02e58b louiz’
3afb63a6 Florent Le Coz
bf7b05ef Florent Le Coz
b60cbda4 Florent Le Coz
58000c36 louiz’
db503b23 louiz’
b60cbda4 Florent Le Coz
f89361c3 louiz’
0bb4f144 Félix Baylac-Jacqué
f89361c3 louiz’
d9d30dd7 Florent Le Coz
3afb63a6 Florent Le Coz
d9d30dd7 Florent Le Coz
5c9d2c23 Florent Le Coz
d9d30dd7 Florent Le Coz
5f82c937 Florent Le Coz
3afb63a6 Florent Le Coz
8fd27466 Florent Le Coz
5f82c937 Florent Le Coz
8fd27466 Florent Le Coz
9fa75910 louiz’
64c1b28c Florent Le Coz
897b281e Florent Le Coz
8a912ea3 louiz’
897b281e Florent Le Coz
ed36c265 louiz’
897b281e Florent Le Coz
3afb63a6 Florent Le Coz
8fd27466 Florent Le Coz
e8671042 louiz’
3afb63a6 Florent Le Coz
b1564e4d louiz’
8fd27466 Florent Le Coz
e8671042 louiz’
6804f315 Florent Le Coz
8fd27466 Florent Le Coz
ed36c265 louiz’
9fa75910 louiz’
3afb63a6 Florent Le Coz
9fa75910 louiz’
d5324ac7 Florent Le Coz
d872c2b4 louiz’
5b560078 louiz’
d5324ac7 Florent Le Coz
99aba566 Florent Le Coz
cf618d55 Ailin Nemui
d872c2b4 louiz’
5c9d2c23 Florent Le Coz
5507adbe Florent Le Coz
3afb63a6 Florent Le Coz
5c9d2c23 Florent Le Coz
e8e592d1 Florent Le Coz
5ea51fea Ailin Nemui
3afb63a6 Florent Le Coz
9167cdf1 Florent Le Coz
3afb63a6 Florent Le Coz
5f82c937 Florent Le Coz
3afb63a6 Florent Le Coz
5b560078 louiz’
5ea51fea Ailin Nemui
9fa75910 louiz’
d5324ac7 Florent Le Coz
a50ca30e Florent Le Coz
3afb63a6 Florent Le Coz
8fd27466 Florent Le Coz
7115aa3b Florent Le Coz
5f82c937 Florent Le Coz
8fd27466 Florent Le Coz
ffc820e2 Florent Le Coz
d872c2b4 louiz’
cdc3183d Florent Le Coz
ffc820e2 Florent Le Coz
a50ca30e Florent Le Coz
d872c2b4 louiz’
a50ca30e Florent Le Coz
32384047 louiz’
d872c2b4 louiz’
32384047 louiz’
d872c2b4 louiz’
32384047 louiz’
d872c2b4 louiz’
e2da6fcd louiz’
5ea51fea Ailin Nemui
9fa75910 louiz’
e2da6fcd louiz’
a50ca30e Florent Le Coz
3afb63a6 Florent Le Coz
99aba566 Florent Le Coz
5507adbe Florent Le Coz
3afb63a6 Florent Le Coz
a50ca30e Florent Le Coz
3afb63a6 Florent Le Coz
810ea19f Florent Le Coz
03a8accc Florent Le Coz
64c1b28c Florent Le Coz
9fa75910 louiz’
5607c101 louiz’
0bb4f144 Félix Baylac-Jacqué
9fa75910 louiz’
0bb4f144 Félix Baylac-Jacqué
9fa75910 louiz’
0bb4f144 Félix Baylac-Jacqué
9fa75910 louiz’
0bb4f144 Félix Baylac-Jacqué
5607c101 louiz’
9fa75910 louiz’
5607c101 louiz’
9fa75910 louiz’
0bb4f144 Félix Baylac-Jacqué
9fa75910 louiz’
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
#include <xmpp/biboumi_component.hpp>
#include <utils/timed_events.hpp>
#include <network/poller.hpp>
#include <config/config.hpp>
#include <logger/logger.hpp>
#include <utils/xdg.hpp>
#include <utils/reload.hpp>

#ifdef UDNS_FOUND
# include <network/dns_handler.hpp>
#endif

#include <atomic>
#include <csignal>

#include <identd/identd_server.hpp>

// A flag set by the SIGINT signal handler.
static std::atomic<bool> stop(false);
// Flag set by the SIGUSR1/2 signal handler.
static std::atomic<bool> reload(false);
// A flag indicating that we are wanting to exit the process. i.e: if this
// flag is set and all connections are closed, we can exit properly.
static bool exiting = false;

/**
 * Provide an helpful message to help the user write a minimal working
 * configuration file.
 */
int config_help(const std::string& missing_option)
{
  if (!missing_option.empty())
    log_error("Configuration error: empty value for option ", missing_option, ".");
  log_error("Please provide a configuration file filled like this:\n\n"
            "hostname=irc.example.com\npassword=S3CR3T");
  return 1;
}

int display_help()
{
  std::cout << "Usage: biboumi [-ht] [configuration_file]" << std::endl;
  return 0;
}

static void sigint_handler(int sig, siginfo_t*, void*)
{
  // In 2 seconds, repeat the same signal, to force the exit
  TimedEventsManager::instance().add_event(TimedEvent(std::chrono::steady_clock::now() + 2s,
                                    [sig]() { raise(sig); }));
  stop.store(true);
}

static void sigusr_handler(int, siginfo_t*, void*)
{
  reload.store(true);
}

static void setup_signals()
{
  // Block the signals we want to manage. They will be unblocked only during
  // the epoll_pwait or ppoll calls. This avoids some race conditions,
  // explained in man 2 pselect on linux
  sigset_t mask{};
  sigemptyset(&mask);
  sigaddset(&mask, SIGINT);
  sigaddset(&mask, SIGTERM);
  sigaddset(&mask, SIGUSR1);
  sigaddset(&mask, SIGUSR2);
  sigaddset(&mask, SIGHUP);
  sigprocmask(SIG_BLOCK, &mask, nullptr);

  // Install the signals used to exit the process cleanly, or reload the
  // config
  struct sigaction on_sigint;
  on_sigint.sa_sigaction = &sigint_handler;
  // All signals must be blocked while a signal handler is running
  sigfillset(&on_sigint.sa_mask);
  // we want to catch that signal only once.
  // Sending SIGINT again will "force" an exit
  on_sigint.sa_flags = 0 & SA_RESETHAND;
  sigaction(SIGINT, &on_sigint, nullptr);
  sigaction(SIGTERM, &on_sigint, nullptr);

  // Install a signal to reload the config on SIGUSR1/2
  struct sigaction on_sigusr;
  on_sigusr.sa_sigaction = &sigusr_handler;
  sigfillset(&on_sigusr.sa_mask);
  on_sigusr.sa_flags = 0;
  sigaction(SIGUSR1, &on_sigusr, nullptr);
  sigaction(SIGUSR2, &on_sigusr, nullptr);
  sigaction(SIGHUP, &on_sigusr, nullptr);
}

static int main_loop(std::string hostname, std::string password)
{
  auto p = std::make_shared<Poller>();

#ifdef UDNS_FOUND
  DNSHandler dns_handler(p);
#endif

  auto xmpp_component =
    std::make_shared<BiboumiComponent>(p, hostname, password);
  xmpp_component->start();

  std::unique_ptr<IdentdServer> identd;
  if (Config::get_int("identd_port", 113) != 0)
    identd = std::make_unique<IdentdServer>(*xmpp_component, p, static_cast<uint16_t>(Config::get_int("identd_port", 113)));

  auto timeout = TimedEventsManager::instance().get_timeout();
  while (p->poll(timeout) != -1)
  {
    TimedEventsManager::instance().execute_expired_events();
    // Check for empty irc_clients (not connected, or with no joined
    // channel) and remove them
    xmpp_component->clean();
    if (identd)
      identd->clean();
    if (stop)
    {
      log_info("Signal received, exiting...");
#ifdef SYSTEMD_FOUND
      sd_notify(0, "STOPPING=1");
#endif
      exiting = true;
      stop.store(false);
      xmpp_component->shutdown();
#ifdef UDNS_FOUND
      dns_handler.destroy();
#endif
      if (identd)
        identd->shutdown();
      // Cancel the timer for a potential reconnection
      TimedEventsManager::instance().cancel("XMPP reconnection");
    }
    if (reload)
    {
      log_info("Signal received, reloading the config...");
      ::reload_process();
      reload.store(false);
    }
    // Reconnect to the XMPP server if this was not intended.  This may have
    // happened because we sent something invalid to it and it decided to
    // close the connection.  This is a bug that should be fixed, but we
    // still reconnect automatically instead of dropping everything
    if (!exiting &&
        !xmpp_component->is_connected() &&
        !xmpp_component->is_connecting())
    {
      if (xmpp_component->ever_auth)
        {
          static const std::string reconnect_name{"XMPP reconnection"};
          if (xmpp_component->first_connection_try == true)
            { // immediately re-try to connect
              xmpp_component->reset();
              xmpp_component->start();
            }
          else if (!TimedEventsManager::instance().find_event(reconnect_name))
            { // Re-connecting failed, we now try only each few seconds
              auto reconnect_later = [xmpp_component]()
              {
                xmpp_component->reset();
                xmpp_component->start();
              };
              TimedEvent event(std::chrono::steady_clock::now() + 2s, reconnect_later, reconnect_name);
              TimedEventsManager::instance().add_event(std::move(event));
            }
        }
      else
        {
#ifdef UDNS_FOUND
          dns_handler.destroy();
#endif
          if (identd)
            identd->shutdown();
        }
    }
    // If the only existing connection is the one to the XMPP component:
    // close the XMPP stream.
    if (exiting && xmpp_component->is_connecting())
      xmpp_component->close();
    if (exiting && p->size() == 1 && xmpp_component->is_document_open())
      xmpp_component->close_document();
    if (exiting) // If we are exiting, do not wait for any timed event
      timeout = utils::no_timeout;
    else
      timeout = TimedEventsManager::instance().get_timeout();
  }
  if (!xmpp_component->ever_auth)
    return 1; // To signal that the process did not properly start
  log_info("All connections cleanly closed, have a nice day.");
  return 0;
}

int main(int ac, char** av)
{
  std::string conf_filename{};
  bool test_conf = false;
  if (ac > 1)
    {
      for (int i = 1; i < ac; i++)
        {
          const std::string arg = av[i];
          if ((arg == "-h") || (arg == "--help"))
            return display_help();
          else if ((arg == "-t") || (arg == "--test-config"))
            test_conf = true;
          else if (i + 1 == ac)
            conf_filename = arg;
          else
            {
              std::cerr << "Unknow command line option: " << arg
                        << std::endl;
              return 1;
            }
        }
    }
  if (conf_filename.empty())
    conf_filename = xdg_config_path("biboumi.cfg");
  std::cout << "Using configuration file: " << conf_filename << std::endl;

  if (!Config::read_conf(conf_filename))
    return config_help("");

  const std::string password = Config::get("password", "");
  if (password.empty())
    return config_help("password");
  const std::string hostname = Config::get("hostname", "");
  if (hostname.empty())
    return config_help("hostname");
  if (test_conf)
    {
      std::cout << "biboumi: the configuration file " << conf_filename
        << " syntax is ok" << std::endl;
      return 0;
    }

#ifdef USE_DATABASE
  try
    {
      open_database();
    }
  catch (const std::exception& e)
    {
      log_error(e.what());
      return 1;
    }
#endif

  setup_signals();

  return main_loop(std::move(hostname), std::move(password));
}