Firewall rule look-up
[hcoop/domtool2.git] / src / main.sml
CommitLineData
234b917a
AC
1(* HCoop Domtool (http://hcoop.sourceforge.net/)
2 * Copyright (c) 2006, Adam Chlipala
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
dac62e84 17 *)
234b917a
AC
18
19(* Main interface *)
20
21structure Main :> MAIN = struct
22
36e42cb8 23open Ast MsgTypes Print
234b917a 24
6ae327f8
AC
25structure SM = StringMap
26
aa56e112 27fun init () = Acl.read Config.aclFile
234b917a 28
d189ec0e 29fun check' G fname =
a3698041
AC
30 let
31 val prog = Parse.parse fname
32 in
33 if !ErrorMsg.anyErrors then
d189ec0e 34 G
a3698041 35 else
aa56e112 36 Tycheck.checkFile G (Defaults.tInit ()) prog
a3698041
AC
37 end
38
d189ec0e 39fun basis () =
234b917a 40 let
d189ec0e
AC
41 val dir = Posix.FileSys.opendir Config.libRoot
42
43 fun loop files =
44 case Posix.FileSys.readdir dir of
d612d62c
AC
45 NONE => (Posix.FileSys.closedir dir;
46 files)
d189ec0e
AC
47 | SOME fname =>
48 if String.isSuffix ".dtl" fname then
d612d62c
AC
49 loop (OS.Path.joinDirFile {dir = Config.libRoot,
50 file = fname}
d189ec0e
AC
51 :: files)
52 else
53 loop files
54
55 val files = loop []
c53e82e4 56 val (_, files) = Order.order NONE files
d189ec0e 57 in
6ae327f8
AC
58 if !ErrorMsg.anyErrors then
59 Env.empty
60 else
b3159a70
AC
61 (Tycheck.allowExterns ();
62 foldl (fn (fname, G) => check' G fname) Env.empty files
63 before Tycheck.disallowExterns ())
d189ec0e
AC
64 end
65
66fun check fname =
67 let
68 val _ = ErrorMsg.reset ()
12adf55a 69 val _ = Env.preTycheck ()
d189ec0e
AC
70
71 val b = basis ()
234b917a
AC
72 in
73 if !ErrorMsg.anyErrors then
36e42cb8 74 raise ErrorMsg.Error
234b917a
AC
75 else
76 let
b3159a70 77 val _ = Tycheck.disallowExterns ()
7f012ffd 78 val _ = ErrorMsg.reset ()
d189ec0e 79 val prog = Parse.parse fname
234b917a 80 in
492c1cff 81 if !ErrorMsg.anyErrors then
36e42cb8 82 raise ErrorMsg.Error
492c1cff 83 else
d189ec0e 84 let
aa56e112 85 val G' = Tycheck.checkFile b (Defaults.tInit ()) prog
d189ec0e 86 in
36e42cb8
AC
87 if !ErrorMsg.anyErrors then
88 raise ErrorMsg.Error
89 else
90 (G', #3 prog)
d189ec0e 91 end
234b917a
AC
92 end
93 end
94
c53e82e4
AC
95val notTmp = CharVector.all (fn ch => Char.isAlphaNum ch orelse ch = #"." orelse ch = #"_" orelse ch = #"-")
96
97fun checkDir dname =
98 let
99 val b = basis ()
100
101 val dir = Posix.FileSys.opendir dname
102
103 fun loop files =
104 case Posix.FileSys.readdir dir of
105 NONE => (Posix.FileSys.closedir dir;
106 files)
107 | SOME fname =>
108 if notTmp fname then
109 loop (OS.Path.joinDirFile {dir = dname,
110 file = fname}
111 :: files)
112 else
113 loop files
114
115 val files = loop []
116 val (_, files) = Order.order (SOME b) files
117 in
118 if !ErrorMsg.anyErrors then
1824f573 119 raise ErrorMsg.Error
c53e82e4
AC
120 else
121 (foldl (fn (fname, G) => check' G fname) b files;
1824f573
AC
122 if !ErrorMsg.anyErrors then
123 raise ErrorMsg.Error
124 else
125 ())
c53e82e4
AC
126 end
127
d189ec0e 128fun reduce fname =
a3698041 129 let
d189ec0e 130 val (G, body) = check fname
a3698041
AC
131 in
132 if !ErrorMsg.anyErrors then
d189ec0e 133 NONE
a3698041 134 else
d189ec0e
AC
135 case body of
136 SOME body =>
137 let
138 val body' = Reduce.reduceExp G body
139 in
140 (*printd (PD.hovBox (PD.PPS.Rel 0,
141 [PD.string "Result:",
142 PD.space 1,
143 p_exp body']))*)
144 SOME body'
145 end
146 | _ => NONE
a3698041
AC
147 end
148
d189ec0e
AC
149fun eval fname =
150 case reduce fname of
151 (SOME body') =>
152 if !ErrorMsg.anyErrors then
36e42cb8 153 raise ErrorMsg.Error
d189ec0e 154 else
aa56e112 155 Eval.exec (Defaults.eInit ()) body'
36e42cb8 156 | NONE => raise ErrorMsg.Error
d189ec0e 157
1824f573
AC
158fun eval' fname =
159 case reduce fname of
160 (SOME body') =>
161 if !ErrorMsg.anyErrors then
162 raise ErrorMsg.Error
163 else
164 ignore (Eval.exec' (Defaults.eInit ()) body')
165 | NONE => raise ErrorMsg.Error
166
3b267643
AC
167val dispatcher =
168 Config.dispatcher ^ ":" ^ Int.toString Config.dispatcherPort
559e89e9 169
c9731b9b
AC
170val self =
171 "localhost:" ^ Int.toString Config.slavePort
172
5ee41dd0 173fun requestContext f =
07cc384c 174 let
a56cc2c3
AC
175 val uid = Posix.ProcEnv.getuid ()
176 val user = Posix.SysDB.Passwd.name (Posix.SysDB.getpwuid uid)
5ee41dd0 177
a56cc2c3
AC
178 val () = Acl.read Config.aclFile
179 val () = Domain.setUser user
5ee41dd0
AC
180
181 val () = f ()
aa56e112 182
aa56e112 183 val context = OpenSSL.context (Config.certDir ^ "/" ^ user ^ ".pem",
a088cea6 184 Config.keyDir ^ "/" ^ user ^ "/key.pem",
3b267643 185 Config.trustStore)
5ee41dd0
AC
186 in
187 (user, context)
188 end
07cc384c 189
5ee41dd0
AC
190fun requestBio f =
191 let
192 val (user, context) = requestContext f
193 in
194 (user, OpenSSL.connect (context, dispatcher))
195 end
196
c9731b9b
AC
197fun requestSlaveBio () =
198 let
199 val (user, context) = requestContext (fn () => ())
200 in
201 (user, OpenSSL.connect (context, self))
202 end
203
5ee41dd0
AC
204fun request fname =
205 let
206 val (user, bio) = requestBio (fn () => ignore (check fname))
559e89e9 207
3b267643
AC
208 val inf = TextIO.openIn fname
209
36e42cb8 210 fun loop lines =
3b267643 211 case TextIO.inputLine inf of
36e42cb8
AC
212 NONE => String.concat (List.rev lines)
213 | SOME line => loop (line :: lines)
214
215 val code = loop []
559e89e9 216 in
3b267643 217 TextIO.closeIn inf;
36e42cb8
AC
218 Msg.send (bio, MsgConfig code);
219 case Msg.recv bio of
220 NONE => print "Server closed connection unexpectedly.\n"
221 | SOME m =>
222 case m of
223 MsgOk => print "Configuration succeeded.\n"
224 | MsgError s => print ("Configuration failed: " ^ s ^ "\n")
225 | _ => print "Unexpected server reply.\n";
3b267643 226 OpenSSL.close bio
559e89e9 227 end
aa56e112 228 handle ErrorMsg.Error => ()
559e89e9 229
c53e82e4
AC
230fun requestDir dname =
231 let
1824f573
AC
232 val _ = ErrorMsg.reset ()
233
234 val (user, bio) = requestBio (fn () => checkDir dname)
c53e82e4
AC
235
236 val b = basis ()
237
238 val dir = Posix.FileSys.opendir dname
239
240 fun loop files =
241 case Posix.FileSys.readdir dir of
242 NONE => (Posix.FileSys.closedir dir;
243 files)
244 | SOME fname =>
245 if notTmp fname then
246 loop (OS.Path.joinDirFile {dir = dname,
247 file = fname}
248 :: files)
249 else
250 loop files
251
252 val files = loop []
253 val (_, files) = Order.order (SOME b) files
254
255 val _ = if !ErrorMsg.anyErrors then
256 raise ErrorMsg.Error
257 else
258 ()
259
260 val codes = map (fn fname =>
261 let
262 val inf = TextIO.openIn fname
263
264 fun loop lines =
265 case TextIO.inputLine inf of
266 NONE => String.concat (rev lines)
267 | SOME line => loop (line :: lines)
268 in
269 loop []
270 before TextIO.closeIn inf
271 end) files
272 in
1824f573
AC
273 if !ErrorMsg.anyErrors then
274 ()
275 else
276 (Msg.send (bio, MsgMultiConfig codes);
277 case Msg.recv bio of
278 NONE => print "Server closed connection unexpectedly.\n"
279 | SOME m =>
280 case m of
281 MsgOk => print "Configuration succeeded.\n"
282 | MsgError s => print ("Configuration failed: " ^ s ^ "\n")
283 | _ => print "Unexpected server reply.\n";
284 OpenSSL.close bio)
c53e82e4
AC
285 end
286 handle ErrorMsg.Error => ()
287
62260c5f
AC
288fun requestPing () =
289 let
290 val (_, bio) = requestBio (fn () => ())
291 in
292 OpenSSL.close bio;
293 OS.Process.success
294 end
295 handle _ => OS.Process.failure
296
9f27d58f
AC
297fun requestShutdown () =
298 let
299 val (_, bio) = requestBio (fn () => ())
300 in
301 Msg.send (bio, MsgShutdown);
302 case Msg.recv bio of
303 NONE => print "Server closed connection unexpectedly.\n"
304 | SOME m =>
305 case m of
306 MsgOk => print "Shutdown begun.\n"
307 | MsgError s => print ("Shutdown failed: " ^ s ^ "\n")
308 | _ => print "Unexpected server reply.\n";
309 OpenSSL.close bio
310 end
311
c9731b9b
AC
312fun requestSlavePing () =
313 let
314 val (_, bio) = requestSlaveBio ()
315 in
316 OpenSSL.close bio;
317 OS.Process.success
318 end
319 handle _ => OS.Process.failure
320
321fun requestSlaveShutdown () =
322 let
323 val (_, bio) = requestSlaveBio ()
324 in
325 Msg.send (bio, MsgShutdown);
326 case Msg.recv bio of
327 NONE => print "Server closed connection unexpectedly.\n"
328 | SOME m =>
329 case m of
330 MsgOk => print "Shutdown begun.\n"
331 | MsgError s => print ("Shutdown failed: " ^ s ^ "\n")
332 | _ => print "Unexpected server reply.\n";
333 OpenSSL.close bio
334 end
335
5ee41dd0
AC
336fun requestGrant acl =
337 let
338 val (user, bio) = requestBio (fn () => ())
339 in
340 Msg.send (bio, MsgGrant acl);
341 case Msg.recv bio of
342 NONE => print "Server closed connection unexpectedly.\n"
343 | SOME m =>
344 case m of
345 MsgOk => print "Grant succeeded.\n"
346 | MsgError s => print ("Grant failed: " ^ s ^ "\n")
347 | _ => print "Unexpected server reply.\n";
348 OpenSSL.close bio
349 end
350
411a85f2
AC
351fun requestRevoke acl =
352 let
353 val (user, bio) = requestBio (fn () => ())
354 in
355 Msg.send (bio, MsgRevoke acl);
356 case Msg.recv bio of
357 NONE => print "Server closed connection unexpectedly.\n"
358 | SOME m =>
359 case m of
360 MsgOk => print "Revoke succeeded.\n"
361 | MsgError s => print ("Revoke failed: " ^ s ^ "\n")
362 | _ => print "Unexpected server reply.\n";
363 OpenSSL.close bio
364 end
365
08a04eb4
AC
366fun requestListPerms user =
367 let
368 val (_, bio) = requestBio (fn () => ())
369 in
370 Msg.send (bio, MsgListPerms user);
371 (case Msg.recv bio of
372 NONE => (print "Server closed connection unexpectedly.\n";
373 NONE)
374 | SOME m =>
375 case m of
376 MsgPerms perms => SOME perms
377 | MsgError s => (print ("Listing failed: " ^ s ^ "\n");
378 NONE)
379 | _ => (print "Unexpected server reply.\n";
380 NONE))
381 before OpenSSL.close bio
382 end
383
094877b1
AC
384fun requestWhoHas perm =
385 let
386 val (_, bio) = requestBio (fn () => ())
387 in
388 Msg.send (bio, MsgWhoHas perm);
389 (case Msg.recv bio of
390 NONE => (print "Server closed connection unexpectedly.\n";
391 NONE)
392 | SOME m =>
393 case m of
394 MsgWhoHasResponse users => SOME users
395 | MsgError s => (print ("whohas failed: " ^ s ^ "\n");
396 NONE)
397 | _ => (print "Unexpected server reply.\n";
398 NONE))
399 before OpenSSL.close bio
400 end
401
1824f573
AC
402fun requestRegen () =
403 let
404 val (_, bio) = requestBio (fn () => ())
405 in
406 Msg.send (bio, MsgRegenerate);
407 case Msg.recv bio of
408 NONE => print "Server closed connection unexpectedly.\n"
409 | SOME m =>
410 case m of
411 MsgOk => print "Regeneration succeeded.\n"
412 | MsgError s => print ("Regeneration failed: " ^ s ^ "\n")
413 | _ => print "Unexpected server reply.\n";
414 OpenSSL.close bio
415 end
416
c189cbe9
AC
417fun requestRmdom dom =
418 let
419 val (_, bio) = requestBio (fn () => ())
420 in
421 Msg.send (bio, MsgRmdom dom);
422 case Msg.recv bio of
423 NONE => print "Server closed connection unexpectedly.\n"
424 | SOME m =>
425 case m of
426 MsgOk => print "Removal succeeded.\n"
427 | MsgError s => print ("Removal failed: " ^ s ^ "\n")
428 | _ => print "Unexpected server reply.\n";
429 OpenSSL.close bio
430 end
431
e69e60cc
AC
432fun requestRmuser user =
433 let
434 val (_, bio) = requestBio (fn () => ())
435 in
436 Msg.send (bio, MsgRmuser user);
437 case Msg.recv bio of
438 NONE => print "Server closed connection unexpectedly.\n"
439 | SOME m =>
440 case m of
441 MsgOk => print "Removal succeeded.\n"
442 | MsgError s => print ("Removal failed: " ^ s ^ "\n")
443 | _ => print "Unexpected server reply.\n";
444 OpenSSL.close bio
445 end
446
d541c618
AC
447fun requestDbUser dbtype =
448 let
449 val (_, bio) = requestBio (fn () => ())
450 in
451 Msg.send (bio, MsgCreateDbUser dbtype);
452 case Msg.recv bio of
453 NONE => print "Server closed connection unexpectedly.\n"
454 | SOME m =>
455 case m of
456 MsgOk => print "Your user has been created.\n"
457 | MsgError s => print ("Creation failed: " ^ s ^ "\n")
458 | _ => print "Unexpected server reply.\n";
459 OpenSSL.close bio
460 end
461
86aa5de7
AC
462fun requestDbPasswd rc =
463 let
464 val (_, bio) = requestBio (fn () => ())
465 in
466 Msg.send (bio, MsgDbPasswd rc);
467 case Msg.recv bio of
468 NONE => print "Server closed connection unexpectedly.\n"
469 | SOME m =>
470 case m of
471 MsgOk => print "Your password has been changed.\n"
472 | MsgError s => print ("Password set failed: " ^ s ^ "\n")
473 | _ => print "Unexpected server reply.\n";
474 OpenSSL.close bio
475 end
476
90dd48df
AC
477fun requestDbTable p =
478 let
479 val (user, bio) = requestBio (fn () => ())
480 in
481 Msg.send (bio, MsgCreateDbTable p);
482 case Msg.recv bio of
483 NONE => print "Server closed connection unexpectedly.\n"
484 | SOME m =>
485 case m of
486 MsgOk => print ("Your database " ^ user ^ "_" ^ #dbname p ^ " has been created.\n")
487 | MsgError s => print ("Creation failed: " ^ s ^ "\n")
488 | _ => print "Unexpected server reply.\n";
489 OpenSSL.close bio
490 end
491
1d3ef80e
AC
492fun requestListMailboxes domain =
493 let
494 val (_, bio) = requestBio (fn () => ())
495 in
496 Msg.send (bio, MsgListMailboxes domain);
497 (case Msg.recv bio of
2e96b9d4 498 NONE => Vmail.Error "Server closed connection unexpectedly."
1d3ef80e
AC
499 | SOME m =>
500 case m of
501 MsgMailboxes users => (Msg.send (bio, MsgOk);
502 Vmail.Listing users)
503 | MsgError s => Vmail.Error ("Creation failed: " ^ s)
2e96b9d4 504 | _ => Vmail.Error "Unexpected server reply.")
1d3ef80e
AC
505 before OpenSSL.close bio
506 end
507
08688401
AC
508fun requestNewMailbox p =
509 let
510 val (_, bio) = requestBio (fn () => ())
511 in
512 Msg.send (bio, MsgNewMailbox p);
513 case Msg.recv bio of
514 NONE => print "Server closed connection unexpectedly.\n"
515 | SOME m =>
516 case m of
517 MsgOk => print ("A mapping for " ^ #user p ^ "@" ^ #domain p ^ " has been created.\n")
518 | MsgError s => print ("Creation failed: " ^ s ^ "\n")
519 | _ => print "Unexpected server reply.\n";
520 OpenSSL.close bio
521 end
522
523fun requestPasswdMailbox p =
524 let
525 val (_, bio) = requestBio (fn () => ())
526 in
527 Msg.send (bio, MsgPasswdMailbox p);
528 case Msg.recv bio of
529 NONE => print "Server closed connection unexpectedly.\n"
530 | SOME m =>
531 case m of
532 MsgOk => print ("The password for " ^ #user p ^ "@" ^ #domain p ^ " has been changed.\n")
533 | MsgError s => print ("Set failed: " ^ s ^ "\n")
534 | _ => print "Unexpected server reply.\n";
535 OpenSSL.close bio
536 end
537
538fun requestRmMailbox p =
539 let
540 val (_, bio) = requestBio (fn () => ())
541 in
542 Msg.send (bio, MsgRmMailbox p);
543 case Msg.recv bio of
544 NONE => print "Server closed connection unexpectedly.\n"
545 | SOME m =>
546 case m of
547 MsgOk => print ("The mapping for mailbox " ^ #user p ^ "@" ^ #domain p ^ " has been deleted.\n")
548 | MsgError s => print ("Remove failed: " ^ s ^ "\n")
549 | _ => print "Unexpected server reply.\n";
550 OpenSSL.close bio
551 end
552
2e96b9d4
AC
553fun requestSaQuery addr =
554 let
555 val (_, bio) = requestBio (fn () => ())
556 in
557 Msg.send (bio, MsgSaQuery addr);
558 (case Msg.recv bio of
559 NONE => print "Server closed connection unexpectedly.\n"
560 | SOME m =>
561 case m of
562 MsgSaStatus b => (print ("SpamAssassin filtering for " ^ addr ^ " is "
563 ^ (if b then "ON" else "OFF") ^ ".\n");
564 Msg.send (bio, MsgOk))
565 | MsgError s => print ("Query failed: " ^ s ^ "\n")
566 | _ => print "Unexpected server reply.\n")
567 before OpenSSL.close bio
568 end
569
570fun requestSaSet p =
571 let
572 val (_, bio) = requestBio (fn () => ())
573 in
574 Msg.send (bio, MsgSaSet p);
575 case Msg.recv bio of
576 NONE => print "Server closed connection unexpectedly.\n"
577 | SOME m =>
578 case m of
579 MsgOk => print ("SpamAssassin filtering for " ^ #1 p ^ " is now "
580 ^ (if #2 p then "ON" else "OFF") ^ ".\n")
581 | MsgError s => print ("Set failed: " ^ s ^ "\n")
582 | _ => print "Unexpected server reply.\n";
583 OpenSSL.close bio
584 end
585
2bc5ed22
AC
586fun requestSmtpLog domain =
587 let
588 val (_, bio) = requestBio (fn () => ())
589
590 val _ = Msg.send (bio, MsgSmtpLogReq domain)
591
592 fun loop () =
593 case Msg.recv bio of
594 NONE => print "Server closed connection unexpectedly.\n"
595 | SOME m =>
596 case m of
597 MsgOk => ()
598 | MsgSmtpLogRes line => (print line;
599 loop ())
600 | MsgError s => print ("Log search failed: " ^ s ^ "\n")
601 | _ => print "Unexpected server reply.\n"
602 in
603 loop ();
604 OpenSSL.close bio
605 end
606
75585a67
AC
607fun requestApt {node, pkg} =
608 let
a95a0107
AC
609 val (user, context) = requestContext (fn () => ())
610 val bio = OpenSSL.connect (context, if node = Config.masterNode then
611 dispatcher
612 else
613 Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort)
75585a67 614
a95a0107 615 val _ = Msg.send (bio, MsgQuery (QApt pkg))
75585a67
AC
616
617 fun loop () =
618 case Msg.recv bio of
619 NONE => (print "Server closed connection unexpectedly.\n";
620 OS.Process.failure)
621 | SOME m =>
622 case m of
623 MsgYes => (print "Package is installed.\n";
624 OS.Process.success)
625 | MsgNo => (print "Package is not installed.\n";
626 OS.Process.failure)
627 | MsgError s => (print ("APT query failed: " ^ s ^ "\n");
628 OS.Process.failure)
629 | _ => (print "Unexpected server reply.\n";
630 OS.Process.failure)
631 in
632 loop ()
633 before OpenSSL.close bio
634 end
635
d351d679
AC
636fun requestCron {node, uname} =
637 let
638 val (user, context) = requestContext (fn () => ())
639 val bio = OpenSSL.connect (context, if node = Config.masterNode then
640 dispatcher
641 else
642 Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort)
643
644 val _ = Msg.send (bio, MsgQuery (QCron uname))
645
646 fun loop () =
647 case Msg.recv bio of
648 NONE => (print "Server closed connection unexpectedly.\n";
649 OS.Process.failure)
650 | SOME m =>
651 case m of
652 MsgYes => (print "User has cron permissions.\n";
653 OS.Process.success)
654 | MsgNo => (print "User does not have cron permissions.\n";
655 OS.Process.failure)
656 | MsgError s => (print ("Cron query failed: " ^ s ^ "\n");
657 OS.Process.failure)
658 | _ => (print "Unexpected server reply.\n";
659 OS.Process.failure)
660 in
661 loop ()
662 before OpenSSL.close bio
663 end
664
665fun requestFtp {node, uname} =
666 let
667 val (user, context) = requestContext (fn () => ())
668 val bio = OpenSSL.connect (context, if node = Config.masterNode then
669 dispatcher
670 else
671 Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort)
672
673 val _ = Msg.send (bio, MsgQuery (QFtp uname))
674
675 fun loop () =
676 case Msg.recv bio of
677 NONE => (print "Server closed connection unexpectedly.\n";
678 OS.Process.failure)
679 | SOME m =>
680 case m of
681 MsgYes => (print "User has FTP permissions.\n";
682 OS.Process.success)
683 | MsgNo => (print "User does not have FTP permissions.\n";
684 OS.Process.failure)
685 | MsgError s => (print ("FTP query failed: " ^ s ^ "\n");
686 OS.Process.failure)
687 | _ => (print "Unexpected server reply.\n";
688 OS.Process.failure)
689 in
690 loop ()
691 before OpenSSL.close bio
692 end
693
4d5126e1
AC
694fun requestTrustedPath {node, uname} =
695 let
696 val (user, context) = requestContext (fn () => ())
697 val bio = OpenSSL.connect (context, if node = Config.masterNode then
698 dispatcher
699 else
700 Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort)
701
702 val _ = Msg.send (bio, MsgQuery (QTrustedPath uname))
703
704 fun loop () =
705 case Msg.recv bio of
706 NONE => (print "Server closed connection unexpectedly.\n";
707 OS.Process.failure)
708 | SOME m =>
709 case m of
710 MsgYes => (print "User has trusted path restriction.\n";
711 OS.Process.success)
712 | MsgNo => (print "User does not have trusted path restriction.\n";
713 OS.Process.failure)
714 | MsgError s => (print ("Trusted path query failed: " ^ s ^ "\n");
715 OS.Process.failure)
716 | _ => (print "Unexpected server reply.\n";
717 OS.Process.failure)
718 in
719 loop ()
720 before OpenSSL.close bio
721 end
722
737c68d4
AC
723fun requestSocketPerm {node, uname} =
724 let
725 val (user, context) = requestContext (fn () => ())
726 val bio = OpenSSL.connect (context, if node = Config.masterNode then
727 dispatcher
728 else
729 Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort)
730
731 val _ = Msg.send (bio, MsgQuery (QSocket uname))
732
733 fun loop () =
734 case Msg.recv bio of
735 NONE => (print "Server closed connection unexpectedly.\n";
736 OS.Process.failure)
737 | SOME m =>
738 case m of
739 MsgSocket p => (case p of
740 Any => print "Any\n"
741 | Client => print "Client\n"
742 | Server => print "Server\n"
743 | Nada => print "Nada\n";
744 OS.Process.success)
745 | MsgError s => (print ("Socket permission query failed: " ^ s ^ "\n");
746 OS.Process.failure)
747 | _ => (print "Unexpected server reply.\n";
748 OS.Process.failure)
749 in
750 loop ()
751 before OpenSSL.close bio
752 end
753
f9548f16
AC
754fun requestFirewall {node, uname} =
755 let
756 val (user, context) = requestContext (fn () => ())
757 val bio = OpenSSL.connect (context, if node = Config.masterNode then
758 dispatcher
759 else
760 Domain.nodeIp node ^ ":" ^ Int.toString Config.slavePort)
761
762 val _ = Msg.send (bio, MsgQuery (QFirewall uname))
763
764 fun loop () =
765 case Msg.recv bio of
766 NONE => (print "Server closed connection unexpectedly.\n";
767 OS.Process.failure)
768 | SOME m =>
769 case m of
770 MsgFirewall ls => (app (fn s => (print s; print "\n")) ls;
771 OS.Process.success)
772 | MsgError s => (print ("Firewall query failed: " ^ s ^ "\n");
773 OS.Process.failure)
774 | _ => (print "Unexpected server reply.\n";
775 OS.Process.failure)
776 in
777 loop ()
778 before OpenSSL.close bio
779 end
780
71420f8b 781fun regenerate context =
1824f573
AC
782 let
783 val b = basis ()
71420f8b
AC
784 val () = Tycheck.disallowExterns ()
785
786 val () = Domain.resetGlobal ()
787
788 fun contactNode (node, ip) =
789 if node = Config.defaultNode then
790 Domain.resetLocal ()
791 else let
792 val bio = OpenSSL.connect (context,
793 ip
794 ^ ":"
795 ^ Int.toString Config.slavePort)
796 in
797 Msg.send (bio, MsgRegenerate);
798 case Msg.recv bio of
799 NONE => print "Slave closed connection unexpectedly\n"
800 | SOME m =>
801 case m of
802 MsgOk => print ("Slave " ^ node ^ " pre-regeneration finished\n")
803 | MsgError s => print ("Slave " ^ node
804 ^ " returned error: " ^
805 s ^ "\n")
806 | _ => print ("Slave " ^ node
807 ^ " returned unexpected command\n");
808 OpenSSL.close bio
809 end
1824f573
AC
810
811 fun doUser user =
812 let
813 val _ = Domain.setUser user
814 val _ = ErrorMsg.reset ()
815
816 val dname = Config.domtoolDir user
817
818 val dir = Posix.FileSys.opendir dname
819
820 fun loop files =
821 case Posix.FileSys.readdir dir of
822 NONE => (Posix.FileSys.closedir dir;
823 files)
824 | SOME fname =>
825 if notTmp fname then
826 loop (OS.Path.joinDirFile {dir = dname,
827 file = fname}
828 :: files)
829 else
830 loop files
831
832 val files = loop []
833 val (_, files) = Order.order (SOME b) files
834 in
835 if !ErrorMsg.anyErrors then
836 print ("User " ^ user ^ "'s configuration has errors!\n")
837 else
838 app eval' files
839 end
840 handle IO.Io _ => ()
841 | OS.SysErr (s, _) => print ("System error processing user " ^ user ^ ": " ^ s ^ "\n")
434a7b1f 842 | ErrorMsg.Error => print ("User " ^ user ^ " had a compilation error.\n")
1824f573 843 in
71420f8b 844 app contactNode Config.nodeIps;
1824f573
AC
845 Env.pre ();
846 app doUser (Acl.users ());
847 Env.post ()
848 end
849
e69e60cc
AC
850fun rmuser user =
851 let
852 val doms = Acl.class {user = user, class = "domain"}
853 val doms = List.filter (fn dom =>
854 case Acl.whoHas {class = "domain", value = dom} of
855 [_] => true
856 | _ => false) (StringSet.listItems doms)
857 in
858 Acl.rmuser user;
859 Domain.rmdom doms
860 end
861
c9731b9b
AC
862fun now () = Date.toString (Date.fromTimeUniv (Time.now ()))
863
a95a0107
AC
864fun answerQuery q =
865 case q of
866 QApt pkg => if Apt.installed pkg then MsgYes else MsgNo
d351d679
AC
867 | QCron user => if Cron.allowed user then MsgYes else MsgNo
868 | QFtp user => if Ftp.allowed user then MsgYes else MsgNo
4d5126e1 869 | QTrustedPath user => if TrustedPath.query user then MsgYes else MsgNo
737c68d4 870 | QSocket user => MsgSocket (SocketPerm.query user)
f9548f16 871 | QFirewall user => MsgFirewall (Firewall.query user)
a95a0107
AC
872
873fun describeQuery q =
874 case q of
875 QApt pkg => "Requested installation status of package " ^ pkg
d351d679
AC
876 | QCron user => "Asked about cron permissions for user " ^ user
877 | QFtp user => "Asked about FTP permissions for user " ^ user
4d5126e1 878 | QTrustedPath user => "Asked about trusted path settings for user " ^ user
737c68d4 879 | QSocket user => "Asked about socket permissions for user " ^ user
f9548f16 880 | QFirewall user => "Asked about firewall rules for user " ^ user
a95a0107 881
3b267643 882fun service () =
07cc384c 883 let
aa56e112
AC
884 val () = Acl.read Config.aclFile
885
3b267643
AC
886 val context = OpenSSL.context (Config.serverCert,
887 Config.serverKey,
888 Config.trustStore)
36e42cb8 889 val _ = Domain.set_context context
3b267643 890
60534712 891 val sock = OpenSSL.listen (context, Config.dispatcherPort)
3b267643
AC
892
893 fun loop () =
60534712 894 case OpenSSL.accept sock of
3b267643
AC
895 NONE => ()
896 | SOME bio =>
897 let
aa56e112 898 val user = OpenSSL.peerCN bio
c9731b9b 899 val () = print ("\nConnection from " ^ user ^ " at " ^ now () ^ "\n")
aa56e112
AC
900 val () = Domain.setUser user
901
08688401
AC
902 fun doIt f cleanup =
903 ((case f () of
904 (msgLocal, SOME msgRemote) =>
905 (print msgLocal;
906 print "\n";
907 Msg.send (bio, MsgError msgRemote))
908 | (msgLocal, NONE) =>
909 (print msgLocal;
910 print "\n";
911 Msg.send (bio, MsgOk)))
912 handle OpenSSL.OpenSSL _ =>
913 print "OpenSSL error\n"
914 | OS.SysErr (s, _) =>
915 (print "System error: ";
916 print s;
917 print "\n";
918 Msg.send (bio, MsgError ("System error: " ^ s))
919 handle OpenSSL.OpenSSL _ => ())
920 | Fail s =>
921 (print "Failure: ";
922 print s;
923 print "\n";
924 Msg.send (bio, MsgError ("Failure: " ^ s))
925 handle OpenSSL.OpenSSL _ => ())
926 | ErrorMsg.Error =>
927 (print "Compilation error\n";
928 Msg.send (bio, MsgError "Error during configuration evaluation")
929 handle OpenSSL.OpenSSL _ => ());
930 (cleanup ();
931 ignore (OpenSSL.readChar bio);
932 OpenSSL.close bio)
933 handle OpenSSL.OpenSSL _ => ();
934 loop ())
935
c53e82e4
AC
936 fun doConfig codes =
937 let
938 val _ = print "Configuration:\n"
939 val _ = app (fn s => (print s; print "\n")) codes
940 val _ = print "\n"
941
942 val outname = OS.FileSys.tmpName ()
943
944 fun doOne code =
945 let
946 val outf = TextIO.openOut outname
947 in
948 TextIO.output (outf, code);
949 TextIO.closeOut outf;
1824f573 950 eval' outname
c53e82e4
AC
951 end
952 in
08688401
AC
953 doIt (fn () => (Env.pre ();
954 app doOne codes;
955 Env.post ();
956 Msg.send (bio, MsgOk);
957 ("Configuration complete.", NONE)))
958 (fn () => OS.FileSys.remove outname)
c53e82e4
AC
959 end
960
2e96b9d4
AC
961 fun checkAddr s =
962 case String.fields (fn ch => ch = #"@") s of
963 [user'] =>
964 if user = user' then
965 SOME (SetSA.User s)
966 else
967 NONE
968 | [user', domain] =>
969 if Domain.validEmailUser user' andalso Domain.yourDomain domain then
970 SOME (SetSA.Email s)
971 else
972 NONE
973 | _ => NONE
974
36e42cb8
AC
975 fun cmdLoop () =
976 case Msg.recv bio of
977 NONE => (OpenSSL.close bio
978 handle OpenSSL.OpenSSL _ => ();
979 loop ())
980 | SOME m =>
981 case m of
c53e82e4
AC
982 MsgConfig code => doConfig [code]
983 | MsgMultiConfig codes => doConfig codes
5ee41dd0 984
9f27d58f 985 | MsgShutdown =>
62260c5f
AC
986 if Acl.query {user = user, class = "priv", value = "all"}
987 orelse Acl.query {user = user, class = "priv", value = "shutdown"} then
c9731b9b 988 print ("Domtool dispatcher shutting down at " ^ now () ^ "\n\n")
9f27d58f 989 else
62260c5f
AC
990 (print "Unauthorized shutdown command!\n";
991 OpenSSL.close bio
9f27d58f
AC
992 handle OpenSSL.OpenSSL _ => ();
993 loop ())
994
5ee41dd0 995 | MsgGrant acl =>
08688401
AC
996 doIt (fn () =>
997 if Acl.query {user = user, class = "priv", value = "all"} then
998 (Acl.grant acl;
999 Acl.write Config.aclFile;
1000 ("Granted permission " ^ #value acl ^ " to " ^ #user acl ^ " in " ^ #class acl ^ ".",
1001 NONE))
1002 else
1003 ("Unauthorized user asked to grant a permission!",
1004 SOME "Not authorized to grant privileges"))
1005 (fn () => ())
1006
411a85f2 1007 | MsgRevoke acl =>
08688401
AC
1008 doIt (fn () =>
1009 if Acl.query {user = user, class = "priv", value = "all"} then
1010 (Acl.revoke acl;
1011 Acl.write Config.aclFile;
1012 ("Revoked permission " ^ #value acl ^ " from " ^ #user acl ^ " in " ^ #class acl ^ ".",
1013 NONE))
1014 else
1015 ("Unauthorized user asked to revoke a permission!",
1016 SOME "Not authorized to revoke privileges"))
1017 (fn () => ())
5ee41dd0 1018
08a04eb4 1019 | MsgListPerms user =>
08688401
AC
1020 doIt (fn () =>
1021 (Msg.send (bio, MsgPerms (Acl.queryAll user));
1022 ("Sent permission list for user " ^ user ^ ".",
1023 NONE)))
1024 (fn () => ())
08a04eb4 1025
094877b1 1026 | MsgWhoHas perm =>
08688401
AC
1027 doIt (fn () =>
1028 (Msg.send (bio, MsgWhoHasResponse (Acl.whoHas perm));
1029 ("Sent whohas response for " ^ #class perm ^ " / " ^ #value perm ^ ".",
1030 NONE)))
1031 (fn () => ())
094877b1 1032
e69e60cc 1033 | MsgRmdom doms =>
08688401
AC
1034 doIt (fn () =>
1035 if Acl.query {user = user, class = "priv", value = "all"}
1036 orelse List.all (fn dom => Acl.query {user = user, class = "domain", value = dom}) doms then
1037 (Domain.rmdom doms;
1038 app (fn dom =>
1039 Acl.revokeFromAll {class = "domain", value = dom}) doms;
1040 Acl.write Config.aclFile;
1041 ("Removed domains" ^ foldl (fn (d, s) => s ^ " " ^ d) "" doms ^ ".",
1042 NONE))
1043 else
1044 ("Unauthorized user asked to remove a domain!",
1045 SOME "Not authorized to remove that domain"))
1046 (fn () => ())
1824f573
AC
1047
1048 | MsgRegenerate =>
08688401
AC
1049 doIt (fn () =>
1050 if Acl.query {user = user, class = "priv", value = "regen"}
1051 orelse Acl.query {user = user, class = "priv", value = "all"} then
1052 (regenerate context;
1053 ("Regenerated all configuration.",
1054 NONE))
1055 else
1056 ("Unauthorized user asked to regenerate!",
1057 SOME "Not authorized to regenerate"))
1058 (fn () => ())
e69e60cc 1059
05323cbc 1060 | MsgRmuser user' =>
08688401
AC
1061 doIt (fn () =>
1062 if Acl.query {user = user, class = "priv", value = "all"} then
1063 (rmuser user';
1064 Acl.write Config.aclFile;
1065 ("Removed user " ^ user' ^ ".",
1066 NONE))
1067 else
1068 ("Unauthorized user asked to remove a user!",
1069 SOME "Not authorized to remove users"))
1070 (fn () => ())
d541c618 1071
21d921a5 1072 | MsgCreateDbUser {dbtype, passwd} =>
08688401
AC
1073 doIt (fn () =>
1074 case Dbms.lookup dbtype of
1075 NONE => ("Database user creation request with unknown datatype type " ^ dbtype,
1076 SOME ("Unknown database type " ^ dbtype))
1077 | SOME handler =>
1078 case #adduser handler {user = user, passwd = passwd} of
1079 NONE => ("Added " ^ dbtype ^ " user " ^ user ^ ".",
1080 NONE)
1081 | SOME msg =>
1082 ("Error adding a " ^ dbtype ^ " user " ^ user ^ ": " ^ msg,
1083 SOME ("Error adding user: " ^ msg)))
1084 (fn () => ())
c189cbe9 1085
86aa5de7
AC
1086 | MsgDbPasswd {dbtype, passwd} =>
1087 doIt (fn () =>
1088 case Dbms.lookup dbtype of
1089 NONE => ("Database passwd request with unknown datatype type " ^ dbtype,
1090 SOME ("Unknown database type " ^ dbtype))
1091 | SOME handler =>
1092 case #passwd handler {user = user, passwd = passwd} of
1093 NONE => ("Changed " ^ dbtype ^ " password of user " ^ user ^ ".",
1094 NONE)
1095 | SOME msg =>
1096 ("Error setting " ^ dbtype ^ " password of user " ^ user ^ ": " ^ msg,
1097 SOME ("Error adding user: " ^ msg)))
1098 (fn () => ())
1099
90dd48df 1100 | MsgCreateDbTable {dbtype, dbname} =>
08688401
AC
1101 doIt (fn () =>
1102 if Dbms.validDbname dbname then
1103 case Dbms.lookup dbtype of
1104 NONE => ("Database creation request with unknown datatype type " ^ dbtype,
1105 SOME ("Unknown database type " ^ dbtype))
1106 | SOME handler =>
1107 case #createdb handler {user = user, dbname = dbname} of
1108 NONE => ("Created database " ^ user ^ "_" ^ dbname ^ ".",
1109 NONE)
1110 | SOME msg => ("Error creating database " ^ user ^ "_" ^ dbname ^ ": " ^ msg,
1111 SOME ("Error creating database: " ^ msg))
1112 else
1113 ("Invalid database name " ^ user ^ "_" ^ dbname,
1114 SOME ("Invalid database name " ^ dbname)))
1115 (fn () => ())
1116
1d3ef80e
AC
1117 | MsgListMailboxes domain =>
1118 doIt (fn () =>
1119 if not (Domain.yourDomain domain) then
1120 ("User wasn't authorized to list mailboxes for " ^ domain,
1121 SOME "You're not authorized to configure that domain.")
1122 else
1123 case Vmail.list domain of
1124 Vmail.Listing users => (Msg.send (bio, MsgMailboxes users);
1125 ("Sent mailbox list for " ^ domain,
1126 NONE))
1127 | Vmail.Error msg => ("Error listing mailboxes for " ^ domain ^ ": " ^ msg,
1128 SOME msg))
1129 (fn () => ())
1130
08688401
AC
1131 | MsgNewMailbox {domain, user = emailUser, passwd, mailbox} =>
1132 doIt (fn () =>
1133 if not (Domain.yourDomain domain) then
1134 ("User wasn't authorized to add a mailbox to " ^ domain,
1135 SOME "You're not authorized to configure that domain.")
2e96b9d4 1136 else if not (Domain.validEmailUser emailUser) then
08688401
AC
1137 ("Invalid e-mail username " ^ emailUser,
1138 SOME "Invalid e-mail username")
1139 else if not (CharVector.all Char.isGraph passwd) then
1140 ("Invalid password",
1141 SOME "Invalid password; may only contain printable, non-space characters")
1142 else if not (Domain.yourPath mailbox) then
1143 ("User wasn't authorized to add a mailbox at " ^ mailbox,
1144 SOME "You're not authorized to use that mailbox location.")
1145 else
1146 case Vmail.add {requester = user,
1147 domain = domain, user = emailUser,
1148 passwd = passwd, mailbox = mailbox} of
1149 NONE => ("Added mailbox " ^ emailUser ^ "@" ^ domain ^ " at " ^ mailbox,
1150 NONE)
9ffe2f0f 1151 | SOME msg => ("Error adding mailbox " ^ emailUser ^ "@" ^ domain ^ ": " ^ msg,
08688401
AC
1152 SOME msg))
1153 (fn () => ())
1154
1155 | MsgPasswdMailbox {domain, user = emailUser, passwd} =>
1156 doIt (fn () =>
1157 if not (Domain.yourDomain domain) then
1158 ("User wasn't authorized to change password of a mailbox for " ^ domain,
1159 SOME "You're not authorized to configure that domain.")
2e96b9d4 1160 else if not (Domain.validEmailUser emailUser) then
08688401
AC
1161 ("Invalid e-mail username " ^ emailUser,
1162 SOME "Invalid e-mail username")
1163 else if not (CharVector.all Char.isGraph passwd) then
1164 ("Invalid password",
1165 SOME "Invalid password; may only contain printable, non-space characters")
1166 else
1167 case Vmail.passwd {domain = domain, user = emailUser,
1168 passwd = passwd} of
1169 NONE => ("Changed password of mailbox " ^ emailUser ^ "@" ^ domain,
1170 NONE)
9ffe2f0f 1171 | SOME msg => ("Error changing mailbox password for " ^ emailUser ^ "@" ^ domain ^ ": " ^ msg,
08688401
AC
1172 SOME msg))
1173 (fn () => ())
1174
1175 | MsgRmMailbox {domain, user = emailUser} =>
1176 doIt (fn () =>
1177 if not (Domain.yourDomain domain) then
1178 ("User wasn't authorized to change password of a mailbox for " ^ domain,
1179 SOME "You're not authorized to configure that domain.")
2e96b9d4 1180 else if not (Domain.validEmailUser emailUser) then
08688401
AC
1181 ("Invalid e-mail username " ^ emailUser,
1182 SOME "Invalid e-mail username")
1183 else
1184 case Vmail.rm {domain = domain, user = emailUser} of
1185 NONE => ("Deleted mailbox " ^ emailUser ^ "@" ^ domain,
1186 NONE)
9ffe2f0f 1187 | SOME msg => ("Error deleting mailbox " ^ emailUser ^ "@" ^ domain ^ ": " ^ msg,
08688401
AC
1188 SOME msg))
1189 (fn () => ())
90dd48df 1190
2e96b9d4
AC
1191 | MsgSaQuery addr =>
1192 doIt (fn () =>
1193 case checkAddr addr of
1194 NONE => ("User tried to query SA filtering for " ^ addr,
1195 SOME "You aren't allowed to configure SA filtering for that recipient.")
1196 | SOME addr' => (Msg.send (bio, MsgSaStatus (SetSA.query addr'));
1197 ("Queried SA filtering status for " ^ addr,
1198 NONE)))
1199 (fn () => ())
1200
1201 | MsgSaSet (addr, b) =>
1202 doIt (fn () =>
1203 case checkAddr addr of
1204 NONE => ("User tried to set SA filtering for " ^ addr,
1205 SOME "You aren't allowed to configure SA filtering for that recipient.")
1206 | SOME addr' => (SetSA.set (addr', b);
1207 Msg.send (bio, MsgOk);
1208 ("Set SA filtering status for " ^ addr ^ " to "
1209 ^ (if b then "ON" else "OFF"),
1210 NONE)))
1211 (fn () => ())
1212
2bc5ed22
AC
1213 | MsgSmtpLogReq domain =>
1214 doIt (fn () =>
1215 if not (Domain.yourDomain domain) then
1216 ("Unauthorized user tried to request SMTP logs for " ^ domain,
1217 SOME "You aren't authorized to configure that domain.")
1218 else
1219 (SmtpLog.search (fn line => Msg.send (bio, MsgSmtpLogRes line))
1220 domain;
1221 ("Requested SMTP logs for " ^ domain,
1222 NONE)))
1223 (fn () => ())
1224
a95a0107
AC
1225 | MsgQuery q =>
1226 doIt (fn () => (Msg.send (bio, answerQuery q);
1227 (describeQuery q,
75585a67
AC
1228 NONE)))
1229 (fn () => ())
1230
36e42cb8 1231 | _ =>
08688401
AC
1232 doIt (fn () => ("Unexpected command",
1233 SOME "Unexpected command"))
1234 (fn () => ())
36e42cb8
AC
1235 in
1236 cmdLoop ()
1237 end
97665758
AC
1238 handle OpenSSL.OpenSSL s =>
1239 (print ("OpenSSL error: " ^ s ^ "\n");
1240 OpenSSL.close bio
1241 handle OpenSSL.OpenSSL _ => ();
1242 loop ())
1243 | OS.SysErr (s, _) =>
1244 (print ("System error: " ^ s ^ "\n");
1245 OpenSSL.close bio
1246 handle OpenSSL.OpenSSL _ => ();
1247 loop ())
36e42cb8 1248 in
c9731b9b 1249 print ("Domtool dispatcher starting up at " ^ now () ^ "\n");
361a1e7f 1250 print "Listening for connections....\n";
36e42cb8
AC
1251 loop ();
1252 OpenSSL.shutdown sock
1253 end
1254
1255fun slave () =
1256 let
6e62228d 1257 val host = Slave.hostname ()
36e42cb8
AC
1258
1259 val context = OpenSSL.context (Config.certDir ^ "/" ^ host ^ ".pem",
a088cea6 1260 Config.keyDir ^ "/" ^ host ^ "/key.pem",
36e42cb8
AC
1261 Config.trustStore)
1262
1263 val sock = OpenSSL.listen (context, Config.slavePort)
1264
c9731b9b
AC
1265 val _ = print ("Slave server starting at " ^ now () ^ "\n")
1266
36e42cb8
AC
1267 fun loop () =
1268 case OpenSSL.accept sock of
1269 NONE => ()
1270 | SOME bio =>
1271 let
1272 val peer = OpenSSL.peerCN bio
c9731b9b 1273 val () = print ("\nConnection from " ^ peer ^ " at " ^ now () ^ "\n")
3b267643 1274 in
c9731b9b 1275 if peer = Config.dispatcherName then let
36e42cb8
AC
1276 fun loop' files =
1277 case Msg.recv bio of
1278 NONE => print "Dispatcher closed connection unexpectedly\n"
1279 | SOME m =>
1280 case m of
1281 MsgFile file => loop' (file :: files)
1282 | MsgDoFiles => (Slave.handleChanges files;
1283 Msg.send (bio, MsgOk))
71420f8b
AC
1284 | MsgRegenerate => (Domain.resetLocal ();
1285 Msg.send (bio, MsgOk))
36e42cb8
AC
1286 | _ => (print "Dispatcher sent unexpected command\n";
1287 Msg.send (bio, MsgError "Unexpected command"))
1288 in
1289 loop' [];
1290 ignore (OpenSSL.readChar bio);
1291 OpenSSL.close bio;
1292 loop ()
1293 end
c9731b9b
AC
1294 else if peer = "domtool" then
1295 case Msg.recv bio of
1296 SOME MsgShutdown => (OpenSSL.close bio;
1297 print ("Shutting down at " ^ now () ^ "\n\n"))
1298 | _ => (OpenSSL.close bio;
1299 loop ())
1300 else
a95a0107
AC
1301 case Msg.recv bio of
1302 SOME (MsgQuery q) => (print (describeQuery q ^ "\n");
1303 Msg.send (bio, answerQuery q);
1304 ignore (OpenSSL.readChar bio);
1305 OpenSSL.close bio;
1306 loop ())
1307 | _ => (OpenSSL.close bio;
1308 loop ())
3196000d
AC
1309 end handle OpenSSL.OpenSSL s =>
1310 (print ("OpenSSL error: "^ s ^ "\n");
1311 OpenSSL.close bio
1312 handle OpenSSL.OpenSSL _ => ();
1313 loop ())
7af7d4cb
AC
1314 | OS.SysErr (s, _) =>
1315 (print ("System error: "^ s ^ "\n");
1316 OpenSSL.close bio
1317 handle OpenSSL.OpenSSL _ => ();
1318 loop ())
07cc384c 1319 in
3b267643
AC
1320 loop ();
1321 OpenSSL.shutdown sock
07cc384c
AC
1322 end
1323
44a5ce2f 1324fun listBasis () =
3196000d
AC
1325 let
1326 val dir = Posix.FileSys.opendir Config.libRoot
1327
1328 fun loop files =
1329 case Posix.FileSys.readdir dir of
1330 NONE => (Posix.FileSys.closedir dir;
1331 files)
1332 | SOME fname =>
1333 if String.isSuffix ".dtl" fname then
1334 loop (OS.Path.joinDirFile {dir = Config.libRoot,
1335 file = fname}
1336 :: files)
1337 else
1338 loop files
3196000d 1339 in
44a5ce2f 1340 loop []
3196000d
AC
1341 end
1342
44a5ce2f
AC
1343fun autodocBasis outdir =
1344 Autodoc.autodoc {outdir = outdir, infiles = listBasis ()}
1345
234b917a 1346end