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 //! hdcfile
16 #![allow(missing_docs)]
17
18 use crate::transfer;
19 use std::fs::metadata;
20
21 use std::collections::HashMap;
22 use std::io;
23 use std::path::Path;
24 use std::sync::Arc;
25 use std::io::{Error, ErrorKind};
26 #[cfg(feature = "host")]
27 extern crate ylong_runtime_static as ylong_runtime;
28 use ylong_runtime::sync::Mutex;
29
30 use crate::common::filemanager::FileManager;
31 use crate::common::hdctransfer::*;
32 use crate::config::CompressType;
33 use crate::config::HdcCommand;
34 use crate::config::MessageLevel;
35 use crate::config::TaskMessage;
36 use crate::config::MAX_SIZE_IOBUF;
37 use crate::serializer::serialize::Serialization;
38
39 use super::base::Base;
40 use super::hdctransfer;
41 use crate::serializer::native_struct::TransferConfig;
42 use crate::utils;
43 #[cfg(not(feature = "host"))]
44 use crate::utils::hdc_log::*;
45 #[derive(Debug, Default, Clone, PartialEq, Eq)]
46 pub struct HdcFile {
47 pub file_cnt: u32,
48 pub dir_size: u64,
49 pub file_size: u64,
50 pub file_begin_time: u64,
51 pub dir_begin_time: u64,
52 pub transfer: HdcTransferBase,
53 }
54
55 impl HdcFile {
new(_session_id: u32, _channel_id: u32) -> Self56 pub fn new(_session_id: u32, _channel_id: u32) -> Self {
57 Self {
58 transfer: HdcTransferBase::new(_session_id, _channel_id),
59 ..Default::default()
60 }
61 }
62 }
63 type HdcFile_ = Arc<Mutex<HdcFile>>;
64 type FileTaskMap_ = Arc<Mutex<HashMap<(u32, u32), HdcFile_>>>;
65 pub struct FileTaskMap {}
66 impl FileTaskMap {
get_instance() -> FileTaskMap_67 fn get_instance() -> FileTaskMap_ {
68 static mut MAP: Option<FileTaskMap_> = None;
69 unsafe {
70 MAP.get_or_insert_with(|| Arc::new(Mutex::new(HashMap::new())))
71 .clone()
72 }
73 }
74
put(session_id: u32, channel_id: u32, value: HdcFile)75 pub async fn put(session_id: u32, channel_id: u32, value: HdcFile) {
76 let map = Self::get_instance();
77 let mut map = map.lock().await;
78 map.insert((session_id, channel_id), Arc::new(Mutex::new(value)));
79 }
80
exsit(session_id: u32, channel_id: u32) -> bool81 pub async fn exsit(session_id: u32, channel_id: u32) -> bool {
82 let arc = Self::get_instance();
83 let map = arc.lock().await;
84 let task = map.get(&(session_id, channel_id));
85 task.is_some()
86 }
87
remove(session_id: u32, channel_id: u32) -> Option<HdcFile_>88 pub async fn remove(session_id: u32, channel_id: u32) -> Option<HdcFile_> {
89 let arc = Self::get_instance();
90 let mut map = arc.lock().await;
91 map.remove(&(session_id, channel_id))
92 }
93
get(session_id: u32, channel_id: u32) -> Option<HdcFile_>94 pub async fn get(session_id: u32, channel_id: u32) -> Option<HdcFile_> {
95 let arc = Self::get_instance();
96 let map = arc.lock().await;
97 let task = map.get(&(session_id, channel_id));
98 task.cloned()
99 }
100
stop_task(session_id: u32)101 async fn stop_task(session_id: u32) {
102 let arc = Self::get_instance();
103 let map = arc.lock().await;
104 crate::info!("hdcfile stop task, session_id:{}, task_size: {}", session_id, map.len());
105 for _iter in map.iter() {
106 if _iter.0 .0 != session_id {
107 continue;
108 }
109 let mut task = _iter.1.lock().await;
110 task.transfer.stop_run = true;
111 crate::info!(
112 "session_id:{}, channel_id:{}, set stop_run as true.",
113 session_id,
114 _iter.0 .1
115 );
116 }
117 }
118
dump_task() -> String119 async fn dump_task() -> String {
120 let arc = Self::get_instance();
121 let map = arc.lock().await;
122 let mut result = String::new();
123 for _iter in map.iter() {
124 let task = _iter.1.lock().await;
125 let command = task.transfer.command_str.clone();
126 let line = format!(
127 "session_id:{},\tchannel_id:{},\tcommand:{}\n",
128 _iter.0 .0, _iter.0 .1, command
129 );
130 result.push_str(line.as_str());
131 }
132 result
133 }
134 }
135
check_local_path(session_id: u32, channel_id: u32) -> bool136 async fn check_local_path(session_id: u32, channel_id: u32) -> bool {
137 let Some(task) = FileTaskMap::get(session_id, channel_id).await else {
138 crate::error!(
139 "check_local_path get task is none session_id={session_id},channel_id={channel_id}"
140 );
141 return false;
142 };
143 let mut file_task = task.lock().await;
144 let local_path = file_task.transfer.local_path.clone();
145 let mut file_manager = FileManager::new(local_path);
146 let (open_result, err_msg) = file_manager.open();
147 if open_result {
148 file_task.transfer.transfer_config.file_size = file_manager.file_size();
149 file_task.transfer.file_size = file_task.transfer.transfer_config.file_size;
150 file_task.file_size = file_task.transfer.transfer_config.file_size;
151 file_task.transfer.transfer_config.optional_name = file_task.transfer.local_name.clone();
152 if transfer::base::CheckCompressVersion::get().await
153 && (file_task.transfer.transfer_config.file_size > (MAX_SIZE_IOBUF as u64))
154 {
155 file_task.transfer.transfer_config.compress_type = CompressType::Lz4 as u8;
156 }
157 file_task.transfer.transfer_config.path = file_task.transfer.remote_path.clone();
158 let command_str = format!(
159 "[file send], local_path:{}, optional_name:{}",
160 file_task.transfer.local_path.clone(),
161 file_task.transfer.transfer_config.optional_name
162 );
163 file_task
164 .transfer
165 .command_str
166 .push_str(command_str.as_str());
167 return true;
168 } else {
169 hdctransfer::echo_client(
170 session_id,
171 channel_id,
172 err_msg.as_str(),
173 MessageLevel::Fail,
174 )
175 .await;
176 }
177 false
178 }
179
echo_finish(session_id: u32, channel_id: u32, msg: String)180 async fn echo_finish(session_id: u32, channel_id: u32, msg: String) {
181 hdctransfer::echo_client(
182 session_id,
183 channel_id,
184 msg.as_str(),
185 MessageLevel::Ok,
186 )
187 .await;
188 task_finish(session_id, channel_id).await;
189 }
190
begin_transfer(session_id: u32, channel_id: u32, command: &String) -> bool191 pub async fn begin_transfer(session_id: u32, channel_id: u32, command: &String) -> bool {
192 let (argv, argc) = Base::split_command_to_args(command);
193 if argc < 2 {
194 echo_finish(
195 session_id,
196 channel_id,
197 "Transfer path split failed.".to_string(),
198 )
199 .await;
200 return false;
201 }
202 match set_master_parameters(session_id, channel_id, command, argc, argv).await {
203 Ok(_) => (),
204 Err(e) => {
205 echo_fail(session_id, channel_id, e, false).await;
206 return false;
207 }
208 }
209
210 let Some(task) = FileTaskMap::get(session_id, channel_id).await else {
211 crate::error!(
212 "begin_transfer get task is none session_id={session_id},channel_id={channel_id}"
213 );
214 return false;
215 };
216 let mut task = task.lock().await;
217 task.transfer.is_master = true;
218 drop(task);
219
220 let ret = check_local_path(session_id, channel_id).await;
221 if !ret {
222 do_file_finish(session_id, channel_id, &[1]).await;
223 return true;
224 }
225
226 put_file_check(session_id, channel_id).await;
227 true
228 }
229
set_master_parameters( session_id: u32, channel_id: u32, _command: &str, argc: u32, argv: Vec<String>, ) -> Result<bool, Error>230 async fn set_master_parameters(
231 session_id: u32,
232 channel_id: u32,
233 _command: &str,
234 argc: u32,
235 argv: Vec<String>,
236 ) -> Result<bool, Error> {
237 let Some(task) = FileTaskMap::get(session_id, channel_id).await else {
238 crate::error!(
239 "set_master_parameters get task is none session_id={session_id},channel_id={channel_id}"
240 );
241 return Err(Error::new(ErrorKind::Other, "Other failed"));
242 };
243 let mut task = task.lock().await;
244 let mut i: usize = 0;
245 let mut src_argv_index = 0u32;
246 if task.transfer.server_or_daemon {
247 src_argv_index += 2; // 2: represent the host parameters: "file" "send".
248 } // else: src_argv_index += 0: the host parameters "file" "recv" will be filtered.
249 while i < argc as usize {
250 match &argv[i] as &str {
251 "-z" => {
252 task.transfer.transfer_config.compress_type = CompressType::Lz4 as u8;
253 src_argv_index += 1;
254 }
255 "-a" => {
256 task.transfer.transfer_config.hold_timestamp = true;
257 src_argv_index += 1;
258 }
259 "-sync" => {
260 task.transfer.transfer_config.update_if_new = true;
261 src_argv_index += 1;
262 }
263 "-m" => {
264 src_argv_index += 1;
265 }
266 "-remote" => {
267 src_argv_index += 1;
268 }
269 "-cwd" => {
270 src_argv_index += 2;
271 task.transfer.transfer_config.client_cwd = argv.get(i + 1).unwrap().clone();
272 }
273 _ => {}
274 }
275 i += 1;
276 }
277 if argc == src_argv_index {
278 crate::error!("set_master_parameters argc = {:#?} return false", argc);
279 return Err(Error::new(ErrorKind::Other, "There is no local and remote path"));
280 }
281 task.transfer.remote_path = argv.last().unwrap().clone();
282 task.transfer.local_path = argv.get(argv.len() - 2).unwrap().clone();
283 if task.transfer.server_or_daemon {
284 if src_argv_index + 1 == argc {
285 crate::error!("src_argv_index = {:#?} return false", src_argv_index);
286 return Err(Error::new(ErrorKind::Other, "There is no remote path"));
287 }
288 let cwd = task.transfer.transfer_config.client_cwd.clone();
289 task.transfer.local_path = Base::extract_relative_path(&cwd, &task.transfer.local_path);
290 } else if src_argv_index + 1 == argc {
291 task.transfer.remote_path = String::from(".");
292 task.transfer.local_path = argv.get((argc - 1) as usize).unwrap().clone();
293 }
294 task.transfer.local_name = Base::get_file_name(&mut task.transfer.local_path).unwrap();
295 match metadata(task.transfer.local_path.clone()) {
296 Ok(metadata) => {
297 if !metadata.is_dir() {
298 task.transfer.is_dir = false;
299 return Ok(true);
300 }
301 task.transfer.is_dir = true;
302 task.transfer.task_queue = get_sub_files_resurively(&task.transfer.local_path.clone());
303 task.transfer.base_local_path = get_base_path(task.transfer.local_path.clone());
304
305 if !task.transfer.task_queue.is_empty() {
306 task.transfer.local_path = task.transfer.task_queue.pop().unwrap();
307 task.transfer.local_name =
308 match Base::get_relative_path(&task.transfer.base_local_path, &task.transfer.local_path) {
309 Some(relative_path) => relative_path,
310 None => task.transfer.local_path.clone()
311 };
312 } else {
313 crate::error!("task transfer task_queue is empty");
314 return Err(Error::new(ErrorKind::Other, "Operation failed, because the source folder is empty."));
315 }
316 },
317 Err(error) => {
318 let err_msg = format!("Error opening file: {}, path: {}", error, task.transfer.local_path);
319 crate::error!("{}", err_msg);
320 return Err(Error::new(ErrorKind::Other, err_msg));
321 },
322 }
323 Ok(true)
324 }
325
get_base_path(path: String) -> String326 fn get_base_path(path: String) -> String {
327 let p = Path::new(path.as_str());
328 let parent_path = p.parent();
329 if let Some(pp) = parent_path {
330 pp.display().to_string()
331 } else {
332 path
333 }
334 }
335
put_file_check(session_id: u32, channel_id: u32)336 async fn put_file_check(session_id: u32, channel_id: u32) {
337 let Some(task) = FileTaskMap::get(session_id, channel_id).await else {
338 return;
339 };
340 let task = task.lock().await;
341 let file_check_message = TaskMessage {
342 channel_id,
343 command: HdcCommand::FileCheck,
344 payload: task.transfer.transfer_config.serialize(),
345 };
346 transfer::put(task.transfer.session_id, file_check_message).await;
347 }
348
check_slaver(session_id: u32, channel_id: u32, _payload: &[u8]) -> Result<bool, Error>349 pub async fn check_slaver(session_id: u32, channel_id: u32, _payload: &[u8]) -> Result<bool, Error> {
350 let Some(task) = FileTaskMap::get(session_id, channel_id).await else {
351 crate::error!(
352 "check_slaver get task is none session_id={session_id:?},channel_id={channel_id:?}"
353 );
354 return Err(Error::new(ErrorKind::Other, "Other failed"));
355 };
356 let mut task = task.lock().await;
357 let mut transconfig = TransferConfig {
358 ..Default::default()
359 };
360 let _ = transconfig.parse(_payload.to_owned());
361 task.transfer.file_size = transconfig.file_size;
362 task.file_size = transconfig.file_size;
363 task.transfer.local_path = transconfig.path;
364 task.transfer.is_master = false;
365 task.transfer.index = 0;
366 let command_str = format!(
367 "[file recv],\t local_path:{},\t optional_name:{}\t",
368 task.transfer.local_path.clone(),
369 transconfig.optional_name
370 );
371 task.transfer.command_str.push_str(command_str.as_str());
372 let local_path = task.transfer.local_path.clone();
373 let optional_name = transconfig.optional_name.clone();
374 task.transfer.transfer_config.compress_type = transconfig.compress_type;
375 match hdctransfer::check_local_path(&mut task.transfer, &local_path, &optional_name) {
376 Ok(_) => (),
377 Err(e) => {
378 crate::error!("check_local_path return false channel_id={:#?}", channel_id);
379 return Err(e);
380 },
381 }
382 if task.transfer.transfer_config.update_if_new {
383 crate::error!("task.transfer.transfer_config.update_if_new is true");
384 return Err(Error::new(ErrorKind::Other, "Other failed"));
385 }
386 if task.dir_begin_time == 0 {
387 task.dir_begin_time = utils::get_current_time();
388 }
389 task.file_begin_time = utils::get_current_time();
390 Ok(true)
391 }
392
wake_up_slaver(session_id: u32, channel_id: u32)393 pub async fn wake_up_slaver(session_id: u32, channel_id: u32) {
394 let wake_up_message = TaskMessage {
395 channel_id,
396 command: HdcCommand::KernelWakeupSlavetask,
397 payload: Vec::<u8>::new(),
398 };
399 transfer::put(session_id, wake_up_message).await;
400 }
401
put_file_begin(session_id: u32, channel_id: u32)402 async fn put_file_begin(session_id: u32, channel_id: u32) {
403 let file_begin_message = TaskMessage {
404 channel_id,
405 command: HdcCommand::FileBegin,
406 payload: Vec::<u8>::new(),
407 };
408 transfer::put(session_id, file_begin_message).await;
409 }
410
transfer_next(session_id: u32, channel_id: u32) -> bool411 async fn transfer_next(session_id: u32, channel_id: u32) -> bool {
412 let Some(task) = FileTaskMap::get(session_id, channel_id).await else {
413 crate::error!(
414 "transfer_next get task is none session_id={session_id:?},channel_id={channel_id:?}"
415 );
416 return false;
417 };
418 let mut task = task.lock().await;
419 let Some(local_path) = task.transfer.task_queue.pop() else {
420 crate::error!(
421 "transfer_next get local path is none session_id={session_id:?},channel_id={channel_id:?}"
422 );
423 return false;
424 };
425 task.transfer.local_path = local_path;
426 task.transfer.local_name =
427 match Base::get_relative_path(&task.transfer.base_local_path, &task.transfer.local_path) {
428 Some(relative_path) => relative_path,
429 None => task.transfer.local_path.clone()
430 };
431 drop(task);
432 check_local_path(session_id, channel_id).await
433 }
434
on_all_transfer_finish(session_id: u32, channel_id: u32)435 async fn on_all_transfer_finish(session_id: u32, channel_id: u32) {
436 let Some(task) = FileTaskMap::get(session_id, channel_id).await else {
437 crate::error!(
438 "on_all_transfer_finish get task is none session_id={session_id:?},channel_id={channel_id:?}"
439 );
440 return;
441 };
442 let task = task.lock().await;
443 let last_error = task.transfer.last_error;
444 let size = if task.file_cnt > 1 {
445 task.dir_size
446 } else {
447 task.file_size
448 };
449 let time = if task.file_cnt > 1 {
450 utils::get_current_time() - task.dir_begin_time
451 } else {
452 utils::get_current_time() - task.file_begin_time
453 };
454 let rate = size as f64 / time as f64;
455 #[allow(unused_variables)]
456 let message = if last_error == 0 {
457 format!(
458 "FileTransfer finish, Size:{}, File count = {}, time:{}ms rate:{:.2}kB/s",
459 size, task.file_cnt, time, rate
460 )
461 } else {
462 format!(
463 "Transfer failed: {}: {}",
464 task.transfer.local_path,
465 io::Error::from_raw_os_error(last_error as i32),
466 )
467 };
468 #[cfg(feature = "host")]
469 {
470 let level = if last_error == 0 {
471 transfer::EchoLevel::OK
472 } else {
473 transfer::EchoLevel::FAIL
474 };
475 let _ =
476 transfer::send_channel_msg(task.transfer.channel_id, level, message)
477 .await;
478 hdctransfer::close_channel(channel_id).await;
479 return;
480 }
481 #[allow(unreachable_code)]
482 {
483 let level = if last_error == 0 {
484 MessageLevel::Ok
485 } else {
486 MessageLevel::Fail
487 };
488 hdctransfer::echo_client(
489 task.transfer.session_id,
490 task.transfer.channel_id,
491 message.as_str(),
492 level,
493 )
494 .await;
495 hdctransfer::close_channel(channel_id).await;
496 }
497 }
498
is_task_queue_empty(session_id: u32, channel_id: u32) -> bool499 async fn is_task_queue_empty(session_id: u32, channel_id: u32) -> bool {
500 let Some(task) = FileTaskMap::get(session_id, channel_id).await else {
501 crate::error!(
502 "do_file_finish get task is none session_id={session_id:?},channel_id={channel_id:?}"
503 );
504 return false;
505 };
506 let task = task.lock().await;
507 task.transfer.task_queue.is_empty()
508 }
509
do_file_finish(session_id: u32, channel_id: u32, _payload: &[u8])510 async fn do_file_finish(session_id: u32, channel_id: u32, _payload: &[u8]) {
511 if _payload[0] == 1 {
512 while !is_task_queue_empty(session_id, channel_id).await {
513 if transfer_next(session_id, channel_id).await {
514 put_file_check(session_id, channel_id).await;
515 return;
516 }
517 }
518
519 if is_task_queue_empty(session_id, channel_id).await {
520 let _finish_message = TaskMessage {
521 channel_id,
522 command: HdcCommand::FileFinish,
523 payload: [0].to_vec(),
524 };
525
526 transfer::put(session_id, _finish_message).await;
527 }
528 } else {
529 on_all_transfer_finish(session_id, channel_id).await;
530 task_finish(session_id, channel_id).await;
531 }
532 }
533
put_file_finish(session_id: u32, channel_id: u32)534 async fn put_file_finish(session_id: u32, channel_id: u32) {
535 let Some(task) = FileTaskMap::get(session_id, channel_id).await else {
536 crate::error!(
537 "put_file_finish get task is none session_id={session_id:?},channel_id={channel_id:?}"
538 );
539 return;
540 };
541 let mut task = task.lock().await;
542 let _payload: [u8; 1] = [1];
543 task.file_cnt += 1;
544 task.dir_size += task.file_size;
545 let task_finish_message = TaskMessage {
546 channel_id,
547 command: HdcCommand::FileFinish,
548 payload: _payload.to_vec(),
549 };
550 transfer::put(session_id, task_finish_message).await;
551 }
552
command_dispatch( session_id: u32, channel_id: u32, _command: HdcCommand, _payload: &[u8], _payload_size: u16, ) -> bool553 pub async fn command_dispatch(
554 session_id: u32,
555 channel_id: u32,
556 _command: HdcCommand,
557 _payload: &[u8],
558 _payload_size: u16,
559 ) -> bool {
560 match _command {
561 HdcCommand::FileInit => {
562 let s = String::from_utf8(_payload.to_vec());
563 match s {
564 Ok(str) => {
565 wake_up_slaver(session_id, channel_id).await;
566 begin_transfer(session_id, channel_id, &str).await;
567 }
568 Err(e) => {
569 let err_msg = format!("Transfer failed: arguments is invalid {:?}", e);
570 crate::error!("HdcCommand::FileInit: {}", err_msg);
571 echo_finish(session_id, channel_id, err_msg.to_string()).await;
572 }
573 }
574 }
575 HdcCommand::FileCheck => {
576 match check_slaver(session_id, channel_id, _payload).await {
577 Ok(_) => {
578 put_file_begin(session_id, channel_id).await;
579 },
580 Err(e) => {
581 echo_fail(session_id, channel_id, e, true).await;
582 }
583 }
584 }
585 HdcCommand::FileBegin => {
586 let Some(task) = FileTaskMap::get(session_id, channel_id).await else {
587 crate::error!(
588 "command_dispatch get task is none session_id={session_id:?},channel_id={channel_id:?}"
589 );
590 return false;
591 };
592 let task = task.lock().await;
593 hdctransfer::transfer_begin(&task.transfer, HdcCommand::FileData).await;
594 }
595 HdcCommand::FileData => {
596 let Some(task) = FileTaskMap::get(session_id, channel_id).await else {
597 crate::error!(
598 "command_dispatch get task is none session_id={session_id:?},channel_id={channel_id:?}"
599 );
600 return false;
601 };
602 let mut task = task.lock().await;
603 if hdctransfer::transfer_data(&mut task.transfer, _payload) {
604 drop(task);
605 put_file_finish(session_id, channel_id).await;
606 }
607 }
608 HdcCommand::FileMode | HdcCommand::DirMode => {
609 put_file_mode(session_id, channel_id).await;
610 }
611 HdcCommand::FileFinish => {
612 do_file_finish(session_id, channel_id, _payload).await;
613 }
614 _ => {
615 crate::error!("others, command {:?}", _command);
616 }
617 }
618
619 true
620 }
621
put_file_mode(session_id: u32, channel_id: u32)622 async fn put_file_mode(session_id: u32, channel_id: u32) {
623 let task_message = TaskMessage {
624 channel_id,
625 command: HdcCommand::FileMode,
626 payload: Vec::<u8>::new(),
627 };
628 transfer::put(session_id, task_message).await;
629 }
630
task_finish(session_id: u32, channel_id: u32)631 async fn task_finish(session_id: u32, channel_id: u32) {
632 hdctransfer::transfer_task_finish(channel_id, session_id).await;
633 }
634
stop_task(session_id: u32)635 pub async fn stop_task(session_id: u32) {
636 FileTaskMap::stop_task(session_id).await;
637 }
638
dump_task() -> String639 pub async fn dump_task() -> String {
640 FileTaskMap::dump_task().await
641 }
642
echo_fail(session_id: u32, channel_id: u32, error: Error, is_checked: bool)643 pub async fn echo_fail(session_id: u32, channel_id: u32, error: Error, is_checked: bool) {
644 let message = match FileTaskMap::get(session_id, channel_id).await {
645 Some(task) => {
646 if is_checked {
647 let task = task.lock().await;
648 format!("Error opening file: {}, path: {}", error, task.transfer.local_path)
649 } else {
650 format!("{}", error)
651 }
652 }
653 None => format!(
654 "Error opening file: {}, path: {}",
655 error,
656 "cannot get file path from FileTaskMap",
657 )
658 };
659 hdctransfer::echo_client(
660 session_id,
661 channel_id,
662 message.as_str(),
663 MessageLevel::Fail,
664 )
665 .await;
666 task_finish(session_id, channel_id).await;
667 }