chiark / gitweb /
bundles: Implement DownloadBundle
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 3 May 2021 11:59:28 +0000 (12:59 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 3 May 2021 11:59:28 +0000 (12:59 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
daemon/cmdlistener.rs
src/bin/otter.rs
src/commands.rs

index b85f7ff4a42ddf224c235c4b2e08fd2ccfce28ba..9fdb5166d2259370273408618494eeb59235a594 100644 (file)
@@ -80,7 +80,7 @@ fn execute_and_respond<R,W>(cs: &mut CommandStreamData, cmd: MgmtCommand,
                             -> Result<(), CSE>
   where R: Read, W: Write
 {
-  let /*mut*/ bulk_download: Option<Box<dyn Read>> = None;
+  let mut bulk_download: Option<Box<dyn Read>> = None;
 
   let mut cmd_s = log_enabled!(log::Level::Info)
     .as_some_from(|| format!("{:?}", &cmd))
@@ -251,6 +251,16 @@ fn execute_and_respond<R,W>(cs: &mut CommandStreamData, cmd: MgmtCommand,
       let bundles = bundles.list();
       MR::Bundles { bundles }
     }
+    MC::DownloadBundle { game, id } => {
+      let ag = AccountsGuard::lock();
+      let gref = Instance::lookup_by_name_unauth(&game)?;
+      let mut igu = gref.lock()?;
+      let (ig, _) = cs.check_acl(&ag, &mut igu, PCH::Instance,
+                                     TP_ACCESS_BUNDLES)?;
+      let f = id.open(&ig.name)?.ok_or_else(|| ME::BundleNotFound)?;
+      bulk_download = Some(Box::new(f));
+      Fine
+    }
 
     MC::ListGames { all } => {
       let ag = AccountsGuard::lock();
index 780b6406e832fa68d82e2bfcea546116473498e0..a1e012a65bfc4443bf8ed233b1f86ed1209fc555 100644 (file)
@@ -1402,6 +1402,75 @@ mod list_bundles {
   )}
 }
 
+//---------- download-bundle ----------
+
+mod download_bundle {
+  use super::*;
+
+  #[derive(Default,Debug)]
+  struct Args {
+    table_name: String,
+    index: bundles::Index,
+    output: Option<PathBuf>,
+  }
+
+  fn subargs(sa: &mut Args) -> ArgumentParser {
+    use argparse::*;
+    let mut ap = ArgumentParser::new();
+    ap.refer(&mut sa.table_name).required()
+      .add_argument("TABLE-NAME",Store,"table name");
+    ap.refer(&mut sa.index).required()
+      .add_argument("INDEX",Store,"bundle number");
+    ap.refer(&mut sa.output).metavar("OUTPUT")
+      .add_option(&["-o","--output"],StoreOption,
+                  "write output to OUTPUT (rather than NNNNN.zip");
+    ap
+  }
+
+  #[throws(AE)]
+  fn call(_sc: &Subcommand, ma: MainOpts, args: Vec<String>) {
+    let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
+    let instance_name = ma.instance_name(&args.table_name);
+    let mut chan = access_game(&ma, &args.table_name)?;
+    let kind = bundles::Kind::only();
+    let id = bundles::Id { kind, index: args.index };
+    let path = args.output.unwrap_or_else(|| id.to_string().into());
+
+    let (f, path_tmp): (Box<dyn Write>, _) =
+      if path.as_os_str().as_bytes() == b"-"
+    {
+      (Box::new(io::stdout()), None)
+    } else {
+      let tmp = {
+        let mut w = path.as_os_str().to_owned();
+        w.push(".tmp");
+        PathBuf::from(w)
+      };
+      let f = fs::File::create(&tmp)
+        .with_context(|| tmp.to_debug()).context("create temporary")?;
+      (Box::new(f), Some((path, tmp)))
+    };
+    let mut f = BufWriter::new(f);
+    let cmd = MC::DownloadBundle {
+      game: instance_name.clone(),
+      id,
+    };
+    chan.cmd_withbulk(&cmd, &mut io::empty(), &mut f)
+      .context("download bundle")?;
+    f.flush().context("flush bundle file")?;
+    if let Some((path, tmp)) = path_tmp {
+      fs::rename(&tmp,&path)
+        .with_context(|| path.to_debug()).context("rename after download")?;
+    }
+  }
+
+  inventory::submit!{Subcommand(
+    "download-bundle",
+    "download bundle",
+    call,
+  )}
+}
+
 //---------- list-accounts ----------
 
 mod list_accounts {
index 15218613043c60e78428e146931eb2c31f064165..0d221c4afcbc57b8948e01a71fe4cb2eefbbbd68 100644 (file)
@@ -49,12 +49,10 @@ pub enum MgmtCommand {
   ListBundles {
     game: InstanceName,
   },
-  /*
   DownloadBundle {
     game: InstanceName,
     id: bundles::Id,
   },
-  */
 
   LibraryListByGlob {
     glob: shapelib::ItemSpec,
@@ -233,6 +231,7 @@ pub enum MgmtError {
   UploadCorrupted,
   TooManyBundles,
   BadBundle(String),
+  BundleNotFound,
 }
 impl Display for MgmtError {
   #[throws(fmt::Error)]