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