11496ba7bSLoGin use core::ffi::c_void; 2ab5c8ca4Slogin 3971462beSGnoCiYeH use alloc::{ 4971462beSGnoCiYeH string::{String, ToString}, 5971462beSGnoCiYeH sync::Arc, 6971462beSGnoCiYeH vec::Vec, 7971462beSGnoCiYeH }; 891e9d4abSLoGin use system_error::SystemError; 91496ba7bSLoGin 10971462beSGnoCiYeH use super::{ 11971462beSGnoCiYeH abi::WaitOption, 12bf4a4899SLoGin exit::kernel_wait4, 13971462beSGnoCiYeH fork::{CloneFlags, KernelCloneArgs}, 140d9b7d92SLoGin resource::{RLimit64, RLimitID, RUsage, RUsageWho}, 15bf4a4899SLoGin KernelStack, Pid, ProcessManager, 16971462beSGnoCiYeH }; 17ab5c8ca4Slogin use crate::{ 18bf4a4899SLoGin arch::{interrupt::TrapFrame, MMArch}, 190d9b7d92SLoGin filesystem::{ 200d9b7d92SLoGin procfs::procfs_register_pid, 210d9b7d92SLoGin vfs::{file::FileDescriptorVec, MAX_PATHLEN}, 220d9b7d92SLoGin }, 234fda81ceSLoGin mm::{ucontext::UserStack, verify_area, MemoryManagementArch, VirtAddr}, 241496ba7bSLoGin process::ProcessControlBlock, 25971462beSGnoCiYeH sched::completion::Completion, 261496ba7bSLoGin syscall::{ 27bf4a4899SLoGin user_access::{check_and_clone_cstr, check_and_clone_cstr_array, UserBufferWriter}, 2891e9d4abSLoGin Syscall, 291496ba7bSLoGin }, 30ab5c8ca4Slogin }; 31ab5c8ca4Slogin 328c6f2184Ssspphh //参考资料:https://code.dragonos.org.cn/xref/linux-6.1.9/include/uapi/linux/utsname.h#17 338c6f2184Ssspphh #[repr(C)] 348c6f2184Ssspphh #[derive(Debug, Clone, Copy)] 358c6f2184Ssspphh pub struct PosixOldUtsName { 368c6f2184Ssspphh pub sysname: [u8; 65], 378c6f2184Ssspphh pub nodename: [u8; 65], 388c6f2184Ssspphh pub release: [u8; 65], 398c6f2184Ssspphh pub version: [u8; 65], 408c6f2184Ssspphh pub machine: [u8; 65], 418c6f2184Ssspphh } 428c6f2184Ssspphh 438c6f2184Ssspphh impl PosixOldUtsName { 448c6f2184Ssspphh pub fn new() -> Self { 458c6f2184Ssspphh const SYS_NAME: &[u8] = b"DragonOS"; 468c6f2184Ssspphh const NODENAME: &[u8] = b"DragonOS"; 478c6f2184Ssspphh const RELEASE: &[u8] = env!("CARGO_PKG_VERSION").as_bytes(); 488c6f2184Ssspphh const VERSION: &[u8] = env!("CARGO_PKG_VERSION").as_bytes(); 498c6f2184Ssspphh 508c6f2184Ssspphh #[cfg(target_arch = "x86_64")] 518c6f2184Ssspphh const MACHINE: &[u8] = b"x86_64"; 528c6f2184Ssspphh 538c6f2184Ssspphh #[cfg(target_arch = "aarch64")] 548c6f2184Ssspphh const MACHINE: &[u8] = b"aarch64"; 558c6f2184Ssspphh 568c6f2184Ssspphh #[cfg(target_arch = "riscv64")] 578c6f2184Ssspphh const MACHINE: &[u8] = b"riscv64"; 588c6f2184Ssspphh 598c6f2184Ssspphh let mut r = Self { 608c6f2184Ssspphh sysname: [0; 65], 618c6f2184Ssspphh nodename: [0; 65], 628c6f2184Ssspphh release: [0; 65], 638c6f2184Ssspphh version: [0; 65], 648c6f2184Ssspphh machine: [0; 65], 658c6f2184Ssspphh }; 668c6f2184Ssspphh 678c6f2184Ssspphh r.sysname[0..SYS_NAME.len()].copy_from_slice(SYS_NAME); 688c6f2184Ssspphh r.nodename[0..NODENAME.len()].copy_from_slice(NODENAME); 698c6f2184Ssspphh r.release[0..RELEASE.len()].copy_from_slice(RELEASE); 708c6f2184Ssspphh r.version[0..VERSION.len()].copy_from_slice(VERSION); 718c6f2184Ssspphh r.machine[0..MACHINE.len()].copy_from_slice(MACHINE); 728c6f2184Ssspphh 738c6f2184Ssspphh return r; 748c6f2184Ssspphh } 758c6f2184Ssspphh } 768c6f2184Ssspphh 77ab5c8ca4Slogin impl Syscall { 78840045afSLoGin pub fn fork(frame: &TrapFrame) -> Result<usize, SystemError> { 794ad52e57S裕依2439 ProcessManager::fork(frame, CloneFlags::empty()).map(|pid| pid.into()) 80ab5c8ca4Slogin } 81ab5c8ca4Slogin 82840045afSLoGin pub fn vfork(frame: &TrapFrame) -> Result<usize, SystemError> { 83bf4a4899SLoGin // 由于Linux vfork需要保证子进程先运行(除非子进程调用execve或者exit), 84bf4a4899SLoGin // 而我们目前没有实现这个特性,所以暂时使用fork代替vfork(linux文档表示这样也是也可以的) 85bf4a4899SLoGin Self::fork(frame) 86bf4a4899SLoGin 87bf4a4899SLoGin // 下面是以前的实现,除非我们实现了子进程先运行的特性,否则不要使用,不然会导致父进程数据损坏 88bf4a4899SLoGin // ProcessManager::fork( 89bf4a4899SLoGin // frame, 90bf4a4899SLoGin // CloneFlags::CLONE_VM | CloneFlags::CLONE_FS | CloneFlags::CLONE_SIGNAL, 91bf4a4899SLoGin // ) 92bf4a4899SLoGin // .map(|pid| pid.into()) 93ab5c8ca4Slogin } 94ab5c8ca4Slogin 95ab5c8ca4Slogin pub fn execve( 961496ba7bSLoGin path: *const u8, 971496ba7bSLoGin argv: *const *const u8, 981496ba7bSLoGin envp: *const *const u8, 991496ba7bSLoGin frame: &mut TrapFrame, 1001496ba7bSLoGin ) -> Result<(), SystemError> { 101*2eab6dd7S曾俊 // debug!( 1021496ba7bSLoGin // "execve path: {:?}, argv: {:?}, envp: {:?}\n", 1031496ba7bSLoGin // path, 1041496ba7bSLoGin // argv, 1051496ba7bSLoGin // envp 1061496ba7bSLoGin // ); 107*2eab6dd7S曾俊 // debug!( 108971462beSGnoCiYeH // "before execve: strong count: {}", 109971462beSGnoCiYeH // Arc::strong_count(&ProcessManager::current_pcb()) 110971462beSGnoCiYeH // ); 111971462beSGnoCiYeH 1121496ba7bSLoGin if path.is_null() { 1131496ba7bSLoGin return Err(SystemError::EINVAL); 1141496ba7bSLoGin } 1151496ba7bSLoGin 1161496ba7bSLoGin let x = || { 1171496ba7bSLoGin let path: String = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; 1181496ba7bSLoGin let argv: Vec<String> = check_and_clone_cstr_array(argv)?; 1191496ba7bSLoGin let envp: Vec<String> = check_and_clone_cstr_array(envp)?; 1201496ba7bSLoGin Ok((path, argv, envp)) 1211496ba7bSLoGin }; 1221496ba7bSLoGin let r: Result<(String, Vec<String>, Vec<String>), SystemError> = x(); 1231496ba7bSLoGin if let Err(e) = r { 1241496ba7bSLoGin panic!("Failed to execve: {:?}", e); 1251496ba7bSLoGin } 1261496ba7bSLoGin let (path, argv, envp) = r.unwrap(); 1271496ba7bSLoGin ProcessManager::current_pcb() 1281496ba7bSLoGin .basic_mut() 1291496ba7bSLoGin .set_name(ProcessControlBlock::generate_name(&path, &argv)); 1301496ba7bSLoGin 131876cb89eSGnoCiYeH Self::do_execve(path, argv, envp, frame)?; 132876cb89eSGnoCiYeH 133876cb89eSGnoCiYeH // 关闭设置了O_CLOEXEC的文件描述符 134876cb89eSGnoCiYeH let fd_table = ProcessManager::current_pcb().fd_table(); 135876cb89eSGnoCiYeH fd_table.write().close_on_exec(); 136*2eab6dd7S曾俊 // debug!( 137971462beSGnoCiYeH // "after execve: strong count: {}", 138971462beSGnoCiYeH // Arc::strong_count(&ProcessManager::current_pcb()) 139971462beSGnoCiYeH // ); 140876cb89eSGnoCiYeH 141876cb89eSGnoCiYeH return Ok(()); 142ab5c8ca4Slogin } 143ab5c8ca4Slogin 144ab5c8ca4Slogin pub fn wait4( 1451496ba7bSLoGin pid: i64, 1461496ba7bSLoGin wstatus: *mut i32, 1471496ba7bSLoGin options: i32, 148ab5c8ca4Slogin rusage: *mut c_void, 149ab5c8ca4Slogin ) -> Result<usize, SystemError> { 150bf4a4899SLoGin let options = WaitOption::from_bits(options as u32).ok_or(SystemError::EINVAL)?; 151bf4a4899SLoGin 152bf4a4899SLoGin let wstatus_buf = if wstatus.is_null() { 153bf4a4899SLoGin None 154bf4a4899SLoGin } else { 155bf4a4899SLoGin Some(UserBufferWriter::new( 156bf4a4899SLoGin wstatus, 157bf4a4899SLoGin core::mem::size_of::<i32>(), 158bf4a4899SLoGin true, 159bf4a4899SLoGin )?) 160b7b843beSGnoCiYeH }; 161b7b843beSGnoCiYeH 162bf4a4899SLoGin let mut tmp_rusage = if rusage.is_null() { 163bf4a4899SLoGin None 164bf4a4899SLoGin } else { 165bf4a4899SLoGin Some(RUsage::default()) 166bf4a4899SLoGin }; 1671496ba7bSLoGin 168bf4a4899SLoGin let r = kernel_wait4(pid, wstatus_buf, options, tmp_rusage.as_mut())?; 1691496ba7bSLoGin 170bf4a4899SLoGin if !rusage.is_null() { 171bf4a4899SLoGin let mut rusage_buf = UserBufferWriter::new::<RUsage>( 172bf4a4899SLoGin rusage as *mut RUsage, 173bf4a4899SLoGin core::mem::size_of::<RUsage>(), 174bf4a4899SLoGin true, 175b7b843beSGnoCiYeH )?; 176bf4a4899SLoGin rusage_buf.copy_one_to_user(&tmp_rusage.unwrap(), 0)?; 1771496ba7bSLoGin } 178bf4a4899SLoGin return Ok(r); 179ab5c8ca4Slogin } 180ab5c8ca4Slogin 181ab5c8ca4Slogin /// # 退出进程 182ab5c8ca4Slogin /// 183ab5c8ca4Slogin /// ## 参数 184ab5c8ca4Slogin /// 185ab5c8ca4Slogin /// - status: 退出状态 186ab5c8ca4Slogin pub fn exit(status: usize) -> ! { 1871496ba7bSLoGin ProcessManager::exit(status); 188ab5c8ca4Slogin } 189ab5c8ca4Slogin 1901496ba7bSLoGin /// @brief 获取当前进程的pid 1911496ba7bSLoGin pub fn getpid() -> Result<Pid, SystemError> { 1921496ba7bSLoGin let current_pcb = ProcessManager::current_pcb(); 193393f6915SLoGin return Ok(current_pcb.tgid()); 1941496ba7bSLoGin } 1951496ba7bSLoGin 1961496ba7bSLoGin /// @brief 获取指定进程的pgid 1971496ba7bSLoGin /// 1981496ba7bSLoGin /// @param pid 指定一个进程号 1991496ba7bSLoGin /// 2001496ba7bSLoGin /// @return 成功,指定进程的进程组id 2011496ba7bSLoGin /// @return 错误,不存在该进程 2021496ba7bSLoGin pub fn getpgid(mut pid: Pid) -> Result<Pid, SystemError> { 2031496ba7bSLoGin if pid == Pid(0) { 2041496ba7bSLoGin let current_pcb = ProcessManager::current_pcb(); 2051496ba7bSLoGin pid = current_pcb.pid(); 2061496ba7bSLoGin } 2071496ba7bSLoGin let target_proc = ProcessManager::find(pid).ok_or(SystemError::ESRCH)?; 2081496ba7bSLoGin return Ok(target_proc.basic().pgid()); 2091496ba7bSLoGin } 2101496ba7bSLoGin /// @brief 获取当前进程的父进程id 2111496ba7bSLoGin 2121496ba7bSLoGin /// 若为initproc则ppid设置为0 2131496ba7bSLoGin pub fn getppid() -> Result<Pid, SystemError> { 2141496ba7bSLoGin let current_pcb = ProcessManager::current_pcb(); 2151496ba7bSLoGin return Ok(current_pcb.basic().ppid()); 216ab5c8ca4Slogin } 217971462beSGnoCiYeH 218971462beSGnoCiYeH pub fn clone( 219840045afSLoGin current_trapframe: &TrapFrame, 220971462beSGnoCiYeH clone_args: KernelCloneArgs, 221971462beSGnoCiYeH ) -> Result<usize, SystemError> { 222971462beSGnoCiYeH let flags = clone_args.flags; 223971462beSGnoCiYeH 224971462beSGnoCiYeH let vfork = Arc::new(Completion::new()); 225971462beSGnoCiYeH 226971462beSGnoCiYeH if flags.contains(CloneFlags::CLONE_PIDFD) 227971462beSGnoCiYeH && flags.contains(CloneFlags::CLONE_PARENT_SETTID) 228971462beSGnoCiYeH { 229971462beSGnoCiYeH return Err(SystemError::EINVAL); 230971462beSGnoCiYeH } 231971462beSGnoCiYeH 232971462beSGnoCiYeH let current_pcb = ProcessManager::current_pcb(); 233971462beSGnoCiYeH let new_kstack = KernelStack::new()?; 234971462beSGnoCiYeH let name = current_pcb.basic().name().to_string(); 235971462beSGnoCiYeH let pcb = ProcessControlBlock::new(name, new_kstack); 236971462beSGnoCiYeH // 克隆pcb 237971462beSGnoCiYeH ProcessManager::copy_process(¤t_pcb, &pcb, clone_args, current_trapframe)?; 238971462beSGnoCiYeH ProcessManager::add_pcb(pcb.clone()); 239971462beSGnoCiYeH 240971462beSGnoCiYeH // 向procfs注册进程 241971462beSGnoCiYeH procfs_register_pid(pcb.pid()).unwrap_or_else(|e| { 242971462beSGnoCiYeH panic!( 243971462beSGnoCiYeH "fork: Failed to register pid to procfs, pid: [{:?}]. Error: {:?}", 244971462beSGnoCiYeH pcb.pid(), 245971462beSGnoCiYeH e 246971462beSGnoCiYeH ) 247971462beSGnoCiYeH }); 248971462beSGnoCiYeH 249971462beSGnoCiYeH if flags.contains(CloneFlags::CLONE_VFORK) { 25052bcb59eSGnoCiYeH pcb.thread.write_irqsave().vfork_done = Some(vfork.clone()); 251971462beSGnoCiYeH } 252971462beSGnoCiYeH 25352bcb59eSGnoCiYeH if pcb.thread.read_irqsave().set_child_tid.is_some() { 25452bcb59eSGnoCiYeH let addr = pcb.thread.read_irqsave().set_child_tid.unwrap(); 255971462beSGnoCiYeH let mut writer = 256971462beSGnoCiYeH UserBufferWriter::new(addr.as_ptr::<i32>(), core::mem::size_of::<i32>(), true)?; 257971462beSGnoCiYeH writer.copy_one_to_user(&(pcb.pid().data() as i32), 0)?; 258971462beSGnoCiYeH } 259971462beSGnoCiYeH 260971462beSGnoCiYeH ProcessManager::wakeup(&pcb).unwrap_or_else(|e| { 261971462beSGnoCiYeH panic!( 262971462beSGnoCiYeH "fork: Failed to wakeup new process, pid: [{:?}]. Error: {:?}", 263971462beSGnoCiYeH pcb.pid(), 264971462beSGnoCiYeH e 265971462beSGnoCiYeH ) 266971462beSGnoCiYeH }); 267971462beSGnoCiYeH 268971462beSGnoCiYeH if flags.contains(CloneFlags::CLONE_VFORK) { 269971462beSGnoCiYeH // 等待子进程结束或者exec; 270971462beSGnoCiYeH vfork.wait_for_completion_interruptible()?; 271971462beSGnoCiYeH } 272971462beSGnoCiYeH 273971462beSGnoCiYeH return Ok(pcb.pid().0); 274971462beSGnoCiYeH } 275971462beSGnoCiYeH 276971462beSGnoCiYeH /// 设置线程地址 277971462beSGnoCiYeH pub fn set_tid_address(ptr: usize) -> Result<usize, SystemError> { 2784fda81ceSLoGin verify_area(VirtAddr::new(ptr), core::mem::size_of::<i32>()) 2794fda81ceSLoGin .map_err(|_| SystemError::EFAULT)?; 280971462beSGnoCiYeH 281971462beSGnoCiYeH let pcb = ProcessManager::current_pcb(); 28252bcb59eSGnoCiYeH pcb.thread.write_irqsave().clear_child_tid = Some(VirtAddr::new(ptr)); 283971462beSGnoCiYeH Ok(pcb.pid.0) 284971462beSGnoCiYeH } 285393f6915SLoGin 286393f6915SLoGin pub fn gettid() -> Result<Pid, SystemError> { 287393f6915SLoGin let pcb = ProcessManager::current_pcb(); 288393f6915SLoGin Ok(pcb.pid) 289393f6915SLoGin } 29002e249f3SLoGin 29102e249f3SLoGin pub fn getuid() -> Result<usize, SystemError> { 29202e249f3SLoGin // todo: 增加credit功能之后,需要修改 29302e249f3SLoGin return Ok(0); 29402e249f3SLoGin } 29502e249f3SLoGin 29602e249f3SLoGin pub fn getgid() -> Result<usize, SystemError> { 29702e249f3SLoGin // todo: 增加credit功能之后,需要修改 29802e249f3SLoGin return Ok(0); 29902e249f3SLoGin } 30002e249f3SLoGin 30102e249f3SLoGin pub fn geteuid() -> Result<usize, SystemError> { 30202e249f3SLoGin // todo: 增加credit功能之后,需要修改 30302e249f3SLoGin return Ok(0); 30402e249f3SLoGin } 30502e249f3SLoGin 30602e249f3SLoGin pub fn getegid() -> Result<usize, SystemError> { 30702e249f3SLoGin // todo: 增加credit功能之后,需要修改 30802e249f3SLoGin return Ok(0); 30902e249f3SLoGin } 310be8cdf4bSLoGin 311be8cdf4bSLoGin pub fn get_rusage(who: i32, rusage: *mut RUsage) -> Result<usize, SystemError> { 312be8cdf4bSLoGin let who = RUsageWho::try_from(who)?; 313be8cdf4bSLoGin let mut writer = UserBufferWriter::new(rusage, core::mem::size_of::<RUsage>(), true)?; 314be8cdf4bSLoGin let pcb = ProcessManager::current_pcb(); 315be8cdf4bSLoGin let rusage = pcb.get_rusage(who).ok_or(SystemError::EINVAL)?; 316be8cdf4bSLoGin 317be8cdf4bSLoGin let ubuf = writer.buffer::<RUsage>(0).unwrap(); 318be8cdf4bSLoGin ubuf.copy_from_slice(&[rusage]); 319be8cdf4bSLoGin 320be8cdf4bSLoGin return Ok(0); 321be8cdf4bSLoGin } 3220d9b7d92SLoGin 3230d9b7d92SLoGin /// # 设置资源限制 3240d9b7d92SLoGin /// 3250d9b7d92SLoGin /// TODO: 目前暂时不支持设置资源限制,只提供读取默认值的功能 3260d9b7d92SLoGin /// 3270d9b7d92SLoGin /// ## 参数 3280d9b7d92SLoGin /// 3290d9b7d92SLoGin /// - pid: 进程号 3300d9b7d92SLoGin /// - resource: 资源类型 3310d9b7d92SLoGin /// - new_limit: 新的资源限制 3320d9b7d92SLoGin /// - old_limit: 旧的资源限制 3330d9b7d92SLoGin /// 3340d9b7d92SLoGin /// ## 返回值 3350d9b7d92SLoGin /// 3360d9b7d92SLoGin /// - 成功,0 3370d9b7d92SLoGin /// - 如果old_limit不为NULL,则返回旧的资源限制到old_limit 3380d9b7d92SLoGin /// 3390d9b7d92SLoGin pub fn prlimit64( 3400d9b7d92SLoGin _pid: Pid, 3410d9b7d92SLoGin resource: usize, 342bf4a4899SLoGin _new_limit: *const RLimit64, 3430d9b7d92SLoGin old_limit: *mut RLimit64, 3440d9b7d92SLoGin ) -> Result<usize, SystemError> { 3450d9b7d92SLoGin let resource = RLimitID::try_from(resource)?; 3460d9b7d92SLoGin let mut writer = None; 3470d9b7d92SLoGin 3480d9b7d92SLoGin if !old_limit.is_null() { 3490d9b7d92SLoGin writer = Some(UserBufferWriter::new( 3500d9b7d92SLoGin old_limit, 3510d9b7d92SLoGin core::mem::size_of::<RLimit64>(), 3520d9b7d92SLoGin true, 3530d9b7d92SLoGin )?); 3540d9b7d92SLoGin } 3550d9b7d92SLoGin 3560d9b7d92SLoGin match resource { 3570d9b7d92SLoGin RLimitID::Stack => { 3580d9b7d92SLoGin if let Some(mut writer) = writer { 3590d9b7d92SLoGin let mut rlimit = writer.buffer::<RLimit64>(0).unwrap()[0]; 3600d9b7d92SLoGin rlimit.rlim_cur = UserStack::DEFAULT_USER_STACK_SIZE as u64; 3610d9b7d92SLoGin rlimit.rlim_max = UserStack::DEFAULT_USER_STACK_SIZE as u64; 3620d9b7d92SLoGin } 3630d9b7d92SLoGin return Ok(0); 3640d9b7d92SLoGin } 3650d9b7d92SLoGin 3660d9b7d92SLoGin RLimitID::Nofile => { 3670d9b7d92SLoGin if let Some(mut writer) = writer { 3680d9b7d92SLoGin let mut rlimit = writer.buffer::<RLimit64>(0).unwrap()[0]; 3690d9b7d92SLoGin rlimit.rlim_cur = FileDescriptorVec::PROCESS_MAX_FD as u64; 3700d9b7d92SLoGin rlimit.rlim_max = FileDescriptorVec::PROCESS_MAX_FD as u64; 3710d9b7d92SLoGin } 3720d9b7d92SLoGin return Ok(0); 3730d9b7d92SLoGin } 3740d9b7d92SLoGin 3750d9b7d92SLoGin RLimitID::As | RLimitID::Rss => { 3760d9b7d92SLoGin if let Some(mut writer) = writer { 3770d9b7d92SLoGin let mut rlimit = writer.buffer::<RLimit64>(0).unwrap()[0]; 3780d9b7d92SLoGin rlimit.rlim_cur = MMArch::USER_END_VADDR.data() as u64; 3790d9b7d92SLoGin rlimit.rlim_max = MMArch::USER_END_VADDR.data() as u64; 3800d9b7d92SLoGin } 3810d9b7d92SLoGin return Ok(0); 3820d9b7d92SLoGin } 3830d9b7d92SLoGin 3840d9b7d92SLoGin _ => { 3850d9b7d92SLoGin return Err(SystemError::ENOSYS); 3860d9b7d92SLoGin } 3870d9b7d92SLoGin } 3880d9b7d92SLoGin } 3898c6f2184Ssspphh 3908c6f2184Ssspphh pub fn uname(name: *mut PosixOldUtsName) -> Result<usize, SystemError> { 3918c6f2184Ssspphh let mut writer = 3928c6f2184Ssspphh UserBufferWriter::new(name, core::mem::size_of::<PosixOldUtsName>(), true)?; 3938c6f2184Ssspphh writer.copy_one_to_user(&PosixOldUtsName::new(), 0)?; 3948c6f2184Ssspphh 3958c6f2184Ssspphh return Ok(0); 3968c6f2184Ssspphh } 397ab5c8ca4Slogin } 398