View Javadoc
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  package org.apache.logging.log4j.core.appender.rolling.action;
18  
19  import java.io.IOException;
20  import java.nio.file.FileVisitResult;
21  import java.nio.file.FileVisitor;
22  import java.nio.file.Path;
23  import java.nio.file.SimpleFileVisitor;
24  import java.nio.file.attribute.BasicFileAttributes;
25  import java.nio.file.attribute.FileOwnerAttributeView;
26  import java.nio.file.attribute.PosixFileAttributeView;
27  import java.nio.file.attribute.PosixFilePermission;
28  import java.nio.file.attribute.PosixFilePermissions;
29  import java.util.List;
30  import java.util.Set;
31  
32  import org.apache.logging.log4j.core.Core;
33  import org.apache.logging.log4j.core.config.Configuration;
34  import org.apache.logging.log4j.core.config.plugins.Plugin;
35  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
36  import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
37  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
38  import org.apache.logging.log4j.core.config.plugins.PluginElement;
39  import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
40  import org.apache.logging.log4j.core.lookup.StrSubstitutor;
41  import org.apache.logging.log4j.core.util.FileUtils;
42  import org.apache.logging.log4j.util.Strings;
43  
44  /**
45   * File posix attribute view action.
46   *
47   * Allow to define file permissions, user and group for log files on posix supported OS.
48   */
49  @Plugin(name = "PosixViewAttribute", category = Core.CATEGORY_NAME, printObject = true)
50  public class PosixViewAttributeAction extends AbstractPathAction {
51  
52      /**
53       * File permissions.
54       */
55      private final Set<PosixFilePermission> filePermissions;
56  
57      /**
58       * File owner.
59       */
60      private final String fileOwner;
61  
62      /**
63       * File group.
64       */
65      private final String fileGroup;
66  
67      private PosixViewAttributeAction(final String basePath, final boolean followSymbolicLinks,
68              final int maxDepth, final PathCondition[] pathConditions, final StrSubstitutor subst,
69              final Set<PosixFilePermission> filePermissions,
70              final String fileOwner, final String fileGroup) {
71          super(basePath, followSymbolicLinks, maxDepth, pathConditions, subst);
72          this.filePermissions = filePermissions;
73          this.fileOwner = fileOwner;
74          this.fileGroup = fileGroup;
75      }
76  
77      @PluginBuilderFactory
78      public static Builder newBuilder() {
79          return new Builder();
80      }
81  
82      /**
83       * Builder for the posix view attribute action.
84       */
85      public static class Builder implements org.apache.logging.log4j.core.util.Builder<PosixViewAttributeAction> {
86  
87          @PluginConfiguration
88          private Configuration configuration;
89  
90          private StrSubstitutor subst;
91  
92          @PluginBuilderAttribute
93          @Required(message = "No base path provided")
94          private String basePath;
95  
96          @PluginBuilderAttribute
97          private boolean followLinks = false;
98  
99          @PluginBuilderAttribute
100         private int maxDepth = 1;
101 
102         @PluginElement("PathConditions")
103         private PathCondition[] pathConditions;
104 
105         @PluginBuilderAttribute(value = "filePermissions")
106         private String filePermissionsString;
107 
108         private Set<PosixFilePermission> filePermissions;
109 
110         @PluginBuilderAttribute
111         private String fileOwner;
112 
113         @PluginBuilderAttribute
114         private String fileGroup;
115 
116         @Override
117         public PosixViewAttributeAction build() {
118             if (Strings.isEmpty(basePath)) {
119                 LOGGER.error("Posix file attribute view action not valid because base path is empty.");
120                 return null;
121             }
122 
123             if (filePermissions == null && Strings.isEmpty(filePermissionsString)
124                         && Strings.isEmpty(fileOwner) && Strings.isEmpty(fileGroup)) {
125                 LOGGER.error("Posix file attribute view not valid because nor permissions, user or group defined.");
126                 return null;
127             }
128 
129             if (!FileUtils.isFilePosixAttributeViewSupported()) {
130                 LOGGER.warn("Posix file attribute view defined but it is not supported by this files system.");
131                 return null;
132             }
133 
134             return new PosixViewAttributeAction(basePath, followLinks, maxDepth, pathConditions,
135                     subst != null ? subst : configuration.getStrSubstitutor(),
136                     filePermissions != null ? filePermissions :
137                                 filePermissionsString != null ? PosixFilePermissions.fromString(filePermissionsString) : null,
138                     fileOwner,
139                     fileGroup);
140         }
141 
142         /**
143          * Define required configuration, used to retrieve string substituter.
144          *
145          * @param configuration {@link AbstractPathAction#getStrSubstitutor()}
146          * @return This builder
147          */
148         public Builder withConfiguration(final Configuration configuration) {
149             this.configuration = configuration;
150             return this;
151         }
152 
153         /**
154          * Define string substituter.
155          *
156          * @param subst {@link AbstractPathAction#getStrSubstitutor()}
157          * @return This builder
158          */
159         public Builder withSubst(final StrSubstitutor subst) {
160             this.subst = subst;
161             return this;
162         }
163 
164         /**
165          * Define base path to apply condition before execute posix file attribute action.
166          * @param basePath {@link AbstractPathAction#getBasePath()}
167          * @return This builder
168          */
169         public Builder withBasePath(final String basePath) {
170             this.basePath = basePath;
171             return this;
172         }
173 
174         /**
175          * True to allow synonyms links during search of eligible files.
176          * @param followLinks Follow synonyms links
177          * @return This builder
178          */
179         public Builder withFollowLinks(final boolean followLinks) {
180             this.followLinks = followLinks;
181             return this;
182         }
183 
184         /**
185          * Define max folder depth to search for eligible files to apply posix attribute view.
186          * @param maxDepth Max search depth
187          * @return This builder
188          */
189         public Builder withMaxDepth(final int maxDepth) {
190             this.maxDepth = maxDepth;
191             return this;
192         }
193 
194         /**
195          * Define path conditions to filter files in {@link PosixViewAttributeAction#getBasePath()}.
196          *
197          * @param pathConditions {@link AbstractPathAction#getPathConditions()}
198          * @return This builder
199          */
200         public Builder withPathConditions(final PathCondition[] pathConditions) {
201             this.pathConditions = pathConditions;
202             return this;
203         }
204 
205         /**
206          * Define file permissions in posix format to apply during action execution eligible files.
207          *
208          * Example:
209          * <p>rw-rw-rw
210          * <p>r--r--r--
211          * @param filePermissionsString Permissions to apply
212          * @return This builder
213          */
214         public Builder withFilePermissionsString(final String filePermissionsString) {
215             this.filePermissionsString = filePermissionsString;
216             return this;
217         }
218 
219         /**
220          * Define file permissions to apply during action execution eligible files.
221          * @param filePermissions Permissions to apply
222          * @return This builder
223          */
224         public Builder withFilePermissions(final Set<PosixFilePermission> filePermissions) {
225             this.filePermissions = filePermissions;
226             return this;
227         }
228 
229         /**
230          * Define file owner to apply during action execution eligible files.
231          * @param fileOwner File owner
232          * @return This builder
233          */
234         public Builder withFileOwner(final String fileOwner) {
235             this.fileOwner = fileOwner;
236             return this;
237         }
238 
239         /**
240          * Define file group to apply during action execution eligible files.
241          * @param fileGroup File group
242          * @return This builder
243          */
244         public Builder withFileGroup(final String fileGroup) {
245             this.fileGroup = fileGroup;
246             return this;
247         }
248     }
249 
250     @Override
251     protected FileVisitor<Path> createFileVisitor(final Path basePath,
252             final List<PathCondition> conditions) {
253         return new SimpleFileVisitor<Path>() {
254             @Override
255             public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
256                 for (final PathCondition pathFilter : conditions) {
257                     final Path relative = basePath.relativize(file);
258                     if (!pathFilter.accept(basePath, relative, attrs)) {
259                         LOGGER.trace("Not defining posix attribute base={}, relative={}", basePath, relative);
260                         return FileVisitResult.CONTINUE;
261                     }
262                 }
263                 FileUtils.defineFilePosixAttributeView(file, filePermissions, fileOwner, fileGroup);
264                 return FileVisitResult.CONTINUE;
265             }
266         };
267     }
268 
269     /**
270      * Returns posix file permissions if defined and the OS supports posix file attribute,
271      * null otherwise.
272      * @return File posix permissions
273      * @see PosixFileAttributeView
274      */
275     public Set<PosixFilePermission> getFilePermissions() {
276         return filePermissions;
277     }
278 
279     /**
280      * Returns file owner if defined and the OS supports owner file attribute view,
281      * null otherwise.
282      * @return File owner
283      * @see FileOwnerAttributeView
284      */
285     public String getFileOwner() {
286         return fileOwner;
287     }
288 
289     /**
290      * Returns file group if defined and the OS supports posix/group file attribute view,
291      * null otherwise.
292      * @return File group
293      * @see PosixFileAttributeView
294      */
295     public String getFileGroup() {
296         return fileGroup;
297     }
298 
299     @Override
300     public String toString() {
301         return "PosixViewAttributeAction [filePermissions=" + filePermissions + ", fileOwner="
302                 + fileOwner + ", fileGroup=" + fileGroup + ", getBasePath()=" + getBasePath()
303                 + ", getMaxDepth()=" + getMaxDepth() + ", getPathConditions()="
304                 + getPathConditions() + "]";
305     }
306 
307 }