diff --git a/apps/shell/Makefile.shell b/apps/shell/Makefile.shell index ff0c5486e..864afd80c 100644 --- a/apps/shell/Makefile.shell +++ b/apps/shell/Makefile.shell @@ -6,7 +6,8 @@ shell_src = shell.c shell-reboot.c \ shell-rime-debug.c shell-coffee.c \ shell-wget.c shell-httpd.c shell-irc.c \ shell-checkpoint.c shell-power.c \ - shell-tcpsend.c shell-udpsend.c shell-ping.c + shell-tcpsend.c shell-udpsend.c shell-ping.c \ + shell-rime-sendcmd.c shell-download.c #shell-rsh.c shell_dsc = shell-dsc.c diff --git a/apps/shell/shell-download.c b/apps/shell/shell-download.c new file mode 100644 index 000000000..ac515582c --- /dev/null +++ b/apps/shell/shell-download.c @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2009, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * $Id: shell-download.c,v 1.1 2009/04/16 14:33:47 fros4943 Exp $ + */ + +/** + * \file Shell command for downloading files from a remote node. + * Example usage: + * 'download | write , Fredrik Osterlind + */ + +#include "contiki.h" +#include "shell.h" + +#include "net/rime.h" +#include "cfs/cfs.h" + +#include "dev/leds.h" + +#include +#include + +#define RUNICAST_CHANNEL SHELL_RIME_CHANNEL_DOWNLOAD +#define RUCB_CHANNEL (SHELL_RIME_CHANNEL_DOWNLOAD+1) +#define MAX_RETRANSMISSIONS 8 + +#define DEBUG 0 + +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +/*---------------------------------------------------------------------------*/ +PROCESS(shell_download_process, "download"); +PROCESS(shell_download_server_process, "download server"); +SHELL_COMMAND(download_command, + "download", + "download : download file from remote node", + &shell_download_process); +/*---------------------------------------------------------------------------*/ +static struct runicast_conn runicast; +static struct rucb_conn rucb; +static uint8_t downloading; +static uint8_t req_seq_counter; +static uint8_t req_last_seq; +static int fd; +/*---------------------------------------------------------------------------*/ +static void +write_chunk(struct rucb_conn *c, int offset, int flag, char *data, int datalen) +{ + if (datalen > 0) { + shell_output(&download_command, + data, datalen, NULL, 0); + PRINTF("write_chunk %d at %d bytes\n", datalen, offset); + } + + if (flag == RUCB_FLAG_NEWFILE) { + PRINTF("RUCB_FLAG_NEWFILE\n"); + } + if (flag == RUCB_FLAG_NONE) { + PRINTF("RUCB_FLAG_NONE\n"); + } + if (flag == RUCB_FLAG_LASTCHUNK) { + PRINTF("RUCB_FLAG_LASTCHUNK\n"); + downloading = 0; + process_poll(&shell_download_process); + } +} +/*---------------------------------------------------------------------------*/ +static int +read_chunk(struct rucb_conn *c, int offset, char *to, int maxsize) +{ + int ret; + if (fd < 0) { + /* No file, send EOF */ + leds_off(LEDS_GREEN); + return 0; + } + + cfs_seek(fd, offset, CFS_SEEK_SET); + ret = cfs_read(fd, to, maxsize); + PRINTF("read_chunk %d bytes at %d\n", ret, offset); + + if (ret < maxsize) { + PRINTF("read_chunk DONE\n"); + cfs_close(fd); + fd = -1; + } + + return ret; +} +/*---------------------------------------------------------------------------*/ +static int +timedout(struct rucb_conn *c) +{ +} +/*---------------------------------------------------------------------------*/ +const static struct rucb_callbacks rucb_call = {write_chunk, read_chunk, timedout}; +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(shell_download_process, ev, data) +{ + const char *nextptr; + static rimeaddr_t addr; + int len; + char buf[32]; + + PROCESS_BEGIN(); + + /* Parse node addr */ + addr.u8[0] = shell_strtolong(data, &nextptr); + if(nextptr == data || *nextptr != '.') { + shell_output_str(&download_command, + "download : need node address", ""); + PROCESS_EXIT(); + } + ++nextptr; + addr.u8[1] = shell_strtolong(nextptr, &nextptr); + + /* Get the length of the file, excluding a terminating NUL character. */ + while ((uint8_t)nextptr[0] == ' ') { + nextptr++; + } + len = strlen((char *)nextptr); + + snprintf(buf, sizeof(buf), "%d.%d", addr.u8[0], addr.u8[1]); + /*shell_output_str(&download_command, "Downloading from: ", buf);*/ + + if(len > PACKETBUF_SIZE - 32) { + snprintf(buf, sizeof(buf), "%d", len); + shell_output_str(&download_command, "filename too large: ", buf); + PROCESS_EXIT(); + } + + /*shell_output_str(&download_command, "Downloading file: ", nextptr);*/ + + /* Send file request */ + downloading = 1; + rucb_open(&rucb, RUCB_CHANNEL, &rucb_call); + nextptr--; + req_seq_counter++; + nextptr[0] = req_seq_counter; + packetbuf_copyfrom(nextptr, len+2); + PRINTF("requesting '%s'\n", nextptr); + runicast_send(&runicast, &addr, MAX_RETRANSMISSIONS); + + /* Wait for download to finish */ + leds_on(LEDS_BLUE); + PROCESS_WAIT_UNTIL(!runicast_is_transmitting(&runicast) && !downloading); + leds_off(LEDS_BLUE); + + rucb_close(&rucb); + /*shell_output_str(&download_command, "Done!", "");*/ + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +static void +request_recv(struct runicast_conn *c, rimeaddr_t *from, uint8_t seqno) +{ + const char *filename; + uint8_t seq; + + if (packetbuf_datalen() < 2) { + /* Bad filename, ignore request */ + printf("download: bad filename request (null)\n"); + return; + } + + seq = ((char*)packetbuf_dataptr())[0]; + if (seq == req_last_seq) { + PRINTF("download: ignoring duplicate request\n"); + return; + } + req_last_seq = seq; + filename = ((char*)packetbuf_dataptr())+1; + + PRINTF("file requested: '%s'\n", filename); + + /* Initiate file transfer */ + leds_on(LEDS_GREEN); + if (fd >= 0) { + cfs_close(fd); + } + fd = cfs_open(filename, CFS_READ); + if (fd < 0) { + printf("download: bad filename request (no read access): %s\n", filename); + } else { + PRINTF("download: sending file: %s\n", filename); + } + + rucb_close(&rucb); + rucb_open(&rucb, RUCB_CHANNEL, &rucb_call); + rucb_send(&rucb, from); +} +/*---------------------------------------------------------------------------*/ +static void +request_sent(struct runicast_conn *c, rimeaddr_t *to, uint8_t retransmissions) +{ + process_poll(&shell_download_process); +} +/*---------------------------------------------------------------------------*/ +static void +request_timedout(struct runicast_conn *c, rimeaddr_t *to, uint8_t retransmissions) +{ + shell_output_str(&download_command, + "download: request timed out", ""); + downloading = 0; + process_poll(&shell_download_process); +} +/*---------------------------------------------------------------------------*/ +static const struct runicast_callbacks runicast_callbacks = +{request_recv, request_sent, request_timedout}; +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(shell_download_server_process, ev, data) +{ + PROCESS_EXITHANDLER( runicast_close(&runicast); rucb_close(&rucb); ); + + PROCESS_BEGIN(); + + runicast_open(&runicast, RUNICAST_CHANNEL, &runicast_callbacks); + PROCESS_WAIT_UNTIL(0); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +void +shell_download_init(void) +{ + req_seq_counter = 0; + req_last_seq = -1; + fd = -1; + downloading = 0; + shell_register_command(&download_command); + process_start(&shell_download_server_process, NULL); +} +/*---------------------------------------------------------------------------*/ diff --git a/apps/shell/shell-download.h b/apps/shell/shell-download.h new file mode 100644 index 000000000..8e8d61202 --- /dev/null +++ b/apps/shell/shell-download.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * $Id: shell-download.h,v 1.1 2009/04/16 14:33:47 fros4943 Exp $ + */ + +#ifndef __SHELL_DOWNLOAD_H__ +#define __SHELL_DOWNLOAD_H__ + +#include "shell.h" + +void shell_download_init(void); + +#endif /* __SHELL_DOWNLOAD_H__ */ diff --git a/apps/shell/shell-rime-sendcmd.c b/apps/shell/shell-rime-sendcmd.c new file mode 100644 index 000000000..f203c1e3a --- /dev/null +++ b/apps/shell/shell-rime-sendcmd.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2009, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * $Id: shell-rime-sendcmd.c,v 1.1 2009/04/16 14:33:47 fros4943 Exp $ + */ + +#include "contiki.h" +#include "contiki-conf.h" +#include "shell-rime.h" + +#include "dev/leds.h" + +#include "lib/crc16.h" +#include "lib/random.h" + +#include "net/rime.h" +#include "net/rime/unicast.h" + +#include "net/rime/timesynch.h" + +#if CONTIKI_TARGET_NETSIM +#include "ether.h" +#endif /* CONTIKI_TARGET_NETSIM */ + +#include +#ifndef HAVE_SNPRINTF +int snprintf(char *str, size_t size, const char *format, ...); +#endif /* HAVE_SNPRINTF */ +#include + +#define CMDMSG_HDR_SIZE 2 + +struct cmd_msg { + uint16_t crc; + char sendcmd[1]; +}; + +static struct unicast_conn uc; +/*---------------------------------------------------------------------------*/ +PROCESS(shell_sendcmd_process, "sendcmd"); +PROCESS(shell_sendcmd_server_process, "sendcmd server"); +SHELL_COMMAND(sendcmd_command, + "sendcmd", + "sendcmd : send a command to the specified one-hop node", + &shell_sendcmd_process); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(shell_sendcmd_server_process, ev, data) +{ + static struct process *child_command; + int err; + PROCESS_BEGIN(); + + /* XXX: direct output to null. */ + /* printf("sendcmd server got command string '%s'\n", (char *)data);*/ + err = shell_start_command(data, strlen((char * )data), NULL, &child_command); + if(err == SHELL_FOREGROUND && process_is_running(child_command)) { + PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_EXIT || + (ev == PROCESS_EVENT_EXITED && + data == child_command)); + if(ev == PROCESS_EVENT_EXIT) { + process_exit(child_command); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(shell_sendcmd_process, ev, data) +{ + struct cmd_msg *msg; + int len; + rimeaddr_t addr; + const char *nextptr; + char buf[32]; + + PROCESS_BEGIN(); + + addr.u8[0] = shell_strtolong(data, &nextptr); + if(nextptr == data || *nextptr != '.') { + shell_output_str(&sendcmd_command, + "sendcmd : receiver must be specified", ""); + PROCESS_EXIT(); + } + ++nextptr; + addr.u8[1] = shell_strtolong(nextptr, &nextptr); + + snprintf(buf, sizeof(buf), "%d.%d", addr.u8[0], addr.u8[1]); + shell_output_str(&sendcmd_command, "Sending command to ", buf); + + /* Get the length of the command line, excluding a terminating NUL character. */ + len = strlen((char *)nextptr); + + /* Check the length of the command line to see that it is small + enough to fit in a packet. We count with 32 bytes of header, + which may be a little too much, but at least we are on the safe + side. */ + if(len > PACKETBUF_SIZE - 32) { + snprintf(buf, sizeof(buf), "%d", len); + shell_output_str(&sendcmd_command, "command line too large: ", buf); + PROCESS_EXIT(); + } + + packetbuf_clear(); + msg = packetbuf_dataptr(); + packetbuf_set_datalen(len + 1 + CMDMSG_HDR_SIZE); + strcpy(msg->sendcmd, nextptr); + + /* Terminate the string with a NUL character. */ + msg->sendcmd[len] = 0; + msg->crc = crc16_data(msg->sendcmd, len, 0); + /* printf("sendcmd sending '%s'\n", msg->sendcmd);*/ + unicast_send(&uc, &addr); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +static void +recv_uc(struct unicast_conn *c, rimeaddr_t *from) +{ + struct cmd_msg *msg; + uint16_t crc; + int len; + + msg = packetbuf_dataptr(); + + if(packetbuf_datalen() > 1 + CMDMSG_HDR_SIZE) { + + /* First ensure that the old process is killed. */ + process_exit(&shell_sendcmd_server_process); + + len = packetbuf_datalen() - 1 - CMDMSG_HDR_SIZE; + + /* Make sure that the incoming command is null-terminated. */ + msg->sendcmd[len] = 0; + memcpy(&crc, &msg->crc, sizeof(crc)); + + if(crc == crc16_data(msg->sendcmd, len, 0)) { + /* Start the server process with the incoming command. */ + process_start(&shell_sendcmd_server_process, msg->sendcmd); + } + } +} +static const struct unicast_callbacks unicast_callbacks = { recv_uc }; +/*---------------------------------------------------------------------------*/ +void +shell_rime_sendcmd_init(void) +{ + unicast_open(&uc, SHELL_RIME_CHANNEL_SENDCMD, &unicast_callbacks); + shell_register_command(&sendcmd_command); +} +/*---------------------------------------------------------------------------*/ diff --git a/apps/shell/shell-rime-sendcmd.h b/apps/shell/shell-rime-sendcmd.h new file mode 100644 index 000000000..60f8d7f9b --- /dev/null +++ b/apps/shell/shell-rime-sendcmd.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2009, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * $Id: shell-rime-sendcmd.h,v 1.1 2009/04/16 14:33:47 fros4943 Exp $ + */ + +#ifndef __SHELL_RIME_SENDCMD_H__ +#define __SHELL_RIME_SENDCMD_H__ + +void shell_rime_sendcmd_init(void); + +#endif /* __SHELL_RIME_SENDCMD_H__ */ diff --git a/apps/shell/shell-rime.h b/apps/shell/shell-rime.h index c8121fa67..9ee574a6b 100644 --- a/apps/shell/shell-rime.h +++ b/apps/shell/shell-rime.h @@ -28,7 +28,7 @@ * * This file is part of the Contiki operating system. * - * $Id: shell-rime.h,v 1.2 2008/08/15 18:58:42 adamdunkels Exp $ + * $Id: shell-rime.h,v 1.3 2009/04/16 14:33:47 fros4943 Exp $ */ /** @@ -63,10 +63,16 @@ void shell_rime_init(void); /* Rime channel used by the 'netcmd' command, which uses 1 channels */ #define SHELL_RIME_CHANNEL_NETCMD SHELL_RIME_CHANNEL_PING + 3 +/* Rime channel used by the 'sendcmd' command, which uses 1 channels */ +#define SHELL_RIME_CHANNEL_SENDCMD SHELL_RIME_CHANNEL_NETCMD + 1 + /* Rime channel used by the 'netfile' command, which uses 2 channels */ -#define SHELL_RIME_CHANNEL_NETFILE SHELL_RIME_CHANNEL_NETCMD + 1 +#define SHELL_RIME_CHANNEL_NETFILE SHELL_RIME_CHANNEL_SENDCMD + 1 /* Rime channel used by the 'sendtest' command, which uses 1 channels */ #define SHELL_RIME_CHANNEL_SENDTEST SHELL_RIME_CHANNEL_NETFILE + 2 +/* Rime channel used by the 'download' command, which uses 2 channels */ +#define SHELL_RIME_CHANNEL_DOWNLOAD SHELL_RIME_CHANNEL_SENDTEST + 1 + #endif /* __SHELL_RIME_H__ */ diff --git a/apps/shell/shell.h b/apps/shell/shell.h index c0585c6b1..0aa233657 100644 --- a/apps/shell/shell.h +++ b/apps/shell/shell.h @@ -48,7 +48,7 @@ * * This file is part of the Contiki operating system. * - * $Id: shell.h,v 1.15 2009/03/06 07:23:08 adamdunkels Exp $ + * $Id: shell.h,v 1.16 2009/04/16 14:33:47 fros4943 Exp $ */ /** @@ -357,6 +357,7 @@ struct shell_input { #include "shell-blink.h" #include "shell-checkpoint.h" #include "shell-coffee.h" +#include "shell-download.h" #include "shell-exec.h" #include "shell-file.h" #include "shell-httpd.h" @@ -369,6 +370,7 @@ struct shell_input { #include "shell-rime-debug.h" #include "shell-rime-netcmd.h" #include "shell-rime-ping.h" +#include "shell-rime-sendcmd.h" #include "shell-rime-sniff.h" #include "shell-rime.h" #include "shell-rsh.h"