Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file unix_extended.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532moduleStable0=structopenCore.Core_stablemoduleInet_port=structmoduleV1=structmoduleT=structtypet=int[@@derivingcompare,equal,hash]letof_int_exnx=ifx>0&&x<65536thenxelsefailwith(Core.sprintf"%d is not a valid port number."x);;letto_intx=xincludeSexpable.Of_sexpable.V1(Int.V1)(structtypenonrect=tletof_sexpable=of_int_exnletto_sexpable=to_intend)includeBinable.Of_binable.V1[@alert"-legacy"](Int.V1)(structtypenonrect=tletof_binable=of_int_exnletto_binable=to_intend)include(valComparator.V1.make~compare~sexp_of_t)let%expect_test_=print_string[%bin_digest:t];[%expect{| 698cfa4093fe5e51523842d37b92aeac |}];;endincludeTincludeComparable.V1.Make(T)endendendopenCoreopenPolyopenUnixexternalraw_fork_exec:stdin:File_descr.t->stdout:File_descr.t->stderr:File_descr.t->?working_dir:string->?setuid:int->?setgid:int->?env:stringarray->string->stringarray->Pid.t="extended_ml_spawn_bc""extended_ml_spawn"letraw_fork_exec~stdin~stdout~stderr?working_dir?setuid?setgid?envprogargv=(* [spawn] is generally preferred: it seems better tested and more actively maintained.
It also uses [vfork] so it's more efficient. For now we still must fall back to
[extended_ml_spawn] for the case when [setuid] or [setgid] is requested,
but we should completely switch to [spawn] when/if it supports that. *)matchsetuid,setgidwith|None,None->letenv=Option.map~f:(funenv->Spawn.Env.of_list(Array.to_listenv))envinletcwd=Option.value_map~default:Spawn.Working_dir.Inherit~f:(funcwd->Pathcwd)working_dirinletargv=Array.to_listargvinPid.of_int(Spawn.spawn?env~cwd~prog~argv~stdin~stdout~stderr())|Some_,_|_,Some_->raw_fork_exec~stdin~stdout~stderr?working_dir?setuid?setgid?envprogargv;;moduleEnv=structopenString.Maptypet=stringString.Map.tletempty:t=emptyletget()=Array.fold(Unix.environment())~init:empty~f:(funenvstr->matchString.lsplit2~on:'='strwith|Some(key,data)->set~key~dataenv|None->failwithf"extended_unix.Env.get %S is not in the form of key=value"str());;letadd~key~dataenv=ifString.memkey'='thenfailwithf"extended_unix.Env.add:variable to export in the environment %S contains an \
equal sign"key()elseifString.memkey'\000'thenfailwithf"extended_unix.Env.add:variable to export in the environment %S contains an \
null character"key()elseifString.memdata'\000'thenfailwithf"extended_unix.Env.add:value (%S) to export in the environment for %S contains \
an null character"datakey()elseString.Map.set~key~dataenv;;letto_string_arrayenv=String.Map.to_alistenv|>List.map~f:(fun(k,v)->k^"="^v)|>List.to_array;;endletfork_exec?(stdin=Unix.stdin)?(stdout=Unix.stdout)?(stderr=Unix.stderr)?(path_lookup=true)?env?working_dir?setuid?setgidprogargs=letenv=Option.mapenv~f:(fune->letinit,l=matchewith|`Extendl->Env.get(),l|`Replacel->Env.empty,linList.fold_leftl~init~f:(funenv(key,data)->Env.add~key~dataenv)|>Env.to_string_array)andfull_prog=ifpath_lookupthen(matchShell_internal.whichprogwith|Somes->s|None->failwithf"fork_exec: Process not found %s"prog())elseproginraw_fork_exec~stdin~stdout~stderr?working_dir?setuid?setgid?envfull_prog(Array.of_list(prog::args));;externalseteuid:int->unit="extended_ml_seteuid"externalsetreuid:uid:int->euid:int->unit="extended_ml_setreuid"externalhtonl:Int32.t->Int32.t="extended_ml_htonl"externalntohl:Int32.t->Int32.t="extended_ml_ntohl"let%test_=htonl(ntohl0xdeadbeefl)=0xdeadbeefltypestatvfs={bsize:int(** file system block size *);frsize:int(** fragment size *);blocks:int(** size of fs in frsize units *);bfree:int(** # free blocks *);bavail:int(** # free blocks for non-root *);files:int(** # inodes *);ffree:int(** # free inodes *);favail:int(** # free inodes for non-root *);fsid:int(** file system ID *);flag:int(** mount flags *);namemax:int(** maximum filename length *)}[@@derivingsexp,bin_io](** get file system statistics *)externalstatvfs:string->statvfs="statvfs_stub"(** get load averages *)externalgetloadavg:unit->float*float*float="getloadavg_stub"moduleExtended_passwd=structopenPasswdletof_passwd_line_exns=matchString.splits~on:':'with|[name;passwd;uid;gid;gecos;dir;shell]->{name;passwd;uid=Int.of_stringuid;gid=Int.of_stringgid;gecos;dir;shell}|_->failwithf"of_passwd_line: failed to parse: %s"s();;letof_passwd_lines=Option.try_with(fun()->of_passwd_line_exns)letof_passwd_file_exnfn=Exn.protectx(In_channel.createfn)~f:(funchan->List.map(In_channel.input_lineschan)~f:of_passwd_line_exn)~finally:In_channel.close;;letof_passwd_filef=Option.try_with(fun()->of_passwd_file_exnf)endletstrptime=Core.Unix.strptimemoduleInet_port=structmoduleStable=Stable0.Inet_portmoduleT=structtypet=int[@@derivingcompare,equal,hash]typecomparator_witness=Stable.V1.comparator_witnessletcomparator=Stable.V1.comparatorletsexp_of_t=Stable.V1.sexp_of_tendincludeTletof_int_exn=Stable.V1.of_int_exnletof_intx=trySome(of_int_exnx)with|_->None;;letof_string_exnx=Int.of_stringx|>of_int_exnletof_stringx=trySome(of_string_exnx)with|_->None;;letto_stringx=Int.to_stringxletto_intx=xletarg_type=Command.Spec.Arg_type.createof_string_exnincludeComparable.Make_plain_using_comparator(T)endlet%test_=Inet_port.of_string"88"=Some88let%test_=Inet_port.of_string"2378472398572"=Nonelet%test_=Inet_port.of_int88=Some88let%test_=Inet_port.of_int872342=NonemoduleMac_address=struct(* An efficient internal representation would be something like a 6 byte array,
but let's use a hex string to get this off the ground. *)moduleT=structtypet=string[@@derivingsexp,bin_io,compare,hash]let(=)=String.(=)letequal=(=)letof_strings=letaddr=String.lowercases|>String.filter~f:(function|'a'..'f'|'0'..'9'->true|_->false)inletlength=String.lengthaddriniflength<>12thenfailwithf"MAC address '%s' has the wrong length: %d"slength();addr;;letto_stringt=letrecloopacc=function|a::b::rest->letx=String.of_char_list[a;b]inloop(x::acc)rest|[]->List.revacc|>String.concat~sep:":"|_->assertfalseinloop[](String.to_listt);;letto_string_ciscot=letlst=String.to_listtinleta=List.takelst4|>String.of_char_listandb=List.take(List.droplst4)4|>String.of_char_listandc=List.droplst8|>String.of_char_listinString.concat~sep:"."[a;b;c];;lett_of_sexpsexp=String.t_of_sexpsexp|>of_stringletsexp_of_tt=to_stringt|>String.sexp_of_tlet_flag=Command.Spec.Arg_type.createof_stringendincludeTincludeHashable.Make(T)endlet%test_=Mac_address.to_string(Mac_address.of_string"00:1d:09:68:82:0f")="00:1d:09:68:82:0f";;let%test_=Mac_address.to_string(Mac_address.of_string"00-1d-09-68-82-0f")="00:1d:09:68:82:0f";;let%test_=Mac_address.to_string(Mac_address.of_string"001d.0968.820f")="00:1d:09:68:82:0f";;let%test_=Mac_address.to_string_cisco(Mac_address.of_string"00-1d-09-68-82-0f")="001d.0968.820f";;moduleQuota=structtypebytes=Int63.t[@@derivingsexp]typeinodes=Int63.t[@@derivingsexp]letbytesx=xletinodesx=xtype'unitslimit={soft:'unitsoption[@sexp.option];hard:'unitsoption[@sexp.option];grace:Time.toption[@sexp.option]}[@@derivingsexp]type'unitsusage=private'units(* None is encoded as zero *)type'unitsc_limit={c_soft:'units;c_hard:'units;c_grace:Time.t}letzero_bytes=bytesInt63.zeroletzero_inodes=inodesInt63.zeroletml_limit_of_c_limit~zero{c_soft;c_hard;c_grace}={soft=(ifc_soft=zerothenNoneelseSomec_soft);hard=(ifc_hard=zerothenNoneelseSomec_hard);grace=(ifc_grace=Time.epochthenNoneelseSomec_grace)};;letc_limit_of_ml_limit~zero{soft;hard;grace}={c_soft=(matchsoftwith|None->zero|Somex->x);c_hard=(matchhardwith|None->zero|Somex->x);c_grace=(matchgracewith|None->Time.epoch|Somex->x)};;externalquota_query:[`User|`Group]->id:int->path:string->bytesc_limit*bytesusage*inodesc_limit*inodesusage="quota_query"externalquota_modify:[`User|`Group]->id:int->path:string->bytesc_limit->inodesc_limit->unit="quota_modify"letqueryuser_or_group~id~path=tryletblimit,busage,ilimit,iusage=quota_queryuser_or_group~id~pathinOk(ml_limit_of_c_limit~zero:zero_bytesblimit,busage,ml_limit_of_c_limit~zero:zero_inodesilimit,iusage)with|Unix.Unix_error_asexn->Or_error.of_exnexn;;letsetuser_or_group~id~pathbyte_limitinode_limit=tryOk(quota_modifyuser_or_group~id~path(c_limit_of_ml_limit~zero:zero_bytesbyte_limit)(c_limit_of_ml_limit~zero:zero_inodesinode_limit))with|Unix.Unix_error_asexn->Or_error.of_exnexn;;endmoduleMount_entry=struct(* see: man 3 getmntent *)typet={fsname:string;directory:string;fstype:string;options:string;dump_freq:intoption[@sexp.option];fsck_pass:intoption[@sexp.option]}[@@derivingsexp,fields]letescape_seqs=["040"," ";"011","\t";"012","\n";"134","\\";"\\","\\"]letunescapes=letfind_and_drop_prefixs(prefix,replacement)=Option.map(String.chop_prefix~prefixs)~f:(funs->replacement,s)inletrecloops=matchString.lsplit2s~on:'\\'with|None->[s]|Some(l,r)->(matchList.find_mapescape_seqs~f:(find_and_drop_prefixr)with|None->l::"\\"::loopr|Some(x,r)->l::x::loopr)inString.concat(loops);;letparse_optional_ints=matchInt.of_stringswith|0->None|n->Somen;;letsplit_and_normalizeline=letinside_comment=reffalseinletwhitespace=' 'inString.mapline~f:(funx->ifChar.equalx'#'theninside_comment:=true;ifChar.is_whitespacex||!inside_commentthenwhitespaceelsex)|>String.split~on:whitespace|>List.filter~f:(funx->not(String.is_emptyx));;letparse_lineline=matchsplit_and_normalizeline|>List.map~f:unescapewith|[]->OkNone|fsname::directory::fstype::options::(([]|[_]|[_;_])asdump_freq_and_fsck_pass)->letdump_freq,fsck_pass=matchdump_freq_and_fsck_passwith|[]->None,None|[dump_freq]->Somedump_freq,None|[dump_freq;fsck_pass]->Somedump_freq,Somefsck_pass|_->assertfalseinOr_error.try_with(fun()->letdump_freq=Option.binddump_freq~f:parse_optional_intinletfsck_pass=Option.bindfsck_pass~f:parse_optional_intinifString.equalfstype"ignore"thenNoneelseSome{fsname;directory;fstype;options;dump_freq;fsck_pass})|_->Or_error.error"wrong number of fields"lineString.sexp_of_t;;letvisible_filesystemts=letadd_slash_if_neededs=ifString.is_suffixs~suffix:"/"thenselses^"/"inletoverlaymapt=letremove_prefix=add_slash_if_needed(directoryt)inletrecloopmap=matchString.Map.closest_keymap`Greater_thanremove_prefixwith|None->map|Some(key,_)->ifnot(String.is_prefix~prefix:remove_prefixkey)thenmapelseloop(String.Map.removemapkey)inString.Map.set(loopmap)~key:(directoryt)~data:tinList.foldts~init:String.Map.empty~f:(funmapt->ifnot(String.is_prefix~prefix:"/"(directoryt))thenmapelseoverlaymapt);;endletterminal_width=lazy((* When both stdout and stderr are not terminals, tput outputs 80 rather than the
number of columns, so we can't use [Process.run]. Instead, we use
[open_process_in] so that stderr is still the terminal. But, we don't want
tput's error messages to be sent to stderr and seen by the user, so we first
run tput with no output to see if it succeeds, and only then do we run it with
stderr not redirected. *)tryExn.protectx(Core.Unix.open_process_in"/usr/bin/tput cols &> /dev/null && /usr/bin/tput cols")~f:(funin_channel->In_channel.input_linein_channel|>Option.value_exn|>Int.of_string)~finally:In_channel.closewith|_->90);;