35
36:- module(http_unix_daemon,
37 [ http_daemon/0,
38 http_daemon/1 39 ]).
40:- use_module(library(error)).
41:- use_module(library(apply)).
42:- use_module(library(lists)).
43:- use_module(library(debug)).
44:- use_module(library(broadcast)).
45:- use_module(library(socket)).
46:- use_module(library(option)).
47:- use_module(library(uid)).
48:- use_module(library(unix)).
49:- use_module(library(syslog)).
50:- use_module(library(http/thread_httpd)).
51:- use_module(library(http/http_dispatch)).
52:- use_module(library(http/http_host)).
53:- use_module(library(main)).
54
55:- if(exists_source(library(http/http_ssl_plugin))).
56:- use_module(library(ssl)).
57:- use_module(library(http/http_ssl_plugin)).
58:- endif.
59
60:- multifile
61 http_server_hook/1, 62 http_certificate_hook/3, 63 http:sni_options/2. 64
139
140:- debug(daemon).
141
145
146:- set_prolog_flag(xpce_threaded, false).
147:- set_prolog_flag(message_ide, false). 148:- dynamic interactive/0.
149
278
279http_daemon :-
280 current_prolog_flag(argv, Argv),
281 argv_options(Argv, _RestArgv, Options),
282 http_daemon(Options).
283
293
294http_daemon(Options) :-
295 catch(http_daemon_guarded(Options), Error, start_failed(Error)).
296
297start_failed(Error) :-
298 interactive,
299 !,
300 print_message(warning, Error).
301start_failed(Error) :-
302 print_message(error, Error),
303 halt(1).
304
309
310http_daemon_guarded(Options) :-
311 option(help(true), Options),
312 !,
313 print_message(information, http_daemon(help)),
314 halt.
315http_daemon_guarded(Options) :-
316 setup_debug(Options),
317 kill_x11(Options),
318 option_servers(Options, Servers0),
319 maplist(make_socket, Servers0, Servers),
320 ( option(fork(true), Options, true),
321 option(interactive(false), Options, false),
322 can_switch_user(Options)
323 -> fork(Who),
324 ( Who \== child
325 -> halt
326 ; disable_development_system,
327 setup_syslog(Options),
328 write_pid(Options),
329 setup_output(Options),
330 switch_user(Options),
331 setup_signals(Options),
332 start_servers(Servers),
333 wait(Options)
334 )
335 ; write_pid(Options),
336 switch_user(Options),
337 setup_signals(Options),
338 start_servers(Servers),
339 wait(Options)
340 ).
341
351
352option_servers(Options, Sockets) :-
353 opt_sockets(Options, [], [], Sockets).
354
355opt_sockets([], Options, [], [Socket]) :-
356 !,
357 make_server(http(true), Options, Socket).
358opt_sockets([], _, Sockets, Sockets).
359opt_sockets([H|T], OptsH, Sockets0, Sockets) :-
360 server_option(H),
361 !,
362 append(OptsH, [H], OptsH1),
363 opt_sockets(T, OptsH1, Sockets0, Sockets).
364opt_sockets([H|T0], Opts, Sockets0, Sockets) :-
365 server_start_option(H),
366 !,
367 server_options(T0, T, Opts, SOpts),
368 make_server(H, SOpts, Socket),
369 append(Sockets0, [Socket], Sockets1),
370 opt_sockets(T, Opts, Sockets1, Sockets).
371opt_sockets([_|T], Opts, Sockets0, Sockets) :-
372 opt_sockets(T, Opts, Sockets0, Sockets).
373
374server_options([], [], Options, Options).
375server_options([H|T], Rest, Options0, Options) :-
376 server_option(H),
377 !,
378 generalise_option(H, G),
379 delete(Options0, G, Options1),
380 append(Options1, [H], Options2),
381 server_options(T, Rest, Options2, Options).
382server_options([H|T], [H|T], Options, Options) :-
383 server_start_option(H),
384 !.
385server_options([_|T0], Rest, Options0, Options) :-
386 server_options(T0, Rest, Options0, Options).
387
388generalise_option(H, G) :-
389 H =.. [Name,_],
390 G =.. [Name,_].
391
392server_start_option(http(_)).
393server_start_option(https(_)).
394
395server_option(port(_)).
396server_option(ip(_)).
397server_option(certfile(_)).
398server_option(keyfile(_)).
399server_option(pwfile(_)).
400server_option(password(_)).
401server_option(cipherlist(_)).
402server_option(workers(_)).
403server_option(redirect(_)).
404
405make_server(http(Address0), Options0, server(http, Address, Options)) :-
406 make_address(Address0, 80, Address, Options0, Options).
407make_server(https(Address0), Options0, server(https, Address, SSLOptions)) :-
408 make_address(Address0, 443, Address, Options0, Options),
409 merge_https_options(Options, SSLOptions).
410
411make_address(true, DefPort, Address, Options0, Options) :-
412 !,
413 option(port(Port), Options0, DefPort),
414 ( option(ip(Bind), Options0)
415 -> Address = (Bind:Port)
416 ; Address = Port
417 ),
418 merge_options([port(Port)], Options0, Options).
419make_address(Bind:Port, _, Bind:Port, Options0, Options) :-
420 !,
421 must_be(atom, Bind),
422 must_be(integer, Port),
423 merge_options([port(Port), ip(Bind)], Options0, Options).
424make_address(Port, _, Address, Options0, Options) :-
425 integer(Port),
426 !,
427 ( option(ip(Bind), Options0)
428 -> Address = (Bind:Port)
429 ; Address = Port,
430 merge_options([port(Port)], Options0, Options)
431 ).
432make_address(Spec, _, Address, Options0, Options) :-
433 atomic(Spec),
434 split_string(Spec, ":", "", [BindString, PortString]),
435 number_string(Port, PortString),
436 !,
437 atom_string(Bind, BindString),
438 Address = (Bind:Port),
439 merge_options([port(Port), ip(Bind)], Options0, Options).
440make_address(Spec, _, _, _, _) :-
441 domain_error(address, Spec).
442
443:- dynamic sni/3.
444
445merge_https_options(Options, [SSL|Options]) :-
446 ( option(certfile(CertFile), Options),
447 option(keyfile(KeyFile), Options)
448 -> read_file_to_string(CertFile, Certificate, []),
449 read_file_to_string(KeyFile, Key, []),
450 Pairs = [Certificate-Key],
451 prepare_https_certificate(CertFile, KeyFile, Passwd0)
452 ; Pairs = []
453 ),
454 option(cipherlist(CipherList), Options, 'DEFAULT'),
455 ( string(Passwd0)
456 -> Passwd = Passwd0
457 ; options_password(Options, Passwd)
458 ),
459 findall(HostName-HostOptions, http:sni_options(HostName, HostOptions), SNIs),
460 maplist(sni_contexts, SNIs),
461 SSL = ssl([ certificate_key_pairs(Pairs),
462 cipher_list(CipherList),
463 password(Passwd),
464 sni_hook(http_unix_daemon:sni)
465 ]).
466
467sni_contexts(Host-Options) :-
468 ssl_context(server, SSL, Options),
469 assertz(sni(_, Host, SSL)).
470
478
479prepare_https_certificate(CertFile, KeyFile, Password) :-
480 http_certificate_hook(CertFile, KeyFile, Password),
481 !.
482prepare_https_certificate(_, _, _).
483
484
485options_password(Options, Passwd) :-
486 option(password(Passwd), Options),
487 !.
488options_password(Options, Passwd) :-
489 option(pwfile(File), Options),
490 !,
491 read_file_to_string(File, String, []),
492 split_string(String, "", "\r\n\t ", [Passwd]).
493options_password(_, '').
494
510
511start_servers(Servers) :-
512 broadcast(http(pre_server_start)),
513 maplist(start_server, Servers),
514 broadcast(http(post_server_start)).
515
516start_server(server(_Scheme, Socket, Options)) :-
517 option(redirect(To), Options),
518 !,
519 http_server(server_redirect(To), [tcp_socket(Socket)|Options]).
520start_server(server(_Scheme, Socket, Options)) :-
521 http_server_hook([tcp_socket(Socket)|Options]),
522 !.
523start_server(server(_Scheme, Socket, Options)) :-
524 http_server(http_dispatch, [tcp_socket(Socket)|Options]).
525
526make_socket(server(Scheme, Address, Options),
527 server(Scheme, Socket, Options)) :-
528 tcp_socket(Socket),
529 catch(bind_socket(Socket, Address), Error,
530 make_socket_error(Error, Address)),
531 debug(daemon(socket),
532 'Created socket ~p, listening on ~p', [Socket, Address]).
533
534bind_socket(Socket, Address) :-
535 tcp_setopt(Socket, reuseaddr),
536 tcp_bind(Socket, Address),
537 tcp_listen(Socket, 5).
538
539make_socket_error(error(socket_error(_), _), Address) :-
540 address_port(Address, Port),
541 integer(Port),
542 Port =< 1000,
543 !,
544 verify_root(open_port(Port)).
545make_socket_error(Error, _) :-
546 throw(Error).
547
548address_port(_:Port, Port) :- !.
549address_port(Port, Port).
550
554
555disable_development_system :-
556 set_prolog_flag(editor, '/bin/false').
557
562
563enable_development_system :-
564 assertz(interactive),
565 set_prolog_flag(xpce_threaded, true),
566 set_prolog_flag(message_ide, true),
567 ( current_prolog_flag(xpce_version, _)
568 -> call(pce_dispatch([]))
569 ; true
570 ).
571
572
576
577setup_syslog(Options) :-
578 option(syslog(Ident), Options),
579 !,
580 openlog(Ident, [pid], user).
581setup_syslog(_).
582
583
589
590setup_output(Options) :-
591 option(output(File), Options),
592 !,
593 open(File, write, Out, [encoding(utf8)]),
594 set_stream(Out, buffer(line)),
595 detach_IO(Out).
596setup_output(_) :-
597 open_null_stream(Out),
598 detach_IO(Out).
599
600
605
606write_pid(Options) :-
607 option(pidfile(File), Options),
608 current_prolog_flag(pid, PID),
609 !,
610 setup_call_cleanup(
611 open(File, write, Out),
612 format(Out, '~d~n', [PID]),
613 close(Out)),
614 at_halt(catch(delete_file(File), _, true)).
615write_pid(_).
616
617
622
623switch_user(Options) :-
624 option(user(User), Options),
625 !,
626 verify_root(switch_user(User)),
627 ( option(group(Group), Options)
628 -> set_user_and_group(User, Group)
629 ; set_user_and_group(User)
630 ),
631 prctl(set_dumpable(true)). 632switch_user(_Options) :-
633 verify_no_root.
634
639
640can_switch_user(Options) :-
641 option(user(User), Options),
642 !,
643 verify_root(switch_user(User)).
644can_switch_user(_Options) :-
645 verify_no_root.
646
647verify_root(_Task) :-
648 geteuid(0),
649 !.
650verify_root(Task) :-
651 print_message(error, http_daemon(no_root(Task))),
652 halt(1).
653
654verify_no_root :-
655 geteuid(0),
656 !,
657 throw(error(permission_error(open, server, http),
658 context('Refusing to run HTTP server as root', _))).
659verify_no_root.
660
661:- if(\+current_predicate(prctl/1)).
662prctl(_).
663:- endif.
664
684
685server_redirect(Port, Request) :-
686 integer(Port),
687 http_server_property(Port, scheme(Scheme)),
688 http_public_host(Request, Host, _Port, []),
689 memberchk(request_uri(Location), Request),
690 ( default_port(Scheme, Port)
691 -> format(string(To), '~w://~w~w', [Scheme, Host, Location])
692 ; format(string(To), '~w://~w:~w~w', [Scheme, Host, Port, Location])
693 ),
694 throw(http_reply(moved_temporary(To))).
695server_redirect(true, Request) :-
696 !,
697 http_server_property(P, scheme(https)),
698 server_redirect(P, Request).
699server_redirect(URI, Request) :-
700 memberchk(request_uri(Location), Request),
701 atom_concat(URI, Location, To),
702 throw(http_reply(moved_temporary(To))).
703
704default_port(http, 80).
705default_port(https, 443).
706
707
712
713setup_debug(Options) :-
714 setup_trace(Options),
715 nodebug(_),
716 debug(daemon),
717 enable_debug(Options).
718
719enable_debug([]).
720enable_debug([debug(Topic)|T]) :-
721 !,
722 atom_to_term(Topic, Term, _),
723 debug(Term),
724 enable_debug(T).
725enable_debug([_|T]) :-
726 enable_debug(T).
727
728setup_trace(Options) :-
729 option(gtrace(true), Options),
730 !,
731 gtrace.
732setup_trace(_).
733
734
738
739kill_x11(Options) :-
740 getenv('DISPLAY', Display),
741 Display \== '',
742 option(interactive(false), Options, false),
743 !,
744 setenv('DISPLAY', ''),
745 set_prolog_flag(gui, false).
746kill_x11(_).
747
748
754
755setup_signals(Options) :-
756 option(interactive(true), Options, false),
757 !.
758setup_signals(Options) :-
759 on_signal(int, _, quit),
760 on_signal(term, _, quit),
761 option(sighup(Action), Options, reload),
762 must_be(oneof([reload,quit]), Action),
763 on_signal(usr1, _, logrotate),
764 on_signal(hup, _, Action).
765
766:- public
767 quit/1,
768 reload/1,
769 logrotate/1.
770
771quit(Signal) :-
772 debug(daemon, 'Dying on signal ~w', [Signal]),
773 thread_send_message(main, quit).
774
775reload(Signal) :-
776 debug(daemon, 'Reload on signal ~w', [Signal]),
777 thread_send_message(main, reload).
778
779logrotate(Signal) :-
780 debug(daemon, 'Closing log files on signal ~w', [Signal]),
781 thread_send_message(main, logrotate).
782
791
792wait(Options) :-
793 option(interactive(true), Options, false),
794 !,
795 enable_development_system.
796wait(Options) :-
797 thread_self(Me),
798 option(maintenance_interval(Interval), Options, 300),
799 Interval > 0,
800 !,
801 first_deadline(Interval, FirstDeadline),
802 State = deadline(0),
803 repeat,
804 State = deadline(Count),
805 Deadline is FirstDeadline+Count*Interval,
806 ( thread_get_message(Me, Msg, [deadline(Deadline)])
807 -> catch(ignore(handle_message(Msg)), E,
808 print_message(error, E)),
809 Msg == quit,
810 halt(0)
811 ; Count1 is Count + 1,
812 nb_setarg(1, State, Count1),
813 catch(broadcast(maintenance(Interval, Deadline)), E,
814 print_message(error, E)),
815 fail
816 ).
817wait(_) :-
818 thread_self(Me),
819 repeat,
820 thread_get_message(Me, Msg),
821 catch(ignore(handle_message(Msg)), E,
822 print_message(error, E)),
823 Msg == quit,
824 !,
825 halt(0).
826
827handle_message(reload) :-
828 make,
829 broadcast(logrotate).
830handle_message(logrotate) :-
831 broadcast(logrotate).
832
833first_deadline(Interval, Deadline) :-
834 get_time(Now),
835 Deadline is ((integer(Now) + Interval - 1)//Interval)*Interval.
836
837
838 841
847
848
857
858
859 862
863:- multifile
864 prolog:message//1.
865
866prolog:message(http_daemon(help)) -->
867 [ 'Usage: <program> option ...'-[], nl,
868 'Options:'-[], nl, nl,
869 ' --port=port HTTP port to listen to'-[], nl,
870 ' --ip=IP Only listen to this ip (--ip=localhost)'-[], nl,
871 ' --debug=topic Print debug message for topic'-[], nl,
872 ' --syslog=ident Send output to syslog daemon as ident'-[], nl,
873 ' --user=user Run server under this user'-[], nl,
874 ' --group=group Run server under this group'-[], nl,
875 ' --pidfile=path Write PID to path'-[], nl,
876 ' --output=file Send output to file (instead of syslog)'-[], nl,
877 ' --fork=bool Do/do not fork'-[], nl,
878 ' --http[=Address] Create HTTP server'-[], nl,
879 ' --https[=Address] Create HTTPS server'-[], nl,
880 ' --certfile=file The server certificate'-[], nl,
881 ' --keyfile=file The server private key'-[], nl,
882 ' --pwfile=file File holding password for the private key'-[], nl,
883 ' --password=pw Password for the private key'-[], nl,
884 ' --cipherlist=cs Cipher strings separated by colons'-[], nl,
885 ' --redirect=to Redirect all requests to a URL or port'-[], nl,
886 ' --interactive=bool Enter Prolog toplevel after starting server'-[], nl,
887 ' --gtrace=bool Start (graphical) debugger'-[], nl,
888 ' --sighup=action Action on SIGHUP: reload (default) or quit'-[], nl,
889 ' --workers=count Number of HTTP worker threads'-[], nl, nl,
890 'Boolean options may be written without value (true) or as --no-name (false)'-[], nl,
891 'Address is a port number or host:port, e.g., 8080 or localhost:8080'-[], nl,
892 'Multiple servers can be started by repeating --http and --https'-[], nl,
893 'Each server merges the options before the first --http(s) and up the next'-[]
894 ].
895prolog:message(http_daemon(no_root(switch_user(User)))) -->
896 [ 'Program must be started as root to use --user=~w.'-[User] ].
897prolog:message(http_daemon(no_root(open_port(Port)))) -->
898 [ 'Cannot open port ~w. Only root can open ports below 1000.'-[Port] ].