Coverage Report

Created: 2025-07-01 06:08

/src/logging-log4cxx/src/main/cpp/gzcompressaction.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#include <log4cxx/rolling/gzcompressaction.h>
19
#include <apr_thread_proc.h>
20
#include <apr_strings.h>
21
#include <log4cxx/helpers/exception.h>
22
#include <log4cxx/helpers/transcoder.h>
23
#include <log4cxx/private/action_priv.h>
24
#include <log4cxx/helpers/loglog.h>
25
26
using namespace LOG4CXX_NS;
27
using namespace LOG4CXX_NS::rolling;
28
using namespace LOG4CXX_NS::helpers;
29
30
8
#define priv static_cast<GZCompressActionPrivate*>(m_priv.get())
31
32
struct GZCompressAction::GZCompressActionPrivate : public ActionPrivate
33
{
34
  GZCompressActionPrivate( const File& toRename,
35
    const File& renameTo,
36
    bool deleteSource):
37
1
    source(toRename), destination(renameTo), deleteSource(deleteSource) {}
38
39
  const File source;
40
  File destination;
41
  bool deleteSource;
42
  bool throwIOExceptionOnForkFailure = true;
43
};
44
45
IMPLEMENT_LOG4CXX_OBJECT(GZCompressAction)
46
47
GZCompressAction::GZCompressAction(const File& src,
48
  const File& dest,
49
  bool del)
50
1
  : Action(std::make_unique<GZCompressActionPrivate>(
51
1
        src, dest, del))
52
1
{
53
1
}
Unexecuted instantiation: log4cxx::rolling::GZCompressAction::GZCompressAction(log4cxx::File const&, log4cxx::File const&, bool)
log4cxx::rolling::GZCompressAction::GZCompressAction(log4cxx::File const&, log4cxx::File const&, bool)
Line
Count
Source
50
1
  : Action(std::make_unique<GZCompressActionPrivate>(
51
1
        src, dest, del))
52
1
{
53
1
}
54
55
1
GZCompressAction::~GZCompressAction() {}
56
57
bool GZCompressAction::execute(LOG4CXX_NS::helpers::Pool& p) const
58
1
{
59
1
  if (priv->source.exists(p))
60
1
  {
61
1
    apr_pool_t* aprpool = p.getAPRPool();
62
1
    apr_procattr_t* attr;
63
1
    apr_status_t stat = apr_procattr_create(&attr, aprpool);
64
65
1
    if (stat != APR_SUCCESS)
66
0
    {
67
0
      throw IOException(stat);
68
0
    }
69
70
1
    stat = apr_procattr_io_set(attr, APR_NO_PIPE, APR_FULL_BLOCK, APR_FULL_BLOCK);
71
72
1
    if (stat != APR_SUCCESS)
73
0
    {
74
0
      throw IOException(stat);
75
0
    }
76
77
1
    stat = apr_procattr_cmdtype_set(attr, APR_PROGRAM_PATH);
78
79
1
    if (stat != APR_SUCCESS)
80
0
    {
81
0
      throw IOException(stat);
82
0
    }
83
84
    //
85
    //   set child process output to destination file
86
    //
87
1
    apr_file_t* child_out;
88
1
    apr_int32_t flags = APR_FOPEN_READ | APR_FOPEN_WRITE |
89
1
      APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE;
90
1
    stat = priv->destination.open(&child_out, flags, APR_OS_DEFAULT, p);
91
92
1
    if (stat != APR_SUCCESS)
93
0
    {
94
0
      throw IOException(priv->destination.getName(), stat);
95
0
    }
96
97
1
    stat =  apr_procattr_child_out_set(attr, child_out, NULL);
98
99
1
    if (stat != APR_SUCCESS)
100
0
    {
101
0
      throw IOException(stat);
102
0
    }
103
104
    //
105
    //   redirect the child's error stream to this processes' error stream
106
    //
107
1
    apr_file_t* child_err;
108
1
    stat = apr_file_open_stderr(&child_err, aprpool);
109
110
1
    if (stat == APR_SUCCESS)
111
1
    {
112
1
      stat =  apr_procattr_child_err_set(attr, child_err, NULL);
113
114
1
      if (stat != APR_SUCCESS)
115
0
      {
116
0
        throw IOException(stat);
117
0
      }
118
1
    }
119
120
1
    priv->destination.setAutoDelete(true);
121
122
1
    const char** args = (const char**)
123
1
      apr_palloc(aprpool, 4 * sizeof(*args));
124
1
    int i = 0;
125
1
    args[i++] = "gzip";
126
1
    args[i++] = "-c";
127
1
    args[i++] = Transcoder::encode(priv->source.getPath(), p);
128
1
    args[i++] = NULL;
129
130
1
    apr_proc_t pid;
131
1
    stat = apr_proc_create(&pid, "gzip", args, NULL, attr, aprpool);
132
133
1
    if (stat != APR_SUCCESS)
134
0
    {
135
0
      LogLog::warn(LOG4CXX_STR("Failed to fork gzip during log rotation; leaving log file uncompressed"));
136
0
      if (priv->throwIOExceptionOnForkFailure)
137
0
        throw IOException(LOG4CXX_STR("gzip"), stat);
138
      /* If we fail here (to create the gzip child process),
139
       * skip the compression and consider the rotation to be
140
       * otherwise successful. The caller has already rotated
141
       * the log file (`source` here refers to the
142
       * uncompressed, rotated path, and `destination` the
143
       * same path with `.gz` appended). Remove the empty
144
       * destination file and leave source as-is.
145
       */
146
0
      stat = apr_file_close(child_out);
147
0
      return true;
148
0
    }
149
150
1
    apr_proc_wait(&pid, NULL, NULL, APR_WAIT);
151
1
    stat = apr_file_close(child_out);
152
153
1
    if (stat != APR_SUCCESS)
154
0
    {
155
0
      throw IOException(stat);
156
0
    }
157
158
1
    priv->destination.setAutoDelete(false);
159
160
1
    if (priv->deleteSource)
161
1
    {
162
1
      priv->source.deleteFile(p);
163
1
    }
164
165
1
    return true;
166
1
  }
167
168
0
  return false;
169
1
}
170
171
1
void GZCompressAction::setThrowIOExceptionOnForkFailure(bool throwIO){
172
1
  priv->throwIOExceptionOnForkFailure = throwIO;
173
1
}
174