001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.core.appender.rolling.action; 018 019import java.io.IOException; 020import java.nio.file.FileVisitResult; 021import java.nio.file.FileVisitor; 022import java.nio.file.Path; 023import java.nio.file.SimpleFileVisitor; 024import java.nio.file.attribute.BasicFileAttributes; 025import java.nio.file.attribute.FileOwnerAttributeView; 026import java.nio.file.attribute.PosixFileAttributeView; 027import java.nio.file.attribute.PosixFilePermission; 028import java.nio.file.attribute.PosixFilePermissions; 029import java.util.List; 030import java.util.Set; 031 032import org.apache.logging.log4j.core.Core; 033import org.apache.logging.log4j.core.config.Configuration; 034import org.apache.logging.log4j.core.config.plugins.Plugin; 035import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 036import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 037import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 038import org.apache.logging.log4j.core.config.plugins.PluginElement; 039import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; 040import org.apache.logging.log4j.core.lookup.StrSubstitutor; 041import org.apache.logging.log4j.core.util.FileUtils; 042import org.apache.logging.log4j.util.Strings; 043 044/** 045 * File posix attribute view action. 046 * 047 * Allow to define file permissions, user and group for log files on posix supported OS. 048 */ 049@Plugin(name = "PosixViewAttribute", category = Core.CATEGORY_NAME, printObject = true) 050public class PosixViewAttributeAction extends AbstractPathAction { 051 052 /** 053 * File permissions. 054 */ 055 private final Set<PosixFilePermission> filePermissions; 056 057 /** 058 * File owner. 059 */ 060 private final String fileOwner; 061 062 /** 063 * File group. 064 */ 065 private final String fileGroup; 066 067 private PosixViewAttributeAction(final String basePath, final boolean followSymbolicLinks, 068 final int maxDepth, final PathCondition[] pathConditions, final StrSubstitutor subst, 069 final Set<PosixFilePermission> filePermissions, 070 final String fileOwner, final String fileGroup) { 071 super(basePath, followSymbolicLinks, maxDepth, pathConditions, subst); 072 this.filePermissions = filePermissions; 073 this.fileOwner = fileOwner; 074 this.fileGroup = fileGroup; 075 } 076 077 @PluginBuilderFactory 078 public static Builder newBuilder() { 079 return new Builder(); 080 } 081 082 /** 083 * Builder for the posix view attribute action. 084 */ 085 public static class Builder implements org.apache.logging.log4j.core.util.Builder<PosixViewAttributeAction> { 086 087 @PluginConfiguration 088 private Configuration configuration; 089 090 private StrSubstitutor subst; 091 092 @PluginBuilderAttribute 093 @Required(message = "No base path provided") 094 private String basePath; 095 096 @PluginBuilderAttribute 097 private boolean followLinks = false; 098 099 @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}