ev3duder  0.3.0
EV3 Downloader/Uploader
main.c
Go to the documentation of this file.
1 
6 #define MAIN
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdbool.h>
12 #include <wchar.h>
13 #include <errno.h>
14 
15 #undef assert
16 //FIXME: add better error message
17 #define assert(cond) do{ if (!(cond)) {if (handle) ev3_close(handle);exit(ERR_ARG);}}while(0)
18 
19 #include <hidapi/hidapi.h>
20 #include "btserial.h"
21 #include "tcp.h"
22 #include "ev3_io.h"
23 
24 #include "defs.h"
25 #include "packets.h"
26 #include "error.h"
27 #include "funcs.h"
28 
30 #define VendorID 0x694
31 #define ProductID 0x005 /* EV3 */
33 #define ExecName "/tmp/Executing shell cmd.rbf"
35 // FIXME: general solution
49  static const char EXEC_SUFFIX[] =
50 /*cmd*/ " 2>&1 | awk '"
51  "BEGIN{"
52  "RS=\"\\0\""
53  "};"
54  "{"
55  "printf \"\\t%08x\\n%s\","
56  "length($0),$0"
57  "}"
58  "' | dd bs=1000 of=/dev/lms_usbdev";
59 
60 const char* const usage =
61  "USAGE: ev3duder " "[ --tcp | --usb | --serial ] [=dev1,dev2] \n"
62  " " "[ up loc rem | dl rem loc | rm rem | ls [rem] |\n"
63  " " " mkdir rem | mkrbf rem loc | run rem | exec cmd |\n"
64  " " " wpa2 SSID [pass] | info | tunnel ]\n"
65  " "
66  "rem = remote (EV3) path, loc = local path, dev = device identifier" "\n";
67 const char* const usage_desc =
68  " up\t" "upload local file to remote ev3 brick\n"
69  " dl\t" "download remote file to local system\n"
70  " rm\t" "remove file on ev3 brick\n"
71  " ls\t" "list files. Standard value is '/'\n"
72  " info\t" "attempt a beep and print information about the connection\n"
73  " mkdir\t" "create directory. Relative to path of VM.\n"
74  " mkrbf\t" "create rbf (menu entry) file locally. Sensible upload paths are:\n"
75  "\t" "\t../prjs/BrkProg_SAVE/ Internal memory\n"
76  "\t" "\t../prjs/BrkProg_DL/ Internal memory\n"
77  "\t" "\t./apps/ Internal memory - Applicatios (3rd tab)\n"
78  "\t" "\t/media/card/myapps/ Memory card\n"
79  "\t" "\t/media/usb/myappps/ USB stick\n"
80  "run\t" "instruct the VM to run a rbf file\n"
81  "exec\t" "pass cmd to root shell. Handle with caution\n"
82  "wpa2\t" "connect to WPA-Network SSID, if pass isn't specified, read from stdin\n"
83  "tunnel\t" "connects stdout/stdin to the ev3 VM\n"
84  ;
85 
86 #define FOREACH_ARG(ARG) \
87  ARG(info) \
88 ARG(up) \
89 ARG(dl) \
90 ARG(run) \
91 ARG(ls) \
92 ARG(rm) \
93 ARG(mkdir) \
94 ARG(mkrbf) \
95 ARG(tunnel) \
96 ARG(listen) \
97 ARG(send) \
98 ARG(exec) \
99 ARG(wpa2) \
100 ARG(nop) \
101 ARG(end)
102 
103 #define MK_ENUM(x) ARG_##x,
104 #define MK_STR(x) #x,
106 static const char *args[] = { FOREACH_ARG(MK_STR) };
107 static const char *offline_args[] = { MK_STR(mkrbf) /*MK_STR(tunnel)*/ };
108 
109 static char* my_chrsub(char *s, char old, char new);
110 #ifdef _WIN32
111 #define SANITIZE(s) (my_chrsub((s), '/', '\\'))
112 #else
113 #define SANITIZE
114 #endif
115 static struct {
116  unsigned select:1;
117  unsigned hid:1;
118  unsigned serial:1;
119  unsigned tcp:1;
120 } switches;
121 
122 static struct {
123  char *tcp_id;
124  char *serial_id[2];
125  char *usb_id;
126  unsigned timeout;
127 } params;
128 
129 int main(int argc, char *argv[])
130 {
131  handle = NULL;
132  if (argc == 2 && (
133  strcmp(argv[1], "-h") == 0 ||
134  strcmp(argv[1], "--help") == 0 ||
135  strcmp(argv[1], "/?") == 0))
136  {
137  printf("%s (%s; %s) v%s\n"
138  "Copyright (C) 2015 Ahmad Fatoum\n"
139  "This is free software; see the source for copying conditions. There is NO\n"
140  "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
141  "Source is available under the GNU GPL v3.0 https://github.com/a3f/ev3duder/\n\n",
142  argv[0], CONFIGURATION, SYSTEM, VERSION);
143  puts(usage);
144  puts(usage_desc);
145  return ERR_UNK;
146  }
147  while (argv[1] && *argv[1] == '-')
148  {
149  if (argv[1][1] == '-')
150  { /* switches */
151  char *a = argv[1] + 2;
152  strtok(a, "=");
153  char *device = strtok(NULL, ","),
154  *device2 = strtok(NULL, "");
155 
156  if (a == '\0')
157  {
158  argc--, argv++;
159  break;
160  }
161 
162  if (strcmp("usb", a) == 0 || strcmp("hid", a) == 0)
163  {
164  switches.select = switches.hid = 1;
165  params.usb_id = device;
166  }
167  else if (strcmp("tcp", a) == 0 || strcmp("inet", a) == 0)
168  {
169  switches.select = switches.tcp = 1;
170  params.tcp_id = device;
171  }
172  else if (strcmp("serial", a) == 0 || strcmp("bt", a) == 0)
173  {
174  switches.select = switches.serial = 1;
175  params.serial_id[0] = device;
176  params.serial_id[1] = device2;
177  }
178  else if (strcmp("nop", a) == 0)
179  ; /* So, this program doesn't handle empty *argv, which
180  is understandable, but would ease programming the
181  plugin a bit. So let the plugin just specify
182  --nop. It's just a semicolon
183  */
184  else {
185  fprintf(stderr, "Invalid switch '%s'\n", argv[1]);
186  return ERR_ARG;
187  }
188 
189  argc--, argv++;
190  } else if (argv[1][1] == 't')
191  {
192  // we don't care about negative numbers as any non-integer
193  // means indefinitely. negative numbers are so big they are
194  // practically infinite
195  if (argv[1][2] == '=')
196  params.timeout = atoi(&argv[1][3]);
197  else {
198  params.timeout = atoi(argv[2]);
199  argv++, argc--;
200  }
201  argv++, argc--;
202  } else {
203  fprintf(stderr, "Invalid parameter '%s'\n", argv[1]);
204  return ERR_ARG;
205  }
206 
207  }
208  if (argc == 1) {
209  puts(usage);
210  return ERR_ARG;
211  }
212 
213  int i;
214  for (i = 0; i < (int)ARRAY_SIZE(offline_args); ++i)
215  if (strcmp(argv[1], offline_args[i]) == 0) break;
216 
217  if (i == ARRAY_SIZE(offline_args))
218  {
219  if ((switches.hid || !switches.select) &&
220  (handle = hid_open(VendorID, ProductID, NULL))) // TODO: last one is SerialID, make it specifiable via commandline
221  {
222  if (!switches.select)
223  fputs("USB connection established.\n", stderr);
224  // the things you do for type safety...
225  ev3_write = (int (*)(void*, const u8*, size_t))hid_write;
226  ev3_read_timeout = (int (*)(void*, u8*, size_t, int))hid_read_timeout;
227  ev3_error = (const wchar_t* (*)(void*))hid_error;
228  ev3_close = (void (*)(void*))hid_close;
229  }
230  else if ((switches.serial || !switches.select) &&
231  (handle = bt_open(params.serial_id[0], params.serial_id[1])))
232  {
233  if (!switches.select)
234  fprintf(stderr, "Bluetooth serial connection established (%s).\n", params.serial_id[0]);
239  }
240  else if ((switches.tcp || !switches.select) &&
241  (handle = tcp_open(params.tcp_id, params.timeout)))
242  {
243  if (!switches.select)
244  {
245  struct tcp_handle *info = (struct tcp_handle*)handle;
246  fprintf(stderr, "TCP connection established (%s@%s:%u).\n", info->name, info->ip, info->tcp_port);
247  }
252 
253  }
254  else {
255  puts("EV3 not found. Either plug it into the USB port or pair over Bluetooth.\n");
256 #ifdef __linux__
257  puts("Insufficient access to the usb device might be a reason too, try sudo.");
258 #endif
259  return ERR_COMM;
260  }
261  }
262 
263 
264  for (i = 0; i < ARG_end; ++i)
265  if (strcmp(argv[1], args[i]) == 0) break;
266 
267  argc -= 2;
268  argv += 2;
269 
270  int ret;
271  switch (i)
272  {
273  FILE *fp = NULL;
274  char *buf = NULL;
275  size_t len = 0;
276  case ARG_info:
277  ret = info(argv[0]);
278  break;
279 
280  case ARG_up:
281  assert(argc == 2);
282  fp = fopen(SANITIZE(argv[0]), "rb");
283  if (!fp)
284  {
285  printf("File <%s> doesn't exist.\n", argv[0]);
286  return ERR_IO;
287  }
288  ret = up(fp, argv[1]);
289  break;
290  case ARG_dl:
291  assert(argc <= 2);
292  if (argc == 1)
293  {
294  buf = strrchr(argv[0], '/');
295  if (buf)
296  buf++; // character after slash
297  else
298  buf = argv[0];
299  } else buf = argv[1];
300 
301  fp = fopen(buf, "wb"); //TODO: no sanitize here?
302  if(!fp)
303  {
304  printf("File <%s> couldn't be opened for writing.\n", buf);
305  return ERR_IO;
306  }
307 
308  ret = dl(argv[0], fp);
309  break;
310  case ARG_run:
311  assert(argc == 1);
312  ret = run(argv[0], params.timeout);
313  break;
314  case ARG_nop: // just connect and do nothing
315  return ERR_UNK;
316  case ARG_end:
317  puts(usage);
318  return ERR_ARG;
319  case ARG_ls:
320  assert(argc <= 1);
321  ret = ls(argv[0] ?: "/");
322  break;
323  case ARG_tunnel:
324  ret = tunnel();
325  break;
326  case ARG_listen:
327  ret = listen();
328  break;
329  case ARG_send:
330  ;
331  int send(void);
332  ret = send();
333  break;
334  case ARG_mkdir:
335  assert(argc == 1);
336  ret = mkdir(argv[0]);
337  break;
338  case ARG_mkrbf:
339  assert(argc >= 2);
340  fp = fopen(SANITIZE(argv[1]), "wb");
341  if (!fp)
342  return ERR_IO;
343 
344  len = mkrbf(&buf, argv[0]);
345  fwrite(buf, len, 1, fp);
346  fclose(fp);
347  return ERR_UNK;
348  case ARG_wpa2:
349  assert(argc >= 1);
350  /*{
351  #define MAX_WPA_PASS 32
352  char buf[MAX_WPA_PASS];
353  #define WPA2_TEMP_FILE "/tmp/ev3duder_wpa2.conf"
354  char wpa_passphrase[256];
355  char *pass = argv[1];
356  while (!pass)
357  pass = fgets(buf, MAX_WPA_PASS, stdin);
358  snprintf(wpa_passphrase, sizeof wpa_passphrase, "wpa_passphrase '%s' '%s' > '" WPA2_TEMP "'" argv[0], pass);
359  exec(wpa_passphrase);
360  exec("wpa_supplicant -Dwext -iwlan0 -c" WPA2_TEMP);
361 
362  }*/
363  return ERR_UNK;
364  case ARG_exec:
365  assert(argc >= 1);
366  size_t len_cmd = strlen(argv[0]),
367  len_out = sizeof EXEC_SUFFIX;
368  buf = malloc(len_cmd + len_out);
369  (void)mempcpy(mempcpy(buf,
370  argv[0], len_cmd),
371  EXEC_SUFFIX, len_out);
372  argv[0] = buf;
373  len = mkrbf(&buf, argv[0]);
374  fp = tmpfile();
375  if (!fp)
376  return ERR_IO;
377  fwrite(buf, len, 1, fp);
378  rewind(fp);
379  ret = up(fp, ExecName);
380  if (ret != ERR_UNK)
381  break;
382  ret = run(ExecName, params.timeout);
383  if (ret != ERR_UNK)
384  break;
385 
386  fclose(fp);
387  break;
388  case ARG_rm:
389  assert(argc == 1);
390  ret = rm(argv[0]);
391  break;
392  default:
393  ret = ERR_ARG;
394  printf("<%s> hasn't been implemented yet.\n", argv[0]);
395  }
396 
397  FILE *out = ret == ERR_UNK ? stderr : stdout;
398  if (ret == ERR_COMM)
399  fprintf(out, "%s (%ls)\n", errmsg ?: "-", ev3_error(handle) ?: L"-");
400  else if (ret == ERR_VM) {
401  const char *err;
402  if (errno < (int)ARRAY_SIZE(ev3_error_msgs))
403  err = ev3_error_msgs[errno];
404  else
405  err = "An unknown error occured";
406 
407  fprintf(out, "%s (%s)\n", err ?: "-", errmsg ?: "-");
408  } else {
409  if (errmsg) fprintf(out, "%s\n", errmsg);
410  }
411 
412 // maybe \n to stderr?
413  if (handle) ev3_close(handle);
414  return ret;
415 }
416 
417 #pragma GCC diagnostic ignored "-Wunused-function"
418 static char* my_chrsub(char *s, char old, char new)
419 {
420  char *ptr = s;
421  if (ptr == NULL || *ptr == '\0')
422  return NULL;
423  do
424  {
425  if (*ptr == old) *ptr = new;
426  } while(*++ptr);
427  return s;
428 }
429 
int ls(const char *rem)
list contents of remote directory rem
Definition: ls.c:36
EXTERN void * handle
Definition: ev3_io.h:17
int main(int argc, char *argv[])
Definition: main.c:129
#define MK_STR(x)
Definition: main.c:104
unsigned tcp_port
Definition: tcp.h:19
#define SANITIZE
Definition: main.c:113
Definition: error.h:20
char * serial_id[2]
Definition: main.c:124
EXTERN const wchar_t *(* ev3_error)(void *)
Definition: ev3_io.h:15
int info(const char *arg)
print connection information, beep and exit
Definition: info.c:36
void bt_close(void *handle)
Closes the file descriptor opened by bt_open()
Definition: bt-unix.c:116
#define FOREACH_ARG(ARG)
Definition: main.c:86
void * bt_open(const char *file, const char *file2)
open a bluetooth device described by device. NULL leads to default action
Definition: bt-unix.c:29
char name[96]
Definition: tcp.h:20
int bt_write(void *fd_, const u8 *buf, size_t count)
writes buf[1] till buf[count - 2] to device
Definition: bt-unix.c:51
int send()
Definition: send.c:6
int listen()
Definition: listen.c:7
unsigned select
Definition: main.c:116
Definition: error.h:20
int bt_read(void *fd_, u8 *buf, size_t count, int milliseconds)
writes buf[1] till buf[count - 2] to device
Definition: bt-unix.c:77
char ip[48]
Definition: tcp.h:18
#define assert(cond)
Definition: main.c:17
const char *const usage
Definition: main.c:60
Definition: error.h:20
#define ARRAY_SIZE(array)
Definition: defs.h:19
EXTERN const char *const ev3_error_msgs[ERRORS_END+1]
Definition: error.h:71
Error enumerations and decriptions.
int dl(const char *path, FILE *fp)
download remote source rem to local file loc
Definition: dl.c:26
char * usb_id
Definition: main.c:125
int run(const char *rem, unsigned timeout)
run remote .rbf file rem via VM
Definition: run.c:30
EXTERN void(* ev3_close)(void *)
Definition: ev3_io.h:16
int up(FILE *loc, const char *rem)
upload local file loc to remote destination rem
Definition: up.c:29
Input/Output wrappers for Bluetooth.
unsigned timeout
Definition: main.c:126
#define MK_ENUM(x)
Definition: main.c:103
EXTERN int(* ev3_read_timeout)(void *, u8 *, size_t, int milliseconds)
Definition: ev3_io.h:14
#define ExecName
Filename used for executing command with exec
Definition: main.c:34
void * tcp_open(const char *serial, unsigned timeout)
connects to a ev3 device on the same subnet.
Definition: tcp.c:84
unsigned tcp
Definition: main.c:119
int rm(const char *rem)
remove remote file or directory rem
Definition: rm.c:27
#define ProductID
EV3.
Definition: main.c:32
packed structs for the packets.
ARGS
Definition: main.c:105
int mkdir(const char *rem)
create directory rem on remote system
Definition: mkdir.c:29
int(* tcp_write)(void *device, const u8 *buf, size_t count)
writes buf[1] till buf[count - 2] to device
Definition: tcp.c:271
Definition: error.h:20
uint8_t u8
Definition: defs.h:9
const char *const usage_desc
Definition: main.c:67
char * tcp_id
Definition: main.c:123
Definition: error.h:20
char serial[15]
Definition: tcp.h:17
int tunnel()
tunnel stdio to established ev3 connection
Definition: tunnel.c:35
void tcp_close(void *sock)
releases the socket file descriptor opened by tcp_open()
Definition: tcp.c:241
size_t mkrbf(char **buf, const char *cmd)
fill *buf with a rbf file executing cmd
Definition: mkrbf.c:29
const wchar_t * bt_error(void *fd_)
Returns an error string describing the last error occured.
Definition: bt-unix.c:127
EXTERN const char * errmsg
global variable for last error message
Definition: error.h:25
BSD sockets/Winsock2 Input/Output wrappers.
contains declarations for ev3 commands
const wchar_t * tcp_error(void *fd_)
Returns an error string describing the last error occured.
Definition: tcp.c:256
#define VendorID
LEGO GROUP.
Definition: main.c:30
Definition: tcp.h:11
unsigned hid
Definition: main.c:117
EXTERN int(* ev3_write)(void *, const u8 *, size_t)
Definition: ev3_io.h:13
int(* tcp_read)(void *device, u8 *buf, size_t count, int milliseconds)
writes buf[1] till buf[count - 2] to device
Definition: tcp.c:272