1 /*
2  * Copyright (C) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 //! app
16 #![allow(missing_docs)]
17 
18 use crate::utils::hdc_log::*;
19 use crate::common::filemanager::FileManager;
20 use crate::common::hdctransfer::{self, HdcTransferBase};
21 use crate::config;
22 use crate::config::HdcCommand;
23 use crate::config::TaskMessage;
24 use crate::serializer::native_struct::TransferConfig;
25 use crate::serializer::serialize::Serialization;
26 use crate::tar::decompress::Decompress;
27 use crate::transfer;
28 use std::collections::HashMap;
29 use std::path::PathBuf;
30 use std::sync::Arc;
31 use ylong_runtime::sync::Mutex;
32 
33 use std::fs::{create_dir_all, File, *};
34 use std::io::{self, Error, ErrorKind};
35 use std::process::Command;
36 
37 #[derive(Debug, Default, Clone, PartialEq, Eq)]
38 pub struct DaemonAppTask {
39     pub result_msg: Vec<u8>,
40     pub transfer: HdcTransferBase,
41 }
42 
43 impl DaemonAppTask {
newnull44     pub fn new(_session_id: u32, _channel_id: u32) -> Self {
45         Self {
46             result_msg: vec![],
47             transfer: HdcTransferBase::new(_session_id, _channel_id),
48         }
49     }
50 }
51 
52 type DaemonAppTask_ = Arc<Mutex<DaemonAppTask>>;
53 type AppTaskMap_ = Arc<Mutex<HashMap<(u32, u32), DaemonAppTask_>>>;
54 pub struct AppTaskMap {}
55 impl AppTaskMap {
get_instancenull56     fn get_instance() -> AppTaskMap_ {
57         static mut MAP: Option<AppTaskMap_> = None;
58         unsafe {
59             MAP.get_or_insert_with(|| Arc::new(Mutex::new(HashMap::new())))
60                 .clone()
61         }
62     }
63 
64     pub async fn put(session_id: u32, channel_id: u32, value: DaemonAppTask) {
65         let map = Self::get_instance();
66         let mut map = map.lock().await;
67         map.insert((session_id, channel_id), Arc::new(Mutex::new(value)));
68     }
69 
70     pub async fn exsit(session_id: u32, channel_id: u32) -> bool {
71         let arc = Self::get_instance();
72         let map = arc.lock().await;
73         let task = map.get(&(session_id, channel_id));
74         task.is_some()
75     }
76 
77     pub async fn remove(session_id: u32, channel_id: u32) -> Option<DaemonAppTask_> {
78         let arc = Self::get_instance();
79         let mut map = arc.lock().await;
80         map.remove(&(session_id, channel_id))
81     }
82 
83     pub async fn get(session_id: u32, channel_id: u32) -> Option<DaemonAppTask_> {
84         let arc = Self::get_instance();
85         let map = arc.lock().await;
86         let Some(task) = map.get(&(session_id, channel_id)) else {
87             crate::error!(
88                 "session_id:{} channel_id:{} not found",
89                 session_id,
90                 channel_id
91             );
92             return None;
93         };
94         Some(task.clone())
95     }
96 
97     async fn stop_task(session_id: u32) {
98         let arc = Self::get_instance();
99         let map = arc.lock().await;
100         crate::info!("app stop_task, session_id:{}, task_size: {}", session_id, map.len());
101         for _iter in map.iter() {
102             if _iter.0 .0 != session_id {
103                 continue;
104             }
105             let mut task = _iter.1.lock().await;
106             task.transfer.stop_run = true;
107         }
108     }
109 
110     async fn dump_task() -> String {
111         let arc = Self::get_instance();
112         let map = arc.lock().await;
113         let mut result = String::new();
114         for _iter in map.iter() {
115             let task = _iter.1.lock().await;
116             let command = task.transfer.command_str.clone();
117             let line = format!(
118                 "session_id:{},\tchannel_id:{},\tcommand:{}",
119                 _iter.0 .0, _iter.0 .1, command
120             );
121             result.push_str(line.as_str());
122         }
123         result
124     }
125 }
126 
tar_to_dirnull127 fn tar_to_dir(tar_path: PathBuf) -> Result<String, String> {
128     if !tar_path.exists() {
129         return Err(format!("{} is not exist!", tar_path.display()));
130     }
131 
132     match Decompress::file(tar_path.display().to_string().as_str()) {
133         Ok(decompress) => {
134             let tar_string = tar_path.display().to_string();
135             let dir = tar_string.trim_end_matches(".tar");
136             if let Err(err) = decompress.decompress(dir) {
137                 return Err(format!("Decompress failed, {err}"));
138             }
139             Ok(dir.to_string())
140         }
141         Err(err) => Err(format!("Decompress failed, {err}")),
142     }
143 }
144 
145 async fn do_app_check(session_id: u32, channel_id: u32, _payload: &[u8]) -> bool {
146     let Some(arc) = AppTaskMap::get(session_id, channel_id).await else {
147         crate::error!(
148             "session_id:{} channel_id:{} not found",
149             session_id,
150             channel_id
151         );
152         return false;
153     };
154     let mut task = arc.lock().await;
155     let mut transconfig = TransferConfig {
156         ..Default::default()
157     };
158     let _ = transconfig.parse(_payload.to_owned());
159     task.transfer.transfer_config.options = transconfig.options.clone();
160     task.transfer.transfer_config.function_name = transconfig.function_name.clone();
161     let tmp_dir = String::from(config::INSTALL_TMP_DIR);
162     let local_path = tmp_dir.clone() + transconfig.optional_name.as_str();
163     task.transfer.command_str = format!(
164         "[{}],\tlocal_path:{}\n",
165         transconfig.function_name, local_path
166     );
167     task.transfer.is_master = false;
168     task.transfer.local_path = local_path;
169     task.transfer.file_size = transconfig.file_size;
170     task.transfer.index = 0;
171     let state = metadata(tmp_dir.clone());
172     if let Ok(metadata_obj) = state {
173         if metadata_obj.is_dir() {
174             return File::create(task.transfer.local_path.clone()).is_ok();
175         }
176     } else {
177         crate::debug!("{tmp_dir} is not exist.");
178         let _ = create_dir_all(tmp_dir);
179         return File::create(task.transfer.local_path.clone()).is_ok();
180     }
181     false
182 }
183 
184 async fn put_file_begin(session_id: u32, channel_id: u32) {
185     let file_begin_message = TaskMessage {
186         channel_id,
187         command: HdcCommand::AppBegin,
188         payload: Vec::<u8>::new(),
189     };
190     transfer::put(session_id, file_begin_message).await;
191 }
192 
193 async fn put_app_finish(
194     session_id: u32,
195     channel_id: u32,
196     mode: u8,
197     exit_status: u8,
198     result: &mut [u8],
199 ) {
200     let mut msg = Vec::<u8>::new();
201     msg.push(mode);
202     msg.push(exit_status);
203     let Some(arc) = AppTaskMap::get(session_id, channel_id).await else {
204         crate::error!(
205             "session_id:{} channel_id:{} not found",
206             session_id,
207             channel_id
208         );
209         return;
210     };
211     let mut task = arc.lock().await;
212     task.result_msg.append(&mut result.to_vec());
213     msg.append(&mut task.result_msg.clone());
214 
215     let app_finish_message = TaskMessage {
216         channel_id,
217         command: HdcCommand::AppFinish,
218         payload: msg.clone(),
219     };
220     transfer::put(session_id, app_finish_message).await;
221 }
222 
223 async fn app_uninstall(session_id: u32, channel_id: u32, _payload: &[u8]) {
224     let mut str = match String::from_utf8(_payload.to_vec()) {
225         Ok(v) => v,
226         Err(e) => {
227             crate::error!(
228                 "session_id:{} channel_id:{} uninstall error:{}",
229                 session_id,
230                 channel_id,
231                 e
232             );
233             put_app_finish(
234                 session_id,
235                 channel_id,
236                 0,
237                 0,
238                 &mut e.to_string().into_bytes()[..],
239             )
240             .await;
241             return;
242         }
243     };
244     str = str.trim_end_matches('\0').to_string();
245 
246     let (opt, package): (Vec<String>, Vec<String>) = str
247         .split(' ')
248         .map(String::from)
249         .partition(|word| word.starts_with('-'));
250     do_app_uninstall(session_id, channel_id, opt.join(" "), package.join(" ")).await;
251 }
252 
253 async fn handle_execute_result(
254     session_id: u32,
255     channel_id: u32,
256     result: Result<Vec<u8>, Error>,
257     mode: u8,
258 ) {
259     match &result {
260         Ok(message) => {
261             let mut m: Vec<u8> = message.clone();
262             put_app_finish(session_id, channel_id, mode, 1, &mut m[..]).await;
263         }
264         Err(err) => {
265             crate::error!(
266                 "session_id:{} channel_id:{} error:{}",
267                 session_id,
268                 channel_id,
269                 err
270             );
271             put_app_finish(
272                 session_id,
273                 channel_id,
274                 mode,
275                 0,
276                 &mut err.to_string().into_bytes()[..],
277             )
278             .await;
279         }
280     }
281 }
282 
283 async fn do_app_uninstall(session_id: u32, channel_id: u32, options: String, package: String) {
284     let mode = config::AppModeType::UnInstall as u8;
285     // let cmd = String::new();
286     let cmd = if !options.contains('n') {
287         format!("bm uninstall {} -n {}", options, package)
288     } else {
289         format!("bm uninstall {} {}", options, package)
290     };
291     crate::debug!("channel_id {channel_id}, cmd {cmd}");
292     let result = execute_cmd(&cmd);
293     handle_execute_result(session_id, channel_id, result, mode).await;
294 }
295 
296 async fn do_app_install(session_id: u32, channel_id: u32) {
297     let Some(arc) = AppTaskMap::get(session_id, channel_id).await else {
298         crate::error!(
299             "session_id:{} channel_id:{} not found",
300             session_id,
301             channel_id
302         );
303         return;
304     };
305     let task = arc.lock().await;
306     let options = task.transfer.transfer_config.options.clone();
307     let mut local_path = task.transfer.local_path.clone();
308     drop(task);
309     if local_path.ends_with(".tar") {
310         match tar_to_dir(PathBuf::from(local_path.clone())) {
311             Ok(dir) => {
312                 let _ = remove_file(local_path.clone());
313                 local_path = dir
314             }
315             Err(err) => {
316                 crate::error!("{err}");
317             }
318         }
319     }
320     let mode = config::AppModeType::Install as u8;
321     let cmd = if !options.contains('p') && !options.contains('s') {
322         format!("bm install {} -p {}", options, local_path)
323     } else {
324         format!("bm install {} {}", options, local_path)
325     };
326     crate::debug!("channel_id {channel_id}, cmd {cmd}");
327     let result = execute_cmd(&cmd);
328     handle_execute_result(session_id, channel_id, result, mode).await;
329     let _ = FileManager::remove_file(local_path.as_str());
330 }
331 
execute_cmdnull332 fn execute_cmd(cmd: &String) -> io::Result<Vec<u8>> {
333     let result = Command::new(config::SHELL_PROG).args(["-c", cmd]).output();
334     match result {
335         Ok(output) => {
336             let msg = [output.stdout, output.stderr].concat();
337             let mut str = match String::from_utf8(msg) {
338                 Ok(s) => s,
339                 Err(e) => {
340                     let error_msg = format!("cmd execute error: {}", e);
341                     crate::error!("{error_msg}");
342                     return Err(Error::new(ErrorKind::Other, error_msg));
343                 }
344             };
345             str = str.replace('\n', " ");
346             Ok(str.into_bytes())
347         }
348         Err(e) => Err(Error::new(ErrorKind::Other, e.to_string())),
349     }
350 }
351 
352 async fn on_transfer_finish(session_id: u32, channel_id: u32) {
353     let Some(arc) = AppTaskMap::get(session_id, channel_id).await else {
354         crate::error!(
355             "session_id:{} channel_id:{} not found",
356             session_id,
357             channel_id
358         );
359         return;
360     };
361     let task = arc.lock().await;
362     let function_name = task.transfer.transfer_config.function_name.clone();
363     drop(task);
364     if function_name == config::TRANSFER_FUNC_NAME {
365         do_app_install(session_id, channel_id).await;
366     }
367 }
368 
369 async fn transfer_fail(session_id: u32, channel_id: u32, error: &str) {
370     let mode = config::AppModeType::Install as u8;
371     put_app_finish(
372         session_id,
373         channel_id,
374         mode,
375         0,
376         &mut error.to_string().into_bytes()[..],
377     )
378     .await;
379 }
380 
381 pub async fn command_dispatch(
382     session_id: u32,
383     channel_id: u32,
384     _command: HdcCommand,
385     _payload: &[u8],
386     _payload_size: u16,
387 ) -> bool {
388     match _command {
389         HdcCommand::AppCheck => {
390             if do_app_check(session_id, channel_id, _payload).await {
391                 put_file_begin(session_id, channel_id).await;
392             } else {
393                 transfer_fail(session_id, channel_id, "check file fail.").await;
394             }
395         }
396         HdcCommand::AppUninstall => {
397             app_uninstall(session_id, channel_id, _payload).await;
398         }
399         HdcCommand::AppData => {
400             let Some(arc) = AppTaskMap::get(session_id, channel_id).await else {
401                 crate::error!(
402                     "session_id:{} channel_id:{} not found",
403                     session_id,
404                     channel_id
405                 );
406                 return false;
407             };
408             let mut task = arc.lock().await;
409             if task.transfer.stop_run {
410                 crate::error!("stop_run {}", task.transfer.stop_run);
411                 return false;
412             }
413             if hdctransfer::transfer_data(&mut task.transfer, _payload) {
414                 drop(task);
415                 on_transfer_finish(session_id, channel_id).await;
416             }
417         }
418         _ => {
419             crate::error!(
420                 "session_id:{} channel_id:{}, command:{:?} not support",
421                 session_id,
422                 channel_id,
423                 _command
424             );
425         }
426     }
427     true
428 }
429 
430 pub async fn stop_task(session_id: u32) {
431     AppTaskMap::stop_task(session_id).await;
432 }
433 
434 pub async fn dump_task() -> String {
435     AppTaskMap::dump_task().await
436 }
437