src/helper: usage/help updates
[fw/openocd] / src / helper / ioutil.c
1 /***************************************************************************
2  *   Copyright (C) 2007-2008 by Ã˜yvind Harboe                              *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19
20 /* this file contains various functionality useful to standalone systems */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "log.h"
27 #include "time_support.h"
28
29 #ifdef HAVE_ARPA_INET_H
30 #include <arpa/inet.h>
31 #endif
32 #ifdef HAVE_DIRENT_H
33 #include <dirent.h>
34 #endif
35 #ifdef HAVE_NETDB_H
36 #include <netdb.h>
37 #endif
38 #ifdef HAVE_NET_IF_H
39 #include <net/if.h>
40 #endif
41 #ifdef HAVE_SYS_IOCTL_H
42 #include <sys/ioctl.h>
43 #endif
44 #ifdef HAVE_SYS_STAT_H
45 #include <sys/stat.h>
46 #endif
47 #ifdef HAVE_IFADDRS_H
48 #include <ifaddrs.h>
49 #endif
50 #ifdef HAVE_MALLOC_H
51 #if !BUILD_ECOSBOARD
52 #include <malloc.h>
53 #endif
54 #endif
55
56
57 /* loads a file and returns a pointer to it in memory. The file contains
58  * a 0 byte(sentinel) after len bytes - the length of the file. */
59 int loadFile(const char *fileName, void **data, size_t *len)
60 {
61         // ensure returned length is always sane
62         *len = 0;
63
64         FILE * pFile;
65         pFile = fopen(fileName,"rb");
66         if (pFile == NULL)
67         {
68                 LOG_ERROR("Can't open %s\n", fileName);
69                 return ERROR_FAIL;
70         }
71         if (fseek(pFile, 0, SEEK_END) != 0)
72         {
73                 LOG_ERROR("Can't open %s\n", fileName);
74                 fclose(pFile);
75                 return ERROR_FAIL;
76         }
77         long fsize = ftell(pFile);
78         if (fsize == -1)
79         {
80                 LOG_ERROR("Can't open %s\n", fileName);
81                 fclose(pFile);
82                 return ERROR_FAIL;
83         }
84         *len = fsize;
85
86         if (fseek(pFile, 0, SEEK_SET) != 0)
87         {
88                 LOG_ERROR("Can't open %s\n", fileName);
89                 fclose(pFile);
90                 return ERROR_FAIL;
91         }
92         *data = malloc(*len + 1);
93         if (*data == NULL)
94         {
95                 LOG_ERROR("Can't open %s\n", fileName);
96                 fclose(pFile);
97                 return ERROR_FAIL;
98         }
99
100         if (fread(*data, 1, *len, pFile)!=*len)
101         {
102                 fclose(pFile);
103                 free(*data);
104                 LOG_ERROR("Can't open %s\n", fileName);
105                 return ERROR_FAIL;
106         }
107         fclose(pFile);
108
109         // 0-byte after buffer (not included in *len) serves as a sentinel
110         char *buf = (char *)*data;
111         buf[*len] = 0;
112
113         return ERROR_OK;
114 }
115
116 COMMAND_HANDLER(handle_cat_command)
117 {
118         if (CMD_ARGC != 1)
119         {
120                 command_print(CMD_CTX, "cat <filename>");
121                 return ERROR_INVALID_ARGUMENTS;
122         }
123
124         // NOTE!!! we only have line printing capability so we print the entire file as a single line.
125         void *data;
126         size_t len;
127
128         int retval = loadFile(CMD_ARGV[0], &data, &len);
129         if (retval == ERROR_OK)
130         {
131                 command_print(CMD_CTX, "%s", (char *)data);
132                 free(data);
133         }
134         else
135         {
136                 command_print(CMD_CTX, "%s not found %d", CMD_ARGV[0], retval);
137         }
138
139         return ERROR_OK;
140 }
141
142 COMMAND_HANDLER(handle_trunc_command)
143 {
144         if (CMD_ARGC != 1)
145         {
146                 command_print(CMD_CTX, "trunc <filename>");
147                 return ERROR_INVALID_ARGUMENTS;
148         }
149
150         FILE *config_file = NULL;
151         config_file = fopen(CMD_ARGV[0], "w");
152         if (config_file != NULL)
153                 fclose(config_file);
154
155         return ERROR_OK;
156 }
157
158 COMMAND_HANDLER(handle_meminfo_command)
159 {
160         static int prev = 0;
161         struct mallinfo info;
162
163         if (CMD_ARGC != 0)
164         {
165                 command_print(CMD_CTX, "meminfo");
166                 return ERROR_INVALID_ARGUMENTS;
167         }
168
169         info = mallinfo();
170
171         if (prev > 0)
172         {
173                 command_print(CMD_CTX, "Diff:            %d", prev - info.fordblks);
174         }
175         prev = info.fordblks;
176
177         command_print(CMD_CTX, "Available ram:   %d", info.fordblks);
178
179         return ERROR_OK;
180 }
181
182
183 COMMAND_HANDLER(handle_append_command)
184 {
185         if (CMD_ARGC < 1)
186         {
187                 command_print(CMD_CTX,
188                                 "append <filename> [<string1>, [<string2>, ...]]");
189                 return ERROR_INVALID_ARGUMENTS;
190         }
191
192         int retval = ERROR_FAIL;
193         FILE *config_file = NULL;
194
195         config_file = fopen(CMD_ARGV[0], "a");
196         if (config_file != NULL)
197         {
198                 fseek(config_file, 0, SEEK_END);
199
200                 unsigned i;
201                 for (i = 1; i < CMD_ARGC; i++)
202                 {
203                         if (fwrite(CMD_ARGV[i], 1, strlen(CMD_ARGV[i]),
204                                         config_file) != strlen(CMD_ARGV[i]))
205                                 break;
206                         if (i != CMD_ARGC - 1)
207                         {
208                                 if (fwrite(" ", 1, 1, config_file) != 1)
209                                         break;
210                         }
211                 }
212                 if ((i == CMD_ARGC) && (fwrite("\n", 1, 1, config_file) == 1))
213                         retval = ERROR_OK;
214
215                 fclose(config_file);
216         }
217
218         return retval;
219 }
220
221
222
223 COMMAND_HANDLER(handle_cp_command)
224 {
225         if (CMD_ARGC != 2)
226         {
227                 return ERROR_INVALID_ARGUMENTS;
228         }
229
230         // NOTE!!! we only have line printing capability so we print the entire file as a single line.
231         void *data;
232         size_t len;
233
234         int retval = loadFile(CMD_ARGV[0], &data, &len);
235         if (retval != ERROR_OK)
236                 return retval;
237
238         FILE *f = fopen(CMD_ARGV[1], "wb");
239         if (f == NULL)
240                 retval = ERROR_INVALID_ARGUMENTS;
241
242         size_t pos = 0;
243         for (;;)
244         {
245                 size_t chunk = len - pos;
246                 static const size_t maxChunk = 512 * 1024; // ~1/sec
247                 if (chunk > maxChunk)
248                 {
249                         chunk = maxChunk;
250                 }
251
252                 if ((retval == ERROR_OK) && (fwrite(((char *)data) + pos, 1, chunk, f) != chunk))
253                         retval = ERROR_INVALID_ARGUMENTS;
254
255                 if (retval != ERROR_OK)
256                 {
257                         break;
258                 }
259
260                 command_print(CMD_CTX, "%zu", len - pos);
261
262                 pos += chunk;
263
264                 if (pos == len)
265                         break;
266         }
267
268         if (retval == ERROR_OK)
269         {
270                 command_print(CMD_CTX, "Copied %s to %s", CMD_ARGV[0], CMD_ARGV[1]);
271         } else
272         {
273                 command_print(CMD_CTX, "Failed: %d", retval);
274         }
275
276         if (data != NULL)
277                 free(data);
278         if (f != NULL)
279                 fclose(f);
280
281         if (retval != ERROR_OK)
282                 unlink(CMD_ARGV[1]);
283
284         return retval;
285 }
286
287
288
289
290 #define SHOW_RESULT(a, b) LOG_ERROR(#a " failed %d\n", (int)b)
291
292 #define IOSIZE 512
293 void copyfile(char *name2, char *name1)
294 {
295
296         int err;
297         char buf[IOSIZE];
298         int fd1, fd2;
299         ssize_t done, wrote;
300
301         fd1 = open(name1, O_WRONLY | O_CREAT, 0664);
302         if (fd1 < 0)
303                 SHOW_RESULT(open, fd1);
304
305         fd2 = open(name2, O_RDONLY);
306         if (fd2 < 0)
307                 SHOW_RESULT(open, fd2);
308
309         for (;;)
310         {
311                 done = read(fd2, buf, IOSIZE);
312                 if (done < 0)
313                 {
314                         SHOW_RESULT(read, done);
315                         break;
316                 }
317
318         if (done == 0) break;
319
320                 wrote = write(fd1, buf, done);
321         if (wrote != done) SHOW_RESULT(write, wrote);
322
323         if (wrote != done) break;
324         }
325
326         err = close(fd1);
327     if (err < 0) SHOW_RESULT(close, err);
328
329         err = close(fd2);
330     if (err < 0) SHOW_RESULT(close, err);
331
332 }
333
334 /* utility fn to copy a directory */
335 void copydir(char *name, char *destdir)
336 {
337         int err;
338         DIR *dirp;
339
340         dirp = opendir(destdir);
341         if (dirp == NULL)
342         {
343                 mkdir(destdir, 0777);
344         } else
345         {
346                 err = closedir(dirp);
347         }
348
349         dirp = opendir(name);
350     if (dirp == NULL) SHOW_RESULT(opendir, -1);
351
352         for (;;)
353         {
354                 struct dirent *entry = readdir(dirp);
355
356                 if (entry == NULL)
357                         break;
358
359                 if (strcmp(entry->d_name, ".") == 0)
360                         continue;
361                 if (strcmp(entry->d_name, "..") == 0)
362                         continue;
363
364                 int isDir = 0;
365                 struct stat buf;
366                 char fullPath[PATH_MAX];
367                 strncpy(fullPath, name, PATH_MAX);
368                 strcat(fullPath, "/");
369                 strncat(fullPath, entry->d_name, PATH_MAX - strlen(fullPath));
370
371                 if (stat(fullPath, &buf) == -1)
372                 {
373                         LOG_ERROR("unable to read status from %s", fullPath);
374                         break;
375                 }
376                 isDir = S_ISDIR(buf.st_mode) != 0;
377
378                 if (isDir)
379                         continue;
380
381                 //        diag_printf("<INFO>: entry %14s",entry->d_name);
382                 char fullname[PATH_MAX];
383                 char fullname2[PATH_MAX];
384
385                 strcpy(fullname, name);
386                 strcat(fullname, "/");
387                 strcat(fullname, entry->d_name);
388
389                 strcpy(fullname2, destdir);
390                 strcat(fullname2, "/");
391                 strcat(fullname2, entry->d_name);
392                 //        diag_printf("from %s to %s\n", fullname, fullname2);
393                 copyfile(fullname, fullname2);
394
395                 //       diag_printf("\n");
396         }
397
398         err = closedir(dirp);
399     if (err < 0) SHOW_RESULT(stat, err);
400 }
401
402
403
404
405 COMMAND_HANDLER(handle_rm_command)
406 {
407         if (CMD_ARGC != 1)
408                 return ERROR_INVALID_ARGUMENTS;
409
410         bool del = false;
411         if (rmdir(CMD_ARGV[0]) == 0)
412                 del = true;
413         else if (unlink(CMD_ARGV[0]) == 0)
414                 del = true;
415
416         return del ? ERROR_OK : ERROR_FAIL;
417 }
418
419
420 static int
421 zylinjtag_Jim_Command_ls(Jim_Interp *interp,
422                                    int argc,
423                 Jim_Obj * const *argv)
424 {
425         if (argc != 2)
426         {
427                 Jim_WrongNumArgs(interp, 1, argv, "ls ?dir?");
428                 return JIM_ERR;
429         }
430
431         char *name = (char*) Jim_GetString(argv[1], NULL);
432
433         DIR *dirp = NULL;
434         dirp = opendir(name);
435         if (dirp == NULL)
436         {
437                 return JIM_ERR;
438         }
439         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
440
441         for (;;)
442         {
443                 struct dirent *entry = NULL;
444                 entry = readdir(dirp);
445                 if (entry == NULL)
446                         break;
447
448                 if ((strcmp(".", entry->d_name) == 0)||(strcmp("..", entry->d_name) == 0))
449                         continue;
450
451         Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, entry->d_name, strlen(entry->d_name)));
452         }
453         closedir(dirp);
454
455         Jim_SetResult(interp, objPtr);
456
457         return JIM_OK;
458 }
459
460 static int
461 zylinjtag_Jim_Command_peek(Jim_Interp *interp,
462                                    int argc,
463                 Jim_Obj * const *argv)
464 {
465         if (argc != 2)
466         {
467                 Jim_WrongNumArgs(interp, 1, argv, "peek ?address?");
468                 return JIM_ERR;
469         }
470
471         long address;
472         if (Jim_GetLong(interp, argv[1], &address) != JIM_OK)
473                 return JIM_ERR;
474
475         int value = *((volatile int *) address);
476
477         Jim_SetResult(interp, Jim_NewIntObj(interp, value));
478
479         return JIM_OK;
480 }
481
482 static int
483 zylinjtag_Jim_Command_poke(Jim_Interp *interp,
484                                    int argc,
485                 Jim_Obj * const *argv)
486 {
487         if (argc != 3)
488         {
489                 Jim_WrongNumArgs(interp, 1, argv, "poke ?address? ?value?");
490                 return JIM_ERR;
491         }
492
493         long address;
494         if (Jim_GetLong(interp, argv[1], &address) != JIM_OK)
495                 return JIM_ERR;
496         long value;
497         if (Jim_GetLong(interp, argv[2], &value) != JIM_OK)
498                 return JIM_ERR;
499
500         *((volatile int *) address) = value;
501
502         return JIM_OK;
503 }
504
505
506 /* not so pretty code to fish out ip number*/
507 static int zylinjtag_Jim_Command_ip(Jim_Interp *interp, int argc,
508                 Jim_Obj * const *argv)
509 {
510 #if !defined(__CYGWIN__)
511         Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
512
513         struct ifaddrs *ifa = NULL, *ifp = NULL;
514
515         if (getifaddrs(&ifp) < 0)
516         {
517                 return JIM_ERR;
518         }
519
520         for (ifa = ifp; ifa; ifa = ifa->ifa_next)
521         {
522                 char ip[200];
523                 socklen_t salen;
524
525                 if (ifa->ifa_addr->sa_family == AF_INET)
526                         salen = sizeof(struct sockaddr_in);
527                 else if (ifa->ifa_addr->sa_family == AF_INET6)
528                         salen = sizeof(struct sockaddr_in6);
529                 else
530                         continue;
531
532                 if (getnameinfo(ifa->ifa_addr, salen, ip, sizeof(ip), NULL, 0,
533                                 NI_NUMERICHOST) < 0)
534                 {
535                         continue;
536                 }
537
538                 Jim_AppendString(interp, tclOutput, ip, strlen(ip));
539                 break;
540
541         }
542
543         freeifaddrs(ifp);
544 #else
545         Jim_Obj *tclOutput = Jim_NewStringObj(interp, "fixme!!!", 0);
546         LOG_ERROR("NOT IMPLEMENTED!!!");
547 #endif
548         Jim_SetResult(interp, tclOutput);
549
550         return JIM_OK;
551 }
552
553 /* not so pretty code to fish out eth0 mac address */
554 static int zylinjtag_Jim_Command_mac(Jim_Interp *interp, int argc,
555                 Jim_Obj * const *argv)
556 {
557
558
559         struct ifreq *ifr, *ifend;
560         struct ifreq ifreq;
561         struct ifconf ifc;
562         struct ifreq ifs[5];
563         int SockFD;
564
565         SockFD = socket(AF_INET, SOCK_DGRAM, 0);
566         if (SockFD < 0)
567         {
568                 return JIM_ERR;
569         }
570
571         ifc.ifc_len = sizeof(ifs);
572         ifc.ifc_req = ifs;
573         if (ioctl(SockFD, SIOCGIFCONF, &ifc) < 0)
574         {
575                 close(SockFD);
576                 return JIM_ERR;
577         }
578
579         ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
580         for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
581         {
582                 //if (ifr->ifr_addr.sa_family == AF_INET)
583                 {
584                         if (strcmp("eth0", ifr->ifr_name) != 0)
585                                 continue;
586                         strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
587                         if (ioctl(SockFD, SIOCGIFHWADDR, &ifreq) < 0)
588                         {
589                                 close(SockFD);
590                                 return JIM_ERR;
591                         }
592
593                         close(SockFD);
594
595
596                         Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
597
598                         char buffer[256];
599                         sprintf(buffer, "%02x-%02x-%02x-%02x-%02x-%02x",
600                                         ifreq.ifr_hwaddr.sa_data[0]&0xff,
601                                         ifreq.ifr_hwaddr.sa_data[1]&0xff,
602                                         ifreq.ifr_hwaddr.sa_data[2]&0xff,
603                                         ifreq.ifr_hwaddr.sa_data[3]&0xff,
604                                         ifreq.ifr_hwaddr.sa_data[4]&0xff,
605                                         ifreq.ifr_hwaddr.sa_data[5]&0xff);
606
607                         Jim_AppendString(interp, tclOutput, buffer, strlen(buffer));
608
609                         Jim_SetResult(interp, tclOutput);
610
611                         return JIM_OK;
612                 }
613         }
614         close(SockFD);
615
616         return JIM_ERR;
617
618 }
619
620 static const struct command_registration ioutil_command_handlers[] = {
621         {
622                 .name = "cat",
623                 .handler = handle_cat_command,
624                 .mode = COMMAND_ANY,
625                 .help = "display text file content",
626                 .usage= "file_name",
627         },
628         {
629                 .name = "trunc",
630                 .handler = handle_trunc_command,
631                 .mode = COMMAND_ANY,
632                 .help = "truncate a file to zero length",
633                 .usage= "file_name",
634         },
635         {
636                 .name = "cp",
637                 .handler = handle_cp_command,
638                 .mode = COMMAND_ANY,
639                 .help = "copy a file",
640                 .usage = "src_file_name dst_file_name",
641         },
642         {
643                 .name = "append_file",
644                 .handler = handle_append_command,
645                 .mode = COMMAND_ANY,
646                 .help = "append a variable number of strings to a file",
647                 .usage= "file_name [string ...]",
648         },
649         {
650                 .name = "meminfo",
651                 .handler = handle_meminfo_command,
652                 .mode = COMMAND_ANY,
653                 .help = "display free heap space",
654         },
655         {
656                 .name = "rm",
657                 .mode = COMMAND_ANY,
658                 .handler = handle_rm_command,
659                 .help = "remove a directory or file",
660                 .usage = "file_name",
661         },
662
663         /*
664          * REVISIT shouldn't most, or all, these zylinjtag_*()
665          * entries be #ifdef ZY1000?  If not, why so they have
666          * those names?
667          *
668          * Peek and poke are security holes -- they manipulate
669          * server-internal addresses.
670          */
671
672         // jim handlers
673         {
674                 .name = "peek",
675                 .mode = COMMAND_ANY,
676                 .jim_handler = zylinjtag_Jim_Command_peek,
677                 .help = "peek at a memory address",
678                 .usage = "address",
679         },
680         {
681                 .name = "poke",
682                 .mode = COMMAND_ANY,
683                 .jim_handler = zylinjtag_Jim_Command_poke,
684                 .help = "poke at a memory address",
685                 .usage = "address value",
686         },
687         {
688                 .name = "ls",
689                 .mode = COMMAND_ANY,
690                 .jim_handler = zylinjtag_Jim_Command_ls,
691                 .help = "show a listing of files",
692                 .usage = "dirname",
693         },
694         {
695                 .name = "mac",
696                 .mode = COMMAND_ANY,
697                 .jim_handler = zylinjtag_Jim_Command_mac,
698                 .help = "show MAC address",
699         },
700         {
701                 .name = "ip",
702                 .jim_handler = zylinjtag_Jim_Command_ip,
703                 .mode = COMMAND_ANY,
704                 .help = "show IP address",
705         },
706         COMMAND_REGISTRATION_DONE
707 };
708
709 int ioutil_init(struct command_context *cmd_ctx)
710 {
711         return register_commands(cmd_ctx, NULL, ioutil_command_handlers);
712 }