1cc290419Sopenharmony_ci/* 2cc290419Sopenharmony_ci * Copyright (C) 2023 Huawei Device Co., Ltd. 3cc290419Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4cc290419Sopenharmony_ci * you may not use this file except in compliance with the License. 5cc290419Sopenharmony_ci * You may obtain a copy of the License at 6cc290419Sopenharmony_ci * 7cc290419Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8cc290419Sopenharmony_ci * 9cc290419Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10cc290419Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11cc290419Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12cc290419Sopenharmony_ci * See the License for the specific language governing permissions and 13cc290419Sopenharmony_ci * limitations under the License. 14cc290419Sopenharmony_ci */ 15cc290419Sopenharmony_ci//! shell 16cc290419Sopenharmony_ci#![allow(missing_docs)] 17cc290419Sopenharmony_ci 18cc290419Sopenharmony_ci#[allow(unused_imports)] 19cc290419Sopenharmony_ciuse crate::daemon_lib::task_manager; 20cc290419Sopenharmony_ciuse crate::utils::hdc_log::*; 21cc290419Sopenharmony_ciuse crate::common::base::Base; 22cc290419Sopenharmony_ciuse crate::config::TaskMessage; 23cc290419Sopenharmony_ciuse crate::config::{HdcCommand, MessageLevel, SHELL_PROG}; 24cc290419Sopenharmony_ciuse crate::transfer; 25cc290419Sopenharmony_ci 26cc290419Sopenharmony_ciuse std::collections::HashMap; 27cc290419Sopenharmony_ciuse std::io::{self, Error, ErrorKind}; 28cc290419Sopenharmony_ciuse std::mem::MaybeUninit; 29cc290419Sopenharmony_ciuse std::os::fd::AsRawFd; 30cc290419Sopenharmony_ciuse std::process::Stdio; 31cc290419Sopenharmony_ciuse std::sync::{Arc, Once}; 32cc290419Sopenharmony_ciuse std::env; 33cc290419Sopenharmony_ci 34cc290419Sopenharmony_ciuse ylong_runtime::process::pty_process::{Pty, PtyCommand}; 35cc290419Sopenharmony_ciuse ylong_runtime::process::{Child, Command, ChildStdin, ChildStdout, ChildStderr}; 36cc290419Sopenharmony_ciuse ylong_runtime::io::{AsyncReadExt, AsyncWriteExt, AsyncBufReader}; 37cc290419Sopenharmony_ciuse ylong_runtime::sync::{mpsc, Mutex}; 38cc290419Sopenharmony_ciuse ylong_runtime::sync::error::TryRecvError::Closed; 39cc290419Sopenharmony_ci 40cc290419Sopenharmony_ci 41cc290419Sopenharmony_ci// -----inner common functions----- 42cc290419Sopenharmony_ci#[derive(Debug)] 43cc290419Sopenharmony_cistruct ShellTaskID { 44cc290419Sopenharmony_ci session_id: u32, 45cc290419Sopenharmony_ci channel_id: u32, 46cc290419Sopenharmony_ci} 47cc290419Sopenharmony_ci 48cc290419Sopenharmony_cifn trim_quotation_for_cmd(cmd_input: String) -> String { 49cc290419Sopenharmony_ci let mut cmd = cmd_input.trim().to_string(); 50cc290419Sopenharmony_ci if cmd.starts_with('"') && cmd.ends_with('"') { 51cc290419Sopenharmony_ci cmd = match cmd.strip_prefix('"') { 52cc290419Sopenharmony_ci Some(cmd_res) => cmd_res.to_string(), 53cc290419Sopenharmony_ci None => cmd, 54cc290419Sopenharmony_ci }; 55cc290419Sopenharmony_ci cmd = match cmd.strip_suffix('"') { 56cc290419Sopenharmony_ci Some(cmd_res) => cmd_res.to_string(), 57cc290419Sopenharmony_ci None => cmd, 58cc290419Sopenharmony_ci }; 59cc290419Sopenharmony_ci } 60cc290419Sopenharmony_ci cmd 61cc290419Sopenharmony_ci} 62cc290419Sopenharmony_ci 63cc290419Sopenharmony_ciasync fn shell_channel_close(channel_id: u32, session_id: u32){ 64cc290419Sopenharmony_ci let message = TaskMessage { 65cc290419Sopenharmony_ci channel_id, 66cc290419Sopenharmony_ci command: HdcCommand::KernelChannelClose, 67cc290419Sopenharmony_ci payload: [1].to_vec(), 68cc290419Sopenharmony_ci }; 69cc290419Sopenharmony_ci transfer::put(session_id, message).await; 70cc290419Sopenharmony_ci} 71cc290419Sopenharmony_ci 72cc290419Sopenharmony_cipub async fn stop_task(session_id: u32) { 73cc290419Sopenharmony_ci PtyMap::stop_task(session_id).await; 74cc290419Sopenharmony_ci ShellExecuteMap::stop_task(session_id).await; 75cc290419Sopenharmony_ci} 76cc290419Sopenharmony_ci 77cc290419Sopenharmony_cipub async fn dump_task() -> String { 78cc290419Sopenharmony_ci PtyMap::dump_task().await 79cc290419Sopenharmony_ci} 80cc290419Sopenharmony_ci 81cc290419Sopenharmony_ci// -----interactive shell inplementation----- 82cc290419Sopenharmony_cipub struct PtyTask { 83cc290419Sopenharmony_ci pub handle: ylong_runtime::task::JoinHandle<()>, 84cc290419Sopenharmony_ci pub tx: mpsc::BoundedSender<Vec<u8>>, 85cc290419Sopenharmony_ci pub session_id: u32, 86cc290419Sopenharmony_ci pub channel_id: u32, 87cc290419Sopenharmony_ci pub cmd: Option<String>, 88cc290419Sopenharmony_ci} 89cc290419Sopenharmony_ci 90cc290419Sopenharmony_cistruct PtyProcess { 91cc290419Sopenharmony_ci pub pty: Pty, 92cc290419Sopenharmony_ci pub child: Arc<Mutex<Child>>, 93cc290419Sopenharmony_ci} 94cc290419Sopenharmony_ci 95cc290419Sopenharmony_ciimpl PtyProcess { 96cc290419Sopenharmony_ci fn new(pty: Pty, child: Arc<Mutex<Child>>) -> Self { 97cc290419Sopenharmony_ci Self { 98cc290419Sopenharmony_ci pty, 99cc290419Sopenharmony_ci child, 100cc290419Sopenharmony_ci } 101cc290419Sopenharmony_ci } 102cc290419Sopenharmony_ci} 103cc290419Sopenharmony_ci 104cc290419Sopenharmony_ci// hdc shell "/system/bin/uitest start-daemon /data/app/el2/100/base/com.ohos.devicetest/cache/shmf &" 105cc290419Sopenharmony_ci// hdc shell "nohup test.sh &" 106cc290419Sopenharmony_ci// async cmd will ignor stdout and stderr, if you want the output, cmd format is: 107cc290419Sopenharmony_ci// hdc shell "cmd_xxx >/data/local/tmp/log 2>&1 &" 108cc290419Sopenharmony_ci// example: 109cc290419Sopenharmony_ci// hdc shell "nohup /data/local/tmp/test.sh >/data/local/tmp/log 2>&1 &" 110cc290419Sopenharmony_ci// hdc shell "/data/local/tmp/test.sh >/data/local/tmp/log 2>&1 &" 111cc290419Sopenharmony_cifn init_pty_process(cmd: Option<String>, _channel_id: u32) -> io::Result<PtyProcess> { 112cc290419Sopenharmony_ci let pty = match Pty::new() { 113cc290419Sopenharmony_ci Ok(pty) => pty, 114cc290419Sopenharmony_ci Err(e) => { 115cc290419Sopenharmony_ci let msg = format!("pty create error: {}", e); 116cc290419Sopenharmony_ci crate::error!("{msg}"); 117cc290419Sopenharmony_ci return Err(io::Error::new(io::ErrorKind::Other, msg)); 118cc290419Sopenharmony_ci } 119cc290419Sopenharmony_ci }; 120cc290419Sopenharmony_ci 121cc290419Sopenharmony_ci let pts = match pty.pts() { 122cc290419Sopenharmony_ci Ok(pts) => pts, 123cc290419Sopenharmony_ci Err(e) => { 124cc290419Sopenharmony_ci let msg = format!("pty pts error: {}", e); 125cc290419Sopenharmony_ci crate::error!("{msg}"); 126cc290419Sopenharmony_ci return Err(io::Error::new(io::ErrorKind::Other, msg)); 127cc290419Sopenharmony_ci } 128cc290419Sopenharmony_ci }; 129cc290419Sopenharmony_ci let child = match cmd { 130cc290419Sopenharmony_ci None => { 131cc290419Sopenharmony_ci crate::debug!("input cmd is None. channel_id {_channel_id}"); 132cc290419Sopenharmony_ci let mut command = PtyCommand::new(SHELL_PROG); 133cc290419Sopenharmony_ci 134cc290419Sopenharmony_ci unsafe { 135cc290419Sopenharmony_ci command.pre_exec(|| { 136cc290419Sopenharmony_ci Base::de_init_process(); 137cc290419Sopenharmony_ci 138cc290419Sopenharmony_ci let home_dir = match env::var("HOME") { 139cc290419Sopenharmony_ci Ok(dir) => dir, 140cc290419Sopenharmony_ci Err(_) => String::from("/") 141cc290419Sopenharmony_ci }; 142cc290419Sopenharmony_ci let _ = std::env::set_current_dir(home_dir); 143cc290419Sopenharmony_ci 144cc290419Sopenharmony_ci Ok(()) 145cc290419Sopenharmony_ci }); 146cc290419Sopenharmony_ci } 147cc290419Sopenharmony_ci 148cc290419Sopenharmony_ci command.spawn(&pts)? 149cc290419Sopenharmony_ci } 150cc290419Sopenharmony_ci Some(mut cmd) => { 151cc290419Sopenharmony_ci crate::debug!("input cmd [{}]", cmd); 152cc290419Sopenharmony_ci cmd = trim_quotation_for_cmd(cmd); 153cc290419Sopenharmony_ci let params = ["-c", cmd.as_str()].to_vec(); 154cc290419Sopenharmony_ci let mut proc = PtyCommand::new(SHELL_PROG); 155cc290419Sopenharmony_ci let command = proc.args(params); 156cc290419Sopenharmony_ci command.spawn(&pts)? 157cc290419Sopenharmony_ci } 158cc290419Sopenharmony_ci }; 159cc290419Sopenharmony_ci Ok(PtyProcess::new( 160cc290419Sopenharmony_ci pty, 161cc290419Sopenharmony_ci Arc::new(Mutex::new(child)), 162cc290419Sopenharmony_ci )) 163cc290419Sopenharmony_ci} 164cc290419Sopenharmony_ci 165cc290419Sopenharmony_ciasync fn subprocess_task( 166cc290419Sopenharmony_ci cmd: Option<String>, 167cc290419Sopenharmony_ci session_id: u32, 168cc290419Sopenharmony_ci channel_id: u32, 169cc290419Sopenharmony_ci ret_command: HdcCommand, 170cc290419Sopenharmony_ci mut rx: mpsc::BoundedReceiver<Vec<u8>>, 171cc290419Sopenharmony_ci) { 172cc290419Sopenharmony_ci let mut pty_process = match init_pty_process(cmd.clone(), channel_id) { 173cc290419Sopenharmony_ci Err(e) => { 174cc290419Sopenharmony_ci let msg = format!("execute cmd [{cmd:?}] fail: {e:?}"); 175cc290419Sopenharmony_ci crate::error!("{}", msg); 176cc290419Sopenharmony_ci crate::common::hdctransfer::echo_client( 177cc290419Sopenharmony_ci session_id, 178cc290419Sopenharmony_ci channel_id, 179cc290419Sopenharmony_ci &msg, 180cc290419Sopenharmony_ci MessageLevel::Fail, 181cc290419Sopenharmony_ci ) 182cc290419Sopenharmony_ci .await; 183cc290419Sopenharmony_ci shell_channel_close(channel_id, session_id).await; 184cc290419Sopenharmony_ci return; 185cc290419Sopenharmony_ci } 186cc290419Sopenharmony_ci Ok(pty) => pty, 187cc290419Sopenharmony_ci }; 188cc290419Sopenharmony_ci PtyChildProcessMap::put(session_id, channel_id, pty_process.child.clone()).await; 189cc290419Sopenharmony_ci let mut buf = [0_u8; 30720]; 190cc290419Sopenharmony_ci loop { 191cc290419Sopenharmony_ci ylong_runtime::select! { 192cc290419Sopenharmony_ci read_res = pty_process.pty.read(&mut buf) => { 193cc290419Sopenharmony_ci match read_res { 194cc290419Sopenharmony_ci Ok(bytes) => { 195cc290419Sopenharmony_ci let message = TaskMessage { 196cc290419Sopenharmony_ci channel_id, 197cc290419Sopenharmony_ci command: ret_command, 198cc290419Sopenharmony_ci payload: buf[..bytes].to_vec(), 199cc290419Sopenharmony_ci }; 200cc290419Sopenharmony_ci transfer::put(session_id, message).await; 201cc290419Sopenharmony_ci } 202cc290419Sopenharmony_ci Err(e) => { 203cc290419Sopenharmony_ci crate::warn!("pty read failed: {e:?}"); 204cc290419Sopenharmony_ci break; 205cc290419Sopenharmony_ci } 206cc290419Sopenharmony_ci } 207cc290419Sopenharmony_ci }, 208cc290419Sopenharmony_ci recv_res = rx.recv() => { 209cc290419Sopenharmony_ci match recv_res { 210cc290419Sopenharmony_ci Ok(val) => { 211cc290419Sopenharmony_ci if val[..].contains(&0x4_u8) { 212cc290419Sopenharmony_ci // ctrl-D: end pty 213cc290419Sopenharmony_ci crate::info!("ctrl-D: end pty"); 214cc290419Sopenharmony_ci break; 215cc290419Sopenharmony_ci } else if val[..].contains(&0x3_u8) { 216cc290419Sopenharmony_ci // ctrl-C: end process 217cc290419Sopenharmony_ci crate::info!("ctrl-C: end process"); 218cc290419Sopenharmony_ci unsafe { 219cc290419Sopenharmony_ci let tpgid = libc::tcgetpgrp(pty_process.pty.as_raw_fd()); 220cc290419Sopenharmony_ci if tpgid > 1 { 221cc290419Sopenharmony_ci libc::kill(tpgid,libc::SIGINT); 222cc290419Sopenharmony_ci } 223cc290419Sopenharmony_ci } 224cc290419Sopenharmony_ci continue; 225cc290419Sopenharmony_ci } else if val[..].contains(&0x11_u8) { 226cc290419Sopenharmony_ci // ctrl-Q: dump process 227cc290419Sopenharmony_ci crate::info!("ctrl-Q: dump process"); 228cc290419Sopenharmony_ci let dump_message = task_manager::dump_running_task_info().await; 229cc290419Sopenharmony_ci crate::debug!("dump_message: {}", dump_message); 230cc290419Sopenharmony_ci #[cfg(feature = "hdc_debug")] 231cc290419Sopenharmony_ci let message = TaskMessage { 232cc290419Sopenharmony_ci channel_id, 233cc290419Sopenharmony_ci command: ret_command, 234cc290419Sopenharmony_ci payload: dump_message.as_bytes().to_vec(), 235cc290419Sopenharmony_ci }; 236cc290419Sopenharmony_ci #[cfg(feature = "hdc_debug")] 237cc290419Sopenharmony_ci transfer::put(session_id, message).await; 238cc290419Sopenharmony_ci } 239cc290419Sopenharmony_ci if let Err(e) = pty_process.pty.write_all(&val).await { 240cc290419Sopenharmony_ci crate::warn!( 241cc290419Sopenharmony_ci "session_id: {} channel_id: {}, pty write failed: {e:?}", 242cc290419Sopenharmony_ci session_id, channel_id 243cc290419Sopenharmony_ci ); 244cc290419Sopenharmony_ci break; 245cc290419Sopenharmony_ci } 246cc290419Sopenharmony_ci } 247cc290419Sopenharmony_ci Err(e) => { 248cc290419Sopenharmony_ci crate::debug!("rx recv failed: {e:?}"); 249cc290419Sopenharmony_ci } 250cc290419Sopenharmony_ci } 251cc290419Sopenharmony_ci } 252cc290419Sopenharmony_ci } 253cc290419Sopenharmony_ci 254cc290419Sopenharmony_ci { 255cc290419Sopenharmony_ci let mut child_lock = pty_process.child.lock().await; 256cc290419Sopenharmony_ci let status = child_lock.try_wait(); 257cc290419Sopenharmony_ci match status { 258cc290419Sopenharmony_ci Ok(Some(exit_status)) => { 259cc290419Sopenharmony_ci crate::debug!("interactive shell finish a process {:?}", exit_status); 260cc290419Sopenharmony_ci } 261cc290419Sopenharmony_ci Ok(None) => {} 262cc290419Sopenharmony_ci Err(e) => { 263cc290419Sopenharmony_ci crate::error!("interactive shell wait failed: {e:?}"); 264cc290419Sopenharmony_ci break; 265cc290419Sopenharmony_ci } 266cc290419Sopenharmony_ci } 267cc290419Sopenharmony_ci } 268cc290419Sopenharmony_ci } 269cc290419Sopenharmony_ci 270cc290419Sopenharmony_ci let mut child_lock = pty_process.child.lock().await; 271cc290419Sopenharmony_ci 272cc290419Sopenharmony_ci let kill_result = child_lock.kill().await; 273cc290419Sopenharmony_ci crate::debug!("subprocess_task kill child(session_id {session_id}, channel_id {channel_id}), result:{:?}", kill_result); 274cc290419Sopenharmony_ci match child_lock.wait().await { 275cc290419Sopenharmony_ci Ok(exit_status) => { 276cc290419Sopenharmony_ci PtyMap::del(session_id, channel_id).await; 277cc290419Sopenharmony_ci crate::debug!( 278cc290419Sopenharmony_ci "subprocess_task waiting child exit success, status:{:?}.", 279cc290419Sopenharmony_ci exit_status 280cc290419Sopenharmony_ci ); 281cc290419Sopenharmony_ci } 282cc290419Sopenharmony_ci Err(e) => { 283cc290419Sopenharmony_ci let kill_result = child_lock.kill().await; 284cc290419Sopenharmony_ci crate::debug!( 285cc290419Sopenharmony_ci "subprocess_task child exit status {:?}, kill child, result:{:?}", 286cc290419Sopenharmony_ci e, 287cc290419Sopenharmony_ci kill_result 288cc290419Sopenharmony_ci ); 289cc290419Sopenharmony_ci } 290cc290419Sopenharmony_ci } 291cc290419Sopenharmony_ci 292cc290419Sopenharmony_ci match child_lock.wait().await { 293cc290419Sopenharmony_ci Ok(exit_status) => { 294cc290419Sopenharmony_ci PtyMap::del(session_id, channel_id).await; 295cc290419Sopenharmony_ci crate::debug!( 296cc290419Sopenharmony_ci "subprocess_task waiting child exit success, status:{:?}.", 297cc290419Sopenharmony_ci exit_status 298cc290419Sopenharmony_ci ); 299cc290419Sopenharmony_ci } 300cc290419Sopenharmony_ci Err(e) => { 301cc290419Sopenharmony_ci crate::debug!("subprocess_task waiting child exit fail, error:{:?}.", e); 302cc290419Sopenharmony_ci } 303cc290419Sopenharmony_ci } 304cc290419Sopenharmony_ci 305cc290419Sopenharmony_ci let message = TaskMessage { 306cc290419Sopenharmony_ci channel_id, 307cc290419Sopenharmony_ci command: HdcCommand::KernelChannelClose, 308cc290419Sopenharmony_ci payload: vec![1], 309cc290419Sopenharmony_ci }; 310cc290419Sopenharmony_ci transfer::put(session_id, message).await; 311cc290419Sopenharmony_ci} 312cc290419Sopenharmony_ci 313cc290419Sopenharmony_ciimpl PtyTask { 314cc290419Sopenharmony_ci pub fn new( 315cc290419Sopenharmony_ci session_id: u32, 316cc290419Sopenharmony_ci channel_id: u32, 317cc290419Sopenharmony_ci option_cmd: Option<String>, 318cc290419Sopenharmony_ci ret_command: HdcCommand, 319cc290419Sopenharmony_ci ) -> Self { 320cc290419Sopenharmony_ci let (tx, rx) = ylong_runtime::sync::mpsc::bounded_channel::<Vec<u8>>(16); 321cc290419Sopenharmony_ci let cmd = option_cmd.clone(); 322cc290419Sopenharmony_ci crate::debug!("PtyTask new session_id {session_id}, channel_id {channel_id}"); 323cc290419Sopenharmony_ci let handle = ylong_runtime::spawn(subprocess_task( 324cc290419Sopenharmony_ci option_cmd, 325cc290419Sopenharmony_ci session_id, 326cc290419Sopenharmony_ci channel_id, 327cc290419Sopenharmony_ci ret_command, 328cc290419Sopenharmony_ci rx, 329cc290419Sopenharmony_ci )); 330cc290419Sopenharmony_ci Self { 331cc290419Sopenharmony_ci handle, 332cc290419Sopenharmony_ci tx, 333cc290419Sopenharmony_ci session_id, 334cc290419Sopenharmony_ci channel_id, 335cc290419Sopenharmony_ci cmd, 336cc290419Sopenharmony_ci } 337cc290419Sopenharmony_ci } 338cc290419Sopenharmony_ci} 339cc290419Sopenharmony_ci 340cc290419Sopenharmony_ciimpl Drop for PtyTask { 341cc290419Sopenharmony_ci fn drop(&mut self) { 342cc290419Sopenharmony_ci crate::info!( 343cc290419Sopenharmony_ci "PtyTask Drop session_id {}, channel_id {}", 344cc290419Sopenharmony_ci self.session_id, 345cc290419Sopenharmony_ci self.channel_id 346cc290419Sopenharmony_ci ); 347cc290419Sopenharmony_ci } 348cc290419Sopenharmony_ci} 349cc290419Sopenharmony_ci 350cc290419Sopenharmony_citype Child_ = Arc<Mutex<Child>>; 351cc290419Sopenharmony_citype PtyChildProcessMap_ = Arc<Mutex<HashMap<(u32, u32), Child_>>>; 352cc290419Sopenharmony_cipub struct PtyChildProcessMap {} 353cc290419Sopenharmony_ciimpl PtyChildProcessMap { 354cc290419Sopenharmony_ci fn get_instance() -> PtyChildProcessMap_ { 355cc290419Sopenharmony_ci static mut PTY_CHILD_MAP: Option<PtyChildProcessMap_> = None; 356cc290419Sopenharmony_ci unsafe { 357cc290419Sopenharmony_ci PTY_CHILD_MAP 358cc290419Sopenharmony_ci .get_or_insert_with(|| Arc::new(Mutex::new(HashMap::new()))) 359cc290419Sopenharmony_ci .clone() 360cc290419Sopenharmony_ci } 361cc290419Sopenharmony_ci } 362cc290419Sopenharmony_ci 363cc290419Sopenharmony_ci pub async fn get(session_id: u32, channel_id: u32) -> Option<Child_> { 364cc290419Sopenharmony_ci let pty_child_map = Self::get_instance(); 365cc290419Sopenharmony_ci let map = pty_child_map.lock().await; 366cc290419Sopenharmony_ci if let Some(pty_child) = map.get(&(session_id, channel_id)) { 367cc290419Sopenharmony_ci return Some(pty_child.clone()); 368cc290419Sopenharmony_ci } 369cc290419Sopenharmony_ci None 370cc290419Sopenharmony_ci } 371cc290419Sopenharmony_ci 372cc290419Sopenharmony_ci #[allow(unused)] 373cc290419Sopenharmony_ci pub async fn put(session_id: u32, channel_id: u32, pty_child: Child_) { 374cc290419Sopenharmony_ci let pty_child_map = Self::get_instance(); 375cc290419Sopenharmony_ci let mut map = pty_child_map.lock().await; 376cc290419Sopenharmony_ci map.insert((session_id, channel_id), pty_child); 377cc290419Sopenharmony_ci } 378cc290419Sopenharmony_ci 379cc290419Sopenharmony_ci pub async fn del(session_id: u32, channel_id: u32) { 380cc290419Sopenharmony_ci let pty_child_map = Self::get_instance(); 381cc290419Sopenharmony_ci let mut map = pty_child_map.lock().await; 382cc290419Sopenharmony_ci map.remove(&(session_id, channel_id)); 383cc290419Sopenharmony_ci } 384cc290419Sopenharmony_ci} 385cc290419Sopenharmony_ci 386cc290419Sopenharmony_citype PtyMap_ = Arc<Mutex<HashMap<(u32, u32), Arc<PtyTask>>>>; 387cc290419Sopenharmony_cipub struct PtyMap {} 388cc290419Sopenharmony_ciimpl PtyMap { 389cc290419Sopenharmony_ci fn get_instance() -> PtyMap_ { 390cc290419Sopenharmony_ci static mut PTY_MAP: Option<PtyMap_> = None; 391cc290419Sopenharmony_ci unsafe { 392cc290419Sopenharmony_ci PTY_MAP 393cc290419Sopenharmony_ci .get_or_insert_with(|| Arc::new(Mutex::new(HashMap::new()))) 394cc290419Sopenharmony_ci .clone() 395cc290419Sopenharmony_ci } 396cc290419Sopenharmony_ci } 397cc290419Sopenharmony_ci 398cc290419Sopenharmony_ci pub async fn get(session_id: u32, channel_id: u32) -> Option<Arc<PtyTask>> { 399cc290419Sopenharmony_ci let pty_map = Self::get_instance(); 400cc290419Sopenharmony_ci let map = pty_map.lock().await; 401cc290419Sopenharmony_ci if let Some(pty_task) = map.get(&(session_id, channel_id)) { 402cc290419Sopenharmony_ci return Some(pty_task.clone()); 403cc290419Sopenharmony_ci } 404cc290419Sopenharmony_ci None 405cc290419Sopenharmony_ci } 406cc290419Sopenharmony_ci 407cc290419Sopenharmony_ci pub async fn put(session_id: u32, channel_id: u32, pty_task: PtyTask) { 408cc290419Sopenharmony_ci let pty_map = Self::get_instance(); 409cc290419Sopenharmony_ci let mut map = pty_map.lock().await; 410cc290419Sopenharmony_ci let arc_pty_task = Arc::new(pty_task); 411cc290419Sopenharmony_ci map.insert((session_id, channel_id), arc_pty_task); 412cc290419Sopenharmony_ci } 413cc290419Sopenharmony_ci 414cc290419Sopenharmony_ci pub async fn del(session_id: u32, channel_id: u32) { 415cc290419Sopenharmony_ci let pty_map = Self::get_instance(); 416cc290419Sopenharmony_ci let mut map = pty_map.lock().await; 417cc290419Sopenharmony_ci map.remove(&(session_id, channel_id)); 418cc290419Sopenharmony_ci PtyChildProcessMap::del(session_id, channel_id).await; 419cc290419Sopenharmony_ci } 420cc290419Sopenharmony_ci 421cc290419Sopenharmony_ci pub async fn stop_task(session_id: u32) { 422cc290419Sopenharmony_ci let pty_map = Self::get_instance(); 423cc290419Sopenharmony_ci { 424cc290419Sopenharmony_ci let map = pty_map.lock().await; 425cc290419Sopenharmony_ci crate::info!("hdc shell stop_task, session_id:{}, task_size: {}", session_id, map.len()); 426cc290419Sopenharmony_ci for _iter in map.iter() { 427cc290419Sopenharmony_ci if _iter.0 .0 != session_id { 428cc290419Sopenharmony_ci continue; 429cc290419Sopenharmony_ci } 430cc290419Sopenharmony_ci if let Some(pty_child) = PtyChildProcessMap::get(session_id, _iter.0 .1).await { 431cc290419Sopenharmony_ci let mut child = pty_child.lock().await; 432cc290419Sopenharmony_ci let kill_result = child.kill().await; 433cc290419Sopenharmony_ci crate::debug!( 434cc290419Sopenharmony_ci "do map clear kill child, result:{:?}, session_id {}, channel_id {}", 435cc290419Sopenharmony_ci kill_result, 436cc290419Sopenharmony_ci session_id, 437cc290419Sopenharmony_ci _iter.0 .1 438cc290419Sopenharmony_ci ); 439cc290419Sopenharmony_ci match child.wait().await { 440cc290419Sopenharmony_ci Ok(exit_status) => { 441cc290419Sopenharmony_ci crate::debug!( 442cc290419Sopenharmony_ci "waiting child exit success, status:{:?}, session_id {}, channel_id {}", 443cc290419Sopenharmony_ci exit_status, 444cc290419Sopenharmony_ci session_id, 445cc290419Sopenharmony_ci _iter.0.1 446cc290419Sopenharmony_ci ); 447cc290419Sopenharmony_ci } 448cc290419Sopenharmony_ci Err(e) => { 449cc290419Sopenharmony_ci crate::error!( 450cc290419Sopenharmony_ci "waiting child exit fail, error:{:?}, session_id {}, channel_id {}", 451cc290419Sopenharmony_ci e, 452cc290419Sopenharmony_ci session_id, 453cc290419Sopenharmony_ci _iter.0 .1 454cc290419Sopenharmony_ci ); 455cc290419Sopenharmony_ci } 456cc290419Sopenharmony_ci } 457cc290419Sopenharmony_ci PtyChildProcessMap::del(session_id, _iter.0 .1).await; 458cc290419Sopenharmony_ci } 459cc290419Sopenharmony_ci crate::debug!( 460cc290419Sopenharmony_ci "Clear tty task, session_id: {}, channel_id:{}", 461cc290419Sopenharmony_ci _iter.0 .0, 462cc290419Sopenharmony_ci session_id 463cc290419Sopenharmony_ci ); 464cc290419Sopenharmony_ci } 465cc290419Sopenharmony_ci } 466cc290419Sopenharmony_ci } 467cc290419Sopenharmony_ci 468cc290419Sopenharmony_ci pub async fn dump_task() -> String { 469cc290419Sopenharmony_ci let arc = Self::get_instance(); 470cc290419Sopenharmony_ci let map = arc.lock().await; 471cc290419Sopenharmony_ci let mut result = String::new(); 472cc290419Sopenharmony_ci for _iter in map.iter() { 473cc290419Sopenharmony_ci let command = match &_iter.1.cmd { 474cc290419Sopenharmony_ci Some(b) => b, 475cc290419Sopenharmony_ci _ => "", 476cc290419Sopenharmony_ci }; 477cc290419Sopenharmony_ci result.push_str(&format!( 478cc290419Sopenharmony_ci "session_id:{},\tchannel_id:{},\tcommand:{}\n", 479cc290419Sopenharmony_ci _iter.1.session_id, _iter.1.channel_id, command 480cc290419Sopenharmony_ci )); 481cc290419Sopenharmony_ci } 482cc290419Sopenharmony_ci result 483cc290419Sopenharmony_ci } 484cc290419Sopenharmony_ci} 485cc290419Sopenharmony_ci 486cc290419Sopenharmony_ci// -----noninteractive shell implementation----- 487cc290419Sopenharmony_ci 488cc290419Sopenharmony_citype ShellExecuteMap_ = Mutex<HashMap<(u32, u32), Arc<ShellExecuteTask>>>; 489cc290419Sopenharmony_cipub struct ShellExecuteMap {} 490cc290419Sopenharmony_ciimpl ShellExecuteMap { 491cc290419Sopenharmony_ci fn get_instance() -> &'static ShellExecuteMap_ { 492cc290419Sopenharmony_ci static mut SHELLEXECUTE_MAP: MaybeUninit<ShellExecuteMap_> = MaybeUninit::uninit(); 493cc290419Sopenharmony_ci static ONCE: Once = Once::new(); 494cc290419Sopenharmony_ci 495cc290419Sopenharmony_ci unsafe { 496cc290419Sopenharmony_ci ONCE.call_once(|| { 497cc290419Sopenharmony_ci SHELLEXECUTE_MAP = MaybeUninit::new(Mutex::new(HashMap::new())); 498cc290419Sopenharmony_ci } 499cc290419Sopenharmony_ci ); 500cc290419Sopenharmony_ci &*SHELLEXECUTE_MAP.as_ptr() 501cc290419Sopenharmony_ci } 502cc290419Sopenharmony_ci } 503cc290419Sopenharmony_ci 504cc290419Sopenharmony_ci pub async fn put(session_id: u32, channel_id: u32, shell_execute_task: ShellExecuteTask) { 505cc290419Sopenharmony_ci let shell_execute_map = Self::get_instance(); 506cc290419Sopenharmony_ci let mut map = shell_execute_map.lock().await; 507cc290419Sopenharmony_ci let arc_shell_execute_task = Arc::new(shell_execute_task); 508cc290419Sopenharmony_ci map.insert((session_id, channel_id), arc_shell_execute_task); 509cc290419Sopenharmony_ci } 510cc290419Sopenharmony_ci 511cc290419Sopenharmony_ci pub async fn del(session_id: u32, channel_id: u32) { 512cc290419Sopenharmony_ci let shell_execute_map = Self::get_instance(); 513cc290419Sopenharmony_ci let mut map = shell_execute_map.lock().await; 514cc290419Sopenharmony_ci map.remove(&(session_id, channel_id)); 515cc290419Sopenharmony_ci } 516cc290419Sopenharmony_ci 517cc290419Sopenharmony_ci pub async fn stop_task(session_id: u32) { 518cc290419Sopenharmony_ci let shell_execute_map = Self::get_instance(); 519cc290419Sopenharmony_ci { 520cc290419Sopenharmony_ci let mut map = shell_execute_map.lock().await; 521cc290419Sopenharmony_ci let mut channel_vec = vec![]; 522cc290419Sopenharmony_ci for iter in map.iter() { 523cc290419Sopenharmony_ci if iter.0 .0 != session_id { 524cc290419Sopenharmony_ci continue; 525cc290419Sopenharmony_ci } 526cc290419Sopenharmony_ci iter.1.handle.cancel(); 527cc290419Sopenharmony_ci channel_vec.push(iter.0 .1); 528cc290419Sopenharmony_ci crate::debug!( 529cc290419Sopenharmony_ci "Clear shell_execute_map task, session_id: {}, channel_id:{}, task_size: {}", 530cc290419Sopenharmony_ci session_id, 531cc290419Sopenharmony_ci iter.0 .1, 532cc290419Sopenharmony_ci map.len(), 533cc290419Sopenharmony_ci ); 534cc290419Sopenharmony_ci } 535cc290419Sopenharmony_ci for channel_id in channel_vec{ 536cc290419Sopenharmony_ci map.remove(&(session_id, channel_id)); 537cc290419Sopenharmony_ci } 538cc290419Sopenharmony_ci } 539cc290419Sopenharmony_ci } 540cc290419Sopenharmony_ci 541cc290419Sopenharmony_ci} 542cc290419Sopenharmony_ci 543cc290419Sopenharmony_cipub struct ShellExecuteTask { 544cc290419Sopenharmony_ci pub handle: ylong_runtime::task::JoinHandle<()>, 545cc290419Sopenharmony_ci pub tx: mpsc::BoundedSender<Vec<u8>>, 546cc290419Sopenharmony_ci pub session_id: u32, 547cc290419Sopenharmony_ci pub channel_id: u32, 548cc290419Sopenharmony_ci pub cmd: String, 549cc290419Sopenharmony_ci} 550cc290419Sopenharmony_ci 551cc290419Sopenharmony_ci 552cc290419Sopenharmony_ciasync fn watch_pipe_states(rx: &mut mpsc::BoundedReceiver<Vec<u8>>, child_in: &mut ChildStdin) -> io::Result<()> { 553cc290419Sopenharmony_ci match rx.try_recv() { 554cc290419Sopenharmony_ci Err(e) => { 555cc290419Sopenharmony_ci if e == Closed { 556cc290419Sopenharmony_ci return Err(Error::new(ErrorKind::Other, "pipe closed")); 557cc290419Sopenharmony_ci } 558cc290419Sopenharmony_ci // 执行top指令时,存在短暂无返回值场景,此时返回值为Err(Empty),需要返回Empty 559cc290419Sopenharmony_ci Ok(()) 560cc290419Sopenharmony_ci }, 561cc290419Sopenharmony_ci Ok(val) => { 562cc290419Sopenharmony_ci crate::debug!("pipe recv {:?}", val); 563cc290419Sopenharmony_ci let _ = child_in.write_all(&val).await; 564cc290419Sopenharmony_ci Ok(()) 565cc290419Sopenharmony_ci } 566cc290419Sopenharmony_ci } 567cc290419Sopenharmony_ci} 568cc290419Sopenharmony_ci 569cc290419Sopenharmony_ci 570cc290419Sopenharmony_ciasync fn read_buf_from_stdout_stderr(child_out_reader: &mut AsyncBufReader<ChildStdout>, child_err_reader: &mut AsyncBufReader<ChildStderr>, shell_task_id: &ShellTaskID, ret_command: HdcCommand) { 571cc290419Sopenharmony_ci let mut buffer = Vec::new(); 572cc290419Sopenharmony_ci if let Ok(n) = child_out_reader.read_to_end(&mut buffer).await { 573cc290419Sopenharmony_ci crate::debug!("read {n} bytes child_out after child exit"); 574cc290419Sopenharmony_ci if n > 0 { 575cc290419Sopenharmony_ci let message = TaskMessage { 576cc290419Sopenharmony_ci channel_id: shell_task_id.channel_id, 577cc290419Sopenharmony_ci command: ret_command, 578cc290419Sopenharmony_ci payload: buffer, 579cc290419Sopenharmony_ci }; 580cc290419Sopenharmony_ci transfer::put(shell_task_id.session_id, message).await; 581cc290419Sopenharmony_ci } 582cc290419Sopenharmony_ci } 583cc290419Sopenharmony_ci 584cc290419Sopenharmony_ci let mut buffer = Vec::new(); 585cc290419Sopenharmony_ci if let Ok(n) = child_err_reader.read_to_end(&mut buffer).await { 586cc290419Sopenharmony_ci crate::debug!("read {n} bytes child_err child exit"); 587cc290419Sopenharmony_ci if n > 0 { 588cc290419Sopenharmony_ci let message = TaskMessage { 589cc290419Sopenharmony_ci channel_id: shell_task_id.channel_id, 590cc290419Sopenharmony_ci command: ret_command, 591cc290419Sopenharmony_ci payload: buffer, 592cc290419Sopenharmony_ci }; 593cc290419Sopenharmony_ci transfer::put(shell_task_id.session_id, message).await; 594cc290419Sopenharmony_ci } 595cc290419Sopenharmony_ci } 596cc290419Sopenharmony_ci} 597cc290419Sopenharmony_ci 598cc290419Sopenharmony_ciasync fn task_for_shell_execute( 599cc290419Sopenharmony_ci cmd_param: String, 600cc290419Sopenharmony_ci shell_task_id: ShellTaskID, 601cc290419Sopenharmony_ci ret_command: HdcCommand, 602cc290419Sopenharmony_ci mut rx: mpsc::BoundedReceiver<Vec<u8>>, 603cc290419Sopenharmony_ci) { 604cc290419Sopenharmony_ci crate::info!( 605cc290419Sopenharmony_ci "Execute cmd:[{:?}], session_id: {}, channel_id: {}", 606cc290419Sopenharmony_ci cmd_param, 607cc290419Sopenharmony_ci shell_task_id.session_id, 608cc290419Sopenharmony_ci shell_task_id.channel_id, 609cc290419Sopenharmony_ci ); 610cc290419Sopenharmony_ci let cmd = trim_quotation_for_cmd(cmd_param); 611cc290419Sopenharmony_ci let mut shell_cmd = Command::new(SHELL_PROG); 612cc290419Sopenharmony_ci shell_cmd.args(["-c", &cmd]) 613cc290419Sopenharmony_ci .stdout(Stdio::piped()) 614cc290419Sopenharmony_ci .stdin(Stdio::piped()) 615cc290419Sopenharmony_ci .stderr(Stdio::piped()) 616cc290419Sopenharmony_ci .kill_on_drop(true); 617cc290419Sopenharmony_ci 618cc290419Sopenharmony_ci unsafe { 619cc290419Sopenharmony_ci shell_cmd.pre_exec(|| { 620cc290419Sopenharmony_ci Base::de_init_process(); 621cc290419Sopenharmony_ci libc::setsid(); 622cc290419Sopenharmony_ci let pid = libc::getpid(); 623cc290419Sopenharmony_ci libc::setpgid(pid, pid); 624cc290419Sopenharmony_ci Ok(()) 625cc290419Sopenharmony_ci }); 626cc290419Sopenharmony_ci } 627cc290419Sopenharmony_ci 628cc290419Sopenharmony_ci if let Ok(mut child) = shell_cmd.spawn() { 629cc290419Sopenharmony_ci 630cc290419Sopenharmony_ci let mut child_in = match child.take_stdin() { 631cc290419Sopenharmony_ci Some(child_in_inner) => { 632cc290419Sopenharmony_ci child_in_inner 633cc290419Sopenharmony_ci }, 634cc290419Sopenharmony_ci None => { 635cc290419Sopenharmony_ci crate::error!("take_stdin failed"); 636cc290419Sopenharmony_ci shell_channel_close(shell_task_id.channel_id, shell_task_id.session_id).await; 637cc290419Sopenharmony_ci return; 638cc290419Sopenharmony_ci }, 639cc290419Sopenharmony_ci }; 640cc290419Sopenharmony_ci 641cc290419Sopenharmony_ci let child_out = match child.take_stdout() { 642cc290419Sopenharmony_ci Some(child_out_inner) => { 643cc290419Sopenharmony_ci child_out_inner 644cc290419Sopenharmony_ci }, 645cc290419Sopenharmony_ci None => { 646cc290419Sopenharmony_ci crate::error!("take_stdin failed"); 647cc290419Sopenharmony_ci shell_channel_close(shell_task_id.channel_id, shell_task_id.session_id).await; 648cc290419Sopenharmony_ci return; 649cc290419Sopenharmony_ci }, 650cc290419Sopenharmony_ci }; 651cc290419Sopenharmony_ci 652cc290419Sopenharmony_ci let child_err = match child.take_stderr() { 653cc290419Sopenharmony_ci Some(child_err_inner) => { 654cc290419Sopenharmony_ci child_err_inner 655cc290419Sopenharmony_ci }, 656cc290419Sopenharmony_ci None => { 657cc290419Sopenharmony_ci crate::error!("take_stdin failed"); 658cc290419Sopenharmony_ci shell_channel_close(shell_task_id.channel_id, shell_task_id.session_id).await; 659cc290419Sopenharmony_ci return; 660cc290419Sopenharmony_ci }, 661cc290419Sopenharmony_ci }; 662cc290419Sopenharmony_ci 663cc290419Sopenharmony_ci let mut child_out_reader = ylong_runtime::io::AsyncBufReader::new(child_out); 664cc290419Sopenharmony_ci let mut child_err_reader = ylong_runtime::io::AsyncBufReader::new(child_err); 665cc290419Sopenharmony_ci let mut buf_out = [0u8; 30720]; 666cc290419Sopenharmony_ci let mut buf_err = [0u8; 30720]; 667cc290419Sopenharmony_ci 668cc290419Sopenharmony_ci loop { 669cc290419Sopenharmony_ci ylong_runtime::select! { 670cc290419Sopenharmony_ci read_res = child_out_reader.read(&mut buf_out) => { 671cc290419Sopenharmony_ci match read_res { 672cc290419Sopenharmony_ci Ok(bytes) => { 673cc290419Sopenharmony_ci let message = TaskMessage { 674cc290419Sopenharmony_ci channel_id: shell_task_id.channel_id, 675cc290419Sopenharmony_ci command: ret_command, 676cc290419Sopenharmony_ci payload: buf_out[..bytes].to_vec(), 677cc290419Sopenharmony_ci }; 678cc290419Sopenharmony_ci transfer::put(shell_task_id.session_id, message).await; 679cc290419Sopenharmony_ci } 680cc290419Sopenharmony_ci Err(e) => { 681cc290419Sopenharmony_ci crate::warn!("pty read failed: {e:?}"); 682cc290419Sopenharmony_ci break; 683cc290419Sopenharmony_ci } 684cc290419Sopenharmony_ci } 685cc290419Sopenharmony_ci }, 686cc290419Sopenharmony_ci 687cc290419Sopenharmony_ci read_res = child_err_reader.read(&mut buf_err) => { 688cc290419Sopenharmony_ci match read_res { 689cc290419Sopenharmony_ci Ok(bytes) => { 690cc290419Sopenharmony_ci let message = TaskMessage { 691cc290419Sopenharmony_ci channel_id: shell_task_id.channel_id, 692cc290419Sopenharmony_ci command: ret_command, 693cc290419Sopenharmony_ci payload: buf_err[..bytes].to_vec(), 694cc290419Sopenharmony_ci }; 695cc290419Sopenharmony_ci transfer::put(shell_task_id.session_id, message).await; 696cc290419Sopenharmony_ci } 697cc290419Sopenharmony_ci Err(e) => { 698cc290419Sopenharmony_ci crate::warn!("pty read failed: {e:?}"); 699cc290419Sopenharmony_ci break; 700cc290419Sopenharmony_ci } 701cc290419Sopenharmony_ci } 702cc290419Sopenharmony_ci } 703cc290419Sopenharmony_ci } 704cc290419Sopenharmony_ci 705cc290419Sopenharmony_ci if (watch_pipe_states(&mut rx, &mut child_in).await).is_err() { 706cc290419Sopenharmony_ci crate::warn!("pipe closed shell_task_id:{:?}", shell_task_id); 707cc290419Sopenharmony_ci break; 708cc290419Sopenharmony_ci } 709cc290419Sopenharmony_ci 710cc290419Sopenharmony_ci match child.try_wait() { 711cc290419Sopenharmony_ci Ok(Some(status)) => { 712cc290419Sopenharmony_ci crate::debug!("child exited with:{status} shell_task_id:{:?}", shell_task_id); 713cc290419Sopenharmony_ci read_buf_from_stdout_stderr(&mut child_out_reader, &mut child_err_reader, &shell_task_id, ret_command).await; 714cc290419Sopenharmony_ci break; 715cc290419Sopenharmony_ci }, 716cc290419Sopenharmony_ci Ok(None) => {}, 717cc290419Sopenharmony_ci Err(e) => { 718cc290419Sopenharmony_ci crate::warn!("child exited with: {:?} shell_task_id:{:?}", e, shell_task_id); 719cc290419Sopenharmony_ci break; 720cc290419Sopenharmony_ci } 721cc290419Sopenharmony_ci } 722cc290419Sopenharmony_ci } 723cc290419Sopenharmony_ci 724cc290419Sopenharmony_ci let _ = child.kill().await; 725cc290419Sopenharmony_ci crate::debug!("child kill shell_task_id:{:?}", shell_task_id); 726cc290419Sopenharmony_ci let _ = child.wait().await; 727cc290419Sopenharmony_ci crate::debug!("shell execute finish shell_task_id:{:?}", shell_task_id); 728cc290419Sopenharmony_ci } else { 729cc290419Sopenharmony_ci crate::debug!("shell spawn failed shell_task_id:{:?}", shell_task_id); 730cc290419Sopenharmony_ci } 731cc290419Sopenharmony_ci 732cc290419Sopenharmony_ci ShellExecuteMap::del(shell_task_id.session_id, shell_task_id.channel_id).await; 733cc290419Sopenharmony_ci shell_channel_close(shell_task_id.channel_id, shell_task_id.session_id).await; 734cc290419Sopenharmony_ci} 735cc290419Sopenharmony_ci 736cc290419Sopenharmony_ci 737cc290419Sopenharmony_ci 738cc290419Sopenharmony_ciimpl ShellExecuteTask { 739cc290419Sopenharmony_ci pub fn new( 740cc290419Sopenharmony_ci session_id: u32, 741cc290419Sopenharmony_ci channel_id: u32, 742cc290419Sopenharmony_ci cmd_param: String, 743cc290419Sopenharmony_ci ret_command: HdcCommand, 744cc290419Sopenharmony_ci ) -> Self { 745cc290419Sopenharmony_ci let (tx, rx) = ylong_runtime::sync::mpsc::bounded_channel::<Vec<u8>>(16); 746cc290419Sopenharmony_ci let cmd = cmd_param.clone(); 747cc290419Sopenharmony_ci crate::debug!("ShellExecuteTask new session_id {session_id}, channel_id {channel_id}"); 748cc290419Sopenharmony_ci let shell_task_id = ShellTaskID {session_id, channel_id}; 749cc290419Sopenharmony_ci let handle = ylong_runtime::spawn(task_for_shell_execute( 750cc290419Sopenharmony_ci cmd_param, 751cc290419Sopenharmony_ci shell_task_id, 752cc290419Sopenharmony_ci ret_command, 753cc290419Sopenharmony_ci rx, 754cc290419Sopenharmony_ci )); 755cc290419Sopenharmony_ci Self { 756cc290419Sopenharmony_ci handle, 757cc290419Sopenharmony_ci tx, 758cc290419Sopenharmony_ci session_id, 759cc290419Sopenharmony_ci channel_id, 760cc290419Sopenharmony_ci cmd, 761cc290419Sopenharmony_ci } 762cc290419Sopenharmony_ci } 763cc290419Sopenharmony_ci} 764