Contiki 3.x
shell-download.c
Go to the documentation of this file.
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
35  *
36  * Shell command for downloading files from a remote node.
37  * Example usage:
38  * 'download <node addr> <filename> | write <local_filename>'.
39  *
40  * \author Luca Mottola <luca@sics.se>, Fredrik Osterlind <fros@sics.se>
41  */
42 
43 #include "contiki.h"
44 #include "shell.h"
45 
46 #include "net/rime/rime.h"
47 #include "cfs/cfs.h"
48 
49 #include "dev/leds.h"
50 
51 #include <stdio.h>
52 #include <string.h>
53 
54 #define RUNICAST_CHANNEL SHELL_RIME_CHANNEL_DOWNLOAD
55 #define RUCB_CHANNEL (SHELL_RIME_CHANNEL_DOWNLOAD+1)
56 #define MAX_RETRANSMISSIONS 8
57 
58 #define DEBUG 0
59 
60 #if DEBUG
61 #define PRINTF(...) printf(__VA_ARGS__)
62 #else
63 #define PRINTF(...)
64 #endif
65 
66 /*---------------------------------------------------------------------------*/
67 PROCESS(shell_download_process, "download");
68 PROCESS(shell_download_server_process, "download server");
69 SHELL_COMMAND(download_command,
70  "download",
71  "download <node addr> <filename>: download file from remote node",
72  &shell_download_process);
73 /*---------------------------------------------------------------------------*/
74 static struct runicast_conn runicast;
75 static struct rucb_conn rucb;
76 static uint8_t downloading;
77 static uint8_t req_seq_counter;
78 static uint8_t req_last_seq;
79 static int fd;
80 /*---------------------------------------------------------------------------*/
81 static void
82 write_chunk(struct rucb_conn *c, int offset, int flag, char *data, int datalen)
83 {
84  if(datalen > 0) {
85  shell_output(&download_command,
86  data, datalen, NULL, 0);
87  PRINTF("write_chunk %d at %d bytes\n", datalen, offset);
88  }
89 
90  if(flag == RUCB_FLAG_NEWFILE) {
91  PRINTF("RUCB_FLAG_NEWFILE\n");
92  } else if(flag == RUCB_FLAG_NONE) {
93  PRINTF("RUCB_FLAG_NONE\n");
94  }
95  if(flag == RUCB_FLAG_LASTCHUNK) {
96  PRINTF("RUCB_FLAG_LASTCHUNK\n");
97  downloading = 0;
98  process_poll(&shell_download_process);
99  }
100 }
101 /*---------------------------------------------------------------------------*/
102 static int
103 read_chunk(struct rucb_conn *c, int offset, char *to, int maxsize)
104 {
105  int ret;
106  if(fd < 0) {
107  /* No file, send EOF */
108  leds_off(LEDS_GREEN);
109  return 0;
110  }
111 
112  cfs_seek(fd, offset, CFS_SEEK_SET);
113  ret = cfs_read(fd, to, maxsize);
114  PRINTF("read_chunk %d bytes at %d\n", ret, offset);
115 
116  if(ret < maxsize) {
117  PRINTF("read_chunk DONE\n");
118  cfs_close(fd);
119  fd = -1;
120  }
121 
122  return ret;
123 }
124 /*---------------------------------------------------------------------------*/
125 static void
126 timedout(struct rucb_conn *c)
127 {
128 }
129 /*---------------------------------------------------------------------------*/
130 static const struct rucb_callbacks rucb_call = { write_chunk, read_chunk, timedout };
131 /*---------------------------------------------------------------------------*/
132 PROCESS_THREAD(shell_download_process, ev, data)
133 {
134  const char *nextptr;
135  static linkaddr_t addr;
136  int len;
137  char buf[32];
138 
139  PROCESS_BEGIN();
140 
141  /* Parse node addr */
142  addr.u8[0] = shell_strtolong(data, &nextptr);
143  if(nextptr == data || *nextptr != '.') {
144  shell_output_str(&download_command,
145  "download <node addr> <filename>: need node address", "");
146  PROCESS_EXIT();
147  }
148  ++nextptr;
149  addr.u8[1] = shell_strtolong(nextptr, &nextptr);
150 
151  /* Get the length of the file, excluding a terminating NUL character. */
152  while(nextptr[0] == ' ') {
153  nextptr++;
154  }
155  len = strlen(nextptr);
156 
157  /*snprintf(buf, sizeof(buf), "%d.%d", addr.u8[0], addr.u8[1]);*/
158  /*shell_output_str(&download_command, "Downloading from: ", buf);*/
159 
160  if(len > PACKETBUF_SIZE - 32) {
161  snprintf(buf, sizeof(buf), "%d", len);
162  shell_output_str(&download_command, "filename too large: ", buf);
163  PROCESS_EXIT();
164  }
165 
166  /*shell_output_str(&download_command, "Downloading file: ", nextptr);*/
167 
168  /* Send file request */
169  downloading = 1;
170  rucb_open(&rucb, RUCB_CHANNEL, &rucb_call);
171  packetbuf_clear();
172  *((uint8_t *)packetbuf_dataptr()) = ++req_seq_counter;
173  memcpy(((char *)packetbuf_dataptr()) + 1, nextptr, len + 1);
174  packetbuf_set_datalen(len + 2);
175  PRINTF("requesting '%s'\n", nextptr);
176  runicast_send(&runicast, &addr, MAX_RETRANSMISSIONS);
177 
178  /* Wait for download to finish */
179  leds_on(LEDS_BLUE);
180  PROCESS_WAIT_UNTIL(!runicast_is_transmitting(&runicast) && !downloading);
181  leds_off(LEDS_BLUE);
182 
183  rucb_close(&rucb);
184  /*shell_output_str(&download_command, "Done!", "");*/
185 
186  PROCESS_END();
187 }
188 /*---------------------------------------------------------------------------*/
189 static void
190 request_recv(struct runicast_conn *c, const linkaddr_t *from, uint8_t seqno)
191 {
192  const char *filename;
193  uint8_t seq;
194 
195  if(packetbuf_datalen() < 2) {
196  /* Bad filename, ignore request */
197  printf("download: bad filename request (null)\n");
198  return;
199  }
200 
201  seq = ((uint8_t *)packetbuf_dataptr())[0];
202  if(seq == req_last_seq) {
203  PRINTF("download: ignoring duplicate request\n");
204  return;
205  }
206  req_last_seq = seq;
207  filename = ((char *)packetbuf_dataptr()) + 1;
208 
209  PRINTF("file requested: '%s'\n", filename);
210 
211  /* Initiate file transfer */
212  leds_on(LEDS_GREEN);
213  if(fd >= 0) {
214  cfs_close(fd);
215  }
216  fd = cfs_open(filename, CFS_READ);
217  if(fd < 0) {
218  printf("download: bad filename request (no read access): %s\n", filename);
219  } else {
220  PRINTF("download: sending file: %s\n", filename);
221  }
222 
223  rucb_close(&rucb);
224  rucb_open(&rucb, RUCB_CHANNEL, &rucb_call);
225  rucb_send(&rucb, from);
226 }
227 /*---------------------------------------------------------------------------*/
228 static void
229 request_sent(struct runicast_conn *c, const linkaddr_t *to,
230  uint8_t retransmissions)
231 {
232  process_poll(&shell_download_process);
233 }
234 /*---------------------------------------------------------------------------*/
235 static void
236 request_timedout(struct runicast_conn *c, const linkaddr_t *to,
237  uint8_t retransmissions)
238 {
239  shell_output_str(&download_command, "download: request timed out", "");
240  downloading = 0;
241  process_poll(&shell_download_process);
242 }
243 /*---------------------------------------------------------------------------*/
244 static const struct runicast_callbacks runicast_callbacks =
245 {request_recv, request_sent, request_timedout};
246 /*---------------------------------------------------------------------------*/
247 PROCESS_THREAD(shell_download_server_process, ev, data)
248 {
249  PROCESS_EXITHANDLER( runicast_close(&runicast); rucb_close(&rucb); );
250 
251  PROCESS_BEGIN();
252 
253  runicast_open(&runicast, RUNICAST_CHANNEL, &runicast_callbacks);
255 
256  PROCESS_END();
257 }
258 /*---------------------------------------------------------------------------*/
259 void
260 shell_download_init(void)
261 {
262  req_seq_counter = 0;
263  req_last_seq = -1;
264  fd = -1;
265  downloading = 0;
266  shell_register_command(&download_command);
267  process_start(&shell_download_server_process, NULL);
268 }
269 /*---------------------------------------------------------------------------*/
void * packetbuf_dataptr(void)
Get a pointer to the data in the packetbuf.
Definition: packetbuf.c:158
int cfs_open(const char *name, int flags)
Open a file.
Definition: cfs-coffee.c:1011
void process_poll(struct process *p)
Request a process to be polled.
Definition: process.c:371
static uip_ds6_addr_t * addr
Pointer to a router list entry.
Definition: uip-nd6.c:124
void cfs_close(int fd)
Close an open file.
Definition: cfs-coffee.c:1047
void packetbuf_clear(void)
Clear and reset the packetbuf.
Definition: packetbuf.c:76
#define CFS_SEEK_SET
Specify that cfs_seek() should compute the offset from the beginning of the file. ...
Definition: cfs.h:127
Main header file for the Contiki shell
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_END()
Define the end of a process.
Definition: process.h:131
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
unsigned long shell_strtolong(const char *str, const char **retstr)
Convert a string to a number.
Definition: shell.c:521
#define CFS_READ
Specify that cfs_open() should open a file for reading.
Definition: cfs.h:90
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
#define PROCESS_EXIT()
Exit the currently running process.
Definition: process.h:200
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
#define PACKETBUF_SIZE
The size of the packetbuf, in bytes.
Definition: packetbuf.h:66
#define SHELL_COMMAND(name, command, description, process)
Define a shell command.
Definition: shell.h:219
void shell_register_command(struct shell_command *c)
Register a command with the shell.
Definition: shell.c:413
#define PROCESS_EXITHANDLER(handler)
Specify an action when a process exits.
Definition: process.h:254
Header file for the Rime stack
#define NULL
The null pointer.
void packetbuf_set_datalen(uint16_t len)
Set the length of the data in the packetbuf.
Definition: packetbuf.c:151
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99
uint16_t packetbuf_datalen(void)
Get the length of the data in the packetbuf.
Definition: packetbuf.c:170
#define PROCESS_WAIT_UNTIL(c)
Wait for a condition to occur.
Definition: process.h:192
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:1057
CFS header file.
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120