1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 mainCfg = config.services.httpd; 8 9 httpd = mainCfg.package.out; 10 11 version24 = !versionOlder httpd.version "2.4"; 12 13 httpdConf = mainCfg.configFile; 14 15 php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ }; 16 17 phpMajorVersion = head (splitString "." php.version); 18 19 mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = httpd; }; 20 21 defaultListen = cfg: if cfg.enableSSL 22 then [{ip = "*"; port = 443;}] 23 else [{ip = "*"; port = 80;}]; 24 25 getListen = cfg: 26 let list = (lib.optional (cfg.port != 0) {ip = "*"; port = cfg.port;}) ++ cfg.listen; 27 in if list == [] 28 then defaultListen cfg 29 else list; 30 31 listenToString = l: "${l.ip}:${toString l.port}"; 32 33 extraModules = attrByPath ["extraModules"] [] mainCfg; 34 extraForeignModules = filter isAttrs extraModules; 35 extraApacheModules = filter isString extraModules; 36 37 38 makeServerInfo = cfg: { 39 # Canonical name must not include a trailing slash. 40 canonicalNames = 41 let defaultPort = (head (defaultListen cfg)).port; in 42 map (port: 43 (if cfg.enableSSL then "https" else "http") + "://" + 44 cfg.hostName + 45 (if port != defaultPort then ":${toString port}" else "") 46 ) (map (x: x.port) (getListen cfg)); 47 48 # Admin address: inherit from the main server if not specified for 49 # a virtual host. 50 adminAddr = if cfg.adminAddr != null then cfg.adminAddr else mainCfg.adminAddr; 51 52 vhostConfig = cfg; 53 serverConfig = mainCfg; 54 fullConfig = config; # machine config 55 }; 56 57 58 allHosts = [mainCfg] ++ mainCfg.virtualHosts; 59 60 61 callSubservices = serverInfo: defs: 62 let f = svc: 63 let 64 svcFunction = 65 if svc ? function then svc.function 66 # instead of using serviceType="mediawiki"; you can copy mediawiki.nix to any location outside nixpkgs, modify it at will, and use serviceExpression=./mediawiki.nix; 67 else if svc ? serviceExpression then import (toString svc.serviceExpression) 68 else import (toString "${toString ./.}/${if svc ? serviceType then svc.serviceType else svc.serviceName}.nix"); 69 config = (evalModules 70 { modules = [ { options = res.options; config = svc.config or svc; } ]; 71 check = false; 72 }).config; 73 defaults = { 74 extraConfig = ""; 75 extraModules = []; 76 extraModulesPre = []; 77 extraPath = []; 78 extraServerPath = []; 79 globalEnvVars = []; 80 robotsEntries = ""; 81 startupScript = ""; 82 enablePHP = false; 83 enablePerl = false; 84 phpOptions = ""; 85 options = {}; 86 documentRoot = null; 87 }; 88 res = defaults // svcFunction { inherit config lib pkgs serverInfo php; }; 89 in res; 90 in map f defs; 91 92 93 # !!! callSubservices is expensive 94 subservicesFor = cfg: callSubservices (makeServerInfo cfg) cfg.extraSubservices; 95 96 mainSubservices = subservicesFor mainCfg; 97 98 allSubservices = mainSubservices ++ concatMap subservicesFor mainCfg.virtualHosts; 99 100 101 enableSSL = any (vhost: vhost.enableSSL) allHosts; 102 103 104 # Names of modules from ${httpd}/modules that we want to load. 105 apacheModules = 106 [ # HTTP authentication mechanisms: basic and digest. 107 "auth_basic" "auth_digest" 108 109 # Authentication: is the user who he claims to be? 110 "authn_file" "authn_dbm" "authn_anon" 111 (if version24 then "authn_core" else "authn_alias") 112 113 # Authorization: is the user allowed access? 114 "authz_user" "authz_groupfile" "authz_host" 115 116 # Other modules. 117 "ext_filter" "include" "log_config" "env" "mime_magic" 118 "cern_meta" "expires" "headers" "usertrack" /* "unique_id" */ "setenvif" 119 "mime" "dav" "status" "autoindex" "asis" "info" "dav_fs" 120 "vhost_alias" "negotiation" "dir" "imagemap" "actions" "speling" 121 "userdir" "alias" "rewrite" "proxy" "proxy_http" 122 ] 123 ++ optionals version24 [ 124 "mpm_${mainCfg.multiProcessingModule}" 125 "authz_core" 126 "unixd" 127 "cache" "cache_disk" 128 "slotmem_shm" 129 "socache_shmcb" 130 # For compatibility with old configurations, the new module mod_access_compat is provided. 131 "access_compat" 132 ] 133 ++ (if mainCfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ]) 134 ++ optional enableSSL "ssl" 135 ++ extraApacheModules; 136 137 138 allDenied = if version24 then '' 139 Require all denied 140 '' else '' 141 Order deny,allow 142 Deny from all 143 ''; 144 145 allGranted = if version24 then '' 146 Require all granted 147 '' else '' 148 Order allow,deny 149 Allow from all 150 ''; 151 152 153 loggingConf = (if mainCfg.logFormat != "none" then '' 154 ErrorLog ${mainCfg.logDir}/error_log 155 156 LogLevel notice 157 158 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined 159 LogFormat "%h %l %u %t \"%r\" %>s %b" common 160 LogFormat "%{Referer}i -> %U" referer 161 LogFormat "%{User-agent}i" agent 162 163 CustomLog ${mainCfg.logDir}/access_log ${mainCfg.logFormat} 164 '' else '' 165 ErrorLog /dev/null 166 ''); 167 168 169 browserHacks = '' 170 BrowserMatch "Mozilla/2" nokeepalive 171 BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 172 BrowserMatch "RealPlayer 4\.0" force-response-1.0 173 BrowserMatch "Java/1\.0" force-response-1.0 174 BrowserMatch "JDK/1\.0" force-response-1.0 175 BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully 176 BrowserMatch "^WebDrive" redirect-carefully 177 BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully 178 BrowserMatch "^gnome-vfs" redirect-carefully 179 ''; 180 181 182 sslConf = '' 183 SSLSessionCache ${if version24 then "shmcb" else "shm"}:${mainCfg.stateDir}/ssl_scache(512000) 184 185 ${if version24 then "Mutex" else "SSLMutex"} posixsem 186 187 SSLRandomSeed startup builtin 188 SSLRandomSeed connect builtin 189 190 SSLProtocol All -SSLv2 -SSLv3 191 SSLCipherSuite HIGH:!aNULL:!MD5:!EXP 192 SSLHonorCipherOrder on 193 ''; 194 195 196 mimeConf = '' 197 TypesConfig ${httpd}/conf/mime.types 198 199 AddType application/x-x509-ca-cert .crt 200 AddType application/x-pkcs7-crl .crl 201 AddType application/x-httpd-php .php .phtml 202 203 <IfModule mod_mime_magic.c> 204 MIMEMagicFile ${httpd}/conf/magic 205 </IfModule> 206 ''; 207 208 209 perServerConf = isMainServer: cfg: let 210 211 serverInfo = makeServerInfo cfg; 212 213 subservices = callSubservices serverInfo cfg.extraSubservices; 214 215 maybeDocumentRoot = fold (svc: acc: 216 if acc == null then svc.documentRoot else assert svc.documentRoot == null; acc 217 ) null ([ cfg ] ++ subservices); 218 219 documentRoot = if maybeDocumentRoot != null then maybeDocumentRoot else 220 pkgs.runCommand "empty" {} "mkdir -p $out"; 221 222 documentRootConf = '' 223 DocumentRoot "${documentRoot}" 224 225 <Directory "${documentRoot}"> 226 Options Indexes FollowSymLinks 227 AllowOverride None 228 ${allGranted} 229 </Directory> 230 ''; 231 232 robotsTxt = 233 concatStringsSep "\n" (filter (x: x != "") ( 234 # If this is a vhost, the include the entries for the main server as well. 235 (if isMainServer then [] else [mainCfg.robotsEntries] ++ map (svc: svc.robotsEntries) mainSubservices) 236 ++ [cfg.robotsEntries] 237 ++ (map (svc: svc.robotsEntries) subservices))); 238 239 in '' 240 ${concatStringsSep "\n" (map (n: "ServerName ${n}") serverInfo.canonicalNames)} 241 242 ${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases} 243 244 ${if cfg.sslServerCert != null then '' 245 SSLCertificateFile ${cfg.sslServerCert} 246 SSLCertificateKeyFile ${cfg.sslServerKey} 247 ${if cfg.sslServerChain != null then '' 248 SSLCertificateChainFile ${cfg.sslServerChain} 249 '' else ""} 250 '' else ""} 251 252 ${if cfg.enableSSL then '' 253 SSLEngine on 254 '' else if enableSSL then /* i.e., SSL is enabled for some host, but not this one */ 255 '' 256 SSLEngine off 257 '' else ""} 258 259 ${if isMainServer || cfg.adminAddr != null then '' 260 ServerAdmin ${cfg.adminAddr} 261 '' else ""} 262 263 ${if !isMainServer && mainCfg.logPerVirtualHost then '' 264 ErrorLog ${mainCfg.logDir}/error_log-${cfg.hostName} 265 CustomLog ${mainCfg.logDir}/access_log-${cfg.hostName} ${cfg.logFormat} 266 '' else ""} 267 268 ${optionalString (robotsTxt != "") '' 269 Alias /robots.txt ${pkgs.writeText "robots.txt" robotsTxt} 270 ''} 271 272 ${if isMainServer || maybeDocumentRoot != null then documentRootConf else ""} 273 274 ${if cfg.enableUserDir then '' 275 276 UserDir public_html 277 UserDir disabled root 278 279 <Directory "/home/*/public_html"> 280 AllowOverride FileInfo AuthConfig Limit Indexes 281 Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec 282 <Limit GET POST OPTIONS> 283 ${allGranted} 284 </Limit> 285 <LimitExcept GET POST OPTIONS> 286 ${allDenied} 287 </LimitExcept> 288 </Directory> 289 290 '' else ""} 291 292 ${if cfg.globalRedirect != null && cfg.globalRedirect != "" then '' 293 RedirectPermanent / ${cfg.globalRedirect} 294 '' else ""} 295 296 ${ 297 let makeFileConf = elem: '' 298 Alias ${elem.urlPath} ${elem.file} 299 ''; 300 in concatMapStrings makeFileConf cfg.servedFiles 301 } 302 303 ${ 304 let makeDirConf = elem: '' 305 Alias ${elem.urlPath} ${elem.dir}/ 306 <Directory ${elem.dir}> 307 Options +Indexes 308 ${allGranted} 309 AllowOverride All 310 </Directory> 311 ''; 312 in concatMapStrings makeDirConf cfg.servedDirs 313 } 314 315 ${concatMapStrings (svc: svc.extraConfig) subservices} 316 317 ${cfg.extraConfig} 318 ''; 319 320 321 confFile = pkgs.writeText "httpd.conf" '' 322 323 ServerRoot ${httpd} 324 325 ${optionalString version24 '' 326 DefaultRuntimeDir ${mainCfg.stateDir}/runtime 327 ''} 328 329 PidFile ${mainCfg.stateDir}/httpd.pid 330 331 ${optionalString (mainCfg.multiProcessingModule != "prefork") '' 332 # mod_cgid requires this. 333 ScriptSock ${mainCfg.stateDir}/cgisock 334 ''} 335 336 <IfModule prefork.c> 337 MaxClients ${toString mainCfg.maxClients} 338 MaxRequestsPerChild ${toString mainCfg.maxRequestsPerChild} 339 </IfModule> 340 341 ${let 342 listen = concatMap getListen allHosts; 343 toStr = listen: "Listen ${listenToString listen}\n"; 344 uniqueListen = uniqList {inputList = map toStr listen;}; 345 in concatStrings uniqueListen 346 } 347 348 User ${mainCfg.user} 349 Group ${mainCfg.group} 350 351 ${let 352 load = {name, path}: "LoadModule ${name}_module ${path}\n"; 353 allModules = 354 concatMap (svc: svc.extraModulesPre) allSubservices 355 ++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules 356 ++ optional mainCfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; } 357 ++ optional enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; } 358 ++ optional enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; } 359 ++ concatMap (svc: svc.extraModules) allSubservices 360 ++ extraForeignModules; 361 in concatMapStrings load allModules 362 } 363 364 AddHandler type-map var 365 366 <Files ~ "^\.ht"> 367 ${allDenied} 368 </Files> 369 370 ${mimeConf} 371 ${loggingConf} 372 ${browserHacks} 373 374 Include ${httpd}/conf/extra/httpd-default.conf 375 Include ${httpd}/conf/extra/httpd-autoindex.conf 376 Include ${httpd}/conf/extra/httpd-multilang-errordoc.conf 377 Include ${httpd}/conf/extra/httpd-languages.conf 378 379 ${if enableSSL then sslConf else ""} 380 381 # Fascist default - deny access to everything. 382 <Directory /> 383 Options FollowSymLinks 384 AllowOverride None 385 ${allDenied} 386 </Directory> 387 388 # But do allow access to files in the store so that we don't have 389 # to generate <Directory> clauses for every generated file that we 390 # want to serve. 391 <Directory /nix/store> 392 ${allGranted} 393 </Directory> 394 395 # Generate directives for the main server. 396 ${perServerConf true mainCfg} 397 398 # Always enable virtual hosts; it doesn't seem to hurt. 399 ${let 400 listen = concatMap getListen allHosts; 401 uniqueListen = uniqList {inputList = listen;}; 402 directives = concatMapStrings (listen: "NameVirtualHost ${listenToString listen}\n") uniqueListen; 403 in optionalString (!version24) directives 404 } 405 406 ${let 407 makeVirtualHost = vhost: '' 408 <VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}> 409 ${perServerConf false vhost} 410 </VirtualHost> 411 ''; 412 in concatMapStrings makeVirtualHost mainCfg.virtualHosts 413 } 414 ''; 415 416 417 enablePHP = mainCfg.enablePHP || any (svc: svc.enablePHP) allSubservices; 418 419 enablePerl = mainCfg.enablePerl || any (svc: svc.enablePerl) allSubservices; 420 421 422 # Generate the PHP configuration file. Should probably be factored 423 # out into a separate module. 424 phpIni = pkgs.runCommand "php.ini" 425 { options = concatStringsSep "\n" 426 ([ mainCfg.phpOptions ] ++ (map (svc: svc.phpOptions) allSubservices)); 427 } 428 '' 429 cat ${php}/etc/php.ini > $out 430 echo "$options" >> $out 431 ''; 432 433in 434 435 436{ 437 438 ###### interface 439 440 options = { 441 442 services.httpd = { 443 444 enable = mkOption { 445 type = types.bool; 446 default = false; 447 description = "Whether to enable the Apache HTTP Server."; 448 }; 449 450 package = mkOption { 451 type = types.package; 452 default = pkgs.apacheHttpd; 453 defaultText = "pkgs.apacheHttpd"; 454 description = '' 455 Overridable attribute of the Apache HTTP Server package to use. 456 ''; 457 }; 458 459 configFile = mkOption { 460 type = types.path; 461 default = confFile; 462 defaultText = "confFile"; 463 example = literalExample ''pkgs.writeText "httpd.conf" "# my custom config file ..."''; 464 description = '' 465 Override the configuration file used by Apache. By default, 466 NixOS generates one automatically. 467 ''; 468 }; 469 470 extraConfig = mkOption { 471 type = types.lines; 472 default = ""; 473 description = '' 474 Cnfiguration lines appended to the generated Apache 475 configuration file. Note that this mechanism may not work 476 when <option>configFile</option> is overridden. 477 ''; 478 }; 479 480 extraModules = mkOption { 481 type = types.listOf types.unspecified; 482 default = []; 483 example = literalExample ''[ "proxy_connect" { name = "php5"; path = "''${pkgs.php}/modules/libphp5.so"; } ]''; 484 description = '' 485 Additional Apache modules to be used. These can be 486 specified as a string in the case of modules distributed 487 with Apache, or as an attribute set specifying the 488 <varname>name</varname> and <varname>path</varname> of the 489 module. 490 ''; 491 }; 492 493 logPerVirtualHost = mkOption { 494 type = types.bool; 495 default = false; 496 description = '' 497 If enabled, each virtual host gets its own 498 <filename>access_log</filename> and 499 <filename>error_log</filename>, namely suffixed by the 500 <option>hostName</option> of the virtual host. 501 ''; 502 }; 503 504 user = mkOption { 505 type = types.str; 506 default = "wwwrun"; 507 description = '' 508 User account under which httpd runs. The account is created 509 automatically if it doesn't exist. 510 ''; 511 }; 512 513 group = mkOption { 514 type = types.str; 515 default = "wwwrun"; 516 description = '' 517 Group under which httpd runs. The account is created 518 automatically if it doesn't exist. 519 ''; 520 }; 521 522 logDir = mkOption { 523 type = types.path; 524 default = "/var/log/httpd"; 525 description = '' 526 Directory for Apache's log files. It is created automatically. 527 ''; 528 }; 529 530 stateDir = mkOption { 531 type = types.path; 532 default = "/run/httpd"; 533 description = '' 534 Directory for Apache's transient runtime state (such as PID 535 files). It is created automatically. Note that the default, 536 <filename>/run/httpd</filename>, is deleted at boot time. 537 ''; 538 }; 539 540 virtualHosts = mkOption { 541 type = types.listOf (types.submodule ( 542 { options = import ./per-server-options.nix { 543 inherit lib; 544 forMainServer = false; 545 }; 546 })); 547 default = []; 548 example = [ 549 { hostName = "foo"; 550 documentRoot = "/data/webroot-foo"; 551 } 552 { hostName = "bar"; 553 documentRoot = "/data/webroot-bar"; 554 } 555 ]; 556 description = '' 557 Specification of the virtual hosts served by Apache. Each 558 element should be an attribute set specifying the 559 configuration of the virtual host. The available options 560 are the non-global options permissible for the main host. 561 ''; 562 }; 563 564 enableMellon = mkOption { 565 type = types.bool; 566 default = false; 567 description = "Whether to enable the mod_auth_mellon module."; 568 }; 569 570 enablePHP = mkOption { 571 type = types.bool; 572 default = false; 573 description = "Whether to enable the PHP module."; 574 }; 575 576 phpPackage = mkOption { 577 type = types.package; 578 default = pkgs.php; 579 defaultText = "pkgs.php"; 580 description = '' 581 Overridable attribute of the PHP package to use. 582 ''; 583 }; 584 585 enablePerl = mkOption { 586 type = types.bool; 587 default = false; 588 description = "Whether to enable the Perl module (mod_perl)."; 589 }; 590 591 phpOptions = mkOption { 592 type = types.lines; 593 default = ""; 594 example = 595 '' 596 date.timezone = "CET" 597 ''; 598 description = 599 "Options appended to the PHP configuration file <filename>php.ini</filename>."; 600 }; 601 602 multiProcessingModule = mkOption { 603 type = types.str; 604 default = "prefork"; 605 example = "worker"; 606 description = 607 '' 608 Multi-processing module to be used by Apache. Available 609 modules are <literal>prefork</literal> (the default; 610 handles each request in a separate child process), 611 <literal>worker</literal> (hybrid approach that starts a 612 number of child processes each running a number of 613 threads) and <literal>event</literal> (a recent variant of 614 <literal>worker</literal> that handles persistent 615 connections more efficiently). 616 ''; 617 }; 618 619 maxClients = mkOption { 620 type = types.int; 621 default = 150; 622 example = 8; 623 description = "Maximum number of httpd processes (prefork)"; 624 }; 625 626 maxRequestsPerChild = mkOption { 627 type = types.int; 628 default = 0; 629 example = 500; 630 description = 631 "Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited"; 632 }; 633 } 634 635 # Include the options shared between the main server and virtual hosts. 636 // (import ./per-server-options.nix { 637 inherit lib; 638 forMainServer = true; 639 }); 640 641 }; 642 643 644 ###### implementation 645 646 config = mkIf config.services.httpd.enable { 647 648 assertions = [ { assertion = mainCfg.enableSSL == true 649 -> mainCfg.sslServerCert != null 650 && mainCfg.sslServerKey != null; 651 message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; } 652 ]; 653 654 warnings = map (cfg: ''apache-httpd's port option is deprecated. Use listen = [{/*ip = "*"; */ port = ${toString cfg.port};}]; instead'' ) (lib.filter (cfg: cfg.port != 0) allHosts); 655 656 users.users = optionalAttrs (mainCfg.user == "wwwrun") (singleton 657 { name = "wwwrun"; 658 group = mainCfg.group; 659 description = "Apache httpd user"; 660 uid = config.ids.uids.wwwrun; 661 }); 662 663 users.groups = optionalAttrs (mainCfg.group == "wwwrun") (singleton 664 { name = "wwwrun"; 665 gid = config.ids.gids.wwwrun; 666 }); 667 668 environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices; 669 670 services.httpd.phpOptions = 671 '' 672 ; Needed for PHP's mail() function. 673 sendmail_path = sendmail -t -i 674 '' + optionalString (!isNull config.time.timeZone) '' 675 676 ; Apparently PHP doesn't use $TZ. 677 date.timezone = "${config.time.timeZone}" 678 ''; 679 680 systemd.services.httpd = 681 { description = "Apache HTTPD"; 682 683 wantedBy = [ "multi-user.target" ]; 684 wants = [ "keys.target" ]; 685 after = [ "network.target" "fs.target" "postgresql.service" "keys.target" ]; 686 687 path = 688 [ httpd pkgs.coreutils pkgs.gnugrep ] 689 ++ # Needed for PHP's mail() function. !!! Probably the 690 # ssmtp module should export the path to sendmail in 691 # some way. 692 optional config.networking.defaultMailServer.directDelivery pkgs.ssmtp 693 ++ concatMap (svc: svc.extraServerPath) allSubservices; 694 695 environment = 696 optionalAttrs enablePHP { PHPRC = phpIni; } 697 // optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH = "${pkgs.xmlsec}/lib"; } 698 // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices)); 699 700 preStart = 701 '' 702 mkdir -m 0750 -p ${mainCfg.stateDir} 703 [ $(id -u) != 0 ] || chown root.${mainCfg.group} ${mainCfg.stateDir} 704 ${optionalString version24 '' 705 mkdir -m 0750 -p "${mainCfg.stateDir}/runtime" 706 [ $(id -u) != 0 ] || chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime" 707 ''} 708 mkdir -m 0700 -p ${mainCfg.logDir} 709 710 # Get rid of old semaphores. These tend to accumulate across 711 # server restarts, eventually preventing it from restarting 712 # successfully. 713 for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${mainCfg.user} ' | cut -f2 -d ' '); do 714 ${pkgs.utillinux}/bin/ipcrm -s $i 715 done 716 717 # Run the startup hooks for the subservices. 718 for i in ${toString (map (svn: svn.startupScript) allSubservices)}; do 719 echo Running Apache startup hook $i... 720 $i 721 done 722 ''; 723 724 serviceConfig.ExecStart = "@${httpd}/bin/httpd httpd -f ${httpdConf}"; 725 serviceConfig.ExecStop = "${httpd}/bin/httpd -f ${httpdConf} -k graceful-stop"; 726 serviceConfig.ExecReload = "${httpd}/bin/httpd -f ${httpdConf} -k graceful"; 727 serviceConfig.Type = "forking"; 728 serviceConfig.PIDFile = "${mainCfg.stateDir}/httpd.pid"; 729 serviceConfig.Restart = "always"; 730 serviceConfig.RestartSec = "5s"; 731 }; 732 733 }; 734}