aboutsummaryrefslogtreecommitdiff
path: root/web/apache/service.cxx
blob: 96ff855e3caf7e28da12774ccf9c545582af3ea4 (plain)
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
// file      : web/apache/service.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#include <web/apache/service>

#include <unistd.h> // getppid()
#include <signal.h> // kill()

#include <httpd.h>
#include <http_config.h>

#include <memory>    // unique_ptr
#include <string>
#include <cassert>
#include <utility>   // move()
#include <cstring>   // strlen()
#include <exception>

#include <web/module>

using namespace std;

namespace web
{
  namespace apache
  {
    void service::
    init_directives ()
    {
      assert (cmds == nullptr);

      // Fill apache module directive definitions. Directives share
      // common name space in apache configuration file, so to prevent name
      // clash have to form directive name as a combination of module and
      // option names: <module name>-<option name>. This why for option
      // bar of module foo the corresponding directive will appear in apache
      // configuration file as foo-bar.
      //
      const option_descriptions& od (exemplar_.options ());
      unique_ptr<command_rec[]> directives (new command_rec[od.size () + 1]);
      command_rec* d (directives.get ());

      for (const auto& o: od)
      {
        auto i (option_descriptions_.emplace (name_ + "-" + o.first, o.second));
        assert (i.second);

        *d++ =
          {
            i.first->first.c_str (),
            reinterpret_cast<cmd_func> (parse_option),
            this,
            RSRC_CONF,
            // Move away from TAKE1 to be able to handle empty string and
            // no-value.
            //
            RAW_ARGS,
            nullptr
          };
      }

      *d = {nullptr, nullptr, nullptr, 0, RAW_ARGS, nullptr};
      cmds = directives.release ();
    }

    const char* service::
    parse_option (cmd_parms* parms, void*, const char* args) noexcept
    {
      // @@ Current implementation does not consider configuration context
      //    (server config, virtual host, directory) for directive parsing, nor
      //    for request handling.
      //
      service& srv (*reinterpret_cast<service*> (parms->cmd->cmd_data));

      if (srv.options_parsed_)
        // Apache is inside the second pass of its messy initialization cycle
        // (more details at http://wiki.apache.org/httpd/ModuleLife). Just
        // ignore it.
        //
        return 0;

      // 'args' is an optionally double-quoted string. It uses double quotes
      // to distinguish empty string from no-value case.
      //
      assert (args != nullptr);

      optional<string> value;
      if (auto l = strlen (args))
        value = l >= 2 && args[0] == '"' && args[l - 1] == '"'
          ? string (args + 1, l - 2)
          : args;

      return srv.add_option (parms->cmd->name, move (value));
    }

    const char* service::
    add_option (const char* name, optional<string> value)
    {
      auto i (option_descriptions_.find (name));
      assert (i != option_descriptions_.end ());

      // Check that option value presense is expected.
      //
      if (i->second != static_cast<bool> (value))
        return value ? "unexpected value" : "value expected";

      options_.emplace_back (name + name_.length () + 1, move (value));
      return 0;
    }

    void service::
    init_worker (log& l) noexcept
    {
      const string func_name (
        "web::apache::service<" + name_ + ">::init_worker");

      try
      {
        exemplar_.init (options_, l);
      }
      catch (const exception& e)
      {
        l.write (nullptr, 0, func_name.c_str (), APLOG_EMERG, e.what ());

        // Terminate the root apache process. Indeed we can only try to
        // terminate the process, and most likely will fail in a production
        // environment where the apache root process usually runs under root,
        // and worker processes run under some other user. This is why the
        // implementation should consider the possibility of not being
        // initialized at the time of HTTP request processing. In such a case
        // it should respond with an internal server error (500 HTTP status),
        // reporting misconfiguration.
        //
        ::kill (::getppid (), SIGTERM);
      }
      catch (...)
      {
        l.write (nullptr,
                 0,
                 func_name.c_str (),
                 APLOG_EMERG,
                 "unknown error");

        // Terminate the root apache process.
        //
        ::kill (::getppid (), SIGTERM);
      }
    }
  }
}