1/*
2 * logger.c : Implementation of the SvnServe logger API
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24
25
26#define APR_WANT_STRFUNC
27#include <apr_want.h>
28
29#include "svn_error.h"
30#include "svn_io.h"
31#include "svn_pools.h"
32#include "svn_time.h"
33
34#include "private/svn_mutex.h"
35
36#include "svn_private_config.h"
37#include "logger.h"
38
39#ifdef HAVE_UNISTD_H
40#include <unistd.h>   /* For getpid() */
41#endif
42
43struct logger_t
44{
45  /* actual log file / stream object */
46  svn_stream_t *stream;
47
48  /* mutex used to serialize access to this structure */
49  svn_mutex__t *mutex;
50
51  /* private pool used for temporary allocations */
52  apr_pool_t *pool;
53};
54
55svn_error_t *
56logger__create_for_stderr(logger_t **logger,
57                          apr_pool_t *pool)
58{
59  logger_t *result = apr_pcalloc(pool, sizeof(*result));
60  result->pool = svn_pool_create(pool);
61
62  SVN_ERR(svn_stream_for_stderr(&result->stream, pool));
63  SVN_ERR(svn_mutex__init(&result->mutex, TRUE, pool));
64
65  *logger = result;
66
67  return SVN_NO_ERROR;
68}
69
70svn_error_t *
71logger__create(logger_t **logger,
72               const char *filename,
73               apr_pool_t *pool)
74{
75  logger_t *result = apr_pcalloc(pool, sizeof(*result));
76  apr_file_t *file;
77
78  SVN_ERR(svn_io_file_open(&file, filename,
79                           APR_WRITE | APR_CREATE | APR_APPEND,
80                           APR_OS_DEFAULT, pool));
81  SVN_ERR(svn_mutex__init(&result->mutex, TRUE, pool));
82
83  result->stream = svn_stream_from_aprfile2(file, FALSE,  pool);
84  result->pool = svn_pool_create(pool);
85
86  *logger = result;
87
88  return SVN_NO_ERROR;
89}
90
91void
92logger__log_error(logger_t *logger,
93                  svn_error_t *err,
94                  repository_t *repository,
95                  client_info_t *client_info)
96{
97  if (logger && err)
98    {
99      const char *timestr, *continuation;
100      const char *user, *repos, *remote_host;
101      char errbuf[256];
102      /* 8192 from MAX_STRING_LEN in from httpd-2.2.4/include/httpd.h */
103      char errstr[8192];
104
105      svn_error_clear(svn_mutex__lock(logger->mutex));
106
107      timestr = svn_time_to_cstring(apr_time_now(), logger->pool);
108      remote_host = client_info && client_info->remote_host
109                  ? client_info->remote_host
110                  : "-";
111      user = client_info && client_info->user
112           ? client_info->user
113           : "-";
114      repos = repository && repository->repos_name
115            ? repository->repos_name
116             : "-";
117
118      continuation = "";
119      while (err)
120        {
121          const char *message = svn_err_best_message(err, errbuf, sizeof(errbuf));
122          /* based on httpd-2.2.4/server/log.c:log_error_core */
123          apr_size_t len = apr_snprintf(errstr, sizeof(errstr),
124                                        "%" APR_PID_T_FMT
125                                        " %s %s %s %s ERR%s %s %ld %d ",
126                                        getpid(), timestr, remote_host, user,
127                                        repos, continuation,
128                                        err->file ? err->file : "-", err->line,
129                                        err->apr_err);
130
131          len += escape_errorlog_item(errstr + len, message,
132                                      sizeof(errstr) - len);
133          /* Truncate for the terminator (as apr_snprintf does) */
134          if (len > sizeof(errstr) - sizeof(APR_EOL_STR)) {
135            len = sizeof(errstr) - sizeof(APR_EOL_STR);
136          }
137
138          memcpy(errstr + len, APR_EOL_STR, sizeof(APR_EOL_STR));
139          len += sizeof(APR_EOL_STR) -1;  /* add NL, ex terminating NUL */
140
141          svn_error_clear(svn_stream_write(logger->stream, errstr, &len));
142
143          continuation = "-";
144          err = err->child;
145        }
146
147      svn_pool_clear(logger->pool);
148
149      svn_error_clear(svn_mutex__unlock(logger->mutex, SVN_NO_ERROR));
150    }
151}
152
153svn_error_t *
154logger__write(logger_t *logger,
155              const char *errstr,
156              apr_size_t len)
157{
158  SVN_MUTEX__WITH_LOCK(logger->mutex,
159                       svn_stream_write(logger->stream, errstr, &len));
160  return SVN_NO_ERROR;
161}
162