From e5e6b77984daf761b8049f44ebae936c250105a3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 9 Dec 2016 20:40:24 +0100 Subject: [PATCH] Cleanups and avoid redownloading. --- ethcore/src/client/client.rs | 3 + ethcore/src/client/updater.rs | 117 ++++++++++++++++++++++------------ 2 files changed, 81 insertions(+), 39 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 6d65f1cee..f068b8d99 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -678,6 +678,9 @@ impl Client { self.check_snooze(); if let Some(ref mut updater) = *self.updater.lock() { updater.tick(); + if updater.installed.is_some() { + info!("Client should restart now."); + } } } diff --git a/ethcore/src/client/updater.rs b/ethcore/src/client/updater.rs index fdbc9463a..75768132c 100644 --- a/ethcore/src/client/updater.rs +++ b/ethcore/src/client/updater.rs @@ -15,8 +15,8 @@ // along with Parity. If not, see . use std::sync::{Weak}; -use std::{fs, env}; -use std::path::PathBuf; +use std::{io, os, fs, env}; +use std::path::{Path, PathBuf}; use util::misc::{VersionInfo, ReleaseTrack/*, platform*/}; use util::{Address, H160, H256, FixedHash, Mutex}; use client::operations::Operations; @@ -54,7 +54,8 @@ pub struct Updater { // This does change pub latest: Option, pub ready: Option, - + // If Some, client should restart itself. + pub installed: Option, } const CLIENT_ID: &'static str = "parity"; @@ -75,6 +76,7 @@ impl Updater { this_fork: None, latest: None, ready: None, + installed: None, }; u.this_fork = u.operations.release(CLIENT_ID, &u.this.hash.into()).ok() @@ -107,12 +109,40 @@ impl Updater { self.ready.clone() } + #[cfg(windows)] + fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + os::windows::fs::symlink_file(src, dst) + } + + #[cfg(not(windows))] + fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { + os::unix::fs::symlink(src, dst) + } + /// Actually upgrades the client. Assumes that the binary has been downloaded. /// @returns `true` on success. pub fn execute_upgrade(&mut self) -> bool { - // TODO: link ~/.parity-updates/parity to self.ready - // TODO: restart parity. - unimplemented!() + (|| -> Result { + if let Some(r) = self.ready.take() { + let p = Self::update_file_path(&r.version); + let n = Self::updates_latest(); + let _ = fs::remove_file(&n); + match Self::symlink(p, n) { + Ok(_) => { + info!("Completed upgrade to {}", &r.version); + self.installed = Some(r); + Ok(true) + } + Err(e) => { + self.ready = Some(r); + Err(format!("Unable to create soft-link for update {:?}", e)) + } + } + } else { + warn!("Execute upgrade called when no upgrade ready."); + Ok(false) + } + })().unwrap_or_else(|e| { warn!("{}", e); false }) } /// Returns true iff the current version is capable of forming consensus. @@ -165,37 +195,40 @@ impl Updater { }) } - fn fetch_done(&mut self, _r: Result) { - let fetched = self.fetching.lock().take().unwrap(); - match _r { - Ok(b) => { - info!("Fetched latest version ({}) OK to {}", fetched.version, b.display()); - let mut dest = PathBuf::from(env::home_dir().unwrap().to_str().expect("env filesystem paths really should be valid; qed")); - dest.push(".parity-updates"); - match fs::create_dir_all(&dest) { - Ok(_) => { - dest.push(format!("parity-{}-{:?}", fetched.version, fetched.version.hash)); - match fs::copy(&b, &dest) { - Ok(_) => { - info!("Copied file to {}", dest.display()); - let auto = match self.update_policy.filter { - UpdateFilter::All => true, - UpdateFilter::Critical if fetched.is_critical /* TODO: or is on a bad fork */ => true, - _ => false, - }; - self.ready = Some(fetched); - if auto { - self.execute_upgrade(); - } - }, - Err(e) => warn!("Unable to copy update: {:?}", e), - } - }, - Err(e) => warn!("Unable to create updates path: {:?}", e), - } - }, - Err(e) => warn!("Unable to fetch update ({}): {:?}", fetched.version, e), - } + fn update_file_path(v: &VersionInfo) -> PathBuf { + let mut dest = PathBuf::from(env::home_dir().unwrap().to_str().expect("env filesystem paths really should be valid; qed")); + dest.push(".parity-updates"); + dest.push(format!("parity-{}.{}.{}-{:?}", v.version.major, v.version.minor, v.version.patch, v.hash)); + dest + } + + fn updates_latest() -> PathBuf { + let mut dest = PathBuf::from(env::home_dir().unwrap().to_str().expect("env filesystem paths really should be valid; qed")); + dest.push(".parity-updates"); + dest.push("parity"); + dest + } + + fn fetch_done(&mut self, result: Result) { + (|| -> Result<(), String> { + let fetched = self.fetching.lock().take().unwrap(); + let b = result.map_err(|e| format!("Unable to fetch update ({}): {:?}", fetched.version, e))?; + info!("Fetched latest version ({}) OK to {}", fetched.version, b.display()); + let dest = Self::update_file_path(&fetched.version); + fs::create_dir_all(dest.parent().expect("at least one thing pushed; qed")).map_err(|e| format!("Unable to create updates path: {:?}", e))?; + fs::copy(&b, &dest).map_err(|e| format!("Unable to copy update: {:?}", e))?; + info!("Copied file to {}", dest.display()); + let auto = match self.update_policy.filter { + UpdateFilter::All => true, + UpdateFilter::Critical if fetched.is_critical /* TODO: or is on a bad fork */ => true, + _ => false, + }; + self.ready = Some(fetched); + if auto { + self.execute_upgrade(); + } + Ok(()) + })().unwrap_or_else(|e| warn!("{}", e)); } pub fn tick(&mut self) { @@ -215,13 +248,19 @@ impl Updater { "unreleased".into() } ); - if self.update_policy.enable_downloading && latest.track.version.hash != self.version_info().hash && self.ready.as_ref().map_or(true, |t| *t != latest.track) { + if self.update_policy.enable_downloading && latest.track.version.hash != self.version_info().hash && self.installed.as_ref().or(self.ready.as_ref()).map_or(true, |t| *t != latest.track) { if let Some(b) = latest.track.binary { let mut fetching = self.fetching.lock(); if fetching.is_none() { info!("Attempting to get parity binary {}", b); let c = self.client.clone(); - let f = move |r: Result| if let Some(c) = c.upgrade() { c.updater().as_mut().expect("updater exists; updater only owned by client; qed").fetch_done(r); }; + let f = move |r: Result| { + if let Some(client) = c.upgrade() { + if let Some(updater) = client.updater().as_mut() { + updater.fetch_done(r); + } + } + }; if let Some(fetch) = self.fetch.clone().upgrade() { fetch.fetch(b, Box::new(f)).ok(); *fetching = Some(latest.track.clone());