at 22.05-pre 16 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.tomcat; 8 tomcat = cfg.package; 9in 10 11{ 12 13 meta = { 14 maintainers = with maintainers; [ danbst ]; 15 }; 16 17 ###### interface 18 19 options = { 20 21 services.tomcat = { 22 enable = mkEnableOption "Apache Tomcat"; 23 24 package = mkOption { 25 type = types.package; 26 default = pkgs.tomcat85; 27 defaultText = literalExpression "pkgs.tomcat85"; 28 example = lib.literalExpression "pkgs.tomcat9"; 29 description = '' 30 Which tomcat package to use. 31 ''; 32 }; 33 34 purifyOnStart = mkOption { 35 type = types.bool; 36 default = false; 37 description = '' 38 On startup, the `baseDir` directory is populated with various files, 39 subdirectories and symlinks. If this option is enabled, these items 40 (except for the `logs` and `work` subdirectories) are first removed. 41 This prevents interference from remainders of an old configuration 42 (libraries, webapps, etc.), so it's recommended to enable this option. 43 ''; 44 }; 45 46 baseDir = mkOption { 47 type = lib.types.path; 48 default = "/var/tomcat"; 49 description = '' 50 Location where Tomcat stores configuration files, web applications 51 and logfiles. Note that it is partially cleared on each service startup 52 if `purifyOnStart` is enabled. 53 ''; 54 }; 55 56 logDirs = mkOption { 57 default = []; 58 type = types.listOf types.path; 59 description = "Directories to create in baseDir/logs/"; 60 }; 61 62 extraConfigFiles = mkOption { 63 default = []; 64 type = types.listOf types.path; 65 description = "Extra configuration files to pull into the tomcat conf directory"; 66 }; 67 68 extraEnvironment = mkOption { 69 type = types.listOf types.str; 70 default = []; 71 example = [ "ENVIRONMENT=production" ]; 72 description = "Environment Variables to pass to the tomcat service"; 73 }; 74 75 extraGroups = mkOption { 76 default = []; 77 type = types.listOf types.str; 78 example = [ "users" ]; 79 description = "Defines extra groups to which the tomcat user belongs."; 80 }; 81 82 user = mkOption { 83 type = types.str; 84 default = "tomcat"; 85 description = "User account under which Apache Tomcat runs."; 86 }; 87 88 group = mkOption { 89 type = types.str; 90 default = "tomcat"; 91 description = "Group account under which Apache Tomcat runs."; 92 }; 93 94 javaOpts = mkOption { 95 type = types.either (types.listOf types.str) types.str; 96 default = ""; 97 description = "Parameters to pass to the Java Virtual Machine which spawns Apache Tomcat"; 98 }; 99 100 catalinaOpts = mkOption { 101 type = types.either (types.listOf types.str) types.str; 102 default = ""; 103 description = "Parameters to pass to the Java Virtual Machine which spawns the Catalina servlet container"; 104 }; 105 106 sharedLibs = mkOption { 107 type = types.listOf types.str; 108 default = []; 109 description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications"; 110 }; 111 112 serverXml = mkOption { 113 type = types.lines; 114 default = ""; 115 description = " 116 Verbatim server.xml configuration. 117 This is mutually exclusive with the virtualHosts options. 118 "; 119 }; 120 121 commonLibs = mkOption { 122 type = types.listOf types.str; 123 default = []; 124 description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications and the servlet container"; 125 }; 126 127 webapps = mkOption { 128 type = types.listOf types.path; 129 default = [ tomcat.webapps ]; 130 defaultText = literalExpression "[ pkgs.tomcat85.webapps ]"; 131 description = "List containing WAR files or directories with WAR files which are web applications to be deployed on Tomcat"; 132 }; 133 134 virtualHosts = mkOption { 135 type = types.listOf (types.submodule { 136 options = { 137 name = mkOption { 138 type = types.str; 139 description = "name of the virtualhost"; 140 }; 141 aliases = mkOption { 142 type = types.listOf types.str; 143 description = "aliases of the virtualhost"; 144 default = []; 145 }; 146 webapps = mkOption { 147 type = types.listOf types.path; 148 description = '' 149 List containing web application WAR files and/or directories containing 150 web applications and configuration files for the virtual host. 151 ''; 152 default = []; 153 }; 154 }; 155 }); 156 default = []; 157 description = "List consisting of a virtual host name and a list of web applications to deploy on each virtual host"; 158 }; 159 160 logPerVirtualHost = mkOption { 161 type = types.bool; 162 default = false; 163 description = "Whether to enable logging per virtual host."; 164 }; 165 166 jdk = mkOption { 167 type = types.package; 168 default = pkgs.jdk; 169 defaultText = literalExpression "pkgs.jdk"; 170 description = "Which JDK to use."; 171 }; 172 173 axis2 = { 174 175 enable = mkOption { 176 default = false; 177 type = types.bool; 178 description = "Whether to enable an Apache Axis2 container"; 179 }; 180 181 services = mkOption { 182 default = []; 183 type = types.listOf types.str; 184 description = "List containing AAR files or directories with AAR files which are web services to be deployed on Axis2"; 185 }; 186 187 }; 188 189 }; 190 191 }; 192 193 194 ###### implementation 195 196 config = mkIf config.services.tomcat.enable { 197 198 users.groups.tomcat.gid = config.ids.gids.tomcat; 199 200 users.users.tomcat = 201 { uid = config.ids.uids.tomcat; 202 description = "Tomcat user"; 203 home = "/homeless-shelter"; 204 extraGroups = cfg.extraGroups; 205 }; 206 207 systemd.services.tomcat = { 208 description = "Apache Tomcat server"; 209 wantedBy = [ "multi-user.target" ]; 210 after = [ "network.target" ]; 211 212 preStart = '' 213 ${lib.optionalString cfg.purifyOnStart '' 214 # Delete most directories/symlinks we create from the existing base directory, 215 # to get rid of remainders of an old configuration. 216 # The list of directories to delete is taken from the "mkdir" command below, 217 # excluding "logs" (because logs are valuable) and "work" (because normally 218 # session files are there), and additionally including "bin". 219 rm -rf ${cfg.baseDir}/{conf,virtualhosts,temp,lib,shared/lib,webapps,bin} 220 ''} 221 222 # Create the base directory 223 mkdir -p \ 224 ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work} 225 chown ${cfg.user}:${cfg.group} \ 226 ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work} 227 228 # Create a symlink to the bin directory of the tomcat component 229 ln -sfn ${tomcat}/bin ${cfg.baseDir}/bin 230 231 # Symlink the config files in the conf/ directory (except for catalina.properties and server.xml) 232 for i in $(ls ${tomcat}/conf | grep -v catalina.properties | grep -v server.xml); do 233 ln -sfn ${tomcat}/conf/$i ${cfg.baseDir}/conf/`basename $i` 234 done 235 236 ${if cfg.extraConfigFiles != [] then '' 237 for i in ${toString cfg.extraConfigFiles}; do 238 ln -sfn $i ${cfg.baseDir}/conf/`basename $i` 239 done 240 '' else ""} 241 242 # Create a modified catalina.properties file 243 # Change all references from CATALINA_HOME to CATALINA_BASE and add support for shared libraries 244 sed -e 's|''${catalina.home}|''${catalina.base}|g' \ 245 -e 's|shared.loader=|shared.loader=''${catalina.base}/shared/lib/*.jar|' \ 246 ${tomcat}/conf/catalina.properties > ${cfg.baseDir}/conf/catalina.properties 247 248 ${if cfg.serverXml != "" then '' 249 cp -f ${pkgs.writeTextDir "server.xml" cfg.serverXml}/* ${cfg.baseDir}/conf/ 250 '' else 251 let 252 hostElementForVirtualHost = virtualHost: '' 253 <Host name="${virtualHost.name}" appBase="virtualhosts/${virtualHost.name}/webapps" 254 unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false"> 255 '' + concatStrings (innerElementsForVirtualHost virtualHost) + '' 256 </Host> 257 ''; 258 innerElementsForVirtualHost = virtualHost: 259 (map (alias: '' 260 <Alias>${alias}</Alias> 261 '') virtualHost.aliases) 262 ++ (optional cfg.logPerVirtualHost '' 263 <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs/${virtualHost.name}" 264 prefix="${virtualHost.name}_access_log." pattern="combined" resolveHosts="false"/> 265 ''); 266 hostElementsString = concatMapStringsSep "\n" hostElementForVirtualHost cfg.virtualHosts; 267 hostElementsSedString = replaceStrings ["\n"] ["\\\n"] hostElementsString; 268 in '' 269 # Create a modified server.xml which also includes all virtual hosts 270 sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\\"${escapeShellArg hostElementsSedString} \ 271 ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml 272 '' 273 } 274 ${optionalString (cfg.logDirs != []) '' 275 for i in ${toString cfg.logDirs}; do 276 mkdir -p ${cfg.baseDir}/logs/$i 277 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/$i 278 done 279 ''} 280 ${optionalString cfg.logPerVirtualHost (toString (map (h: '' 281 mkdir -p ${cfg.baseDir}/logs/${h.name} 282 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name} 283 '') cfg.virtualHosts))} 284 285 # Symlink all the given common libs files or paths into the lib/ directory 286 for i in ${tomcat} ${toString cfg.commonLibs}; do 287 if [ -f $i ]; then 288 # If the given web application is a file, symlink it into the common/lib/ directory 289 ln -sfn $i ${cfg.baseDir}/lib/`basename $i` 290 elif [ -d $i ]; then 291 # If the given web application is a directory, then iterate over the files 292 # in the special purpose directories and symlink them into the tomcat tree 293 294 for j in $i/lib/*; do 295 ln -sfn $j ${cfg.baseDir}/lib/`basename $j` 296 done 297 fi 298 done 299 300 # Symlink all the given shared libs files or paths into the shared/lib/ directory 301 for i in ${toString cfg.sharedLibs}; do 302 if [ -f $i ]; then 303 # If the given web application is a file, symlink it into the common/lib/ directory 304 ln -sfn $i ${cfg.baseDir}/shared/lib/`basename $i` 305 elif [ -d $i ]; then 306 # If the given web application is a directory, then iterate over the files 307 # in the special purpose directories and symlink them into the tomcat tree 308 309 for j in $i/shared/lib/*; do 310 ln -sfn $j ${cfg.baseDir}/shared/lib/`basename $j` 311 done 312 fi 313 done 314 315 # Symlink all the given web applications files or paths into the webapps/ directory 316 for i in ${toString cfg.webapps}; do 317 if [ -f $i ]; then 318 # If the given web application is a file, symlink it into the webapps/ directory 319 ln -sfn $i ${cfg.baseDir}/webapps/`basename $i` 320 elif [ -d $i ]; then 321 # If the given web application is a directory, then iterate over the files 322 # in the special purpose directories and symlink them into the tomcat tree 323 324 for j in $i/webapps/*; do 325 ln -sfn $j ${cfg.baseDir}/webapps/`basename $j` 326 done 327 328 # Also symlink the configuration files if they are included 329 if [ -d $i/conf/Catalina ]; then 330 for j in $i/conf/Catalina/*; do 331 mkdir -p ${cfg.baseDir}/conf/Catalina/localhost 332 ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j` 333 done 334 fi 335 fi 336 done 337 338 ${toString (map (virtualHost: '' 339 # Create webapps directory for the virtual host 340 mkdir -p ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps 341 342 # Modify ownership 343 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps 344 345 # Symlink all the given web applications files or paths into the webapps/ directory 346 # of this virtual host 347 for i in "${if virtualHost ? webapps then toString virtualHost.webapps else ""}"; do 348 if [ -f $i ]; then 349 # If the given web application is a file, symlink it into the webapps/ directory 350 ln -sfn $i ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $i` 351 elif [ -d $i ]; then 352 # If the given web application is a directory, then iterate over the files 353 # in the special purpose directories and symlink them into the tomcat tree 354 355 for j in $i/webapps/*; do 356 ln -sfn $j ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $j` 357 done 358 359 # Also symlink the configuration files if they are included 360 if [ -d $i/conf/Catalina ]; then 361 for j in $i/conf/Catalina/*; do 362 mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name} 363 ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j` 364 done 365 fi 366 fi 367 done 368 '') cfg.virtualHosts)} 369 370 ${optionalString cfg.axis2.enable '' 371 # Copy the Axis2 web application 372 cp -av ${pkgs.axis2}/webapps/axis2 ${cfg.baseDir}/webapps 373 374 # Turn off addressing, which causes many errors 375 sed -i -e 's%<module ref="addressing"/>%<!-- <module ref="addressing"/> -->%' ${cfg.baseDir}/webapps/axis2/WEB-INF/conf/axis2.xml 376 377 # Modify permissions on the Axis2 application 378 chown -R ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps/axis2 379 380 # Symlink all the given web service files or paths into the webapps/axis2/WEB-INF/services directory 381 for i in ${toString cfg.axis2.services}; do 382 if [ -f $i ]; then 383 # If the given web service is a file, symlink it into the webapps/axis2/WEB-INF/services 384 ln -sfn $i ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $i` 385 elif [ -d $i ]; then 386 # If the given web application is a directory, then iterate over the files 387 # in the special purpose directories and symlink them into the tomcat tree 388 389 for j in $i/webapps/axis2/WEB-INF/services/*; do 390 ln -sfn $j ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $j` 391 done 392 393 # Also symlink the configuration files if they are included 394 if [ -d $i/conf/Catalina ]; then 395 for j in $i/conf/Catalina/*; do 396 ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j` 397 done 398 fi 399 fi 400 done 401 ''} 402 ''; 403 404 serviceConfig = { 405 Type = "forking"; 406 PermissionsStartOnly = true; 407 PIDFile="/run/tomcat/tomcat.pid"; 408 RuntimeDirectory = "tomcat"; 409 User = cfg.user; 410 Environment=[ 411 "CATALINA_BASE=${cfg.baseDir}" 412 "CATALINA_PID=/run/tomcat/tomcat.pid" 413 "JAVA_HOME='${cfg.jdk}'" 414 "JAVA_OPTS='${builtins.toString cfg.javaOpts}'" 415 "CATALINA_OPTS='${builtins.toString cfg.catalinaOpts}'" 416 ] ++ cfg.extraEnvironment; 417 ExecStart = "${tomcat}/bin/startup.sh"; 418 ExecStop = "${tomcat}/bin/shutdown.sh"; 419 }; 420 }; 421 }; 422}