From c8822551aada51968d670f80e3916bba4469fd37 Mon Sep 17 00:00:00 2001 From: Thomas Gazagnaire Date: Tue, 15 May 2012 17:24:33 +0200 Subject: [PATCH] [opam server] start implementing the OPAM server repository pluggin. It compiles, but untested. Morever 'opam-server-download' and 'opam-server-upload' are missing. Most of the code is here, need to test it and finish to write more boring script stuff... --- Makefile | 5 +- opam.ocp | 36 ++ specs/roadmap.pdf | Bin 246345 -> 246341 bytes specs/roadmap.tex | 18 +- src/file.ml | 10 + src/file.mli | 6 + src/repo/server/client.ml | 91 +++++ src/repo/server/client.mli | 36 ++ src/repo/server/daemon.ml | 87 +++++ src/repo/server/daemon.mli | 28 ++ src/repo/server/download.ml | 4 + src/repo/server/init.ml | 34 ++ src/repo/server/key.ml | 47 +++ src/{server/keys.ml => repo/server/key.mli} | 45 +-- src/repo/server/protocol.ml | 244 +++++++++++++ src/repo/server/protocol.mli | 57 +++ .../opam_server.ml => repo/server/server.ml} | 39 +- src/repo/server/update.ml | 4 + src/repo/server/upload.ml | 45 +++ src/server/protocol.ml | 69 ---- src/server/server.ml | 338 ------------------ 21 files changed, 773 insertions(+), 470 deletions(-) create mode 100644 src/repo/server/client.ml create mode 100644 src/repo/server/client.mli create mode 100644 src/repo/server/daemon.ml create mode 100644 src/repo/server/daemon.mli create mode 100644 src/repo/server/download.ml create mode 100644 src/repo/server/init.ml create mode 100644 src/repo/server/key.ml rename src/{server/keys.ml => repo/server/key.mli} (72%) create mode 100644 src/repo/server/protocol.ml create mode 100644 src/repo/server/protocol.mli rename src/{server/opam_server.ml => repo/server/server.ml} (75%) create mode 100644 src/repo/server/update.ml create mode 100644 src/repo/server/upload.ml delete mode 100644 src/server/protocol.ml delete mode 100644 src/server/server.ml diff --git a/Makefile b/Makefile index 41d72af8dbf..8b553916d59 100644 --- a/Makefile +++ b/Makefile @@ -2,9 +2,10 @@ BIN = /usr/local/bin OCPBUILD ?= ./_obuild/unixrun ./boot/ocp-build.boot OCAMLC=ocamlc SRC_EXT=src_ext -TARGETS = opam \ +TARGETS = opam opam-server \ opam-rsync-init opam-rsync-update opam-rsync-download opam-rsync-upload \ - opam-git-init opam-git-update opam-git-download opam-git-upload + opam-git-init opam-git-update opam-git-download opam-git-upload \ + opam-server-init opam-server-update opam-server-download opam-server-upload .PHONY: all diff --git a/opam.ocp b/opam.ocp index 138788c84ab..a299345609e 100644 --- a/opam.ocp +++ b/opam.ocp @@ -91,3 +91,39 @@ begin program "opam-git-upload" requires = [ "opam-lib" ] end + +(* SERVER *) +begin library "opam-server-lib" + files = [ + "src/repo/server/protocol.ml" + "src/repo/server/key.ml" + "src/repo/server/daemon.ml" + "src/repo/server/client.ml" + ] + requires = [ "opam-lib" ] +end + +begin program "opam-server" + files = [ "src/repo/server/server.ml" ] + requires = [ "opam-server-lib" ] +end + +begin program "opam-server-init" + files = [ "src/repo/server/init.ml" ] + requires = [ "opam-server-lib" ] +end + +begin program "opam-server-update" + files = [ "src/repo/server/update.ml" ] + requires = [ "opam-server-lib" ] +end + +begin program "opam-server-download" + files = [ "src/repo/server/download.ml" ] + requires = [ "opam-server-lib" ] +end + +begin program "opam-server-upload" + files = [ "src/repo/server/upload.ml" ] + requires = [ "opam-server-lib" ] +end diff --git a/specs/roadmap.pdf b/specs/roadmap.pdf index d385ce13154dd72f4ca92ae40a175597c2da2195..882381c2f83821bdf4008cb0f859275903477374 100644 GIT binary patch delta 16824 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 delta 16863 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 t -> unit val read : filename -> t val safe_read: filename -> t + val to_raw: t -> raw + val of_raw: raw -> t end module Config = struct diff --git a/src/file.mli b/src/file.mli index 7a85cdca0d2..aae2d841f15 100644 --- a/src/file.mli +++ b/src/file.mli @@ -20,6 +20,12 @@ module type IO_FILE = sig (** Read file contents. Return [empty] if the file does not exist. *) val safe_read: filename -> t + (** Return the file contents *) + val to_raw: t -> raw + + (** Convert a raw string into a file *) + val of_raw: raw -> t + end (** Configuration file: [$opam/config] *) diff --git a/src/repo/server/client.ml b/src/repo/server/client.ml new file mode 100644 index 00000000000..eb76dd46dc1 --- /dev/null +++ b/src/repo/server/client.ml @@ -0,0 +1,91 @@ +(***********************************************************************) +(* *) +(* Copyright 2012 OCamlPro *) +(* Copyright 2012 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of *) +(* the GNU Public License version 3.0. *) +(* *) +(* TypeRex 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 General Public License for more details. *) +(* *) +(***********************************************************************) + +open Unix +open Protocol +open Types + +let rpc host = + let addr = ADDR_INET (host, default_port) in + process_client (open_connection addr) + +let protocol_error x msg = match x with + | Error r -> Globals.error_and_exit "Protocol error: %s (%s)" msg r + | _ -> Globals.error_and_exit "Protocol error: %s" msg + +let client_version host = + let req = ClientVersion Globals.opam_version in + match rpc host req with + | ServerVersion v -> + if v <> Globals.opam_version then + Globals.error_and_exit "API versions differ!" + | x -> protocol_error x "client_version" + +let get_list host = + client_version host; + match rpc host GetList with + | PackageList l -> + List.fold_left (fun accu (n,v) -> + NV.Set.add (NV.create (N.of_string n) (V.of_string v)) accu + ) NV.Set.empty l + | x -> protocol_error x "get_list" + +let get_opam host nv = + client_version host; + let req = GetOPAM (N.to_string (NV.name nv), V.to_string (NV.version nv)) in + match rpc host req with + | OPAM s -> File.OPAM.of_raw (Raw.of_string s) + | x -> protocol_error x "get_opam" + +let get_descr host nv = + client_version host; + let req = GetDescr (N.to_string (NV.name nv), V.to_string (NV.version nv)) in + match rpc host req with + | Descr s -> File.Descr.of_raw (Raw.of_string s) + | x -> protocol_error x "get_descr" + +let get_archive host nv = + client_version host; + let req = GetArchive (N.to_string (NV.name nv), V.to_string (NV.version nv)) in + match rpc host req with + | Archive s -> Raw.of_string s + | x -> protocol_error x "get_archive" + +let new_package host opam descr archive = + client_version host; + let n = File.OPAM.name opam in + let v = File.OPAM.version opam in + let req = NewPackage (N.to_string n, V.to_string v, + Raw.to_string (File.OPAM.to_raw opam), + Raw.to_string (File.Descr.to_raw descr), + Raw.to_string archive) in + match rpc host req with + | Key s -> Key.of_string s + | x -> protocol_error x "new_package" + +let new_version host opam descr archive key = + client_version host; + let n = File.OPAM.name opam in + let v = File.OPAM.version opam in + let req = NewVersion (N.to_string n, V.to_string v, + Raw.to_string (File.OPAM.to_raw opam), + Raw.to_string (File.Descr.to_raw descr), + Raw.to_string archive, + Key.to_string key) in + match rpc host req with + | OK -> () + | x -> protocol_error x "new_package" + + diff --git a/src/repo/server/client.mli b/src/repo/server/client.mli new file mode 100644 index 00000000000..b6bbca50473 --- /dev/null +++ b/src/repo/server/client.mli @@ -0,0 +1,36 @@ +(***********************************************************************) +(* *) +(* Copyright 2012 OCamlPro *) +(* Copyright 2012 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of *) +(* the GNU Public License version 3.0. *) +(* *) +(* TypeRex 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 General Public License for more details. *) +(* *) +(***********************************************************************) + +(** Client-side for OPAM server repositories *) + +open Types + +(** Get the list of available packages *) +val get_list: Unix.inet_addr -> NV.Set.t + +(** Get an OPAM file *) +val get_opam: Unix.inet_addr -> nv -> File.OPAM.t + +(** Get a desrciption file *) +val get_descr: Unix.inet_addr -> nv -> File.Descr.t + +(** Get an archive file *) +val get_archive: Unix.inet_addr -> nv -> raw + +(** Upload a new package *) +val new_package: Unix.inet_addr -> File.OPAM.t -> File.Descr.t -> raw -> Key.t + +(** Upload a new package version *) +val new_version: Unix.inet_addr -> File.OPAM.t -> File.Descr.t -> raw -> Key.t -> unit diff --git a/src/repo/server/daemon.ml b/src/repo/server/daemon.ml new file mode 100644 index 00000000000..f35062f7417 --- /dev/null +++ b/src/repo/server/daemon.ml @@ -0,0 +1,87 @@ +(***********************************************************************) +(* *) +(* Copyright 2012 OCamlPro *) +(* Copyright 2012 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of *) +(* the GNU Public License version 3.0. *) +(* *) +(* TypeRex 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 General Public License for more details. *) +(* *) +(***********************************************************************) + +open Protocol +open Types + +let log fmt = Globals.log "DAEMON" fmt + +type t = { + (* ~/.opam-server/ *) + global: Path.G.t; + + (* ~/.opam-server/opam/ files *) + available: NV.Set.t +} + +let load_state () = + let global = Path.G.create (Dirname.of_string !Globals.root_path) in + let available = Path.G.available global in + { global; available } + +let get_file n v fn = + let t = load_state () in + let nv = NV.create (N.of_string n) (V.of_string v) in + Run.read (Filename.to_string (fn t.global nv)) + +let write_files n v o d a = + let t = load_state () in + let nv = NV.create (N.of_string n) (V.of_string v) in + let write fn c = + Run.write (Filename.to_string (fn t.global nv)) c in + write Path.G.opam o; + write Path.G.descr d; + write Path.G.archive a + +let process_request id = function + | ClientVersion v -> + log "ClientVersion %s" v; + if v = Globals.opam_version then + ServerVersion v + else + Error "Wrong API version" + | GetList -> + log "GetList"; + let t = load_state () in + let l = NV.Set.fold (fun nv l -> + (N.to_string (NV.name nv), V.to_string (NV.version nv)) :: l + ) t.available [] in + PackageList (List.rev l) + | GetOPAM (n,v) -> + log "GetOPAM (%s,%s)" n v; + OPAM (get_file n v Path.G.opam) + | GetDescr (n,v) -> + log "GetDescr (%s,%s)" n v; + Descr (get_file n v Path.G.descr) + | GetArchive (n,v) -> + log "GetArchive (%s,%s)" n v; + Archive (get_file n v Path.G.archive) + | NewPackage (n,v,o,d,a) -> + log "NewPackage (%s,%s,%s,%s,_)" n v o d; + write_files n v o d a; + let key = Key.create () in + Key.write (N.of_string n) key; + Key (Key.to_string key) + | NewVersion (n,v,o,d,a,k) -> + log "NewVersion (%s,%s,%s,%s,_,%s)" n v o d k; + let key = Key.read (N.of_string n) in + if key = (Digest.string k) then ( + write_files n v o d a; + OK + ) else + Error "Wrong key" + +let process (stdin, stdout) fn = + process_server (stdin, stdout) fn diff --git a/src/repo/server/daemon.mli b/src/repo/server/daemon.mli new file mode 100644 index 00000000000..498e55ede91 --- /dev/null +++ b/src/repo/server/daemon.mli @@ -0,0 +1,28 @@ +(***********************************************************************) +(* *) +(* Copyright 2012 OCamlPro *) +(* Copyright 2012 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of *) +(* the GNU Public License version 3.0. *) +(* *) +(* TypeRex 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 General Public License for more details. *) +(* *) +(***********************************************************************) + +(** Server daemon *) + +open Protocol + +(** Main request processing function. [process_request id req] + processes the client request [req] and procuces a server + answer. Eventual log messages are tagged with [id]. *) +val process_request: string -> client_to_server -> server_to_client + +(** Synchronous processing of client requests. [process channels fn] + will read incoming requests on channels, compute the server + response using [fn] and write the result to the channels. *) +val process: (in_channel * out_channel) -> (client_to_server -> server_to_client) -> unit diff --git a/src/repo/server/download.ml b/src/repo/server/download.ml new file mode 100644 index 00000000000..b533a10f596 --- /dev/null +++ b/src/repo/server/download.ml @@ -0,0 +1,4 @@ +(* Download script for OPAM server repositories *) + +let _ = + failwith "TODO" diff --git a/src/repo/server/init.ml b/src/repo/server/init.ml new file mode 100644 index 00000000000..9c6283dc11c --- /dev/null +++ b/src/repo/server/init.ml @@ -0,0 +1,34 @@ +(* Init script for OPAM server repositories *) + +let _ = + if Array.length Sys.argv <> 2 then ( + Printf.eprintf "Usage: opam-git-init "; + exit 1 + ) + +open Types +open Protocol +open Unix + +let local_path = Path.R.of_path (Dirname.of_string (Run.cwd ())) +let remote_address = + try inet_addr_of_string Sys.argv.(1) + with _ -> + (gethostbyname Sys.argv.(1)).h_addr_list.(0) + +let () = + let s = Client.get_list remote_address in + let opams = + NV.Set.fold (fun nv accu -> + (nv, Client.get_opam remote_address nv) :: accu + ) s [] in + let descrs = + NV.Set.fold (fun nv accu -> + (nv, Client.get_descr remote_address nv) :: accu + ) s [] in + List.iter (fun (nv,c) -> + File.OPAM.write (Path.R.opam local_path nv) c + ) opams; + List.iter (fun (nv,c) -> + File.Descr.write (Path.R.descr local_path nv) c + ) descrs diff --git a/src/repo/server/key.ml b/src/repo/server/key.ml new file mode 100644 index 00000000000..679cbe2c2e9 --- /dev/null +++ b/src/repo/server/key.ml @@ -0,0 +1,47 @@ +(***********************************************************************) +(* *) +(* Copyright 2012 OCamlPro *) +(* Copyright 2012 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of *) +(* the GNU Public License version 3.0. *) +(* *) +(* TypeRex 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 General Public License for more details. *) +(* *) +(***********************************************************************) + +type t = string + +let make n = + let s = String.create n in + for i = 0 to n-1 do + s.[i] <- char_of_int (Random.int 255) + done; + s + +let len = 128 + +let create () = + make len + +let to_string x = x + +let of_string x = x + +let (/) = Filename.concat + +let hash_dir () = + !Globals.root_path / "hashes" + +let hash_path n = + hash_dir () / "hashes" / Types.N.to_string n + +let read n = + Run.read (hash_path n) + +let write n c = + Run.mkdir (hash_dir ()); + Run.write (hash_path n) (Digest.string c) diff --git a/src/server/keys.ml b/src/repo/server/key.mli similarity index 72% rename from src/server/keys.ml rename to src/repo/server/key.mli index 1263c857ab4..254e1922bfc 100644 --- a/src/server/keys.ml +++ b/src/repo/server/key.mli @@ -13,41 +13,24 @@ (* *) (***********************************************************************) -module type RANDOM_KEY = sig - type t - val new_key : unit -> t - val to_string : t -> string -end - -module Random_key : RANDOM_KEY = struct - type t = string - - let make n = - String.implode (List.init n (fun _ -> char_of_int (Random.int 255))) - - let len = 128 - - let n = ref (make len) - - let new_key _ = - let k = !n in - let () = n := make len in - k - - let to_string x = x -end - - - - - - - - +open Types +(** Security keys *) +(** Type for keys *) +type t +(** Create a new key *) +val create: unit -> t +(** Convert the key to string *) +val to_string: t -> string +(** Convert a string to a key *) +val of_string: string -> t +(** Read key hash in {i $opam-server/hashes/$name} *) +val read: name -> string +(** Write key hash in {i $opam-server/keys/$name} *) +val write: name -> t -> unit diff --git a/src/repo/server/protocol.ml b/src/repo/server/protocol.ml new file mode 100644 index 00000000000..a8e8ff36a51 --- /dev/null +++ b/src/repo/server/protocol.ml @@ -0,0 +1,244 @@ +(***********************************************************************) +(* *) +(* Copyright 2012 OCamlPro *) +(* Copyright 2012 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of *) +(* the GNU Public License version 3.0. *) +(* *) +(* TypeRex 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 General Public License for more details. *) +(* *) +(***********************************************************************) + +type client_to_server = + | ClientVersion of string (* client version *) + | GetList + | GetOPAM of string (* name *) * string (* version *) + | GetDescr of string (* name *) * string (* version *) + | GetArchive of string (* name *) * string (* version *) + | NewPackage of string (* name *) * string (* version *) + * string (* opam *) * string (* descr *) + * string (* archive *) + | NewVersion of string (* name *) * string (* version *) + * string (* opam *) * string (* descr *) + * string (* archive *) * string (* key *) + +type server_to_client = + | ServerVersion of string (* server version *) + | PackageList of (string (* name *) * string (* version *)) list + | OPAM of string (* opam *) + | Descr of string (* descr *) + | Archive of string (* archive *) + | Key of string (* key *) + | OK + | Error of string (* server error *) + +let default_port = 9999 + +let pack_byte b n = + output_byte b n + +let unpack_byte b = + input_byte b + +(* Adapted from core binary_packing.ml *) +let pack_of_int i = + let v = Int64.of_int i in + let top3 = Int64.to_int (Int64.shift_right v 40) in + let mid3 = Int64.to_int (Int64.shift_right v 16) in + let bot2 = Int64.to_int v in + let buf = String.create 8 in + buf.[0] <- Char.unsafe_chr (0xFF land (top3 lsr 16)); + buf.[1] <- Char.unsafe_chr (0xFF land (top3 lsr 8)); + buf.[2] <- Char.unsafe_chr (0xFF land top3); + buf.[3] <- Char.unsafe_chr (0xFF land (mid3 lsr 16)); + buf.[4] <- Char.unsafe_chr (0xFF land (mid3 lsr 8)); + buf.[5] <- Char.unsafe_chr (0xFF land mid3); + buf.[6] <- Char.unsafe_chr (0xFF land (bot2 lsr 8)); + buf.[7] <- Char.unsafe_chr (0xFF land bot2); + buf + +let pack_int b i = + output_string b (pack_of_int i) + +(* Adapted from core binary_packing.ml *) +let unpack_int b = + let buf = String.create 8 in + really_input b buf 0 8; + let i = Int64.logor + (Int64.logor + (Int64.shift_left + (Int64.of_int (Char.code buf.[0] lsl 16 + lor Char.code buf.[1] lsl 8 + lor Char.code buf.[2])) + 40) + (Int64.shift_left + (Int64.of_int (Char.code buf.[3] lsl 16 + lor Char.code buf.[4] lsl 8 + lor Char.code buf.[5])) + 16)) + (Int64.of_int (Char.code buf.[6] lsl 8 + lor Char.code buf.[7])) in + Int64.to_int i + +let pack_string b s = + pack_int b (String.length s); + output_string b s + +let pack_strings b l = + List.iter (pack_string b) l + +let unpack_string b = + let n = unpack_int b in + let s = String.create n in + really_input b s 0 n; + s + +exception Bad_packet of int + +module Client_to_server = struct + + let int_of_t = function + | ClientVersion _ -> 0 + | GetList -> 1 + | GetOPAM _ -> 2 + | GetDescr _ -> 3 + | GetArchive _ -> 4 + | NewPackage _ -> 5 + | NewVersion _ -> 6 + + let args_of_t = function + | ClientVersion v -> [v] + | GetList -> [] + | GetOPAM (n,v) + | GetDescr (n,v) + | GetArchive (n,v)-> [n;v] + | NewPackage (n,v,o,d,a) -> [n;v;o;d;a] + | NewVersion (n,v,o,d,a,k) -> [n;v;o;d;a;k] + + let to_channel chan t = + pack_byte chan (int_of_t t); + pack_strings chan (args_of_t t) + + let of_channel chan = + match unpack_byte chan with + | 0 -> ClientVersion (unpack_string chan) + | 1 -> GetList + | 2 -> + let n = unpack_string chan in + let v = unpack_string chan in + GetOPAM (n, v) + | 3 -> + let n = unpack_string chan in + let v = unpack_string chan in + GetDescr (n, v) + | 4 -> + let n = unpack_string chan in + let v = unpack_string chan in + GetArchive (n, v) + | 5 -> + let n = unpack_string chan in + let v = unpack_string chan in + let o = unpack_string chan in + let d = unpack_string chan in + let a = unpack_string chan in + NewPackage (n, v, o, d, a) + | 6 -> + let n = unpack_string chan in + let v = unpack_string chan in + let o = unpack_string chan in + let d = unpack_string chan in + let a = unpack_string chan in + let k = unpack_string chan in + NewVersion (n, v, o, d, a, k) + | i -> + raise (Bad_packet i) + +end + +module Server_to_client = struct + + + let int_of_t = function + | ServerVersion _ -> 0 + | PackageList _ -> 1 + | OPAM _ -> 2 + | Descr _ -> 3 + | Archive _ -> 4 + | Key _ -> 5 + | OK -> 6 + | Error _ -> 7 + + let args_of_t = function + | PackageList l -> + let p accu (n,v) = v :: n :: accu in + pack_of_int (List.length l) :: List.fold_left p [] l + | ServerVersion s + | OPAM s + | Descr s + | Archive s + | Key s + | Error s -> [s] + | OK -> [] + + let to_channel chan t = + pack_byte chan (int_of_t t); + pack_strings chan (args_of_t t) + + let of_channel chan = + match unpack_byte chan with + | 0 -> ServerVersion (unpack_string chan) + | 1 -> + let n = unpack_int chan in + let rec aux accu = function + | 0 -> List.rev accu + | k -> + let n = unpack_string chan in + let v = unpack_string chan in + aux ((n, v) :: accu) (k-1) in + PackageList (aux [] n) + | 2 -> OPAM (unpack_string chan) + | 3 -> Descr (unpack_string chan) + | 4 -> Archive (unpack_string chan) + | 5 -> Key (unpack_string chan) + | 6 -> OK + | 7 -> Error (unpack_string chan) + | i -> raise (Bad_packet i) +end + +module Client = struct + + let write stdout t = + Client_to_server.to_channel stdout t; + flush stdout + + let read stdin = + Server_to_client.of_channel stdin + + let process (stdin, stdout) t = + write stdout t; + read stdin + +end + +let process_client = Client.process + +module Server = struct + + let write stdout t = + Server_to_client.to_channel stdout t; + flush stdout + + let read stdin = + Client_to_server.of_channel stdin + + let process (stdin, stdout) fn = + let r = read stdin in + write stdout (fn r) + +end + +let process_server = Server.process diff --git a/src/repo/server/protocol.mli b/src/repo/server/protocol.mli new file mode 100644 index 00000000000..8a8591e749a --- /dev/null +++ b/src/repo/server/protocol.mli @@ -0,0 +1,57 @@ +(***********************************************************************) +(* *) +(* Copyright 2012 OCamlPro *) +(* Copyright 2012 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of *) +(* the GNU Public License version 3.0. *) +(* *) +(* TypeRex 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 General Public License for more details. *) +(* *) +(***********************************************************************) + +(** Message exchanged between the client and the server *) +type client_to_server = + | ClientVersion of string (* client version *) + | GetList + | GetOPAM of string (* name *) * string (* version *) + | GetDescr of string (* name *) * string (* version *) + | GetArchive of string (* name *) * string (* version *) + | NewPackage of string (* name *) * string (* version *) + * string (* opam *) * string (* descr *) + * string (* archive *) + | NewVersion of string (* name *) * string (* version *) + * string (* opam *) * string (* descr *) + * string (* archive *) * string (* key *) + +(** Message exchanged between the server and the client *) +type server_to_client = + | ServerVersion of string (* server version *) + | PackageList of (string (* name *) * string (* version *)) list + | OPAM of string (* opam *) + | Descr of string (* descr *) + | Archive of string (* archive *) + | Key of string (* key *) + | OK + | Error of string (* server error *) + +(** Default port *) +val default_port: int + +(** Client synchronous processing: + - send a message to the server + - wait for the server response *) +val process_client: + (in_channel * out_channel) -> + client_to_server -> server_to_client + +(** Server synchronous processing: + - wait for a client message to come + - answer to the client *) +val process_server: + (in_channel * out_channel) -> + (client_to_server -> server_to_client) -> unit + diff --git a/src/server/opam_server.ml b/src/repo/server/server.ml similarity index 75% rename from src/server/opam_server.ml rename to src/repo/server/server.ml index 0e62558b9c1..8ec9dc621d5 100644 --- a/src/server/opam_server.ml +++ b/src/repo/server/server.ml @@ -16,8 +16,8 @@ open Sys open Unix open File -open Server -open Protocol + +let default_root_path = Filename.concat Globals.home ".opam-server" let usage = Printf.sprintf "%s -p [--debug]" Sys.argv.(0) @@ -36,44 +36,43 @@ This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" Sys.argv.(0) Globals.version -let port = ref Globals.default_port +let port = ref Protocol.default_port let host = ref (inet_addr_of_string "127.0.0.1") let set_host h = try host := inet_addr_of_string h with exn -> raise (Arg.Bad ("invalid [-i] IP: " ^ h)) let _ = - Globals.root_path := Globals.default_opam_server_path + Globals.root_path := default_root_path let args = Arg.align [ "-p" , Arg.Set_int port , " Set up the listening port (default: 9999)"; - "-i" , Arg.String set_host , " Set up the listening IP address (default: "^(Unix.string_of_inet_addr !host)^")"; + "-i" , Arg.String set_host , " Set up the listening IP address (default: " + ^(Unix.string_of_inet_addr !host)^")"; "--debug" , Arg.Set Globals.debug, " Print more debug messages"; "--version", Arg.Unit version , " Display version information"; "--root" , Arg.Set_string Globals.root_path, - (Printf.sprintf " Change root path (default is %s)" Globals.default_opam_path) + (Printf.sprintf " Change root path (default is %s)" default_root_path) ] let _ = Arg.parse args (fun s -> Printf.eprintf "%s: Unknown\n" s) usage -let server fn = +(* Each time a client request is coming, a process is forked to run + this function. Hence, we really need to init the random generator + with a new seed, otherwise, all forked processes will have the same + id (the first random number with the given seed). *) +let process_connection stdin stdout = + Random.self_init(); + let id = string_of_int (Random.int 1024) in + Daemon.process (stdin, stdout) (Daemon.process_request id) + +let run_server () = let addr = ADDR_INET (!host, !port) in - let state = server_init !Globals.root_path in if !Globals.debug then Globals.msg "Root path is %s.\nListening on port %d (%s) ...\n%!" !Globals.root_path !port (string_of_inet_addr !host); - - establish_server (fn state) addr - -let log id fmt = - Globals.log (Printf.sprintf "REQUEST [%d]" id) fmt - - -let fn state = - Random.self_init(); - let id = string_of_int (Random.int 1024) in - Protocol.add (Daemon.process state id) + establish_server process_connection addr let _ = - handle_unix_error server fn + handle_unix_error run_server () diff --git a/src/repo/server/update.ml b/src/repo/server/update.ml new file mode 100644 index 00000000000..7f9c3a32ed1 --- /dev/null +++ b/src/repo/server/update.ml @@ -0,0 +1,4 @@ +(* Update script for OPAM server repositories *) + +let _ = + failwith "TODO" diff --git a/src/repo/server/upload.ml b/src/repo/server/upload.ml new file mode 100644 index 00000000000..680dc83dd10 --- /dev/null +++ b/src/repo/server/upload.ml @@ -0,0 +1,45 @@ +(* Upload script for OPAM server repositories *) + +let _ = + if Array.length Sys.argv <> 2 then ( + Printf.eprintf "Usage: opam-git-init "; + exit 1 + ) + +open Types +open Protocol +open Unix + +let local_path = Path.R.of_path (Dirname.of_string (Run.cwd ())) +let remote_address = + try inet_addr_of_string Sys.argv.(1) + with _ -> + (gethostbyname Sys.argv.(1)).h_addr_list.(0) + +let () = + let s = Client.get_list remote_address in + let opams = + NV.Set.fold (fun nv accu -> + (nv, Client.get_opam remote_address nv) :: accu + ) s [] in + let descrs = + NV.Set.fold (fun nv accu -> + (nv, Client.get_descr remote_address nv) :: accu + ) s [] in + let available = Path.R.available local_path in + let updates = ref NV.Set.empty in + List.iter (fun (nv,c) -> + (* filter out already existing packages *) + if not (NV.Set.mem nv available) then ( + updates := NV.Set.add nv !updates; + File.OPAM.write (Path.R.opam local_path nv) c + ) + ) opams; + List.iter (fun (nv,c) -> + (* filter out already existing packages *) + if not (NV.Set.mem nv available) then ( + updates := NV.Set.add nv !updates; + File.Descr.write (Path.R.descr local_path nv) c + ) + ) descrs; + File.Updated.write (Path.R.updated local_path) !updates diff --git a/src/server/protocol.ml b/src/server/protocol.ml deleted file mode 100644 index 974a0b3d7d0..00000000000 --- a/src/server/protocol.ml +++ /dev/null @@ -1,69 +0,0 @@ -(***********************************************************************) -(* *) -(* Copyright 2012 OCamlPro *) -(* Copyright 2012 INRIA *) -(* *) -(* All rights reserved. This file is distributed under the terms of *) -(* the GNU Public License version 3.0. *) -(* *) -(* TypeRex 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 General Public License for more details. *) -(* *) -(***********************************************************************) - - -module String = struct - type t = string - let mk x = x - let to_sting x = x - let write x oc = failwith "TODO" - let read ic = failwith "TODO" -end - -module Name : S = String -module Version : S = String -module Key : S = String -module Descr : S = String -module Archive : S = String - -type client_to_server = - | C2S_apidVersion of int - | C2S_getList - | C2S_getSpec of name_version - | C2S_getArchive of name_version - | C2S_newArchive of name_version * raw_binary * raw_binary - | C2S_updateArchive of name_version * raw_binary * raw_binary * security_key - -type server_to_client = - | S2C_apiVersion of int - | S2C_getList of name_version list - | S2C_getSpec of raw_binary - | S2C_getArchive of raw_binary option - | S2C_newArchive of security_key - | S2C_updateArchive - - | S2C_error of string (* server error *) - -type www = client_to_server -> server_to_client - -module type PROTOCOL = sig - val find : in_channel * out_channel -> www - val add : www -> in_channel -> out_channel -> unit -end - -module Protocol : PROTOCOL = struct - - let output_v stdout m = - output_value stdout m ; - flush stdout - - let find (stdin, stdout) m = - output_v stdout m; - (input_value stdin : server_to_client) - - let add f stdin stdout = - output_v stdout (f (input_value stdin : client_to_server)) - -end diff --git a/src/server/server.ml b/src/server/server.ml deleted file mode 100644 index 21c67ed8740..00000000000 --- a/src/server/server.ml +++ /dev/null @@ -1,338 +0,0 @@ -(***********************************************************************) -(* *) -(* Copyright 2012 OCamlPro *) -(* Copyright 2012 INRIA *) -(* *) -(* All rights reserved. This file is distributed under the terms of *) -(* the GNU Public License version 3.0. *) -(* *) -(* TypeRex 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 General Public License for more details. *) -(* *) -(***********************************************************************) - - -open Namespace -open Path -open File -open Unix -open Uri -open Protocol - -module type SERVER = -sig - type t - - (** [apiVersion t client] returns whether the API version for the - server. If the server cannot handle the [client] version, it - raises an exception. If the client cannot handle the server - version, it should take the appropriate decisions.*) - val apiVersion : t -> int -> int - - (** Return the list of the available versions for all packages. *) - val getList : t -> name_version list - - (** Returns the spec file for the corresponding package version. *) - val getSpec : t -> name_version -> raw_binary - - (** Returns the corresponding package archive. [None] means that the - server does not mirror the associated archive. *) - val getArchive : t -> name_version -> raw_binary option - - (** Process a first upload: it contains a spec file and a possible - corresponding archive. - The function returns the secret key associated to - all the packages having the same name *) - val newArchive : t -> name_version -> raw_binary -> raw_binary -> security_key - - (** Same as [newArchive] but for subsequent upload (using the secret key) *) - val updateArchive : t -> name_version -> raw_binary -> raw_binary -> security_key -> unit - -end - -type server_state = - { home : Path.t (* ~/.opam-server *) - ; opam_version : int } - -let server_init home = - { home = Path.init home - ; opam_version = Globals.api_version } - -exception Server_error of string - -let error fmt = - Printf.kprintf (fun s -> raise (Server_error s)) fmt - -(* Used by the server to process things *) -module Server : SERVER with type t = server_state = struct - - type t = server_state - - (* Return all the .opam files *) - let read_index home = - List.fold_left - (fun map nv -> - let file = File.Spec.find_err (Path.index home (Some nv)) in - NV_map.add nv file map) - NV_map.empty - (Path.index_list home) - - let string_of_nv (n, v) = Namespace.string_of_nv n v - - let apiVersion t client = - if client <> Globals.api_version then - error "incompatible API version. client=%d server=%d" - client Globals.api_version - else - Globals.api_version - - let getList t = - Path.index_list t.home - - let getSpec t n_v = - let index = read_index t.home in - try Raw_binary (File.Spec.to_string (File.Spec.filter_external_ressources (NV_map.find n_v index))) - with Not_found -> error "%S not found" (string_of_nv n_v) - - let getArchive t n_v = - let spec = File.Spec.find_err (Path.index t.home (Some n_v)) in - - (* look at an archive in the same path - having the right name *) - let p = Path.archives_targz t.home (Some n_v) in - match Path.find_binary p with - | Path.File s -> Some s - | _ -> - match File.Spec.sources spec with - | [] -> error "Cannot find %S in local repository and no external url provided" (string_of_nv n_v) - | urls -> - (* if some urls are provided, check for external urls *) - let external_urls = - List.fold_left (fun accu -> function External (_,s), o -> (s, o) :: accu | _ -> accu) [] urls in - if external_urls <> [] then - (* clients can fetch archives *) - None - else - error "No archive associated to package %S" (string_of_nv n_v) - - let storeArchive t n_v opam archive = - let opam_file = Path.index t.home (Some n_v) in - let archive_file = Path.archives_targz t.home (Some n_v) in - begin match opam with - | Raw_binary s -> File.Spec.add opam_file (File.Spec.parse s) - end; - Path.add archive_file (Path.File (Binary archive)) - - let processArchive o_key t (name, v) spec archive = - let hashes = Path.hashes t.home name in - let key = - match o_key, File.Security_key.find hashes with - | None, None -> - let key = Random_key.new_key () in - File.Security_key.add hashes key; - key - | Some k0, Some k1 -> - if k0 = k1 then - k0 - else - error - "secret keys differ for package %S" - (Namespace.string_of_name name) - | Some _, None -> - error - "no previous keys stored for package %S" - (Namespace.string_of_name name) - | None, Some _ -> - error - "no key given and the package %S has already been uploaded" - (Namespace.string_of_name name) in - storeArchive t (name, v) spec archive; - key - - let newArchive = processArchive None - - let updateArchive t n_v spec archive key = - let (_ : security_key) = processArchive (Some key) t n_v spec archive in - () -end - - -exception Connection_error of string -let connection_error fmt = - Printf.kprintf (fun str -> - raise (Connection_error str) - ) fmt - -(* Used by the client to communicate with the server *) -module RemoteServer : SERVER with type t = url = struct - - open Protocol - - type t = url - - (* untyped message exchange *) - let send url m = - if url.uri = Some Git then - connection_error "%s is not a valid OPAM server" url.hostname; - let host = - try (gethostbyname url.hostname).h_addr_list.(0) - with Not_found -> - connection_error "%s is not a valid host address" url.hostname in - let port = match url.port with - | None -> Globals.error_and_exit "No port provided" - | Some p -> p in - let addr = ADDR_INET (host, port) in - try - Protocol.find (open_connection addr) m - with _ -> - connection_error - "The server (%s) is unreachable. Please check your network configuration." - (string_of_url url) - - let dyn_error str = - Globals.error_and_exit "Protocol error: %S" str - - let error msg = - Globals.error_and_exit "SERVER: %s" msg - - let apiVersion t s = - match send t (C2S_apidVersion s) with - | S2C_apiVersion nl -> nl - | S2C_error s -> error s - | _ -> dyn_error "apiVersion" - - let check_version t = - let server_version = apiVersion t Globals.api_version in - if server_version <> Globals.api_version then - Globals.error_and_exit "API version error. client=%d server=%d" - Globals.api_version server_version - - let getList t = - check_version t; - match send t C2S_getList with - | S2C_getList nl -> nl - | S2C_error s -> error s - | _ -> dyn_error "getList" - - let getSpec t name_version = - check_version t; - match send t (C2S_getSpec name_version) with - | S2C_getSpec o -> o - | S2C_error s -> error s - | _ -> dyn_error "getOpam" - - let getArchive t nv = - check_version t; - match send t (C2S_getArchive nv) with - | S2C_getArchive a -> a - | S2C_error s -> error s - | _ -> dyn_error "getArchive" - - let newArchive t nv opam archive = - check_version t; - match send t (C2S_newArchive (nv, opam, archive)) with - | S2C_newArchive o -> o - | S2C_error s -> error s - | _ -> dyn_error "newArchive" - - let updateArchive t nv opam archive k = - check_version t; - match send t (C2S_updateArchive (nv, opam, archive, k)) with - | S2C_updateArchive -> () - | S2C_error s -> error s - | _ -> dyn_error "updateArchive" - -end - -module Daemon = struct - - open Protocol - - let log id fmt = - Globals.log (Printf.sprintf "[%s]" id) fmt - - let protect f x = - try f x - with e -> - let msg = Printexc.to_string e in - Globals.error "%s" msg; - S2C_error msg - - let file = Filename.temp_file "lock" "dat" - - let flock id = - let l = ref 0 in - let rec loop () = - if Sys.file_exists id && !l < 5 then begin - log id "Filesytem busy. Waiting 1s (%d)" !l; - sleep 1; - loop () - end else if Sys.file_exists id then begin - log id "Too many attemps. Cancelling ..."; - error "Too many attemps. Cancelling ..."; - end else begin - let oc = open_out file in - output_string oc id; - flush oc; - close_out oc; - log id "lock %s" id; - end in - loop () - - let funlock id = - if Sys.file_exists file then - let ic = open_in file in - let s = input_line ic in - if s = id then begin - log id "unlock %s" id; - Unix.unlink file; - end - - let with_flock id f = - try - flock id; - let r = f () in - funlock id; - r - with e -> - funlock id; - raise e - - let process t id request = - log id "Processing an incoming request"; - match request with - - | C2S_apidVersion s -> - log id "acceptedVersion"; - protect (fun () -> S2C_apiVersion (Server.apiVersion t s)) () - - | C2S_getList -> - log id "getList"; - protect (fun () -> S2C_getList (Server.getList t)) () - - | C2S_getSpec nv -> - log id "getSpec"; - protect (fun () -> S2C_getSpec (Server.getSpec t nv)) () - - | C2S_getArchive nv -> - log id "getArchive"; - protect (fun () -> S2C_getArchive (Server.getArchive t nv)) () - - | C2S_newArchive (nv, opam, archive) -> - log id "newArchive"; - protect - (with_flock id) - (fun () -> S2C_newArchive (Server.newArchive t nv opam archive)) - - | C2S_updateArchive (nv, opam, archive, k) -> - log id "updateArchive [%s]" - (Digest.to_hex (Digest.string (match k with Random s -> s))); - protect - (with_flock id) - (fun () -> - Server.updateArchive t nv opam archive k; - S2C_updateArchive) - -end