]> git.immae.eu Git - perso/Immae/Config/Nix.git/blob - nixops/modules/websites/apache/httpd_prod.nix
31904e0f083d6b019706c4c3cb25ea208fc777ed
[perso/Immae/Config/Nix.git] / nixops / modules / websites / apache / httpd_prod.nix
1 { config, lib, pkgs, ... }:
2
3 with lib;
4
5 let
6
7 mainCfg = config.services.httpdProd;
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 ${mainCfg.sslProtocols}
191 SSLCipherSuite ${mainCfg.sslCiphers}
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" { preferLocalBuild = true; } "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 TraceEnable off
380
381 ${if enableSSL then sslConf else ""}
382
383 # Fascist default - deny access to everything.
384 <Directory />
385 Options FollowSymLinks
386 AllowOverride None
387 ${allDenied}
388 </Directory>
389
390 # Generate directives for the main server.
391 ${perServerConf true mainCfg}
392
393 # Always enable virtual hosts; it doesn't seem to hurt.
394 ${let
395 listen = concatMap getListen allHosts;
396 uniqueListen = uniqList {inputList = listen;};
397 directives = concatMapStrings (listen: "NameVirtualHost ${listenToString listen}\n") uniqueListen;
398 in optionalString (!version24) directives
399 }
400
401 ${let
402 makeVirtualHost = vhost: ''
403 <VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}>
404 ${perServerConf false vhost}
405 </VirtualHost>
406 '';
407 in concatMapStrings makeVirtualHost mainCfg.virtualHosts
408 }
409 '';
410
411
412 enablePHP = mainCfg.enablePHP || any (svc: svc.enablePHP) allSubservices;
413
414 enablePerl = mainCfg.enablePerl || any (svc: svc.enablePerl) allSubservices;
415
416
417 # Generate the PHP configuration file. Should probably be factored
418 # out into a separate module.
419 phpIni = pkgs.runCommand "php.ini"
420 { options = concatStringsSep "\n"
421 ([ mainCfg.phpOptions ] ++ (map (svc: svc.phpOptions) allSubservices));
422 }
423 ''
424 cat ${php}/etc/php.ini > $out
425 echo "$options" >> $out
426 '';
427
428 in
429
430
431 {
432
433 ###### interface
434
435 options = {
436
437 services.httpdProd = {
438
439 enable = mkOption {
440 type = types.bool;
441 default = false;
442 description = "Whether to enable the Apache HTTP Server.";
443 };
444
445 package = mkOption {
446 type = types.package;
447 default = pkgs.apacheHttpd;
448 defaultText = "pkgs.apacheHttpd";
449 description = ''
450 Overridable attribute of the Apache HTTP Server package to use.
451 '';
452 };
453
454 configFile = mkOption {
455 type = types.path;
456 default = confFile;
457 defaultText = "confFile";
458 example = literalExample ''pkgs.writeText "httpd.conf" "# my custom config file ..."'';
459 description = ''
460 Override the configuration file used by Apache. By default,
461 NixOS generates one automatically.
462 '';
463 };
464
465 extraConfig = mkOption {
466 type = types.lines;
467 default = "";
468 description = ''
469 Cnfiguration lines appended to the generated Apache
470 configuration file. Note that this mechanism may not work
471 when <option>configFile</option> is overridden.
472 '';
473 };
474
475 extraModules = mkOption {
476 type = types.listOf types.unspecified;
477 default = [];
478 example = literalExample ''[ "proxy_connect" { name = "php5"; path = "''${pkgs.php}/modules/libphp5.so"; } ]'';
479 description = ''
480 Additional Apache modules to be used. These can be
481 specified as a string in the case of modules distributed
482 with Apache, or as an attribute set specifying the
483 <varname>name</varname> and <varname>path</varname> of the
484 module.
485 '';
486 };
487
488 logPerVirtualHost = mkOption {
489 type = types.bool;
490 default = false;
491 description = ''
492 If enabled, each virtual host gets its own
493 <filename>access_log</filename> and
494 <filename>error_log</filename>, namely suffixed by the
495 <option>hostName</option> of the virtual host.
496 '';
497 };
498
499 user = mkOption {
500 type = types.str;
501 default = "wwwrun";
502 description = ''
503 User account under which httpd runs. The account is created
504 automatically if it doesn't exist.
505 '';
506 };
507
508 group = mkOption {
509 type = types.str;
510 default = "wwwrun";
511 description = ''
512 Group under which httpd runs. The account is created
513 automatically if it doesn't exist.
514 '';
515 };
516
517 logDir = mkOption {
518 type = types.path;
519 default = "/var/log/httpd";
520 description = ''
521 Directory for Apache's log files. It is created automatically.
522 '';
523 };
524
525 stateDir = mkOption {
526 type = types.path;
527 default = "/run/httpd";
528 description = ''
529 Directory for Apache's transient runtime state (such as PID
530 files). It is created automatically. Note that the default,
531 <filename>/run/httpd</filename>, is deleted at boot time.
532 '';
533 };
534
535 virtualHosts = mkOption {
536 type = types.listOf (types.submodule (
537 { options = import ./per-server-options.nix {
538 inherit lib;
539 forMainServer = false;
540 };
541 }));
542 default = [];
543 example = [
544 { hostName = "foo";
545 documentRoot = "/data/webroot-foo";
546 }
547 { hostName = "bar";
548 documentRoot = "/data/webroot-bar";
549 }
550 ];
551 description = ''
552 Specification of the virtual hosts served by Apache. Each
553 element should be an attribute set specifying the
554 configuration of the virtual host. The available options
555 are the non-global options permissible for the main host.
556 '';
557 };
558
559 enableMellon = mkOption {
560 type = types.bool;
561 default = false;
562 description = "Whether to enable the mod_auth_mellon module.";
563 };
564
565 enablePHP = mkOption {
566 type = types.bool;
567 default = false;
568 description = "Whether to enable the PHP module.";
569 };
570
571 phpPackage = mkOption {
572 type = types.package;
573 default = pkgs.php;
574 defaultText = "pkgs.php";
575 description = ''
576 Overridable attribute of the PHP package to use.
577 '';
578 };
579
580 enablePerl = mkOption {
581 type = types.bool;
582 default = false;
583 description = "Whether to enable the Perl module (mod_perl).";
584 };
585
586 phpOptions = mkOption {
587 type = types.lines;
588 default = "";
589 example =
590 ''
591 date.timezone = "CET"
592 '';
593 description =
594 "Options appended to the PHP configuration file <filename>php.ini</filename>.";
595 };
596
597 multiProcessingModule = mkOption {
598 type = types.str;
599 default = "prefork";
600 example = "worker";
601 description =
602 ''
603 Multi-processing module to be used by Apache. Available
604 modules are <literal>prefork</literal> (the default;
605 handles each request in a separate child process),
606 <literal>worker</literal> (hybrid approach that starts a
607 number of child processes each running a number of
608 threads) and <literal>event</literal> (a recent variant of
609 <literal>worker</literal> that handles persistent
610 connections more efficiently).
611 '';
612 };
613
614 maxClients = mkOption {
615 type = types.int;
616 default = 150;
617 example = 8;
618 description = "Maximum number of httpd processes (prefork)";
619 };
620
621 maxRequestsPerChild = mkOption {
622 type = types.int;
623 default = 0;
624 example = 500;
625 description =
626 "Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited";
627 };
628
629 sslCiphers = mkOption {
630 type = types.str;
631 default = "HIGH:!aNULL:!MD5:!EXP";
632 description = "Cipher Suite available for negotiation in SSL proxy handshake.";
633 };
634
635 sslProtocols = mkOption {
636 type = types.str;
637 default = "All -SSLv2 -SSLv3 -TLSv1";
638 example = "All -SSLv2 -SSLv3";
639 description = "Allowed SSL/TLS protocol versions.";
640 };
641 }
642
643 # Include the options shared between the main server and virtual hosts.
644 // (import ./per-server-options.nix {
645 inherit lib;
646 forMainServer = true;
647 });
648
649 };
650
651
652 ###### implementation
653
654 config = mkIf config.services.httpdProd.enable {
655
656 assertions = [ { assertion = mainCfg.enableSSL == true
657 -> mainCfg.sslServerCert != null
658 && mainCfg.sslServerKey != null;
659 message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; }
660 ];
661
662 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);
663
664 environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices;
665
666 services.httpdProd.phpOptions =
667 ''
668 ; Needed for PHP's mail() function.
669 sendmail_path = sendmail -t -i
670 '' + optionalString (!isNull config.time.timeZone) ''
671
672 ; Apparently PHP doesn't use $TZ.
673 date.timezone = "${config.time.timeZone}"
674 '';
675
676 systemd.services.httpdProd =
677 { description = "Apache HTTPD";
678
679 wantedBy = [ "multi-user.target" ];
680 wants = [ "keys.target" ];
681 after = [ "network.target" "fs.target" "postgresql.service" "keys.target" ];
682
683 path =
684 [ httpd pkgs.coreutils pkgs.gnugrep ]
685 ++ # Needed for PHP's mail() function. !!! Probably the
686 # ssmtp module should export the path to sendmail in
687 # some way.
688 optional config.networking.defaultMailServer.directDelivery pkgs.ssmtp
689 ++ concatMap (svc: svc.extraServerPath) allSubservices;
690
691 environment =
692 optionalAttrs enablePHP { PHPRC = phpIni; }
693 // optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH = "${pkgs.xmlsec}/lib"; }
694 // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
695
696 preStart =
697 ''
698 mkdir -m 0750 -p ${mainCfg.stateDir}
699 [ $(id -u) != 0 ] || chown root.${mainCfg.group} ${mainCfg.stateDir}
700 ${optionalString version24 ''
701 mkdir -m 0750 -p "${mainCfg.stateDir}/runtime"
702 [ $(id -u) != 0 ] || chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime"
703 ''}
704 mkdir -m 0700 -p ${mainCfg.logDir}
705
706 # Get rid of old semaphores. These tend to accumulate across
707 # server restarts, eventually preventing it from restarting
708 # successfully.
709 for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${mainCfg.user} ' | cut -f2 -d ' '); do
710 ${pkgs.utillinux}/bin/ipcrm -s $i
711 done
712
713 # Run the startup hooks for the subservices.
714 for i in ${toString (map (svn: svn.startupScript) allSubservices)}; do
715 echo Running Apache startup hook $i...
716 $i
717 done
718 '';
719
720 serviceConfig.ExecStart = "@${httpd}/bin/httpd httpd -f ${httpdConf}";
721 serviceConfig.ExecStop = "${httpd}/bin/httpd -f ${httpdConf} -k graceful-stop";
722 serviceConfig.ExecReload = "${httpd}/bin/httpd -f ${httpdConf} -k graceful";
723 serviceConfig.Type = "forking";
724 serviceConfig.PIDFile = "${mainCfg.stateDir}/httpd.pid";
725 serviceConfig.Restart = "always";
726 serviceConfig.RestartSec = "5s";
727 };
728
729 };
730 }