summaryrefslogtreecommitdiff
path: root/modules/loggers/mod_journald.c
blob: 0175b527363633b212367be34f9b138a3c86ba2e (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
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
/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

#include <stdint.h>
#include <ap_config.h>
#include "ap_mpm.h"
#include "ap_provider.h"
#include <http_core.h>
#include <httpd.h>
#include <http_log.h>
#include <apr_version.h>
#include <apr_pools.h>
#include <apr_strings.h>
#include "unixd.h"
#include "scoreboard.h"
#include "mpm_common.h"
#include "mod_log_config.h"

#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif

/* XXX: keep this after any other #include.
 * Some systemd versions use the "inline" keyword which is not
 * c89/c90 compliant, so override it...
 */
#if defined(__STDC__) && (!defined(__STDC_VERSION__) \
                          || __STDC_VERSION__ < 199901L)
#undef inline
#define inline APR_INLINE
#endif
#define SD_JOURNAL_SUPPRESS_LOCATION 1
#include <systemd/sd-journal.h>

#define MAX_ENTRIES 15

static int journald_info_get_priority(int level)
{
    switch(level) {
        /* We don't use EMERG here, because journald broadcasts EMERG messages
         * to all terminals. APLOG_EMERG is usually not used in this context.
         * in httpd code. */
        case APLOG_EMERG:   return LOG_ALERT;
        case APLOG_ALERT:   return LOG_ALERT;
        case APLOG_CRIT:    return LOG_CRIT;
        case APLOG_ERR:     return LOG_ERR;
        case APLOG_WARNING: return LOG_WARNING;
        case APLOG_NOTICE:  return LOG_NOTICE;
        case APLOG_INFO:    return LOG_INFO;
        case APLOG_DEBUG:   return LOG_DEBUG;
        case -1:            return LOG_INFO;
        default:            return LOG_DEBUG;
    }
    return LOG_INFO;
}

static apr_pool_t *journald_info_get_pool(const ap_errorlog_info *info)
{
    if (info->r && info->r->pool)
        return info->r->pool;
    if (info->c && info->c->pool)
        return info->c->pool;
    if (info->pool)
        return info->pool;
    if (info->s && info->s->process && info->s->process->pool)
        return info->s->process->pool;
    return 0;
}

static apr_status_t iovec_add_entry(apr_pool_t *pool, struct iovec *iov,
                                    const char *format, int len, ...)
{
    va_list ap;
    va_start(ap, len);
    iov->iov_base = apr_pvsprintf(pool, format, ap);
    va_end(ap);
    if (!iov->iov_base) {
        return APR_ENOMEM;
    }
    if (len < 0) {
        iov->iov_len = strlen(iov->iov_base);
    }
    else {
        iov->iov_len = len;
    }
    return APR_SUCCESS;
}

static void journald_log(apr_pool_t *pool, const char *log,
                         const char *errstr, int len, int priority,
                         const server_rec *s, const request_rec *r)
{
    apr_pool_t *subpool;
    apr_status_t rv = APR_SUCCESS;
    struct iovec iov[MAX_ENTRIES];
    int iov_size = 0;

    if (apr_pool_create(&subpool, pool) != APR_SUCCESS) {
        /* We were not able to create subpool, log at least what we have. */
        sd_journal_send("MESSAGE=%s", errstr, "LOG=%s", log,
                    "PRIORITY=%i", priority,
                    NULL);
        return;
    }
    apr_pool_tag(subpool, "journald_log");

    /* Adds new entry to iovec if previous additions were successful. */
#define IOVEC_ADD_LEN(FORMAT, VAR, LEN) \
    if (rv == APR_SUCCESS && iov_size < MAX_ENTRIES) { \
        rv = iovec_add_entry(subpool, &iov[iov_size], FORMAT, LEN, VAR); \
        if (rv == APR_SUCCESS) \
            iov_size++; \
    }
#define IOVEC_ADD(FORMAT, VAR) IOVEC_ADD_LEN(FORMAT, VAR, -1)

    IOVEC_ADD_LEN("MESSAGE=%s", errstr, len + 8);
    IOVEC_ADD("LOG=%s", log);
    IOVEC_ADD("PRIORITY=%i", priority);

    if (s) {
        IOVEC_ADD("SERVER_HOSTNAME=%s", s->server_hostname);
    }

    if (r) {
        IOVEC_ADD("REQUEST_HOSTNAME=%s", r->hostname);
        IOVEC_ADD("REQUEST_USER=%s", r->user ? r->user : "");
        IOVEC_ADD("REQUEST_URI=%s", r->uri ? r->uri : "");
        IOVEC_ADD("REQUEST_USERAGENT_IP=%s", r->useragent_ip);
    }

    sd_journal_sendv(iov, iov_size);
    apr_pool_destroy(subpool);
}

static void *journald_error_log_init(apr_pool_t *p, server_rec *s)
{
    void *success = (void *)p; /* anything non-NULL is success */
    return success;
}

static apr_status_t journald_error_log(const ap_errorlog_info *info,
                                       void *handle, const char *errstr,
                                       apr_size_t len)
{
    const server_rec *s = info->s;
    const request_rec *r = info->r;
    apr_pool_t *pool;
    const char *log_name = (s && s->error_fname && *s->error_fname) ?
                            s->error_fname : "error_log";

    pool = journald_info_get_pool(info);
    if (!pool) {
        /* We don't have any pool, so at least log the message without
         * any additional data. */
        sd_journal_send("MESSAGE=%s", errstr, "LOG=%s", "log_name",
                    "PRIORITY=%i", journald_info_get_priority(info->level),
                    NULL);
        return APR_SUCCESS;
    }

    journald_log(pool, log_name, errstr, len,
                 journald_info_get_priority(info->level), s, r);

    return APR_SUCCESS;
}

static const char *journald_error_log_parse(cmd_parms *cmd, const char *arg)
{
    return NULL;
}

static void journald_register_hooks(apr_pool_t *p)
{
    static const ap_errorlog_provider journald_provider = {
        &journald_error_log_init,
        &journald_error_log,
        &journald_error_log_parse,
        0
    };

    ap_register_provider(p, AP_ERRORLOG_PROVIDER_GROUP, "journald",
                         AP_ERRORLOG_PROVIDER_VERSION,
                         &journald_provider);
}

AP_DECLARE_MODULE(journald) =
{
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    journald_register_hooks,
};