Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file sys_utils.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460(**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the "hack" directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*)openHack_coreexceptionNotADirectoryofstringexternalrealpath:string->stringoption="hh_realpath"externalis_nfs:string->bool="hh_is_nfs"(** Hack_option type intead of exception throwing. *)letget_envname=trySome(Sys.getenvname)with|Not_found->Noneletgetenv_user()=letuser_var=ifSys.win32then"USERNAME"else"USER"inletlogname_var="LOGNAME"inletuser=get_envuser_varinletlogname=get_envlogname_varinHack_option.first_someuserlognameletgetenv_home()=lethome_var=ifSys.win32then"APPDATA"else"HOME"inget_envhome_varletgetenv_term()=letterm_var="TERM"in(* This variable does not exist on windows. *)get_envterm_varletpath_sep=ifSys.win32then";"else":"letnull_path=ifSys.win32then"nul"else"/dev/null"lettemp_dir_name=ifSys.win32thenFilename.get_temp_dir_name()else"/tmp"letgetenv_path()=letpath_var="PATH"in(* Same variable on windows *)get_envpath_varletopen_in_no_failfn=tryopen_infnwithe->lete=Printexc.to_stringeinPrintf.fprintfstderr"Could not open_in: '%s' (%s)\n"fne;exit3letopen_in_bin_no_failfn=tryopen_in_binfnwithe->lete=Printexc.to_stringeinPrintf.fprintfstderr"Could not open_in_bin: '%s' (%s)\n"fne;exit3letclose_in_no_failfnic=tryclose_inicwithe->lete=Printexc.to_stringeinPrintf.fprintfstderr"Could not close: '%s' (%s)\n"fne;exit3letopen_out_no_failfn=tryopen_outfnwithe->lete=Printexc.to_stringeinPrintf.fprintfstderr"Could not open_out: '%s' (%s)\n"fne;exit3letopen_out_bin_no_failfn=tryopen_out_binfnwithe->lete=Printexc.to_stringeinPrintf.fprintfstderr"Could not open_out_bin: '%s' (%s)\n"fne;exit3letclose_out_no_failfnoc=tryclose_outocwithe->lete=Printexc.to_stringeinPrintf.fprintfstderr"Could not close: '%s' (%s)\n"fne;exit3letcat=Disk.catletcat_no_failfilename=letic=open_in_bin_no_failfilenameinletlen=in_channel_lengthicinletbuf=Buffer.createleninBuffer.add_channelbuficlen;letcontent=Buffer.contentsbufinclose_in_no_failfilenameic;contentletnl_regexp=Str.regexp"[\r\n]"letsplit_lines=Str.splitnl_regexp(** Returns true if substring occurs somewhere inside str. *)letstring_containsstrsubstring=(* regexp_string matches only this string and nothing else. *)letre=Str.regexp_stringsubstringintry(Str.search_forwardrestr0)>=0withNot_found->falseletexec_readcmd=letic=Unix.open_process_incmdinletresult=input_lineicinassert(Unix.close_process_inic=Unix.WEXITED0);resultletexec_read_lines?(reverse=false)cmd=letic=Unix.open_process_incmdinletresult=ref[]in(trywhiletruedoresult:=input_lineic::!resultdone;withEnd_of_file->());assert(Unix.close_process_inic=Unix.WEXITED0);ifnotreversethenList.rev!resultelse!result(** Deletes the file given by "path". If it is a directory, recursively
* deletes all its contents then removes the directory itself. *)letrecrm_dir_treepath=trybeginletstats=Unix.lstatpathinmatchstats.Unix.st_kindwith|Unix.S_DIR->letcontents=Sys.readdirpathinList.iter(Array.to_listcontents)~f:(funname->letname=Filename.concatpathnameinrm_dir_treename);Unix.rmdirpath|Unix.S_LNK|Unix.S_REG|Unix.S_CHR|Unix.S_BLK|Unix.S_FIFO|Unix.S_SOCK->Unix.unlinkpathendwith(* Path has been deleted out from under us - can ignore it. *)|Sys_error(s)whens=Printf.sprintf"%s: No such file or directory"path->()|Unix.Unix_error(Unix.ENOENT,_,_)->()letrestart()=letcmd=Sys.argv.(0)inletargv=Sys.argvinUnix.execvcmdargvletlogname_impl()=matchgetenv_user()with|Someuser->user|None->(* If this function is generally useful, it can be lifted to toplevel
in this file, but this is the only place we need it for now. *)letexec_try_readcmd=letic=Unix.open_process_incmdinletout=trySome(input_lineic)withEnd_of_file->Noneinletstatus=Unix.close_process_inicinmatchout,statuswith|Some_,Unix.WEXITED0->out|_->NoneintryUtils.unsafe_opt(exec_try_read"logname")withInvalid_argument_->tryUtils.unsafe_opt(exec_try_read"id -un")withInvalid_argument_->"[unknown]"letlogname_ref=refNoneletlogname()=if!logname_ref=Nonethenlogname_ref:=Some(logname_impl());Utils.unsafe_opt!logname_refletwith_umaskumaskf=letold_umask=ref0inUtils.with_context~enter:(fun()->old_umask:=Unix.umaskumask)~exit:(fun()->ignore(Unix.umask!old_umask))~do_:fletwith_umaskumaskf=ifSys.win32thenf()elsewith_umaskumaskfletread_stdin_to_string()=letbuf=Buffer.create4096intrywhiletruedoBuffer.add_stringbuf(input_linestdin);Buffer.add_charbuf'\n'done;assertfalsewithEnd_of_file->Buffer.contentsbufletread_all?(buf_size=4096)ic=letbuf=Buffer.createbuf_sizein(trywhiletruedoletdata=Bytes.createbuf_sizeinletbytes_read=inputicdata0buf_sizeinifbytes_read=0thenraiseExit;Buffer.add_subbytesbufdata0bytes_read;donewithExit->());Buffer.contentsbuf(**
* Like Python's os.path.expanduser, though probably doesn't cover some cases.
* Roughly follow's bash's tilde expansion:
* http://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html
*
* ~/foo -> /home/bob/foo if $HOME = "/home/bob"
* ~joe/foo -> /home/joe/foo if joe's home is /home/joe
*)letexpanduserpath=Str.substitute_first(Str.regexp"^~\\([^/]*\\)")beginfuns->matchStr.matched_group1swith|""->beginmatchgetenv_home()with|None->(Unix.getpwuid(Unix.getuid())).Unix.pw_dir|Somehome->homeend|unixname->try(Unix.getpwnamunixname).Unix.pw_dirwithNot_found->Str.matched_stringsendpath(* Turns out it's surprisingly complex to figure out the path to the current
executable, which we need in order to extract its embedded libraries. If
argv[0] is a path, then we can use that; sometimes it's just the exe name,
so we have to search $PATH for it the same way shells do. for example:
https://www.gnu.org/software/bash/manual/html_node/Command-Search-and-Execution.html
There are other options which might be more reliable when they exist, like
using the `_` env var set by bash, or /proc/self/exe on Linux, but they are
not portable. *)letexecutable_path:unit->string=letexecutable_path_=refNoneinletdir_sep=Filename.dir_sep.[0]inletsearch_pathpath=letpaths=matchgetenv_path()with|None->failwith"Unable to determine executable path"|Somepaths->Str.split(Str.regexp_stringpath_sep)pathsinletpath=List.fold_leftpaths~f:beginfunaccp->matchaccwith|Some_->acc|None->realpath(expanduser(Filename.concatppath))end~init:Noneinmatchpathwith|Somepath->path|None->failwith"Unable to determine executable path"infun()->match!executable_path_with|Somepath->path|None->letpath=Sys.executable_nameinletpath=ifString.containspathdir_septhenmatchrealpathpathwith|Somepath->path|None->failwith"Unable to determine executable path"elsesearch_pathpathinexecutable_path_:=Somepath;pathletlines_of_in_channelic=letrecloopaccum=matchtrySome(input_lineic)with_e->Nonewith|None->List.revaccum|Some(line)->loop(line::accum)inloop[]letlines_of_filefilename=letic=open_infilenameintryletresult=lines_of_in_channelicinlet_=close_inicinresultwith_->close_inic;[]letread_filefile=letic=open_in_binfileinletsize=in_channel_lengthicinletbuf=String.createsizeinreally_inputicbuf0size;close_inic;bufletwrite_file~files=letchan=open_outfilein(output_stringchans;close_outchan)letappend_file~files=letchan=open_out_gen[Open_wronly;Open_append;Open_creat]0o666filein(output_stringchans;close_outchan)(* could be in control section too *)letfilemtimefile=(Unix.statfile).Unix.st_mtimeexternallutimes:string->unit="hh_lutimes"lettry_touch~follow_symlinksfile=tryiffollow_symlinksthenUnix.utimesfile0.00.0elselutimesfilewith_->()letrecmkdir_p=function|""->failwith"Unexpected empty directory, should never happen"|dwhennot(Sys.file_existsd)->mkdir_p(Filename.dirnamed);Unix.mkdird0o770;|dwhenSys.is_directoryd->()|d->raise(NotADirectoryd)(* Emulate "mkdir -p", i.e., no error if already exists. *)letmkdir_no_faildir=with_umask0beginfun()->(* Don't set sticky bit since the socket opening code wants to remove any
* old sockets it finds, which may be owned by a different user. *)tryUnix.mkdirdir0o777withUnix.Unix_error(Unix.EEXIST,_,_)->()endletunlink_no_failfn=tryUnix.unlinkfnwithUnix.Unix_error(Unix.ENOENT,_,_)->()letreadlink_no_failfn=ifSys.win32&&Sys.file_existsfnthencatfnelsetryUnix.readlinkfnwith_->fnletsplitextfilename=letroot=Filename.chop_extensionfilenameinletroot_length=String.lengthrootin(* -1 because the extension includes the period, e.g. ".foo" *)letext_length=String.lengthfilename-root_length-1inletext=String.subfilename(root_length+1)ext_lengthinroot,extletis_test_mode()=tryignore@@Sys.getenv"HH_TEST_MODE";truewith_->falseletsymlink=(* Dummy implementation of `symlink` on Windows: we create a text
file containing the targeted-file's path. Symlink are available
on Windows since Vista, but until Seven (included), one should
have administratrive rights in order to create symlink. *)letwin32_symlinksourcedest=write_file~file:destsourceinifSys.win32thenwin32_symlinkelse(* 4.03 adds an optional argument to Unix.symlink that we want to ignore
*)funsourcedest->Unix.symlinksourcedest(* Creates a symlink at <dir>/<linkname.ext> to
* <dir>/<pluralized ext>/<linkname>-<timestamp>.<ext> *)letmake_link_of_timestampedlinkname=letopenUnixinletdir=Filename.dirnamelinknameinmkdir_no_faildir;letbase=Filename.basenamelinknameinletbase,ext=splitextbaseinletdir=Filename.concatdir(Printf.sprintf"%ss"ext)inmkdir_no_faildir;lettm=localtime(time())inletyear=tm.tm_year+1900inlettime_str=Printf.sprintf"%d-%02d-%02d-%02d-%02d-%02d"year(tm.tm_mon+1)tm.tm_mdaytm.tm_hourtm.tm_mintm.tm_secinletfilename=Filename.concatdir(Printf.sprintf"%s-%s.%s"basetime_strext)inunlink_no_faillinkname;symlinkfilenamelinkname;filenameletsetsid=(* Not implemented on Windows. Let's just return the pid *)ifSys.win32thenUnix.getpidelseUnix.setsidletset_signal=ifnotSys.win32thenSys.set_signalelse(fun__->())letsignal=ifnotSys.win32then(funab->ignore(Sys.signalab))else(fun__->())externalget_total_ram:unit->int="hh_sysinfo_totalram"externaluptime:unit->int="hh_sysinfo_uptime"externalnproc:unit->int="nproc"lettotal_ram=get_total_ram()letnbr_procs=nproc()externalset_priorities:cpu_priority:int->io_priority:int->unit="hh_set_priorities"externalpid_of_handle:int->int="pid_of_handle"externalhandle_of_pid_for_termination:int->int="handle_of_pid_for_termination"letterminate_processpid=Unix.killpidSys.sigkillletlstatpath=(* WTF, on Windows `lstat` fails if a directory path ends with an
'/' (or a '\', whatever) *)Unix.lstat@@ifSys.win32&&String_utils.string_ends_withpathFilename.dir_septhenString.subpath0(String.lengthpath-1)elsepathletnormalize_filename_dir_sep=letdir_sep_char=String.getFilename.dir_sep0inString.map(func->ifc=dir_sep_charthen'/'elsec)letname_of_signal=function|swhens=Sys.sigabrt->"SIGABRT (Abnormal termination)"|swhens=Sys.sigalrm->"SIGALRM (Timeout)"|swhens=Sys.sigfpe->"SIGFPE (Arithmetic exception)"|swhens=Sys.sighup->"SIGHUP (Hangup on controlling terminal)"|swhens=Sys.sigill->"SIGILL (Invalid hardware instruction)"|swhens=Sys.sigint->"SIGINT (Interactive interrupt (ctrl-C))"|swhens=Sys.sigkill->"SIGKILL (Termination)"|swhens=Sys.sigpipe->"SIGPIPE (Broken pipe)"|swhens=Sys.sigquit->"SIGQUIT (Interactive termination)"|swhens=Sys.sigsegv->"SIGSEGV (Invalid memory reference)"|swhens=Sys.sigterm->"SIGTERM (Termination)"|swhens=Sys.sigusr1->"SIGUSR1 (Application-defined signal 1)"|swhens=Sys.sigusr2->"SIGUSR2 (Application-defined signal 2)"|swhens=Sys.sigchld->"SIGCHLD (Child process terminated)"|swhens=Sys.sigcont->"SIGCONT (Continue)"|swhens=Sys.sigstop->"SIGSTOP (Stop)"|swhens=Sys.sigtstp->"SIGTSTP (Interactive stop)"|swhens=Sys.sigttin->"SIGTTIN (Terminal read from background process)"|swhens=Sys.sigttou->"SIGTTOU (Terminal write from background process)"|swhens=Sys.sigvtalrm->"SIGVTALRM (Timeout in virtual time)"|swhens=Sys.sigprof->"SIGPROF (Profiling interrupt)"|swhens=Sys.sigbus->"SIGBUS (Bus error)"|swhens=Sys.sigpoll->"SIGPOLL (Pollable event)"|swhens=Sys.sigsys->"SIGSYS (Bad argument to routine)"|swhens=Sys.sigtrap->"SIGTRAP (Trace/breakpoint trap)"|swhens=Sys.sigurg->"SIGURG (Urgent condition on socket)"|swhens=Sys.sigxcpu->"SIGXCPU (Timeout in cpu time)"|swhens=Sys.sigxfsz->"SIGXFSZ (File size limit exceeded)"|other->string_of_intother