Contiki 3.x
shell-download.c
1 /*
2  * Copyright (c) 2009, Swedish Institute of Computer Science.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 /**
34  * \file Shell command for downloading files from a remote node.
35  * Example usage:
36  * 'download <node addr> <filename> | write <local_filename'.
37  *
38  * \author Luca Mottola <luca@sics.se>, Fredrik Osterlind <fros@sics.se>
39  */
40 
41 #include "contiki.h"
42 #include "shell.h"
43 
44 #include "net/rime/rime.h"
45 #include "cfs/cfs.h"
46 
47 #include "dev/leds.h"
48 
49 #include <stdio.h>
50 #include <string.h>
51 
52 #define RUNICAST_CHANNEL SHELL_RIME_CHANNEL_DOWNLOAD
53 #define RUCB_CHANNEL (SHELL_RIME_CHANNEL_DOWNLOAD+1)
54 #define MAX_RETRANSMISSIONS 8
55 
56 #define DEBUG 0
57 
58 #if DEBUG
59 #define PRINTF(...) printf(__VA_ARGS__)
60 #else
61 #define PRINTF(...)
62 #endif
63 
64 /*---------------------------------------------------------------------------*/
65 PROCESS(shell_download_process, "download");
66 PROCESS(shell_download_server_process, "download server");
67 SHELL_COMMAND(download_command,
68  "download",
69  "download <node addr> <filename>: download file from remote node",
70  &shell_download_process);
71 /*---------------------------------------------------------------------------*/
72 static struct runicast_conn runicast;
73 static struct rucb_conn rucb;
74 static uint8_t downloading;
75 static uint8_t req_seq_counter;
76 static uint8_t req_last_seq;
77 static int fd;
78 /*---------------------------------------------------------------------------*/
79 static void
80 write_chunk(struct rucb_conn *c, int offset, int flag, char *data, int datalen)
81 {
82  if(datalen > 0) {
83  shell_output(&download_command,
84  data, datalen, NULL, 0);
85  PRINTF("write_chunk %d at %d bytes\n", datalen, offset);
86  }
87 
88  if(flag == RUCB_FLAG_NEWFILE) {
89  PRINTF("RUCB_FLAG_NEWFILE\n");
90  } else if(flag == RUCB_FLAG_NONE) {
91  PRINTF("RUCB_FLAG_NONE\n");
92  }
93  if(flag == RUCB_FLAG_LASTCHUNK) {
94  PRINTF("RUCB_FLAG_LASTCHUNK\n");
95  downloading = 0;
96  process_poll(&shell_download_process);
97  }
98 }
99 /*---------------------------------------------------------------------------*/
100 static int
101 read_chunk(struct rucb_conn *c, int offset, char *to, int maxsize)
102 {
103  int ret;
104  if(fd < 0) {
105  /* No file, send EOF */
106  leds_off(LEDS_GREEN);
107  return 0;
108  }
109 
110  cfs_seek(fd, offset, CFS_SEEK_SET);
111  ret = cfs_read(fd, to, maxsize);
112  PRINTF("read_chunk %d bytes at %d\n", ret, offset);
113 
114  if(ret < maxsize) {
115  PRINTF("read_chunk DONE\n");
116  cfs_close(fd);
117  fd = -1;
118  }
119 
120  return ret;
121 }
122 /*---------------------------------------------------------------------------*/
123 static void
124 timedout(struct rucb_conn *c)
125 {
126 }
127 /*---------------------------------------------------------------------------*/
128 static const struct rucb_callbacks rucb_call = { write_chunk, read_chunk, timedout };
129 /*---------------------------------------------------------------------------*/
130 PROCESS_THREAD(shell_download_process, ev, data)
131 {
132  const char *nextptr;
133  static linkaddr_t addr;
134  int len;
135  char buf[32];
136 
137  PROCESS_BEGIN();
138 
139  /* Parse node addr */
140  addr.u8[0] = shell_strtolong(data, &nextptr);
141  if(nextptr == data || *nextptr != '.') {
142  shell_output_str(&download_command,
143  "download <node addr> <filename>: need node address", "");
144  PROCESS_EXIT();
145  }
146  ++nextptr;
147  addr.u8[1] = shell_strtolong(nextptr, &nextptr);
148 
149  /* Get the length of the file, excluding a terminating NUL character. */
150  while(nextptr[0] == ' ') {
151  nextptr++;
152  }
153  len = strlen(nextptr);
154 
155  /*snprintf(buf, sizeof(buf), "%d.%d", addr.u8[0], addr.u8[1]);*/
156  /*shell_output_str(&download_command, "Downloading from: ", buf);*/
157 
158  if(len > PACKETBUF_SIZE - 32) {
159  snprintf(buf, sizeof(buf), "%d", len);
160  shell_output_str(&download_command, "filename too large: ", buf);
161  PROCESS_EXIT();
162  }
163 
164  /*shell_output_str(&download_command, "Downloading file: ", nextptr);*/
165 
166  /* Send file request */
167  downloading = 1;
168  rucb_open(&rucb, RUCB_CHANNEL, &rucb_call);
169  packetbuf_clear();
170  *((uint8_t *)packetbuf_dataptr()) = ++req_seq_counter;
171  memcpy(((char *)packetbuf_dataptr()) + 1, nextptr, len + 1);
172  packetbuf_set_datalen(len + 2);
173  PRINTF("requesting '%s'\n", nextptr);
174  runicast_send(&runicast, &addr, MAX_RETRANSMISSIONS);
175 
176  /* Wait for download to finish */
177  leds_on(LEDS_BLUE);
178  PROCESS_WAIT_UNTIL(!runicast_is_transmitting(&runicast) && !downloading);
179  leds_off(LEDS_BLUE);
180 
181  rucb_close(&rucb);
182  /*shell_output_str(&download_command, "Done!", "");*/
183 
184  PROCESS_END();
185 }
186 /*---------------------------------------------------------------------------*/
187 static void
188 request_recv(struct runicast_conn *c, const linkaddr_t *from, uint8_t seqno)
189 {
190  const char *filename;
191  uint8_t seq;
192 
193  if(packetbuf_datalen() < 2) {
194  /* Bad filename, ignore request */
195  printf("download: bad filename request (null)\n");
196  return;
197  }
198 
199  seq = ((uint8_t *)packetbuf_dataptr())[0];
200  if(seq == req_last_seq) {
201  PRINTF("download: ignoring duplicate request\n");
202  return;
203  }
204  req_last_seq = seq;
205  filename = ((char *)packetbuf_dataptr()) + 1;
206 
207  PRINTF("file requested: '%s'\n", filename);
208 
209  /* Initiate file transfer */
210  leds_on(LEDS_GREEN);
211  if(fd >= 0) {
212  cfs_close(fd);
213  }
214  fd = cfs_open(filename, CFS_READ);
215  if(fd < 0) {
216  printf("download: bad filename request (no read access): %s\n", filename);
217  } else {
218  PRINTF("download: sending file: %s\n", filename);
219  }
220 
221  rucb_close(&rucb);
222  rucb_open(&rucb, RUCB_CHANNEL, &rucb_call);
223  rucb_send(&rucb, from);
224 }
225 /*---------------------------------------------------------------------------*/
226 static void
227 request_sent(struct runicast_conn *c, const linkaddr_t *to,
228  uint8_t retransmissions)
229 {
230  process_poll(&shell_download_process);
231 }
232 /*---------------------------------------------------------------------------*/
233 static void
234 request_timedout(struct runicast_conn *c, const linkaddr_t *to,
235  uint8_t retransmissions)
236 {
237  shell_output_str(&download_command, "download: request timed out", "");
238  downloading = 0;
239  process_poll(&shell_download_process);
240 }
241 /*---------------------------------------------------------------------------*/
242 static const struct runicast_callbacks runicast_callbacks =
243 {request_recv, request_sent, request_timedout};
244 /*---------------------------------------------------------------------------*/
245 PROCESS_THREAD(shell_download_server_process, ev, data)
246 {
247  PROCESS_EXITHANDLER( runicast_close(&runicast); rucb_close(&rucb); );
248 
249  PROCESS_BEGIN();
250 
251  runicast_open(&runicast, RUNICAST_CHANNEL, &runicast_callbacks);
253 
254  PROCESS_END();
255 }
256 /*---------------------------------------------------------------------------*/
257 void
258 shell_download_init(void)
259 {
260  req_seq_counter = 0;
261  req_last_seq = -1;
262  fd = -1;
263  downloading = 0;
264  shell_register_command(&download_command);
265  process_start(&shell_download_server_process, NULL);
266 }
267 /*---------------------------------------------------------------------------*/
#define PROCESS_WAIT_UNTIL(c)
Wait for a condition to occur.
Definition: process.h:192
void process_poll(struct process *p)
Request a process to be polled.
Definition: process.c:371
int cfs_open(const char *name, int flags)
Open a file.
Definition: cfs-coffee.c:996
void shell_output_str(struct shell_command *c, char *text1, const char *text2)
Output strings from a shell command.
Definition: shell.c:383
#define PROCESS_EXIT()
Exit the currently running process.
Definition: process.h:200
cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)
Seek to a specified position in an open file.
Definition: cfs-coffee.c:1042
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
Main header file for the Contiki shell
#define NULL
The null pointer.
#define CFS_READ
Specify that cfs_open() should open a file for reading.
Definition: cfs.h:90
void packetbuf_set_datalen(uint16_t len)
Set the length of the data in the packetbuf.
Definition: packetbuf.c:200
Header file for the Rime stack
void shell_output(struct shell_command *c, void *data1, int len1, const void *data2, int len2)
Output data from a shell command.
Definition: shell.c:395
uint16_t packetbuf_datalen(void)
Get the length of the data in the packetbuf.
Definition: packetbuf.c:239
#define LEDS_GREEN
LED3 (Green) -&gt; PC2.
Definition: board.h:81
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
unsigned long shell_strtolong(const char *str, const char **retstr)
Convert a string to a number.
Definition: shell.c:521
#define PROCESS_EXITHANDLER(handler)
Specify an action when a process exits.
Definition: process.h:254
void shell_register_command(struct shell_command *c)
Register a command with the shell.
Definition: shell.c:413
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
void packetbuf_clear(void)
Clear and reset the packetbuf.
Definition: packetbuf.c:77
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99
#define PACKETBUF_SIZE
The size of the packetbuf, in bytes.
Definition: packetbuf.h:65
#define CFS_SEEK_SET
Specify that cfs_seek() should compute the offset from the beginning of the file. ...
Definition: cfs.h:127
void * packetbuf_dataptr(void)
Get a pointer to the data in the packetbuf.
Definition: packetbuf.c:207
#define SHELL_COMMAND(name, command, description, process)
Define a shell command.
Definition: shell.h:219
void cfs_close(int fd)
Close an open file.
Definition: cfs-coffee.c:1032
CFS header file.