Coverage Report

Created: 2026-05-30 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/logging-log4cxx/src/main/cpp/gzcompressaction.cpp
Line
Count
Source
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
0
#define priv static_cast<GZCompressActionPrivate*>(m_priv.get())
31
32
struct GZCompressAction::GZCompressActionPrivate : public ActionPrivate
33
{
34
  GZCompressActionPrivate
35
    ( const File& toRename
36
    , const File& renameTo
37
    , bool deleteSource
38
    )
39
0
    : ActionPrivate{ LOG4CXX_STR("gzip") }
40
0
    , source(toRename)
41
0
    , destination(renameTo)
42
0
    , deleteSource(deleteSource)
43
0
    {}
44
45
  const File source;
46
  File destination;
47
  bool deleteSource;
48
  bool throwIOExceptionOnForkFailure = true;
49
};
50
51
IMPLEMENT_LOG4CXX_OBJECT(GZCompressAction)
52
53
GZCompressAction::GZCompressAction(const File& src,
54
  const File& dest,
55
  bool del)
56
0
  : Action(std::make_unique<GZCompressActionPrivate>(
57
0
        src, dest, del))
58
0
{
59
0
}
Unexecuted instantiation: log4cxx::rolling::GZCompressAction::GZCompressAction(log4cxx::File const&, log4cxx::File const&, bool)
Unexecuted instantiation: log4cxx::rolling::GZCompressAction::GZCompressAction(log4cxx::File const&, log4cxx::File const&, bool)
60
61
0
GZCompressAction::~GZCompressAction() {}
62
63
bool GZCompressAction::execute( LOG4CXX_EXECUTE_ACTION_FORMAL_PARAMETERS ) const
64
0
{
65
0
  if (priv->source.exists())
66
0
  {
67
0
    helpers::Pool tempPool;
68
0
    apr_pool_t* aprpool = tempPool.getAPRPool();
69
0
    apr_procattr_t* attr;
70
0
    apr_status_t stat = apr_procattr_create(&attr, aprpool);
71
72
0
    if (stat != APR_SUCCESS)
73
0
    {
74
0
      throw IOException(stat);
75
0
    }
76
77
0
    stat = apr_procattr_io_set(attr, APR_NO_PIPE, APR_FULL_BLOCK, APR_FULL_BLOCK);
78
79
0
    if (stat != APR_SUCCESS)
80
0
    {
81
0
      throw IOException(stat);
82
0
    }
83
84
0
    stat = apr_procattr_cmdtype_set(attr, APR_PROGRAM_PATH);
85
86
0
    if (stat != APR_SUCCESS)
87
0
    {
88
0
      throw IOException(stat);
89
0
    }
90
91
    //
92
    //   set child process output to destination file
93
    //
94
0
    apr_file_t* child_out;
95
0
    apr_int32_t flags = APR_FOPEN_READ | APR_FOPEN_WRITE |
96
0
      APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE;
97
0
    stat = apr_file_open(&child_out, priv->destination.getAPRPath(), flags, APR_OS_DEFAULT, aprpool);
98
99
0
    if (stat != APR_SUCCESS)
100
0
    {
101
0
      throw IOException(priv->destination.getName(), stat);
102
0
    }
103
104
0
    stat =  apr_procattr_child_out_set(attr, child_out, NULL);
105
106
0
    if (stat != APR_SUCCESS)
107
0
    {
108
0
      throw IOException(stat);
109
0
    }
110
111
    //
112
    //   redirect the child's error stream to this processes' error stream
113
    //
114
0
    apr_file_t* child_err;
115
0
    stat = apr_file_open_stderr(&child_err, aprpool);
116
117
0
    if (stat == APR_SUCCESS)
118
0
    {
119
0
      stat =  apr_procattr_child_err_set(attr, child_err, NULL);
120
121
0
      if (stat != APR_SUCCESS)
122
0
      {
123
0
        throw IOException(stat);
124
0
      }
125
0
    }
126
127
0
    priv->destination.setAutoDelete(true);
128
129
0
    const char* args[4];
130
0
    int i = 0;
131
0
    args[i++] = "gzip";
132
0
    args[i++] = "-c";
133
0
    args[i++] = priv->source.getAPRPath();
134
0
    args[i++] = NULL;
135
136
0
    apr_proc_t pid;
137
0
    stat = apr_proc_create(&pid, "gzip", args, NULL, attr, aprpool);
138
139
0
    if (stat != APR_SUCCESS)
140
0
    {
141
0
      LogLog::warn(LOG4CXX_STR("Failed to fork gzip during log rotation; leaving log file uncompressed"));
142
0
      if (priv->throwIOExceptionOnForkFailure)
143
0
        throw IOException(LOG4CXX_STR("gzip"), stat);
144
      /* If we fail here (to create the gzip child process),
145
       * skip the compression and consider the rotation to be
146
       * otherwise successful. The caller has already rotated
147
       * the log file (`source` here refers to the
148
       * uncompressed, rotated path, and `destination` the
149
       * same path with `.gz` appended). Remove the empty
150
       * destination file and leave source as-is.
151
       */
152
0
      stat = apr_file_close(child_out);
153
0
      return true;
154
0
    }
155
156
0
    apr_proc_wait(&pid, NULL, NULL, APR_WAIT);
157
0
    stat = apr_file_close(child_out);
158
159
0
    if (stat != APR_SUCCESS)
160
0
    {
161
0
      throw IOException(stat);
162
0
    }
163
164
0
    priv->destination.setAutoDelete(false);
165
166
0
    if (priv->deleteSource)
167
0
    {
168
0
      priv->source.deleteFile();
169
0
    }
170
171
0
    return true;
172
0
  }
173
174
0
  return false;
175
0
}
176
177
0
void GZCompressAction::setThrowIOExceptionOnForkFailure(bool throwIO){
178
0
  priv->throwIOExceptionOnForkFailure = throwIO;
179
0
}
180