123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453(* Ocsigen
* http://www.ocsigen.org
* Copyright (C) 2005
* Vincent Balat, Denis Berthod, Nataliya Guts, Jérôme Vouillon
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, with linking exception;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)openLwt.Infixlet()=Random.self_init()(* Without the following line, it stops with "Broken Pipe" without
raising an exception ... *)let_=Sys.set_signalSys.sigpipeSys.Signal_ignore(* Exit gracefully on SIGINT so that profiling will work *)let_=Sys.set_signalSys.sigint(Sys.Signal_handle(fun_->exit0))letsection=Lwt_log.Section.make"ocsigen:main"(* Initialize exception handler for Lwt timeouts: *)let_=Lwt_timeout.set_exn_handler(fune->Lwt_log.ign_error~section~exn:e"Uncaught Exception after lwt timeout")let_warnsockaddrs=Lwt_log.ign_warning_f~section"While talking to %a:%s"(fun()sockaddr->Unix.string_of_inet_addr(Ocsigen_lib.Ip_address.of_sockaddrsockaddr))sockaddrslet_dbgsockaddrs=Lwt_log.ign_info_f~section"While talking to %a:%s"(fun()sockaddr->Unix.string_of_inet_addr(Ocsigen_lib.Ip_address.of_sockaddrsockaddr))sockaddrs(* fatal errors messages *)leterrmsg=function|Dynlink_wrapper.Errore->"Fatal - Dynamic linking error: "^Dynlink_wrapper.error_messagee,6|Unix.Unix_error_ase->"Fatal - "^Printexc.to_stringe,9|Ssl.Private_key_errormsg->"Fatal - bad password: "^msg,10|Ocsigen_config.Config_file_errormsg|Ocsigen_extensions.Error_in_config_filemsg->"Fatal - Error in configuration file: "^msg,50|Xml.Error(s,loc)->letbegin_char,end_char=Xml.rangelocandline=Xml.linelocin(Printf.sprintf"Fatal - Error in configuration file, line %d, characters %d-%d: %s"linebegin_charend_char(Xml.error_msgs),51)|Ocsigen_loader.Dynlink_error(s,Dynlink.Errorerr)->"Fatal - While loading "^s^": "^Dynlink.error_messageerr,52|Ocsigen_loader.Dynlink_error(s,exn)->"Fatal - While loading "^s^": "^Printexc.to_stringexn,52|Ocsigen_loader.Findlib_error_ase->"Fatal - "^Printexc.to_stringe,53|exn->(tryOcsigen_extensions.get_init_exn_handler()exn,20withexn->"Fatal - Uncaught exception: "^Printexc.to_stringexn,100)(* loading new configuration *)letreload_confs=tryOcsigen_extensions.start_initialisation();Ocsigen_parseconfig.later_passs;Ocsigen_extensions.end_initialisation()withe->Ocsigen_extensions.end_initialisation();Lwt_log.ign_error~section(fst(errmsge))(* reloading the config file *)letreload?file()=(* That function cannot be interrupted??? *)Lwt_log.ign_warning~section"Reloading config file";(trymatchOcsigen_parseconfig.parse_config?file()with|[]->()|s::_->reload_confswithe->Ocsigen_messages.errlog(fst(errmsge)));(trymatchOcsigen_parseconfig.parse_config?file()with|[]->()|s::_->reload_confswithe->Lwt_log.ign_error~section(fst(errmsge)));Lwt_log.ign_warning~section"Config file reloaded"let_=letf_s=function|["reopen_logs"]->Ocsigen_messages.open_files()>>=fun()->Lwt_log.ign_warning~section"Log files reopened";Lwt.return()|["reload"]->reload();Lwt.return()|["reload";file]->reload~file();Lwt.return()|["shutdown"]->Ocsigen_cohttp.shutdownNone;Lwt.return()|["shutdown";f]->Ocsigen_cohttp.shutdown(Some(float_of_stringf));Lwt.return()|["gc"]->Gc.compact();Lwt_log.ign_warning~section"Heap compaction requested by user";Lwt.return()|["clearcache"]->Ocsigen_cache.clear_all_caches();Lwt.return()|_->Lwt.failOcsigen_command.Unknown_commandinOcsigen_command.register_command_functionftypeinstruction=Ocsigen_extensions.virtual_hosts->Ocsigen_extensions.config_info->Ocsigen_lib.Url.path->Ocsigen_extensions.extensionletdefault_re_string=".*"lethost?(regexp=default_re_string)?port?default_hostname?default_httpport?default_httpsport?default_protocol_is_https?mime_assoc?charset_assoc?default_directory_index?list_directory_content?follow_symlinks?do_not_serve_404?do_not_serve_403?uploaddir?maxuploadfilesizeinstructions=letdef=Ocsigen_extensions.default_config_info()inletdefaultdefaulto=Option.valueo~defaultinletconfig_info={Ocsigen_extensions.default_hostname=defaultdef.default_hostnamedefault_hostname;default_httpport=defaultdef.default_httpportdefault_httpport;default_httpsport=defaultdef.default_httpsportdefault_httpsport;default_protocol_is_https=defaultdef.default_protocol_is_httpsdefault_protocol_is_https;mime_assoc=defaultdef.mime_assocmime_assoc;charset_assoc=defaultdef.charset_assoccharset_assoc;default_directory_index=defaultdef.default_directory_indexdefault_directory_index;list_directory_content=defaultdef.list_directory_contentlist_directory_content;follow_symlinks=defaultdef.follow_symlinksfollow_symlinks;do_not_serve_404=defaultdef.do_not_serve_404do_not_serve_404;do_not_serve_403=defaultdef.do_not_serve_403do_not_serve_403;uploaddir=defaultdef.uploaddiruploaddir;maxuploadfilesize=defaultdef.maxuploadfilesizemaxuploadfilesize}inletvh=[regexp,Ocsigen_lib.Netstring_pcre.regexpregexp,port]in(vh,config_info,Ocsigen_extensions.compose(List.map(funi->ivhconfig_info[])instructions))letsite?charsetpathinstructionsvhconfig_infoparent_path=letpath=parent_path@Ocsigen_extensions.preprocess_site_pathpathinletcomposite=Ocsigen_extensions.compose(List.map(funi->ivhconfig_infopath)instructions)inOcsigen_extensions.site_extcompositecharsetpathletmain_loop_is_running=reffalseletmainconfig=if!main_loop_is_runningthenLwt_log.ign_fatal"Cannot run main loop twice";main_loop_is_running:=true;try(* initialization functions for modules (Ocsigen extensions or application
code) loaded from now on will be executed directly. *)Ocsigen_loader.set_init_on_loadtrue;letask_for_passwdsslports_=print_string"Please enter the password for the HTTPS server listening on port(s) ";print_string(String.concat", "(List.map(fun(_,p)->string_of_intp)sslports));print_string": ";letold_term=Unix.tcgetattrUnix.stdininletold_echo=old_term.Unix.c_echoinold_term.Unix.c_echo<-false;Unix.tcsetattrUnix.stdinUnix.TCSAFLUSHold_term;tryletr=read_line()inprint_newline();old_term.Unix.c_echo<-old_echo;Unix.tcsetattrUnix.stdinUnix.TCSAFLUSHold_term;rwithexn->old_term.Unix.c_echo<-old_echo;Unix.tcsetattrUnix.stdinUnix.TCSAFLUSHold_term;raiseexninletextensions_connector=Ocsigen_extensions.compute_resultinletrun()=Lwt_main.run(Ocsigen_messages.open_files());letports=Ocsigen_config.get_ports()andssl_ports=Ocsigen_config.get_ssl_ports()inletconnection=matchportswith[]->[`All,80]|l->linletssl_connection=letssl=matchOcsigen_config.get_ssl_info()with|None|Some{Ocsigen_config.ssl_certificate=None;Ocsigen_config.ssl_privatekey=None;_}->None|Some{Ocsigen_config.ssl_certificate=Somecrt;Ocsigen_config.ssl_privatekey=Somekey;_}->Some(crt,key)|Some{Ocsigen_config.ssl_privatekey=None;_}->raise(Ocsigen_config.Config_file_error"SSL key is missing")|Some{Ocsigen_config.ssl_certificate=None;_}->raise(Ocsigen_config.Config_file_error"SSL certificate is missing")inmatchssl_ports,sslwith|[],Some(crt,key)->[`All,443,(crt,key)]|l,Some(crt,key)->List.map(fun(a,p)->a,p,(crt,key))l|_->[]in(* A pipe to communicate with the server *)letcommandpipe=Ocsigen_config.get_command_pipe()inletwith_commandpipe=tryignore(Unix.statcommandpipe);truewithUnix.Unix_error_->(tryletumask=Unix.umask0inUnix.mkfifocommandpipe0o660;ignore(Unix.umaskumask);Lwt_log.ign_warning~section"Command pipe created";truewithe->Lwt_log.ign_warning_f~section~exn:e"Cannot create the command pipe %s. I will continue without."commandpipe;false)inletminthreads=Ocsigen_config.get_minthreads()andmaxthreads=Ocsigen_config.get_maxthreads()inifminthreads>maxthreadsthenraise(Ocsigen_config.Config_file_error"maxthreads should be greater than minthreads");ignore(Lwt_preemptive.initminthreadsmaxthreads(funs->Lwt_log.ign_error~sections));(Lwt.async_exception_hook:=fune->(* replace the default "exit 2" behaviour *)matchewith|Unix.Unix_error(Unix.EPIPE,_,_)->()|_->Lwt_log.ign_error~section~exn:e"Uncaught Exception");(* Now apply host configuration: *)config();ifOcsigen_config.get_silent()then((* Close stderr, stdout stdin if silent *)(* redirect stdout and stderr to /dev/null *)letdevnull=Unix.openfile"/dev/null"[Unix.O_WRONLY]0inUnix.dup2devnullUnix.stdout;Unix.dup2devnullUnix.stderr;Unix.closedevnull;Unix.closeUnix.stdin);(* detach from the terminal *)ifOcsigen_config.get_daemon()thenignore(Unix.setsid());Ocsigen_extensions.end_initialisation();(ifwith_commandpipethenletpipe=Unix.(openfilecommandpipe[O_RDWR;O_NONBLOCK;O_APPEND])0o660|>Lwt_unix.of_unix_file_descr|>Lwt_io.(of_fd~mode:input)inletrecf()=Lwt_io.read_linepipe>>=funs->Ocsigen_messages.warning("Command received: "^s);Lwt.catch(fun()->letprefix,c=matchOcsigen_lib.String.split~multisep:true' 'swith|[]->raiseOcsigen_command.Unknown_command|a::l->(tryletaa,ab=Ocsigen_lib.String.sep':'ainSomeaa,ab::lwithNot_found->None,a::l)inOcsigen_command.get_command_function()?prefixsc)(function|Ocsigen_command.Unknown_command->Lwt_log.ign_warning~section"Unknown command";Lwt.return()|e->Lwt_log.ign_error~section~exn:e"Uncaught Exception after command";Lwt.faile)>>=finignore(f()));Lwt_main.run@@Lwt.join(List.map(fun(address,port)->Ocsigen_cohttp.service~address~port~connector:extensions_connector())connection@(List.map(fun(address,port,(crt,key))->Ocsigen_cohttp.service~ssl:(crt,key,Some(ask_for_passwd[address,port]))~address~port~connector:extensions_connector()))ssl_connection)(*
Ocsigen_messages.warning "Ocsigen has been launched (initialisations ok)";
fst (Lwt.wait ())
*)in(*
let set_passwd_if_needed (ssl, ports, sslports) =
if sslports <> []
then
match ssl with
| None
| Some (None, None) -> ()
| Some (None, _) -> raise (Ocsigen_config.Config_file_error
"SSL certificate is missing")
| Some (_, None) -> raise (Ocsigen_config.Config_file_error
"SSL key is missing")
| Some ((Some c), (Some k)) ->
Ssl.set_password_callback !Server.ssl_context (ask_for_passwd sslports);
Ssl.use_certificate !Server.ssl_context c k
in
*)letwrite_pidpid=matchOcsigen_config.get_pidfile()with|None->()|Somep->letspid=string_of_intpid^"\n"inletlen=String.lengthspidinletf=Unix.openfilep[Unix.O_WRONLY;Unix.O_CREAT;Unix.O_TRUNC]0o640inignore(Unix.write_substringfspid0len);Unix.closefin(* set_passwd_if_needed sslinfo; *)ifOcsigen_config.get_daemon()thenletpid=Unix.fork()inifpid=0thenrun()else(Ocsigen_messages.console(fun()->"Process "^string_of_intpid^" detached");write_pidpid)else(write_pid(Unix.getpid());run())withe->letmsg,errno=errmsgeinOcsigen_messages.errlogmsg;exiterrnoletexecconfig=Ocsigen_config.has_config_file:=true;matchconfigwith|[]->()|[h]->(tryOcsigen_parseconfig.first_passhwithe->letmsg,errno=errmsgeinOcsigen_messages.errlogmsg;exiterrno);main(fun()->(* Now I can load the modules *)Dynlink_wrapper.allow_unsafe_modulestrue;Ocsigen_extensions.start_initialisation();Ocsigen_parseconfig.later_passh;(* As libraries are reloaded each time the config file is
read, we do not allow to register extensions in
libraries. Seems it does not work :-/ *)Dynlink_wrapper.prohibit["Ocsigen_extensions.R"])|_::_::_->Lwt_log.ign_warning~section"Multiple servers not supported anymore"(* Multiple servers not supported any more *)letstart?(ports=[`All,8080])?ssl_ports?ssl_info?default_charset?logdir?datadir?uploaddir?maxuploadfilesize?syslog_facility?configfile?usedefaulthostname?pidfile?mimefile?verbose?veryverbose?silent?daemon?debug?debugmode?minthreads?maxthreads?max_number_of_threads_queued?max_number_of_connections?client_timeout?server_timeout?shutdown_timeout?filebuffersize?maxrequestbodysize?maxrequestbodysizeinmemory?bindir?extdir?command_pipe?disablepartialrequests?respect_pipeline?maxretriesinstructions=Ocsigen_config.set_portsports;Option.iterOcsigen_config.set_ssl_portsssl_ports;Option.iterOcsigen_config.set_logdirlogdir;Option.iterOcsigen_config.set_syslog_facilitysyslog_facility;Option.iterOcsigen_config.set_uploaddiruploaddir;Option.iterOcsigen_config.set_maxuploadfilesizemaxuploadfilesize;Option.iterOcsigen_config.set_datadirdatadir;Option.iterOcsigen_config.set_configfileconfigfile;Option.iterOcsigen_config.set_pidfilepidfile;Option.iterOcsigen_config.set_mimefilemimefile;Option.iterOcsigen_config.set_verboseverbose;Option.iterOcsigen_config.set_silentsilent;Option.iterOcsigen_config.set_daemondaemon;Option.iterOcsigen_config.set_veryverboseveryverbose;Option.iterOcsigen_config.set_debugdebug;Option.iterOcsigen_config.set_minthreadsminthreads;Option.iterOcsigen_config.set_maxthreadsmaxthreads;Option.iterOcsigen_config.set_max_number_of_threads_queuedmax_number_of_threads_queued;Option.iterOcsigen_config.set_max_number_of_connectionsmax_number_of_connections;Option.iterOcsigen_config.set_client_timeoutclient_timeout;Option.iterOcsigen_config.set_server_timeoutserver_timeout;Option.iterOcsigen_config.set_filebuffersizefilebuffersize;Option.iterOcsigen_config.set_maxrequestbodysizemaxrequestbodysize;Option.iterOcsigen_config.set_maxrequestbodysizeinmemorymaxrequestbodysizeinmemory;Option.iterOcsigen_config.set_default_charsetdefault_charset;Option.iterOcsigen_config.set_bindirbindir;Option.iterOcsigen_config.set_extdirextdir;Option.iterOcsigen_config.set_command_pipecommand_pipe;Option.iterOcsigen_config.set_debugmodedebugmode;Option.iterOcsigen_config.set_disablepartialrequestsdisablepartialrequests;Option.iterOcsigen_config.set_usedefaulthostnameusedefaulthostname;Option.iterOcsigen_config.set_respect_pipelinerespect_pipeline;Option.iterOcsigen_config.set_maxretriesmaxretries;Option.iterOcsigen_config.set_shutdown_timeoutshutdown_timeout;Option.iterOcsigen_config.set_ssl_infossl_info;main(fun()->Ocsigen_extensions.start_initialisation();Ocsigen_extensions.set_hostsinstructions)