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