| 
									
										
										
										
											2019-11-22 21:58:26 -07:00
										 |  |  |  | use crate::{
 | 
					
						
							|  |  |  |  |     config::{Config, ExplicitRelease},
 | 
					
						
							|  |  |  |  |     stop_process::stop_process,
 | 
					
						
							|  |  |  |  |     update_manifest::{SignedUpdateManifest, UpdateManifest},
 | 
					
						
							|  |  |  |  | };
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | use chrono::{Local, TimeZone};
 | 
					
						
							|  |  |  |  | use console::{style, Emoji};
 | 
					
						
							|  |  |  |  | use indicatif::{ProgressBar, ProgressStyle};
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  | use serde_derive::Deserialize;
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | use solana_client::rpc_client::RpcClient;
 | 
					
						
							| 
									
										
										
										
											2019-12-24 19:01:21 -07:00
										 |  |  |  | use solana_config_program::{config_instruction, get_config_data, ConfigState};
 | 
					
						
							| 
									
										
										
										
											2019-11-22 21:58:26 -07:00
										 |  |  |  | use solana_sdk::{
 | 
					
						
							|  |  |  |  |     hash::{Hash, Hasher},
 | 
					
						
							|  |  |  |  |     message::Message,
 | 
					
						
							|  |  |  |  |     pubkey::Pubkey,
 | 
					
						
							| 
									
										
										
										
											2020-02-20 14:28:55 -07:00
										 |  |  |  |     signature::{read_keypair_file, Keypair, Signable, Signer},
 | 
					
						
							| 
									
										
										
										
											2019-11-22 21:58:26 -07:00
										 |  |  |  |     transaction::Transaction,
 | 
					
						
							|  |  |  |  | };
 | 
					
						
							|  |  |  |  | use std::{
 | 
					
						
							|  |  |  |  |     fs::{self, File},
 | 
					
						
							|  |  |  |  |     io::{self, BufReader, Read},
 | 
					
						
							|  |  |  |  |     path::{Path, PathBuf},
 | 
					
						
							|  |  |  |  |     sync::mpsc,
 | 
					
						
							|  |  |  |  |     time::{Duration, Instant, SystemTime},
 | 
					
						
							|  |  |  |  | };
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | use tempdir::TempDir;
 | 
					
						
							|  |  |  |  | use url::Url;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  | #[derive(Deserialize, Debug)]
 | 
					
						
							|  |  |  |  | pub struct ReleaseVersion {
 | 
					
						
							|  |  |  |  |     pub target: String,
 | 
					
						
							|  |  |  |  |     pub commit: String,
 | 
					
						
							|  |  |  |  |     channel: String,
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | static TRUCK: Emoji = Emoji("🚚 ", "");
 | 
					
						
							|  |  |  |  | static LOOKING_GLASS: Emoji = Emoji("🔍 ", "");
 | 
					
						
							|  |  |  |  | static BULLET: Emoji = Emoji("• ", "* ");
 | 
					
						
							|  |  |  |  | static SPARKLE: Emoji = Emoji("✨ ", "");
 | 
					
						
							|  |  |  |  | static PACKAGE: Emoji = Emoji("📦 ", "");
 | 
					
						
							| 
									
										
										
										
											2019-09-19 12:03:47 -07:00
										 |  |  |  | static INFORMATION: Emoji = Emoji("ℹ️  ", "");
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | /// Creates a new process bar for processing that will take an unknown amount of time
 | 
					
						
							|  |  |  |  | fn new_spinner_progress_bar() -> ProgressBar {
 | 
					
						
							|  |  |  |  |     let progress_bar = ProgressBar::new(42);
 | 
					
						
							|  |  |  |  |     progress_bar
 | 
					
						
							|  |  |  |  |         .set_style(ProgressStyle::default_spinner().template("{spinner:.green} {wide_msg}"));
 | 
					
						
							|  |  |  |  |     progress_bar.enable_steady_tick(100);
 | 
					
						
							|  |  |  |  |     progress_bar
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /// Pretty print a "name value"
 | 
					
						
							|  |  |  |  | fn println_name_value(name: &str, value: &str) {
 | 
					
						
							|  |  |  |  |     println!("{} {}", style(name).bold(), value);
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  | /// Downloads a file at `url` to a temporary location.  If `expected_sha256` is
 | 
					
						
							|  |  |  |  | /// Some(_), produce an error if the SHA256 of the file contents doesn't match.
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | ///
 | 
					
						
							|  |  |  |  | /// Returns a tuple consisting of:
 | 
					
						
							|  |  |  |  | /// * TempDir - drop this value to clean up the temporary location
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  | /// * PathBuf - path to the downloaded file (within `TempDir`)
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | /// * String  - SHA256 of the release
 | 
					
						
							|  |  |  |  | ///
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  | fn download_to_temp(
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     url: &str,
 | 
					
						
							| 
									
										
										
										
											2019-11-22 21:58:26 -07:00
										 |  |  |  |     expected_sha256: Option<&Hash>,
 | 
					
						
							|  |  |  |  | ) -> Result<(TempDir, PathBuf, Hash), Box<dyn std::error::Error>> {
 | 
					
						
							|  |  |  |  |     fn sha256_file_digest<P: AsRef<Path>>(path: P) -> Result<Hash, Box<dyn std::error::Error>> {
 | 
					
						
							| 
									
										
										
										
											2019-07-22 23:11:40 -07:00
										 |  |  |  |         let input = File::open(path)?;
 | 
					
						
							|  |  |  |  |         let mut reader = BufReader::new(input);
 | 
					
						
							| 
									
										
										
										
											2019-11-22 21:58:26 -07:00
										 |  |  |  |         let mut hasher = Hasher::default();
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 23:11:40 -07:00
										 |  |  |  |         let mut buffer = [0; 1024];
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |         loop {
 | 
					
						
							|  |  |  |  |             let count = reader.read(&mut buffer)?;
 | 
					
						
							|  |  |  |  |             if count == 0 {
 | 
					
						
							|  |  |  |  |                 break;
 | 
					
						
							|  |  |  |  |             }
 | 
					
						
							| 
									
										
										
										
											2019-11-22 21:58:26 -07:00
										 |  |  |  |             hasher.hash(&buffer[..count]);
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2019-11-22 21:58:26 -07:00
										 |  |  |  |         Ok(hasher.result())
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let url = Url::parse(url).map_err(|err| format!("Unable to parse {}: {}", url, err))?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let temp_dir = TempDir::new(clap::crate_name!())?;
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  |     let temp_file = temp_dir.path().join("download");
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-08 13:31:43 -07:00
										 |  |  |  |     let client = reqwest::blocking::Client::new();
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     let progress_bar = new_spinner_progress_bar();
 | 
					
						
							|  |  |  |  |     progress_bar.set_message(&format!("{}Downloading...", TRUCK));
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-24 14:10:59 -06:00
										 |  |  |  |     let response = client.get(url.as_str()).send()?;
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     let download_size = {
 | 
					
						
							|  |  |  |  |         response
 | 
					
						
							| 
									
										
										
										
											2019-09-24 14:10:59 -06:00
										 |  |  |  |             .headers()
 | 
					
						
							|  |  |  |  |             .get(reqwest::header::CONTENT_LENGTH)
 | 
					
						
							|  |  |  |  |             .and_then(|content_length| content_length.to_str().ok())
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |             .and_then(|content_length| content_length.parse().ok())
 | 
					
						
							|  |  |  |  |             .unwrap_or(0)
 | 
					
						
							|  |  |  |  |     };
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     progress_bar.set_length(download_size);
 | 
					
						
							|  |  |  |  |     progress_bar.set_style(
 | 
					
						
							|  |  |  |  |         ProgressStyle::default_bar()
 | 
					
						
							|  |  |  |  |             .template(&format!(
 | 
					
						
							|  |  |  |  |                 "{}{}{}",
 | 
					
						
							|  |  |  |  |                 "{spinner:.green} ",
 | 
					
						
							|  |  |  |  |                 TRUCK,
 | 
					
						
							|  |  |  |  |                 "Downloading [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})"
 | 
					
						
							|  |  |  |  |             ))
 | 
					
						
							|  |  |  |  |             .progress_chars("=> "),
 | 
					
						
							|  |  |  |  |     );
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     struct DownloadProgress<R> {
 | 
					
						
							|  |  |  |  |         progress_bar: ProgressBar,
 | 
					
						
							|  |  |  |  |         response: R,
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     impl<R: Read> Read for DownloadProgress<R> {
 | 
					
						
							|  |  |  |  |         fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
 | 
					
						
							|  |  |  |  |             self.response.read(buf).map(|n| {
 | 
					
						
							|  |  |  |  |                 self.progress_bar.inc(n as u64);
 | 
					
						
							|  |  |  |  |                 n
 | 
					
						
							|  |  |  |  |             })
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let mut source = DownloadProgress {
 | 
					
						
							|  |  |  |  |         progress_bar,
 | 
					
						
							| 
									
										
										
										
											2019-09-24 14:10:59 -06:00
										 |  |  |  |         response,
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     };
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let mut file = File::create(&temp_file)?;
 | 
					
						
							|  |  |  |  |     std::io::copy(&mut source, &mut file)?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let temp_file_sha256 = sha256_file_digest(&temp_file)
 | 
					
						
							|  |  |  |  |         .map_err(|err| format!("Unable to hash {:?}: {}", temp_file, err))?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if expected_sha256.is_some() && expected_sha256 != Some(&temp_file_sha256) {
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:04:18 -07:00
										 |  |  |  |         return Err(io::Error::new(io::ErrorKind::Other, "Incorrect hash").into());
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     }
 | 
					
						
							| 
									
										
										
										
											2019-09-29 17:56:33 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     source.progress_bar.finish_and_clear();
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     Ok((temp_dir, temp_file, temp_file_sha256))
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /// Extracts the release archive into the specified directory
 | 
					
						
							|  |  |  |  | fn extract_release_archive(
 | 
					
						
							|  |  |  |  |     archive: &Path,
 | 
					
						
							|  |  |  |  |     extract_dir: &Path,
 | 
					
						
							|  |  |  |  | ) -> Result<(), Box<dyn std::error::Error>> {
 | 
					
						
							|  |  |  |  |     use bzip2::bufread::BzDecoder;
 | 
					
						
							|  |  |  |  |     use tar::Archive;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let progress_bar = new_spinner_progress_bar();
 | 
					
						
							|  |  |  |  |     progress_bar.set_message(&format!("{}Extracting...", PACKAGE));
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let _ = fs::remove_dir_all(extract_dir);
 | 
					
						
							|  |  |  |  |     fs::create_dir_all(extract_dir)?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let tar_bz2 = File::open(archive)?;
 | 
					
						
							|  |  |  |  |     let tar = BzDecoder::new(BufReader::new(tar_bz2));
 | 
					
						
							|  |  |  |  |     let mut release = Archive::new(tar);
 | 
					
						
							|  |  |  |  |     release.unpack(extract_dir)?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     progress_bar.finish_and_clear();
 | 
					
						
							|  |  |  |  |     Ok(())
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  | fn load_release_version(version_yml: &Path) -> Result<ReleaseVersion, String> {
 | 
					
						
							|  |  |  |  |     let file = File::open(&version_yml)
 | 
					
						
							|  |  |  |  |         .map_err(|err| format!("Unable to open {:?}: {:?}", version_yml, err))?;
 | 
					
						
							|  |  |  |  |     let version: ReleaseVersion = serde_yaml::from_reader(file)
 | 
					
						
							|  |  |  |  |         .map_err(|err| format!("Unable to parse {:?}: {:?}", version_yml, err))?;
 | 
					
						
							|  |  |  |  |     Ok(version)
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  | /// Reads the supported TARGET triple for the given release
 | 
					
						
							|  |  |  |  | fn load_release_target(release_dir: &Path) -> Result<String, String> {
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     let mut version_yml = PathBuf::from(release_dir);
 | 
					
						
							|  |  |  |  |     version_yml.push("solana-release");
 | 
					
						
							|  |  |  |  |     version_yml.push("version.yml");
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  |     let version = load_release_version(&version_yml)?;
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     Ok(version.target)
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /// Time in seconds since the UNIX_EPOCH
 | 
					
						
							|  |  |  |  | fn timestamp_secs() -> u64 {
 | 
					
						
							|  |  |  |  |     SystemTime::now()
 | 
					
						
							|  |  |  |  |         .duration_since(SystemTime::UNIX_EPOCH)
 | 
					
						
							|  |  |  |  |         .unwrap()
 | 
					
						
							|  |  |  |  |         .as_secs()
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /// Create an empty update manifest for the given `update_manifest_keypair` if it doesn't already
 | 
					
						
							|  |  |  |  | /// exist on the cluster
 | 
					
						
							|  |  |  |  | fn new_update_manifest(
 | 
					
						
							|  |  |  |  |     rpc_client: &RpcClient,
 | 
					
						
							|  |  |  |  |     from_keypair: &Keypair,
 | 
					
						
							|  |  |  |  |     update_manifest_keypair: &Keypair,
 | 
					
						
							|  |  |  |  | ) -> Result<(), Box<dyn std::error::Error>> {
 | 
					
						
							|  |  |  |  |     if rpc_client
 | 
					
						
							|  |  |  |  |         .get_account_data(&update_manifest_keypair.pubkey())
 | 
					
						
							|  |  |  |  |         .is_err()
 | 
					
						
							|  |  |  |  |     {
 | 
					
						
							| 
									
										
										
										
											2019-05-13 12:49:37 -07:00
										 |  |  |  |         let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?;
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-24 19:01:21 -07:00
										 |  |  |  |         let lamports = rpc_client
 | 
					
						
							|  |  |  |  |             .get_minimum_balance_for_rent_exemption(SignedUpdateManifest::max_space() as usize)?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 13:03:46 -06:00
										 |  |  |  |         let instructions = config_instruction::create_account::<SignedUpdateManifest>(
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |             &from_keypair.pubkey(),
 | 
					
						
							|  |  |  |  |             &update_manifest_keypair.pubkey(),
 | 
					
						
							| 
									
										
										
										
											2019-12-24 19:01:21 -07:00
										 |  |  |  |             lamports,
 | 
					
						
							| 
									
										
										
										
											2019-07-08 18:33:56 -05:00
										 |  |  |  |             vec![], // additional keys
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |         );
 | 
					
						
							| 
									
										
										
										
											2020-04-24 13:03:46 -06:00
										 |  |  |  |         let mut transaction = Transaction::new_unsigned_instructions(&instructions);
 | 
					
						
							| 
									
										
										
										
											2019-08-15 09:05:51 -07:00
										 |  |  |  |         let signers = [from_keypair, update_manifest_keypair];
 | 
					
						
							|  |  |  |  |         transaction.sign(&signers, recent_blockhash);
 | 
					
						
							| 
									
										
										
										
											2020-05-09 09:06:32 -07:00
										 |  |  |  |         rpc_client.send_and_confirm_transaction(&transaction)?;
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     }
 | 
					
						
							|  |  |  |  |     Ok(())
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /// Update the update manifest on the cluster with new content
 | 
					
						
							|  |  |  |  | fn store_update_manifest(
 | 
					
						
							|  |  |  |  |     rpc_client: &RpcClient,
 | 
					
						
							|  |  |  |  |     from_keypair: &Keypair,
 | 
					
						
							|  |  |  |  |     update_manifest_keypair: &Keypair,
 | 
					
						
							|  |  |  |  |     update_manifest: &SignedUpdateManifest,
 | 
					
						
							|  |  |  |  | ) -> Result<(), Box<dyn std::error::Error>> {
 | 
					
						
							| 
									
										
										
										
											2019-05-13 12:49:37 -07:00
										 |  |  |  |     let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?;
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-12 22:47:12 -07:00
										 |  |  |  |     let signers = [from_keypair, update_manifest_keypair];
 | 
					
						
							|  |  |  |  |     let instruction = config_instruction::store::<SignedUpdateManifest>(
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |         &update_manifest_keypair.pubkey(),
 | 
					
						
							| 
									
										
										
										
											2019-07-09 13:37:18 -06:00
										 |  |  |  |         true,   // update_manifest_keypair is signer
 | 
					
						
							| 
									
										
										
										
											2019-07-08 18:33:56 -05:00
										 |  |  |  |         vec![], // additional keys
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |         update_manifest,
 | 
					
						
							|  |  |  |  |     );
 | 
					
						
							| 
									
										
										
										
											2019-05-12 22:47:12 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 14:37:23 -07:00
										 |  |  |  |     let message = Message::new_with_payer(&[instruction], Some(&from_keypair.pubkey()));
 | 
					
						
							| 
									
										
										
										
											2020-05-09 09:06:32 -07:00
										 |  |  |  |     let transaction = Transaction::new(&signers, message, recent_blockhash);
 | 
					
						
							|  |  |  |  |     rpc_client.send_and_confirm_transaction(&transaction)?;
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     Ok(())
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /// Read the current contents of the update manifest from the cluster
 | 
					
						
							|  |  |  |  | fn get_update_manifest(
 | 
					
						
							|  |  |  |  |     rpc_client: &RpcClient,
 | 
					
						
							|  |  |  |  |     update_manifest_pubkey: &Pubkey,
 | 
					
						
							|  |  |  |  | ) -> Result<UpdateManifest, String> {
 | 
					
						
							| 
									
										
										
										
											2019-08-14 15:54:31 -07:00
										 |  |  |  |     let data = rpc_client
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |         .get_account_data(update_manifest_pubkey)
 | 
					
						
							|  |  |  |  |         .map_err(|err| format!("Unable to fetch update manifest: {}", err))?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-14 15:54:31 -07:00
										 |  |  |  |     let config_data = get_config_data(&data)
 | 
					
						
							|  |  |  |  |         .map_err(|err| format!("Unable to get at config_data to update manifest: {}", err))?;
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     let signed_update_manifest =
 | 
					
						
							| 
									
										
										
										
											2019-08-14 15:54:31 -07:00
										 |  |  |  |         SignedUpdateManifest::deserialize(update_manifest_pubkey, config_data)
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |             .map_err(|err| format!("Unable to deserialize update manifest: {}", err))?;
 | 
					
						
							|  |  |  |  |     Ok(signed_update_manifest.manifest)
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-12 14:13:01 -07:00
										 |  |  |  | /// Bug the user if active_release_bin_dir is not in their PATH
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | fn check_env_path_for_bin_dir(config: &Config) {
 | 
					
						
							|  |  |  |  |     use std::env;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-27 09:11:02 -07:00
										 |  |  |  |     let bin_dir = config
 | 
					
						
							|  |  |  |  |         .active_release_bin_dir()
 | 
					
						
							|  |  |  |  |         .canonicalize()
 | 
					
						
							|  |  |  |  |         .unwrap_or_default();
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     let found = match env::var_os("PATH") {
 | 
					
						
							|  |  |  |  |         Some(paths) => env::split_paths(&paths).any(|path| {
 | 
					
						
							|  |  |  |  |             if let Ok(path) = path.canonicalize() {
 | 
					
						
							| 
									
										
										
										
											2019-04-12 14:13:01 -07:00
										 |  |  |  |                 if path == bin_dir {
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |                     return true;
 | 
					
						
							|  |  |  |  |                 }
 | 
					
						
							|  |  |  |  |             }
 | 
					
						
							|  |  |  |  |             false
 | 
					
						
							|  |  |  |  |         }),
 | 
					
						
							|  |  |  |  |         None => false,
 | 
					
						
							|  |  |  |  |     };
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if !found {
 | 
					
						
							|  |  |  |  |         println!(
 | 
					
						
							|  |  |  |  |             "\nPlease update your PATH environment variable to include the solana programs:\n    PATH=\"{}:$PATH\"\n",
 | 
					
						
							| 
									
										
										
										
											2019-06-08 17:23:35 -07:00
										 |  |  |  |             config.active_release_bin_dir().to_str().unwrap()
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |         );
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							| 
									
										
										
										
											2019-03-15 10:54:54 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  | /// Encodes a UTF-8 string as a null-terminated UCS-2 string in bytes
 | 
					
						
							|  |  |  |  | #[cfg(windows)]
 | 
					
						
							|  |  |  |  | pub fn string_to_winreg_bytes(s: &str) -> Vec<u8> {
 | 
					
						
							|  |  |  |  |     use std::ffi::OsString;
 | 
					
						
							|  |  |  |  |     use std::os::windows::ffi::OsStrExt;
 | 
					
						
							|  |  |  |  |     let v: Vec<_> = OsString::from(format!("{}\x00", s)).encode_wide().collect();
 | 
					
						
							|  |  |  |  |     unsafe { std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 2).to_vec() }
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // This is used to decode the value of HKCU\Environment\PATH. If that
 | 
					
						
							|  |  |  |  | // key is not Unicode (or not REG_SZ | REG_EXPAND_SZ) then this
 | 
					
						
							|  |  |  |  | // returns null.  The winreg library itself does a lossy unicode
 | 
					
						
							|  |  |  |  | // conversion.
 | 
					
						
							|  |  |  |  | #[cfg(windows)]
 | 
					
						
							|  |  |  |  | pub fn string_from_winreg_value(val: &winreg::RegValue) -> Option<String> {
 | 
					
						
							|  |  |  |  |     use std::slice;
 | 
					
						
							|  |  |  |  |     use winreg::enums::RegType;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     match val.vtype {
 | 
					
						
							|  |  |  |  |         RegType::REG_SZ | RegType::REG_EXPAND_SZ => {
 | 
					
						
							|  |  |  |  |             // Copied from winreg
 | 
					
						
							|  |  |  |  |             let words = unsafe {
 | 
					
						
							|  |  |  |  |                 slice::from_raw_parts(val.bytes.as_ptr() as *const u16, val.bytes.len() / 2)
 | 
					
						
							|  |  |  |  |             };
 | 
					
						
							|  |  |  |  |             let mut s = if let Ok(s) = String::from_utf16(words) {
 | 
					
						
							|  |  |  |  |                 s
 | 
					
						
							|  |  |  |  |             } else {
 | 
					
						
							|  |  |  |  |                 return None;
 | 
					
						
							|  |  |  |  |             };
 | 
					
						
							|  |  |  |  |             while s.ends_with('\u{0}') {
 | 
					
						
							|  |  |  |  |                 s.pop();
 | 
					
						
							|  |  |  |  |             }
 | 
					
						
							|  |  |  |  |             Some(s)
 | 
					
						
							| 
									
										
										
										
											2019-06-08 17:15:16 -07:00
										 |  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |         _ => None,
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | // Get the windows PATH variable out of the registry as a String. If
 | 
					
						
							|  |  |  |  | // this returns None then the PATH variable is not Unicode and we
 | 
					
						
							|  |  |  |  | // should not mess with it.
 | 
					
						
							|  |  |  |  | #[cfg(windows)]
 | 
					
						
							|  |  |  |  | fn get_windows_path_var() -> Result<Option<String>, String> {
 | 
					
						
							|  |  |  |  |     use winreg::enums::{HKEY_CURRENT_USER, KEY_READ, KEY_WRITE};
 | 
					
						
							|  |  |  |  |     use winreg::RegKey;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let root = RegKey::predef(HKEY_CURRENT_USER);
 | 
					
						
							|  |  |  |  |     let environment = root
 | 
					
						
							|  |  |  |  |         .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
 | 
					
						
							|  |  |  |  |         .map_err(|err| format!("Unable to open HKEY_CURRENT_USER\\Environment: {}", err))?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let reg_value = environment.get_raw_value("PATH");
 | 
					
						
							|  |  |  |  |     match reg_value {
 | 
					
						
							|  |  |  |  |         Ok(val) => {
 | 
					
						
							|  |  |  |  |             if let Some(s) = string_from_winreg_value(&val) {
 | 
					
						
							|  |  |  |  |                 Ok(Some(s))
 | 
					
						
							|  |  |  |  |             } else {
 | 
					
						
							|  |  |  |  |                 println!("the registry key HKEY_CURRENT_USER\\Environment\\PATH does not contain valid Unicode. Not modifying the PATH variable");
 | 
					
						
							|  |  |  |  |                 return Ok(None);
 | 
					
						
							|  |  |  |  |             }
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							|  |  |  |  |         Err(ref e) if e.kind() == io::ErrorKind::NotFound => Ok(Some(String::new())),
 | 
					
						
							|  |  |  |  |         Err(e) => Err(e.to_string()),
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #[cfg(windows)]
 | 
					
						
							|  |  |  |  | fn add_to_path(new_path: &str) -> Result<bool, String> {
 | 
					
						
							|  |  |  |  |     use std::ptr;
 | 
					
						
							|  |  |  |  |     use winapi::shared::minwindef::*;
 | 
					
						
							|  |  |  |  |     use winapi::um::winuser::{
 | 
					
						
							|  |  |  |  |         SendMessageTimeoutA, HWND_BROADCAST, SMTO_ABORTIFHUNG, WM_SETTINGCHANGE,
 | 
					
						
							| 
									
										
										
										
											2019-06-08 17:15:16 -07:00
										 |  |  |  |     };
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |     use winreg::enums::{RegType, HKEY_CURRENT_USER, KEY_READ, KEY_WRITE};
 | 
					
						
							|  |  |  |  |     use winreg::{RegKey, RegValue};
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |     let old_path = if let Some(s) = get_windows_path_var()? {
 | 
					
						
							|  |  |  |  |         s
 | 
					
						
							|  |  |  |  |     } else {
 | 
					
						
							|  |  |  |  |         return Ok(false);
 | 
					
						
							|  |  |  |  |     };
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if !old_path.contains(&new_path) {
 | 
					
						
							|  |  |  |  |         let mut new_path = new_path.to_string();
 | 
					
						
							|  |  |  |  |         if !old_path.is_empty() {
 | 
					
						
							|  |  |  |  |             new_path.push_str(";");
 | 
					
						
							|  |  |  |  |             new_path.push_str(&old_path);
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         let root = RegKey::predef(HKEY_CURRENT_USER);
 | 
					
						
							|  |  |  |  |         let environment = root
 | 
					
						
							|  |  |  |  |             .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
 | 
					
						
							|  |  |  |  |             .map_err(|err| format!("Unable to open HKEY_CURRENT_USER\\Environment: {}", err))?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         let reg_value = RegValue {
 | 
					
						
							|  |  |  |  |             bytes: string_to_winreg_bytes(&new_path),
 | 
					
						
							|  |  |  |  |             vtype: RegType::REG_EXPAND_SZ,
 | 
					
						
							|  |  |  |  |         };
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         environment
 | 
					
						
							|  |  |  |  |             .set_raw_value("PATH", ®_value)
 | 
					
						
							|  |  |  |  |             .map_err(|err| format!("Unable set HKEY_CURRENT_USER\\Environment\\PATH: {}", err))?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // Tell other processes to update their environment
 | 
					
						
							|  |  |  |  |         unsafe {
 | 
					
						
							|  |  |  |  |             SendMessageTimeoutA(
 | 
					
						
							|  |  |  |  |                 HWND_BROADCAST,
 | 
					
						
							|  |  |  |  |                 WM_SETTINGCHANGE,
 | 
					
						
							|  |  |  |  |                 0 as WPARAM,
 | 
					
						
							|  |  |  |  |                 "Environment\0".as_ptr() as LPARAM,
 | 
					
						
							|  |  |  |  |                 SMTO_ABORTIFHUNG,
 | 
					
						
							|  |  |  |  |                 5000,
 | 
					
						
							|  |  |  |  |                 ptr::null_mut(),
 | 
					
						
							|  |  |  |  |             );
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     println!(
 | 
					
						
							|  |  |  |  |         "\n{}\n  {}\n\n{}",
 | 
					
						
							|  |  |  |  |         style("The HKEY_CURRENT_USER/Environment/PATH registry key has been modified to include:").bold(),
 | 
					
						
							|  |  |  |  |         new_path,
 | 
					
						
							|  |  |  |  |         style("Future applications will automatically have the correct environment, but you may need to restart your current shell.").bold()
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  |     );
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |     Ok(true)
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  | #[cfg(unix)]
 | 
					
						
							|  |  |  |  | fn add_to_path(new_path: &str) -> Result<bool, String> {
 | 
					
						
							|  |  |  |  |     let shell_export_string = format!(r#"export PATH="{}:$PATH""#, new_path);
 | 
					
						
							|  |  |  |  |     let mut modified_rcfiles = false;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // Look for sh, bash, and zsh rc files
 | 
					
						
							|  |  |  |  |     let mut rcfiles = vec![dirs::home_dir().map(|p| p.join(".profile"))];
 | 
					
						
							|  |  |  |  |     if let Ok(shell) = std::env::var("SHELL") {
 | 
					
						
							|  |  |  |  |         if shell.contains("zsh") {
 | 
					
						
							|  |  |  |  |             let zdotdir = std::env::var("ZDOTDIR")
 | 
					
						
							|  |  |  |  |                 .ok()
 | 
					
						
							|  |  |  |  |                 .map(PathBuf::from)
 | 
					
						
							|  |  |  |  |                 .or_else(dirs::home_dir);
 | 
					
						
							|  |  |  |  |             let zprofile = zdotdir.map(|p| p.join(".zprofile"));
 | 
					
						
							|  |  |  |  |             rcfiles.push(zprofile);
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |     }
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |     if let Some(bash_profile) = dirs::home_dir().map(|p| p.join(".bash_profile")) {
 | 
					
						
							|  |  |  |  |         // Only update .bash_profile if it exists because creating .bash_profile
 | 
					
						
							|  |  |  |  |         // will cause .profile to not be read
 | 
					
						
							|  |  |  |  |         if bash_profile.exists() {
 | 
					
						
							|  |  |  |  |             rcfiles.push(Some(bash_profile));
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |     }
 | 
					
						
							|  |  |  |  |     let rcfiles = rcfiles.into_iter().filter_map(|f| f.filter(|f| f.exists()));
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |     // For each rc file, append a PATH entry if not already present
 | 
					
						
							|  |  |  |  |     for rcfile in rcfiles {
 | 
					
						
							|  |  |  |  |         if !rcfile.exists() {
 | 
					
						
							|  |  |  |  |             continue;
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |         fn read_file(path: &Path) -> io::Result<String> {
 | 
					
						
							|  |  |  |  |             let mut file = fs::OpenOptions::new().read(true).open(path)?;
 | 
					
						
							|  |  |  |  |             let mut contents = String::new();
 | 
					
						
							|  |  |  |  |             io::Read::read_to_string(&mut file, &mut contents)?;
 | 
					
						
							|  |  |  |  |             Ok(contents)
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |         match read_file(&rcfile) {
 | 
					
						
							|  |  |  |  |             Err(err) => {
 | 
					
						
							|  |  |  |  |                 println!("Unable to read {:?}: {}", rcfile, err);
 | 
					
						
							|  |  |  |  |             }
 | 
					
						
							|  |  |  |  |             Ok(contents) => {
 | 
					
						
							|  |  |  |  |                 if !contents.contains(&shell_export_string) {
 | 
					
						
							|  |  |  |  |                     println!(
 | 
					
						
							|  |  |  |  |                         "Adding {} to {}",
 | 
					
						
							|  |  |  |  |                         style(&shell_export_string).italic(),
 | 
					
						
							|  |  |  |  |                         style(rcfile.to_str().unwrap()).bold()
 | 
					
						
							|  |  |  |  |                     );
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                     fn append_file(dest: &Path, line: &str) -> io::Result<()> {
 | 
					
						
							|  |  |  |  |                         use std::io::Write;
 | 
					
						
							|  |  |  |  |                         let mut dest_file = fs::OpenOptions::new()
 | 
					
						
							|  |  |  |  |                             .write(true)
 | 
					
						
							|  |  |  |  |                             .append(true)
 | 
					
						
							|  |  |  |  |                             .create(true)
 | 
					
						
							|  |  |  |  |                             .open(dest)?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                         writeln!(&mut dest_file, "{}", line)?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                         dest_file.sync_data()?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                         Ok(())
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  |                     }
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |                     append_file(&rcfile, &shell_export_string).unwrap_or_else(|err| {
 | 
					
						
							|  |  |  |  |                         format!("Unable to append to {:?}: {}", rcfile, err);
 | 
					
						
							|  |  |  |  |                     });
 | 
					
						
							|  |  |  |  |                     modified_rcfiles = true;
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  |                 }
 | 
					
						
							|  |  |  |  |             }
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if modified_rcfiles {
 | 
					
						
							|  |  |  |  |         println!(
 | 
					
						
							|  |  |  |  |             "\n{}\n  {}\n",
 | 
					
						
							| 
									
										
										
										
											2019-04-12 14:13:01 -07:00
										 |  |  |  |             style("Close and reopen your terminal to apply the PATH changes or run the following in your existing shell:").bold().blue(),
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  |             shell_export_string
 | 
					
						
							|  |  |  |  |        );
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     Ok(modified_rcfiles)
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | pub fn init(
 | 
					
						
							|  |  |  |  |     config_file: &str,
 | 
					
						
							|  |  |  |  |     data_dir: &str,
 | 
					
						
							|  |  |  |  |     json_rpc_url: &str,
 | 
					
						
							|  |  |  |  |     update_manifest_pubkey: &Pubkey,
 | 
					
						
							|  |  |  |  |     no_modify_path: bool,
 | 
					
						
							| 
									
										
										
										
											2019-07-31 17:30:17 -07:00
										 |  |  |  |     explicit_release: Option<ExplicitRelease>,
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  | ) -> Result<(), String> {
 | 
					
						
							|  |  |  |  |     let config = {
 | 
					
						
							|  |  |  |  |         // Write new config file only if different, so that running |solana-install init|
 | 
					
						
							|  |  |  |  |         // repeatedly doesn't unnecessarily re-download
 | 
					
						
							|  |  |  |  |         let mut current_config = Config::load(config_file).unwrap_or_default();
 | 
					
						
							|  |  |  |  |         current_config.current_update_manifest = None;
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |         let config = Config::new(
 | 
					
						
							|  |  |  |  |             data_dir,
 | 
					
						
							|  |  |  |  |             json_rpc_url,
 | 
					
						
							|  |  |  |  |             update_manifest_pubkey,
 | 
					
						
							| 
									
										
										
										
											2019-07-31 17:30:17 -07:00
										 |  |  |  |             explicit_release,
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |         );
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |         if current_config != config {
 | 
					
						
							|  |  |  |  |             config.save(config_file)?;
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							|  |  |  |  |         config
 | 
					
						
							|  |  |  |  |     };
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     update(config_file)?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let path_modified = if !no_modify_path {
 | 
					
						
							|  |  |  |  |         add_to_path(&config.active_release_bin_dir().to_str().unwrap())?
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  |     } else {
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |         false
 | 
					
						
							|  |  |  |  |     };
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-28 16:45:01 -07:00
										 |  |  |  |     if !path_modified && !no_modify_path {
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:00:45 -07:00
										 |  |  |  |         check_env_path_for_bin_dir(&config);
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     Ok(())
 | 
					
						
							| 
									
										
										
										
											2019-03-15 10:54:54 -07:00
										 |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-31 17:30:17 -07:00
										 |  |  |  | fn github_release_download_url(release_semver: &str) -> String {
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |     format!(
 | 
					
						
							|  |  |  |  |         "https://github.com/solana-labs/solana/releases/download/v{}/solana-release-{}.tar.bz2",
 | 
					
						
							|  |  |  |  |         release_semver,
 | 
					
						
							|  |  |  |  |         crate::build_env::TARGET
 | 
					
						
							|  |  |  |  |     )
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-31 17:30:17 -07:00
										 |  |  |  | fn release_channel_download_url(release_channel: &str) -> String {
 | 
					
						
							|  |  |  |  |     format!(
 | 
					
						
							|  |  |  |  |         "http://release.solana.com/{}/solana-release-{}.tar.bz2",
 | 
					
						
							|  |  |  |  |         release_channel,
 | 
					
						
							|  |  |  |  |         crate::build_env::TARGET
 | 
					
						
							|  |  |  |  |     )
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  | fn release_channel_version_url(release_channel: &str) -> String {
 | 
					
						
							|  |  |  |  |     format!(
 | 
					
						
							|  |  |  |  |         "http://release.solana.com/{}/solana-release-{}.yml",
 | 
					
						
							|  |  |  |  |         release_channel,
 | 
					
						
							|  |  |  |  |         crate::build_env::TARGET
 | 
					
						
							|  |  |  |  |     )
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | pub fn info(config_file: &str, local_info_only: bool) -> Result<Option<UpdateManifest>, String> {
 | 
					
						
							| 
									
										
										
										
											2019-03-15 10:54:54 -07:00
										 |  |  |  |     let config = Config::load(config_file)?;
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-08 19:25:02 -07:00
										 |  |  |  |     println_name_value("Configuration:", &config_file);
 | 
					
						
							|  |  |  |  |     println_name_value(
 | 
					
						
							|  |  |  |  |         "Active release directory:",
 | 
					
						
							|  |  |  |  |         &config.active_release_dir().to_str().unwrap_or("?"),
 | 
					
						
							|  |  |  |  |     );
 | 
					
						
							| 
									
										
										
										
											2019-07-31 17:30:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     if let Some(explicit_release) = &config.explicit_release {
 | 
					
						
							|  |  |  |  |         match explicit_release {
 | 
					
						
							|  |  |  |  |             ExplicitRelease::Semver(release_semver) => {
 | 
					
						
							| 
									
										
										
										
											2020-04-22 23:26:37 -07:00
										 |  |  |  |                 println_name_value(&format!("{}Release version:", BULLET), &release_semver);
 | 
					
						
							| 
									
										
										
										
											2019-07-31 17:30:17 -07:00
										 |  |  |  |                 println_name_value(
 | 
					
						
							|  |  |  |  |                     &format!("{}Release URL:", BULLET),
 | 
					
						
							|  |  |  |  |                     &github_release_download_url(release_semver),
 | 
					
						
							|  |  |  |  |                 );
 | 
					
						
							|  |  |  |  |             }
 | 
					
						
							|  |  |  |  |             ExplicitRelease::Channel(release_channel) => {
 | 
					
						
							|  |  |  |  |                 println_name_value(&format!("{}Release channel:", BULLET), &release_channel);
 | 
					
						
							|  |  |  |  |                 println_name_value(
 | 
					
						
							|  |  |  |  |                     &format!("{}Release URL:", BULLET),
 | 
					
						
							|  |  |  |  |                     &release_channel_download_url(release_channel),
 | 
					
						
							|  |  |  |  |                 );
 | 
					
						
							|  |  |  |  |             }
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |         return Ok(None);
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     println_name_value("JSON RPC URL:", &config.json_rpc_url);
 | 
					
						
							|  |  |  |  |     println_name_value(
 | 
					
						
							|  |  |  |  |         "Update manifest pubkey:",
 | 
					
						
							|  |  |  |  |         &config.update_manifest_pubkey.to_string(),
 | 
					
						
							|  |  |  |  |     );
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     fn print_update_manifest(update_manifest: &UpdateManifest) {
 | 
					
						
							|  |  |  |  |         let when = Local.timestamp(update_manifest.timestamp_secs as i64, 0);
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |         println_name_value(&format!("{}release date:", BULLET), &when.to_string());
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |         println_name_value(
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |             &format!("{}download URL:", BULLET),
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |             &update_manifest.download_url,
 | 
					
						
							|  |  |  |  |         );
 | 
					
						
							| 
									
										
										
										
											2019-03-15 10:54:54 -07:00
										 |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     match config.current_update_manifest {
 | 
					
						
							|  |  |  |  |         Some(ref update_manifest) => {
 | 
					
						
							|  |  |  |  |             println_name_value("Installed version:", "");
 | 
					
						
							|  |  |  |  |             print_update_manifest(&update_manifest);
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							|  |  |  |  |         None => {
 | 
					
						
							|  |  |  |  |             println_name_value("Installed version:", "None");
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							| 
									
										
										
										
											2019-03-15 10:54:54 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     if local_info_only {
 | 
					
						
							|  |  |  |  |         Ok(None)
 | 
					
						
							|  |  |  |  |     } else {
 | 
					
						
							|  |  |  |  |         let progress_bar = new_spinner_progress_bar();
 | 
					
						
							|  |  |  |  |         progress_bar.set_message(&format!("{}Checking for updates...", LOOKING_GLASS));
 | 
					
						
							|  |  |  |  |         let rpc_client = RpcClient::new(config.json_rpc_url.clone());
 | 
					
						
							|  |  |  |  |         let manifest = get_update_manifest(&rpc_client, &config.update_manifest_pubkey)?;
 | 
					
						
							|  |  |  |  |         progress_bar.finish_and_clear();
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         if Some(&manifest) == config.current_update_manifest.as_ref() {
 | 
					
						
							|  |  |  |  |             println!("\n{}", style("Installation is up to date").italic());
 | 
					
						
							|  |  |  |  |             Ok(None)
 | 
					
						
							|  |  |  |  |         } else {
 | 
					
						
							|  |  |  |  |             println!("\n{}", style("An update is available:").bold());
 | 
					
						
							|  |  |  |  |             print_update_manifest(&manifest);
 | 
					
						
							|  |  |  |  |             Ok(Some(manifest))
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							| 
									
										
										
										
											2019-03-15 10:54:54 -07:00
										 |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | pub fn deploy(
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     json_rpc_url: &str,
 | 
					
						
							|  |  |  |  |     from_keypair_file: &str,
 | 
					
						
							| 
									
										
										
										
											2019-03-15 10:54:54 -07:00
										 |  |  |  |     download_url: &str,
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     update_manifest_keypair_file: &str,
 | 
					
						
							| 
									
										
										
										
											2019-03-15 10:54:54 -07:00
										 |  |  |  | ) -> Result<(), String> {
 | 
					
						
							| 
									
										
										
										
											2019-10-10 17:01:03 -06:00
										 |  |  |  |     let from_keypair = read_keypair_file(from_keypair_file)
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |         .map_err(|err| format!("Unable to read {}: {}", from_keypair_file, err))?;
 | 
					
						
							| 
									
										
										
										
											2019-10-10 17:01:03 -06:00
										 |  |  |  |     let update_manifest_keypair = read_keypair_file(update_manifest_keypair_file)
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |         .map_err(|err| format!("Unable to read {}: {}", update_manifest_keypair_file, err))?;
 | 
					
						
							| 
									
										
										
										
											2019-03-15 10:54:54 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-19 12:03:47 -07:00
										 |  |  |  |     println_name_value("JSON RPC URL:", json_rpc_url);
 | 
					
						
							|  |  |  |  |     println_name_value(
 | 
					
						
							|  |  |  |  |         "Update manifest pubkey:",
 | 
					
						
							|  |  |  |  |         &update_manifest_keypair.pubkey().to_string(),
 | 
					
						
							|  |  |  |  |     );
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:18:33 -07:00
										 |  |  |  |     // Confirm the `json_rpc_url` is good and that `from_keypair` is a valid account
 | 
					
						
							|  |  |  |  |     let rpc_client = RpcClient::new(json_rpc_url.to_string());
 | 
					
						
							|  |  |  |  |     let progress_bar = new_spinner_progress_bar();
 | 
					
						
							|  |  |  |  |     progress_bar.set_message(&format!("{}Checking cluster...", LOOKING_GLASS));
 | 
					
						
							|  |  |  |  |     let balance = rpc_client
 | 
					
						
							| 
									
										
										
										
											2020-05-14 12:24:14 -06:00
										 |  |  |  |         .get_balance(&from_keypair.pubkey())
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:18:33 -07:00
										 |  |  |  |         .map_err(|err| {
 | 
					
						
							|  |  |  |  |             format!(
 | 
					
						
							|  |  |  |  |                 "Unable to get the account balance of {}: {}",
 | 
					
						
							|  |  |  |  |                 from_keypair_file, err
 | 
					
						
							|  |  |  |  |             )
 | 
					
						
							|  |  |  |  |         })?;
 | 
					
						
							|  |  |  |  |     progress_bar.finish_and_clear();
 | 
					
						
							| 
									
										
										
										
											2020-05-14 12:24:14 -06:00
										 |  |  |  |     if balance == 0 {
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:04:18 -07:00
										 |  |  |  |         return Err(format!("{} account balance is empty", from_keypair_file));
 | 
					
						
							| 
									
										
										
										
											2019-03-21 12:18:33 -07:00
										 |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     // Download the release
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  |     let (temp_dir, temp_archive, temp_archive_sha256) = download_to_temp(download_url, None)
 | 
					
						
							|  |  |  |  |         .map_err(|err| format!("Unable to download {}: {}", download_url, err))?;
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-19 12:03:47 -07:00
										 |  |  |  |     if let Ok(update_manifest) = get_update_manifest(&rpc_client, &update_manifest_keypair.pubkey())
 | 
					
						
							|  |  |  |  |     {
 | 
					
						
							|  |  |  |  |         if temp_archive_sha256 == update_manifest.download_sha256 {
 | 
					
						
							|  |  |  |  |             println!(
 | 
					
						
							|  |  |  |  |                 "  {}{}",
 | 
					
						
							|  |  |  |  |                 INFORMATION,
 | 
					
						
							|  |  |  |  |                 style("Update is already deployed").bold()
 | 
					
						
							|  |  |  |  |             );
 | 
					
						
							|  |  |  |  |             return Ok(());
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     // Extract it and load the release version metadata
 | 
					
						
							|  |  |  |  |     let temp_release_dir = temp_dir.path().join("archive");
 | 
					
						
							|  |  |  |  |     extract_release_archive(&temp_archive, &temp_release_dir).map_err(|err| {
 | 
					
						
							|  |  |  |  |         format!(
 | 
					
						
							|  |  |  |  |             "Unable to extract {:?} into {:?}: {}",
 | 
					
						
							|  |  |  |  |             temp_archive, temp_release_dir, err
 | 
					
						
							| 
									
										
										
										
											2019-03-15 10:54:54 -07:00
										 |  |  |  |         )
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     })?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let release_target = load_release_target(&temp_release_dir).map_err(|err| {
 | 
					
						
							|  |  |  |  |         format!(
 | 
					
						
							|  |  |  |  |             "Unable to load release target from {:?}: {}",
 | 
					
						
							|  |  |  |  |             temp_release_dir, err
 | 
					
						
							|  |  |  |  |         )
 | 
					
						
							|  |  |  |  |     })?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     println_name_value("Update target:", &release_target);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let progress_bar = new_spinner_progress_bar();
 | 
					
						
							|  |  |  |  |     progress_bar.set_message(&format!("{}Deploying update...", PACKAGE));
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // Construct an update manifest for the release
 | 
					
						
							|  |  |  |  |     let mut update_manifest = SignedUpdateManifest {
 | 
					
						
							|  |  |  |  |         account_pubkey: update_manifest_keypair.pubkey(),
 | 
					
						
							|  |  |  |  |         ..SignedUpdateManifest::default()
 | 
					
						
							|  |  |  |  |     };
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     update_manifest.manifest.timestamp_secs = timestamp_secs();
 | 
					
						
							|  |  |  |  |     update_manifest.manifest.download_url = download_url.to_string();
 | 
					
						
							|  |  |  |  |     update_manifest.manifest.download_sha256 = temp_archive_sha256;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     update_manifest.sign(&update_manifest_keypair);
 | 
					
						
							|  |  |  |  |     assert!(update_manifest.verify());
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // Store the new update manifest on the cluster
 | 
					
						
							|  |  |  |  |     new_update_manifest(&rpc_client, &from_keypair, &update_manifest_keypair)
 | 
					
						
							|  |  |  |  |         .map_err(|err| format!("Unable to create update manifest: {}", err))?;
 | 
					
						
							|  |  |  |  |     store_update_manifest(
 | 
					
						
							|  |  |  |  |         &rpc_client,
 | 
					
						
							|  |  |  |  |         &from_keypair,
 | 
					
						
							|  |  |  |  |         &update_manifest_keypair,
 | 
					
						
							|  |  |  |  |         &update_manifest,
 | 
					
						
							|  |  |  |  |     )
 | 
					
						
							|  |  |  |  |     .map_err(|err| format!("Unable to store update manifest: {:?}", err))?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     progress_bar.finish_and_clear();
 | 
					
						
							|  |  |  |  |     println!("  {}{}", SPARKLE, style("Deployment successful").bold());
 | 
					
						
							|  |  |  |  |     Ok(())
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-04 08:51:20 -07:00
										 |  |  |  | #[cfg(windows)]
 | 
					
						
							|  |  |  |  | fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> std::io::Result<()> {
 | 
					
						
							|  |  |  |  |     std::os::windows::fs::symlink_dir(src, dst)
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | #[cfg(not(windows))]
 | 
					
						
							|  |  |  |  | fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> std::io::Result<()> {
 | 
					
						
							|  |  |  |  |     std::os::unix::fs::symlink(src, dst)
 | 
					
						
							|  |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | pub fn update(config_file: &str) -> Result<bool, String> {
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |     let mut config = Config::load(config_file)?;
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     let update_manifest = info(config_file, false)?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-31 17:30:17 -07:00
										 |  |  |  |     let release_dir = if let Some(explicit_release) = &config.explicit_release {
 | 
					
						
							| 
									
										
										
										
											2020-02-11 13:32:26 -07:00
										 |  |  |  |         let (download_url, release_dir) = match explicit_release {
 | 
					
						
							| 
									
										
										
										
											2019-07-31 17:30:17 -07:00
										 |  |  |  |             ExplicitRelease::Semver(release_semver) => {
 | 
					
						
							|  |  |  |  |                 let download_url = github_release_download_url(release_semver);
 | 
					
						
							|  |  |  |  |                 let release_dir = config.release_dir(&release_semver);
 | 
					
						
							| 
									
										
										
										
											2020-02-11 13:32:26 -07:00
										 |  |  |  |                 let download_url = if release_dir.join(".ok").exists() {
 | 
					
						
							| 
									
										
										
										
											2019-07-31 17:30:17 -07:00
										 |  |  |  |                     // If this release_semver has already been successfully downloaded, no update
 | 
					
						
							|  |  |  |  |                     // needed
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  |                     println!("{} is present, no download required.", release_semver);
 | 
					
						
							| 
									
										
										
										
											2019-08-06 12:58:50 -06:00
										 |  |  |  |                     None
 | 
					
						
							|  |  |  |  |                 } else {
 | 
					
						
							|  |  |  |  |                     Some(download_url)
 | 
					
						
							|  |  |  |  |                 };
 | 
					
						
							| 
									
										
										
										
											2020-02-11 13:32:26 -07:00
										 |  |  |  |                 (download_url, release_dir)
 | 
					
						
							| 
									
										
										
										
											2019-07-31 17:30:17 -07:00
										 |  |  |  |             }
 | 
					
						
							|  |  |  |  |             ExplicitRelease::Channel(release_channel) => {
 | 
					
						
							|  |  |  |  |                 let release_dir = config.release_dir(&release_channel);
 | 
					
						
							| 
									
										
										
										
											2020-02-11 13:32:26 -07:00
										 |  |  |  |                 let current_release_version_yml =
 | 
					
						
							|  |  |  |  |                     release_dir.join("solana-release").join("version.yml");
 | 
					
						
							|  |  |  |  |                 let download_url = Some(release_channel_download_url(release_channel));
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-11 13:32:26 -07:00
										 |  |  |  |                 if !current_release_version_yml.exists() {
 | 
					
						
							|  |  |  |  |                     (download_url, release_dir)
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  |                 } else {
 | 
					
						
							| 
									
										
										
										
											2020-02-11 13:32:26 -07:00
										 |  |  |  |                     let version_url = release_channel_version_url(release_channel);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                     let (_temp_dir, temp_file, _temp_archive_sha256) =
 | 
					
						
							|  |  |  |  |                         download_to_temp(&version_url, None).map_err(|err| {
 | 
					
						
							|  |  |  |  |                             format!("Unable to download {}: {}", version_url, err)
 | 
					
						
							|  |  |  |  |                         })?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                     let update_release_version = load_release_version(&temp_file)?;
 | 
					
						
							|  |  |  |  |                     let current_release_version =
 | 
					
						
							|  |  |  |  |                         load_release_version(¤t_release_version_yml)?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                     if update_release_version.commit == current_release_version.commit {
 | 
					
						
							|  |  |  |  |                         // Same commit, no update required
 | 
					
						
							|  |  |  |  |                         println!(
 | 
					
						
							|  |  |  |  |                             "Latest {} build is already present, no download required.",
 | 
					
						
							|  |  |  |  |                             release_channel
 | 
					
						
							|  |  |  |  |                         );
 | 
					
						
							|  |  |  |  |                         (None, release_dir)
 | 
					
						
							|  |  |  |  |                     } else {
 | 
					
						
							|  |  |  |  |                         (download_url, release_dir)
 | 
					
						
							|  |  |  |  |                     }
 | 
					
						
							|  |  |  |  |                 }
 | 
					
						
							| 
									
										
										
										
											2019-07-31 17:30:17 -07:00
										 |  |  |  |             }
 | 
					
						
							|  |  |  |  |         };
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-11 13:32:26 -07:00
										 |  |  |  |         if let Some(download_url) = download_url {
 | 
					
						
							| 
									
										
										
										
											2019-08-06 12:58:50 -06:00
										 |  |  |  |             let (_temp_dir, temp_archive, _temp_archive_sha256) =
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  |                 download_to_temp(&download_url, None)
 | 
					
						
							| 
									
										
										
										
											2019-08-06 12:58:50 -06:00
										 |  |  |  |                     .map_err(|err| format!("Unable to download {}: {}", download_url, err))?;
 | 
					
						
							|  |  |  |  |             extract_release_archive(&temp_archive, &release_dir).map_err(|err| {
 | 
					
						
							|  |  |  |  |                 format!(
 | 
					
						
							|  |  |  |  |                     "Unable to extract {:?} to {:?}: {}",
 | 
					
						
							|  |  |  |  |                     temp_archive, release_dir, err
 | 
					
						
							|  |  |  |  |                 )
 | 
					
						
							|  |  |  |  |             })?;
 | 
					
						
							|  |  |  |  |             let _ = fs::create_dir_all(release_dir.join(".ok"));
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |         release_dir
 | 
					
						
							|  |  |  |  |     } else {
 | 
					
						
							|  |  |  |  |         if update_manifest.is_none() {
 | 
					
						
							|  |  |  |  |             return Ok(false);
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							|  |  |  |  |         let update_manifest = update_manifest.unwrap();
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |         if timestamp_secs()
 | 
					
						
							|  |  |  |  |             < u64::from_str_radix(crate::build_env::BUILD_SECONDS_SINCE_UNIX_EPOCH, 10).unwrap()
 | 
					
						
							|  |  |  |  |         {
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:04:18 -07:00
										 |  |  |  |             return Err("Unable to update as system time seems unreliable".to_string());
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |         if let Some(ref current_update_manifest) = config.current_update_manifest {
 | 
					
						
							|  |  |  |  |             if update_manifest.timestamp_secs < current_update_manifest.timestamp_secs {
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:04:18 -07:00
										 |  |  |  |                 return Err("Unable to update to an older version".to_string());
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |             }
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2019-11-22 21:58:26 -07:00
										 |  |  |  |         let release_dir = config.release_dir(&update_manifest.download_sha256.to_string());
 | 
					
						
							| 
									
										
										
										
											2020-02-10 23:19:52 -07:00
										 |  |  |  |         let (_temp_dir, temp_archive, _temp_archive_sha256) = download_to_temp(
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |             &update_manifest.download_url,
 | 
					
						
							|  |  |  |  |             Some(&update_manifest.download_sha256),
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |         )
 | 
					
						
							| 
									
										
										
										
											2019-07-23 12:51:10 -07:00
										 |  |  |  |         .map_err(|err| {
 | 
					
						
							|  |  |  |  |             format!(
 | 
					
						
							|  |  |  |  |                 "Unable to download {}: {}",
 | 
					
						
							|  |  |  |  |                 update_manifest.download_url, err
 | 
					
						
							|  |  |  |  |             )
 | 
					
						
							|  |  |  |  |         })?;
 | 
					
						
							|  |  |  |  |         extract_release_archive(&temp_archive, &release_dir).map_err(|err| {
 | 
					
						
							|  |  |  |  |             format!(
 | 
					
						
							|  |  |  |  |                 "Unable to extract {:?} to {:?}: {}",
 | 
					
						
							|  |  |  |  |                 temp_archive, release_dir, err
 | 
					
						
							|  |  |  |  |             )
 | 
					
						
							|  |  |  |  |         })?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         config.current_update_manifest = Some(update_manifest);
 | 
					
						
							|  |  |  |  |         release_dir
 | 
					
						
							|  |  |  |  |     };
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     let release_target = load_release_target(&release_dir).map_err(|err| {
 | 
					
						
							|  |  |  |  |         format!(
 | 
					
						
							|  |  |  |  |             "Unable to load release target from {:?}: {}",
 | 
					
						
							|  |  |  |  |             release_dir, err
 | 
					
						
							|  |  |  |  |         )
 | 
					
						
							|  |  |  |  |     })?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if release_target != crate::build_env::TARGET {
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:04:18 -07:00
										 |  |  |  |         return Err(format!("Incompatible update target: {}", release_target));
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-12 14:13:01 -07:00
										 |  |  |  |     let _ = fs::remove_dir_all(config.active_release_dir());
 | 
					
						
							| 
									
										
										
										
											2019-06-04 08:51:20 -07:00
										 |  |  |  |     symlink_dir(
 | 
					
						
							| 
									
										
										
										
											2019-04-12 14:13:01 -07:00
										 |  |  |  |         release_dir.join("solana-release"),
 | 
					
						
							|  |  |  |  |         config.active_release_dir(),
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     )
 | 
					
						
							|  |  |  |  |     .map_err(|err| {
 | 
					
						
							|  |  |  |  |         format!(
 | 
					
						
							|  |  |  |  |             "Unable to symlink {:?} to {:?}: {}",
 | 
					
						
							|  |  |  |  |             release_dir,
 | 
					
						
							| 
									
										
										
										
											2019-04-12 14:13:01 -07:00
										 |  |  |  |             config.active_release_dir(),
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |             err
 | 
					
						
							|  |  |  |  |         )
 | 
					
						
							|  |  |  |  |     })?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     config.save(config_file)?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     println!("  {}{}", SPARKLE, style("Update successful").bold());
 | 
					
						
							|  |  |  |  |     Ok(true)
 | 
					
						
							| 
									
										
										
										
											2019-03-15 10:54:54 -07:00
										 |  |  |  | }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | pub fn run(
 | 
					
						
							|  |  |  |  |     config_file: &str,
 | 
					
						
							|  |  |  |  |     program_name: &str,
 | 
					
						
							|  |  |  |  |     program_arguments: Vec<&str>,
 | 
					
						
							|  |  |  |  | ) -> Result<(), String> {
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     let config = Config::load(config_file)?;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-02 08:04:27 -07:00
										 |  |  |  |     let mut full_program_path = config.active_release_bin_dir().join(program_name);
 | 
					
						
							| 
									
										
										
										
											2019-07-03 17:45:08 -07:00
										 |  |  |  |     if cfg!(windows) && full_program_path.extension().is_none() {
 | 
					
						
							| 
									
										
										
										
											2019-07-02 08:04:27 -07:00
										 |  |  |  |         full_program_path.set_extension("exe");
 | 
					
						
							|  |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     if !full_program_path.exists() {
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:04:18 -07:00
										 |  |  |  |         return Err(format!(
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |             "{} does not exist",
 | 
					
						
							|  |  |  |  |             full_program_path.to_str().unwrap()
 | 
					
						
							| 
									
										
										
										
											2019-10-02 18:04:18 -07:00
										 |  |  |  |         ));
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     let mut child_option: Option<std::process::Child> = None;
 | 
					
						
							|  |  |  |  |     let mut now = Instant::now();
 | 
					
						
							| 
									
										
										
										
											2019-07-01 17:10:14 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 17:45:08 -07:00
										 |  |  |  |     let (signal_sender, signal_receiver) = mpsc::channel();
 | 
					
						
							|  |  |  |  |     ctrlc::set_handler(move || {
 | 
					
						
							|  |  |  |  |         let _ = signal_sender.send(());
 | 
					
						
							|  |  |  |  |     })
 | 
					
						
							|  |  |  |  |     .expect("Error setting Ctrl-C handler");
 | 
					
						
							| 
									
										
										
										
											2019-07-01 17:10:14 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     loop {
 | 
					
						
							|  |  |  |  |         child_option = match child_option {
 | 
					
						
							|  |  |  |  |             Some(mut child) => match child.try_wait() {
 | 
					
						
							|  |  |  |  |                 Ok(Some(status)) => {
 | 
					
						
							|  |  |  |  |                     println_name_value(
 | 
					
						
							|  |  |  |  |                         &format!("{} exited with:", program_name),
 | 
					
						
							|  |  |  |  |                         &status.to_string(),
 | 
					
						
							|  |  |  |  |                     );
 | 
					
						
							|  |  |  |  |                     None
 | 
					
						
							|  |  |  |  |                 }
 | 
					
						
							|  |  |  |  |                 Ok(None) => Some(child),
 | 
					
						
							|  |  |  |  |                 Err(err) => {
 | 
					
						
							|  |  |  |  |                     eprintln!("Error attempting to wait for program to exit: {}", err);
 | 
					
						
							|  |  |  |  |                     None
 | 
					
						
							|  |  |  |  |                 }
 | 
					
						
							|  |  |  |  |             },
 | 
					
						
							|  |  |  |  |             None => {
 | 
					
						
							|  |  |  |  |                 match std::process::Command::new(&full_program_path)
 | 
					
						
							|  |  |  |  |                     .args(&program_arguments)
 | 
					
						
							|  |  |  |  |                     .spawn()
 | 
					
						
							|  |  |  |  |                 {
 | 
					
						
							|  |  |  |  |                     Ok(child) => Some(child),
 | 
					
						
							|  |  |  |  |                     Err(err) => {
 | 
					
						
							|  |  |  |  |                         eprintln!("Failed to spawn {}: {:?}", program_name, err);
 | 
					
						
							|  |  |  |  |                         None
 | 
					
						
							|  |  |  |  |                     }
 | 
					
						
							|  |  |  |  |                 }
 | 
					
						
							|  |  |  |  |             }
 | 
					
						
							|  |  |  |  |         };
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-31 17:30:17 -07:00
										 |  |  |  |         if config.explicit_release.is_none() && now.elapsed().as_secs() > config.update_poll_secs {
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |             match update(config_file) {
 | 
					
						
							|  |  |  |  |                 Ok(true) => {
 | 
					
						
							|  |  |  |  |                     // Update successful, kill current process so it will be restart
 | 
					
						
							|  |  |  |  |                     if let Some(ref mut child) = child_option {
 | 
					
						
							| 
									
										
										
										
											2019-07-01 14:08:30 -07:00
										 |  |  |  |                         stop_process(child).unwrap_or_else(|err| {
 | 
					
						
							|  |  |  |  |                             eprintln!("Failed to stop child: {:?}", err);
 | 
					
						
							|  |  |  |  |                         });
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |                     }
 | 
					
						
							|  |  |  |  |                 }
 | 
					
						
							|  |  |  |  |                 Ok(false) => {} // No update available
 | 
					
						
							|  |  |  |  |                 Err(err) => {
 | 
					
						
							|  |  |  |  |                     eprintln!("Failed to apply update: {:?}", err);
 | 
					
						
							|  |  |  |  |                 }
 | 
					
						
							|  |  |  |  |             };
 | 
					
						
							|  |  |  |  |             now = Instant::now();
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2019-07-01 17:10:14 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |         if let Ok(()) = signal_receiver.recv_timeout(Duration::from_secs(1)) {
 | 
					
						
							|  |  |  |  |             // Handle SIGTERM...
 | 
					
						
							|  |  |  |  |             if let Some(ref mut child) = child_option {
 | 
					
						
							|  |  |  |  |                 stop_process(child).unwrap_or_else(|err| {
 | 
					
						
							|  |  |  |  |                     eprintln!("Failed to stop child: {:?}", err);
 | 
					
						
							|  |  |  |  |                 });
 | 
					
						
							|  |  |  |  |             }
 | 
					
						
							|  |  |  |  |             std::process::exit(0);
 | 
					
						
							|  |  |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:12:50 -07:00
										 |  |  |  |     }
 | 
					
						
							| 
									
										
										
										
											2019-03-15 10:54:54 -07:00
										 |  |  |  | }
 |