# HG changeset patch # User Jiri Vanek # Date 1550081675 -3600 # Node ID 0ec171908528cb70b953fa3d9652d7781c8b5ed1 # Parent cb75d5ae33e088bad120b3d04fb92e38b457d466 Os trait implemented for windows * rust-launcher/src/dirs_paths_helper.rs: added (get_os) os dependent factory method to provide os impl with debug off. (check_config_files_paths_global) split to os independent (check_config_files_paths) and rest linux only * rust-launcher/src/jars_helper.rs: (get_bootclasspath) fixed issue with hardcoded cp delimiter in xbootclasspath * rust-launcher/src/main.rs: added (get_os) os dependent factory method to provide os impl with debug parameter * rust-launcher/src/os_access.rs: implemented os trait for windows * rust-launcher/src/utils.rs: better recognition of rop_dir and parent dir in (try_jre_exists_on_path) diff -r cb75d5ae33e0 -r 0ec171908528 ChangeLog --- a/ChangeLog Wed Feb 13 18:05:44 2019 +0100 +++ b/ChangeLog Wed Feb 13 19:14:35 2019 +0100 @@ -1,3 +1,13 @@ +2019-02-13 Alex Kashchenko + + Os trait implemented for windows + * rust-launcher/src/dirs_paths_helper.rs: added (get_os) os dependent factory method to provide os impl with debug off. + (check_config_files_paths_global) split to os independent (check_config_files_paths) and rest linux only + * rust-launcher/src/jars_helper.rs: (get_bootclasspath) fixed issue with hardcoded cp delimiter in xbootclasspath + * rust-launcher/src/main.rs: added (get_os) os dependent factory method to provide os impl with debug parameter + * rust-launcher/src/os_access.rs: implemented os trait for windows + * rust-launcher/src/utils.rs: better recognition of rop_dir and parent dir in (try_jre_exists_on_path) + 2019-02-13 Jiri Vanek Propagating unimplemented DownloadService2. Added tests for it diff -r cb75d5ae33e0 -r 0ec171908528 rust-launcher/src/dirs_paths_helper.rs --- a/rust-launcher/src/dirs_paths_helper.rs Wed Feb 13 18:05:44 2019 +0100 +++ b/rust-launcher/src/dirs_paths_helper.rs Wed Feb 13 19:14:35 2019 +0100 @@ -84,18 +84,34 @@ use os_access; use utils::tests_utils as tu; + #[cfg(not(windows))] + fn get_os() -> os_access::Linux { + os_access::Linux::new(false) + } + + #[cfg(windows)] + fn get_os() -> os_access::Windows { + os_access::Windows::new(false) + } + + + #[test] + fn check_config_files_paths() { + let os = get_os(); + let p3 = super::get_itw_config_file(&os); + assert_ne!(None, p3); + println!("{}", p3.clone().expect("unwrap failed").display()); + assert_eq!(true, p3.clone().expect("unwrap failed").display().to_string().contains("icedtea-web")); + assert_eq!(true, p3.clone().expect("unwrap failed").display().to_string().ends_with("deployment.properties")); + } + #[test] #[cfg(not(windows))] - fn check_config_files_paths() { + fn check_config_files_paths_global() { let os = os_access::Linux::new(false); - let p3 = super::get_itw_config_file(&os); let p6 = super::get_itw_global_config_file(&os); - assert_ne!(None, p3); assert_ne!(None, p6); - println!("{}", p3.clone().expect("unwrap failed").display()); println!("{}", p6.clone().expect("unwrap failed").display()); - assert_eq!(true, p3.clone().expect("unwrap failed").display().to_string().contains("icedtea-web")); - assert_eq!(true, p3.clone().expect("unwrap failed").display().to_string().ends_with("deployment.properties")); assert_eq!(true, p6.clone().expect("unwrap failed").display().to_string().ends_with("deployment.properties")); } diff -r cb75d5ae33e0 -r 0ec171908528 rust-launcher/src/jars_helper.rs --- a/rust-launcher/src/jars_helper.rs Wed Feb 13 18:05:44 2019 +0100 +++ b/rust-launcher/src/jars_helper.rs Wed Feb 13 19:14:35 2019 +0100 @@ -158,8 +158,7 @@ } pub fn get_bootclasspath(jre_path: &std::path::PathBuf, os: &os_access::Os) -> String { - let mut result = String::from("-Xbootclasspath/a"); - result.push(os.get_classpath_separator()); + let mut result = String::from("-Xbootclasspath/a:"); result.push_str(&compose_class_path(get_bootcp_members(jre_path, os), os)); result } diff -r cb75d5ae33e0 -r 0ec171908528 rust-launcher/src/main.rs --- a/rust-launcher/src/main.rs Wed Feb 13 18:05:44 2019 +0100 +++ b/rust-launcher/src/main.rs Wed Feb 13 19:14:35 2019 +0100 @@ -12,13 +12,23 @@ use os_access::Os; use std::env; +#[cfg(not(windows))] +fn get_os(debug: bool) -> os_access::Linux { + os_access::Linux::new(debug) +} + +#[cfg(windows)] +fn get_os(debug: bool) -> os_access::Windows { + os_access::Windows::new(debug) +} + fn is_debug_on() -> bool { match is_debug_on_testable(env::args().collect::>()) { Some(val) => { return val; } _none => { - let os = os_access::Linux::new(false); + let os = get_os(false); return property_from_files_resolver::try_main_verbose_from_properties(&os); } } @@ -63,7 +73,7 @@ } fn main() { - let os = os_access::Linux::new(is_debug_on()); + let os = get_os(is_debug_on()); os.log(&dirs_paths_helper::path_to_string(&dirs_paths_helper::current_program())); let mut info1 = String::new(); write!(&mut info1, "itw-rust-debug: trying jdk over properties ({})", property_from_file::JRE_PROPERTY_NAME).expect("unwrap failed"); diff -r cb75d5ae33e0 -r 0ec171908528 rust-launcher/src/os_access.rs --- a/rust-launcher/src/os_access.rs Wed Feb 13 18:05:44 2019 +0100 +++ b/rust-launcher/src/os_access.rs Wed Feb 13 19:14:35 2019 +0100 @@ -3,6 +3,8 @@ use std::env; use std::fmt::Write; + + pub trait Os { // logging "api" can change fn log(&self, s: &str); @@ -25,16 +27,19 @@ fn get_exec_suffixes(&self) -> &'static [&'static str]; } +#[cfg(not(windows))] pub struct Linux { verbose: bool, } +#[cfg(not(windows))] impl Linux { pub fn new(debug: bool) -> Linux { Linux { verbose: debug } } } +#[cfg(not(windows))] impl Os for Linux { fn log(&self, s: &str) { if self.verbose { @@ -127,3 +132,419 @@ &[""] } } + + +#[cfg(windows)] +pub struct Windows { + verbose: bool, +} + +#[cfg(windows)] +impl Windows { + pub fn new(debug: bool) -> Windows { + Windows { verbose: debug } + } + +} + +#[cfg(windows)] +impl Os for Windows { + fn log(&self, s: &str) { + if self.verbose { + println!("{}", s); + } + } + + fn info(&self, s: &str) { + println!("{}", s); + } + + fn get_registry_jdk(&self) -> Option { + std::panic::catch_unwind(|| { + let path = win::jdk_registry_path(); + Some(std::path::PathBuf::from(path)) + }).unwrap_or_else(|_e| { + // show_error_message(errloc_msg(&e)); + None + }) + } + + fn get_system_config_javadir(&self) -> Option { + None + } + + fn get_user_config_dir(&self) -> Option { + match self.get_home() { + Some(mut p) => { + p.push(".config"); + p.push(dirs_paths_helper::ICEDTEA_WEB); + Some(p) + } + None => None + } + } + + fn get_legacy_system_config_javadir(&self) -> Option { + None + } + + fn get_legacy_user_config_dir(&self) -> Option { + None + } + + fn spawn_java_process(&self, jre_dir: &std::path::PathBuf, args: &Vec) -> std::process::Child { + let mut bin_java = jre_dir.clone(); + bin_java.push("bin"); + bin_java.push("java"); + let mut cmd = std::process::Command::new(&bin_java); + for ar in args.into_iter() { + cmd.arg(ar); + } + cmd.stdin(std::process::Stdio::inherit()); + cmd.stdout(std::process::Stdio::inherit()); + cmd.stderr(std::process::Stdio::inherit()); + let mut info = String::new(); + write!(&mut info, "itw-rust-debug: command {}", format!("{:?}", cmd)).expect("unwrap failed"); + self.log(&info); + let res = cmd.spawn(); + match res { + Ok(child) => child, + Err(_) => panic!("Error spawning JVM process, \ + java executable: [{}], arguments: [{:?}]", bin_java.into_os_string().to_str().expect("path should unwrap"), args) + } + } + + fn get_home(&self) -> Option { + match env::var("USERPROFILE") { + Ok(war) => { + let home_var_path = std::path::PathBuf::from(war); + if dirs_paths_helper::is_dir(&home_var_path) { + return Some(home_var_path); + } + } + Err(_) => {} + } + None + } + + fn get_classpath_separator(&self) -> char { + ';' + } + + //on linux, java is known to be compiled witout any suffix, on windows, it should be .exe + fn get_exec_suffixes(&self) -> &'static [&'static str] { + &[".exe"] + } +} + + +#[cfg(windows)] +#[allow(non_snake_case)] +#[allow(non_camel_case_types)] +mod win { + // https://crates.io/crates/scopeguard + macro_rules! defer { + ($e:expr) => { + let _deferred = ScopeGuard::new((), |_| $e); + } + } + + pub struct ScopeGuard where F: FnMut(&mut T) { + __dropfn: F, + __value: T + } + + impl ScopeGuard where F: FnMut(&mut T) { + pub fn new(v: T, dropfn: F) -> ScopeGuard { + ScopeGuard { + __value: v, + __dropfn: dropfn + } + } + } + + impl Drop for ScopeGuard where F: FnMut(&mut T) { + fn drop(&mut self) { + (self.__dropfn)(&mut self.__value) + } + } + + // https://crates.io/crates/errloc_macros + macro_rules! errloc { + () => { + concat!(file!(), ':', line!()) + } + } + + fn errloc_msg<'a>(e: &'a Box) -> &'a str { + match e.downcast_ref::<&str>() { + Some(st) => st, + None => { + match e.downcast_ref::() { + Some(stw) => stw.as_str(), + None => "()", + } + }, + } + } + + // implementation + + use std; + use std::os::raw::*; + use std::ptr::{null, null_mut}; + + // constants + const CP_UTF8: c_ulong = 65001; + const FORMAT_MESSAGE_ALLOCATE_BUFFER: c_ulong = 0x00000100; + const FORMAT_MESSAGE_FROM_SYSTEM: c_ulong = 0x00001000; + const FORMAT_MESSAGE_IGNORE_INSERTS: c_ulong = 0x00000200; + const LANG_NEUTRAL: c_ushort = 0x00; + const SUBLANG_DEFAULT: c_ushort = 0x01; + const ERROR_SUCCESS: c_ulong = 0; + const READ_CONTROL: c_ulong = 0x00020000; + const STANDARD_RIGHTS_READ: c_ulong = READ_CONTROL; + const KEY_QUERY_VALUE: c_ulong = 0x0001; + const KEY_ENUMERATE_SUB_KEYS: c_ulong = 0x0008; + const KEY_NOTIFY: c_ulong = 0x0010; + const SYNCHRONIZE: c_ulong = 0x00100000; + const REG_SZ: c_ulong = 1; + const KEY_READ: c_ulong = ( + STANDARD_RIGHTS_READ | + KEY_QUERY_VALUE | + KEY_ENUMERATE_SUB_KEYS | + KEY_NOTIFY + ) & (!SYNCHRONIZE); + const HKEY_LOCAL_MACHINE: *mut c_void = 0x80000002 as *mut c_void; + + // function declarations + + extern "system" { + fn MultiByteToWideChar( + CodePage: c_uint, + dwFlags: c_ulong, + lpMultiByteStr: *const c_char, + cbMultiByte: c_int, + lpWideCharStr: *mut c_ushort, + cchWideChar: c_int + ) -> c_int; + + fn WideCharToMultiByte( + CodePage: c_uint, + dwFlags: c_ulong, + lpWideCharStr: *const c_ushort, + cchWideChar: c_int, + lpMultiByteStr: *mut c_char, + cbMultiByte: c_int, + lpDefaultChar: *const c_char, + lpUsedDefaultChar: *mut c_int + ) -> c_int; + + fn GetLastError() -> c_ulong; + + fn FormatMessageW( + dwFlags: c_ulong, + lpSource: *const c_void, + dwMessageId: c_ulong, + dwLanguageId: c_ulong, + lpBuffer: *mut c_ushort, + nSize: c_ulong, + Arguments: *mut *mut c_char + ) -> c_ulong; + + fn LocalFree( + hMem: *mut c_void + ) -> *mut c_void; + + fn RegOpenKeyExW( + hKey: *mut c_void, + lpSubKey: *const c_ushort, + ulOptions: c_ulong, + samDesired: c_ulong, + phkResult: *mut *mut c_void + ) -> c_long; + + fn RegCloseKey( + hKey: *mut c_void + ) -> c_long; + + fn RegQueryValueExW( + hKey: *mut c_void, + lpValueName: *const c_ushort, + lpReserved: *mut c_ulong, + lpType: *mut c_ulong, + lpData: *mut c_uchar, + lpcbData: *mut c_ulong + ) -> c_long; + } + + // windows-specific utilities + + fn MAKELANGID(p: c_ushort, s: c_ushort) -> c_ushort { + (s << 10 | p) + } + + fn widen(st: &str) -> Vec { + unsafe { + let size_needed = MultiByteToWideChar( + CP_UTF8, + 0, + st.as_ptr() as *mut i8, + st.len() as c_int, + null_mut::(), + 0); + if 0 == size_needed { + panic!(format!("Error on string widen calculation, \ + string: [{}], error: [{}]", st, errcode_to_string(GetLastError()))); + } + let mut res: Vec = Vec::new(); + res.resize((size_needed + 1) as usize, 0); + let chars_copied = MultiByteToWideChar( + CP_UTF8, + 0, + st.as_ptr() as *mut i8, + st.len() as c_int, + res.as_mut_ptr(), + size_needed); + if chars_copied != size_needed { + panic!(format!("Error on string widen execution, \ + string: [{}], error: [{}]", st, errcode_to_string(GetLastError()))); + } + res.resize(size_needed as usize, 0); + res + } + } + + fn narrow(wst: &[u16]) -> String { + unsafe { + let size_needed = WideCharToMultiByte( + CP_UTF8, + 0, + wst.as_ptr(), + wst.len() as c_int, + null_mut::(), + 0, + null::(), + null_mut::()); + if 0 == size_needed { + panic!(format!("Error on string narrow calculation, \ + string length: [{}], error code: [{}]", wst.len(), GetLastError())); + } + let mut vec: Vec = Vec::new(); + vec.resize(size_needed as usize, 0); + let bytes_copied = WideCharToMultiByte( + CP_UTF8, + 0, + wst.as_ptr(), + wst.len() as c_int, + vec.as_mut_ptr() as *mut i8, + size_needed, + null::(), + null_mut::()); + if bytes_copied != size_needed { + panic!(format!("Error on string narrow execution, \ + string length: [{}], error code: [{}]", vec.len(), GetLastError())); + } + String::from_utf8(vec).expect(errloc!()) + } + } + + fn errcode_to_string(code: c_ulong) -> String { + if 0 == code { + return String::new(); + } + unsafe { + let mut buf: *mut u16 = null_mut::(); + let size = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + null::(), + code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) as c_ulong, + std::mem::transmute::<*mut *mut u16, *mut u16>(&mut buf), + 0, + null_mut::<*mut c_char>()); + if 0 == size { + return format!("Cannot format code: [{}] \ + into message, error code: [{}]", code, GetLastError()); + } + defer!({ + LocalFree(buf as *mut c_void); + }); + if size <= 2 { + return format!("code: [{}], message: []", code); + } + std::panic::catch_unwind(|| { + let slice = std::slice::from_raw_parts(buf, (size - 2) as usize); + let msg = narrow(slice); + format!("code: [{}], message: [{}]", code, msg) + }).unwrap_or_else(|e| { + format!("Cannot format code: [{}] \ + into message, narrow error: [{}]", code, errloc_msg(&e)) + }) + } + } + + pub fn jdk_registry_path() -> String { + let jdk_key_name = "SOFTWARE\\JavaSoft\\Java Development Kit\\1.8"; + let wjdk_key_name = widen(jdk_key_name); + let java_home = "JavaHome"; + let wjava_home = widen("JavaHome"); + unsafe { + // open root + let mut jdk_key = null_mut::(); + let err_jdk = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + wjdk_key_name.as_ptr(), + 0, + KEY_READ | KEY_ENUMERATE_SUB_KEYS, + &mut jdk_key) as u32; + if ERROR_SUCCESS != err_jdk { + panic!(format!("Error opening registry key, \ + name: [{}], message: [{}]", jdk_key_name, errcode_to_string(err_jdk))); + } + defer!({ + RegCloseKey(jdk_key); + }); + // find out value len + let mut value_len: c_ulong = 0; + let mut value_type: c_ulong = 0; + let err_len = RegQueryValueExW( + jdk_key, + wjava_home.as_ptr(), + null_mut::(), + &mut value_type, + null_mut::(), + &mut value_len) as u32; + if ERROR_SUCCESS != err_len || !(value_len > 0) || REG_SZ != value_type { + panic!(format!("Error opening registry value len, \ + key: [{}], value: [{}], message: [{}]", jdk_key_name, java_home, errcode_to_string(err_len))); + } + // get value + let mut wvalue: Vec = Vec::new(); + wvalue.resize((value_len as usize) / std::mem::size_of::(), 0); + let err_val = RegQueryValueExW( + jdk_key, + wjava_home.as_ptr(), + null_mut::(), + null_mut::(), + wvalue.as_mut_ptr() as *mut c_uchar, + &mut value_len) as u32; + if ERROR_SUCCESS != err_val { + panic!(format!("Error opening registry value, \ + key: [{}], value: [{}], message: [{}]", jdk_key_name, java_home, errcode_to_string(err_val))); + } + // format and return path + let slice = std::slice::from_raw_parts(wvalue.as_ptr(), wvalue.len() - 1 as usize); + let jpath_badslash = narrow(slice); + let mut jpath = jpath_badslash.replace("\\", "/"); + if '/' as u8 != jpath.as_bytes()[jpath.len() - 1] { + jpath.push('/'); + } + return jpath; + } + } + + +} diff -r cb75d5ae33e0 -r 0ec171908528 rust-launcher/src/utils.rs --- a/rust-launcher/src/utils.rs Wed Feb 13 18:05:44 2019 +0100 +++ b/rust-launcher/src/utils.rs Wed Feb 13 19:14:35 2019 +0100 @@ -154,7 +154,7 @@ #[test] fn try_jre_exists_on_path() { - let top_dir = fake_jre(true); + let top_dir = fake_jre(true).canonicalize().expect("canonicalize failed"); let mut master_dir = top_dir.clone(); master_dir.push("bin"); let v1 = super::get_jdk_from_path_conditionally_testable(Some(fo::from(master_dir.clone())), hardcoded_paths::ItwLibSearch::DISTRIBUTION, &TestLogger::create_new()); @@ -191,7 +191,7 @@ let v3 = super::get_jdk_from_path_conditionally_testable(Some(fo::from(master_dir.clone())), hardcoded_paths::ItwLibSearch::BOTH, &TestLogger::create_new()); debuggable_remove_dir(&master_dir); assert_eq!(None, v1); - let parent = std::path::PathBuf::from(master_dir.parent().expect("just created")); + let parent = std::path::PathBuf::from(master_dir.parent().expect("just created")).canonicalize().expect("canonicalize failed"); assert_eq!(Some(parent.clone()), v2); assert_eq!(Some(parent.clone()), v3); }