ed82ba1cd78fa823476b2ab8cea0dd7d530fad21
[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         config_file = fopen(CMD_ARGV[0], "a");
195         if (config_file != NULL)
196         {
197                 fseek(config_file, 0, SEEK_END);
198
199                 unsigned i;
200                 for (i = 1; i < CMD_ARGC; i++)
201                 {
202                         if (fwrite(CMD_ARGV[i], 1, strlen(CMD_ARGV[i]), config_file) != strlen(CMD_ARGV[i]))
203                                 break;
204                         if (i != CMD_ARGC - 1)
205                         {
206                                 if (fwrite(" ", 1, 1, config_file) != 1)
207                                         break;
208                         }
209                 }
210                 if ((i == CMD_ARGC) && (fwrite("\n", 1, 1, config_file) == 1))
211                 {
212                         retval = ERROR_OK;
213                 }
214                 fclose(config_file);
215         }
216
217         return retval;
218 }
219
220
221
222 COMMAND_HANDLER(handle_cp_command)
223 {
224         if (CMD_ARGC != 2)
225         {
226                 return ERROR_INVALID_ARGUMENTS;
227         }
228
229         // NOTE!!! we only have line printing capability so we print the entire file as a single line.
230         void *data;
231         size_t len;
232
233         int retval = loadFile(CMD_ARGV[0], &data, &len);
234         if (retval != ERROR_OK)
235                 return retval;
236
237         FILE *f = fopen(CMD_ARGV[1], "wb");
238         if (f == NULL)
239                 retval = ERROR_INVALID_ARGUMENTS;
240
241         size_t pos = 0;
242         for (;;)
243         {
244                 size_t chunk = len - pos;
245                 static const size_t maxChunk = 512 * 1024; // ~1/sec
246                 if (chunk > maxChunk)
247                 {
248                         chunk = maxChunk;
249                 }
250
251                 if ((retval == ERROR_OK) && (fwrite(((char *)data) + pos, 1, chunk, f) != chunk))
252                         retval = ERROR_INVALID_ARGUMENTS;
253
254                 if (retval != ERROR_OK)
255                 {
256                         break;
257                 }
258
259                 command_print(CMD_CTX, "%zu", len - pos);
260
261                 pos += chunk;
262
263                 if (pos == len)
264                         break;
265         }
266
267         if (retval == ERROR_OK)
268         {
269                 command_print(CMD_CTX, "Copied %s to %s", CMD_ARGV[0], CMD_ARGV[1]);
270         } else
271         {
272                 command_print(CMD_CTX, "Failed: %d", retval);
273         }
274
275         if (data != NULL)
276                 free(data);
277         if (f != NULL)
278                 fclose(f);
279
280         if (retval != ERROR_OK)
281                 unlink(CMD_ARGV[1]);
282
283         return retval;
284 }
285
286
287
288
289 #define SHOW_RESULT(a, b) LOG_ERROR(#a " failed %d\n", (int)b)
290
291 #define IOSIZE 512
292 void copyfile(char *name2, char *name1)
293 {
294
295         int err;
296         char buf[IOSIZE];
297         int fd1, fd2;
298         ssize_t done, wrote;
299
300         fd1 = open(name1, O_WRONLY | O_CREAT, 0664);
301         if (fd1 < 0)
302                 SHOW_RESULT(open, fd1);
303
304         fd2 = open(name2, O_RDONLY);
305         if (fd2 < 0)
306                 SHOW_RESULT(open, fd2);
307
308         for (;;)
309         {
310                 done = read(fd2, buf, IOSIZE);
311                 if (done < 0)
312                 {
313                         SHOW_RESULT(read, done);
314                         break;
315                 }
316
317         if (done == 0) break;
318
319                 wrote = write(fd1, buf, done);
320         if (wrote != done) SHOW_RESULT(write, wrote);
321
322         if (wrote != done) break;
323         }
324
325         err = close(fd1);
326     if (err < 0) SHOW_RESULT(close, err);
327
328         err = close(fd2);
329     if (err < 0) SHOW_RESULT(close, err);
330
331 }
332
333 /* utility fn to copy a directory */
334 void copydir(char *name, char *destdir)
335 {
336         int err;
337         DIR *dirp;
338
339         dirp = opendir(destdir);
340         if (dirp == NULL)
341         {
342                 mkdir(destdir, 0777);
343         } else
344         {
345                 err = closedir(dirp);
346         }
347
348         dirp = opendir(name);
349     if (dirp == NULL) SHOW_RESULT(opendir, -1);
350
351         for (;;)
352         {
353                 struct dirent *entry = readdir(dirp);
354
355                 if (entry == NULL)
356                         break;
357
358                 if (strcmp(entry->d_name, ".") == 0)
359                         continue;
360                 if (strcmp(entry->d_name, "..") == 0)
361                         continue;
362
363                 int isDir = 0;
364                 struct stat buf;
365                 char fullPath[PATH_MAX];
366                 strncpy(fullPath, name, PATH_MAX);
367                 strcat(fullPath, "/");
368                 strncat(fullPath, entry->d_name, PATH_MAX - strlen(fullPath));
369
370                 if (stat(fullPath, &buf) == -1)
371                 {
372                         LOG_ERROR("unable to read status from %s", fullPath);
373                         break;
374                 }
375                 isDir = S_ISDIR(buf.st_mode) != 0;
376
377                 if (isDir)
378                         continue;
379
380                 //        diag_printf("<INFO>: entry %14s",entry->d_name);
381                 char fullname[PATH_MAX];
382                 char fullname2[PATH_MAX];
383
384                 strcpy(fullname, name);
385                 strcat(fullname, "/");
386                 strcat(fullname, entry->d_name);
387
388                 strcpy(fullname2, destdir);
389                 strcat(fullname2, "/");
390                 strcat(fullname2, entry->d_name);
391                 //        diag_printf("from %s to %s\n", fullname, fullname2);
392                 copyfile(fullname, fullname2);
393
394                 //       diag_printf("\n");
395         }
396
397         err = closedir(dirp);
398     if (err < 0) SHOW_RESULT(stat, err);
399 }
400
401
402
403
404 static int
405 zylinjtag_Jim_Command_rm(Jim_Interp *interp,
406                                    int argc,
407                 Jim_Obj * const *argv)
408 {
409         int del;
410         if (argc != 2)
411         {
412                 Jim_WrongNumArgs(interp, 1, argv, "rm ?dirorfile?");
413                 return JIM_ERR;
414         }
415
416         del = 0;
417         if (unlink(Jim_GetString(argv[1], NULL)) == 0)
418                 del = 1;
419         if (rmdir(Jim_GetString(argv[1], NULL)) == 0)
420                 del = 1;
421
422         return del ? JIM_OK : JIM_ERR;
423 }
424
425
426 static int
427 zylinjtag_Jim_Command_ls(Jim_Interp *interp,
428                                    int argc,
429                 Jim_Obj * const *argv)
430 {
431         if (argc != 2)
432         {
433                 Jim_WrongNumArgs(interp, 1, argv, "ls ?dir?");
434                 return JIM_ERR;
435         }
436
437         char *name = (char*) Jim_GetString(argv[1], NULL);
438
439         DIR *dirp = NULL;
440         dirp = opendir(name);
441         if (dirp == NULL)
442         {
443                 return JIM_ERR;
444         }
445         Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
446
447         for (;;)
448         {
449                 struct dirent *entry = NULL;
450                 entry = readdir(dirp);
451                 if (entry == NULL)
452                         break;
453
454                 if ((strcmp(".", entry->d_name) == 0)||(strcmp("..", entry->d_name) == 0))
455                         continue;
456
457         Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, entry->d_name, strlen(entry->d_name)));
458         }
459         closedir(dirp);
460
461         Jim_SetResult(interp, objPtr);
462
463         return JIM_OK;
464 }
465
466 static int
467 zylinjtag_Jim_Command_peek(Jim_Interp *interp,
468                                    int argc,
469                 Jim_Obj * const *argv)
470 {
471         if (argc != 2)
472         {
473                 Jim_WrongNumArgs(interp, 1, argv, "peek ?address?");
474                 return JIM_ERR;
475         }
476
477         long address;
478         if (Jim_GetLong(interp, argv[1], &address) != JIM_OK)
479                 return JIM_ERR;
480
481         int value = *((volatile int *) address);
482
483         Jim_SetResult(interp, Jim_NewIntObj(interp, value));
484
485         return JIM_OK;
486 }
487
488 static int
489 zylinjtag_Jim_Command_poke(Jim_Interp *interp,
490                                    int argc,
491                 Jim_Obj * const *argv)
492 {
493         if (argc != 3)
494         {
495                 Jim_WrongNumArgs(interp, 1, argv, "poke ?address? ?value?");
496                 return JIM_ERR;
497         }
498
499         long address;
500         if (Jim_GetLong(interp, argv[1], &address) != JIM_OK)
501                 return JIM_ERR;
502         long value;
503         if (Jim_GetLong(interp, argv[2], &value) != JIM_OK)
504                 return JIM_ERR;
505
506         *((volatile int *) address) = value;
507
508         return JIM_OK;
509 }
510
511
512 /* not so pretty code to fish out ip number*/
513 static int zylinjtag_Jim_Command_ip(Jim_Interp *interp, int argc,
514                 Jim_Obj * const *argv)
515 {
516 #if !defined(__CYGWIN__)
517         Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
518
519         struct ifaddrs *ifa = NULL, *ifp = NULL;
520
521         if (getifaddrs(&ifp) < 0)
522         {
523                 return JIM_ERR;
524         }
525
526         for (ifa = ifp; ifa; ifa = ifa->ifa_next)
527         {
528                 char ip[200];
529                 socklen_t salen;
530
531                 if (ifa->ifa_addr->sa_family == AF_INET)
532                         salen = sizeof(struct sockaddr_in);
533                 else if (ifa->ifa_addr->sa_family == AF_INET6)
534                         salen = sizeof(struct sockaddr_in6);
535                 else
536                         continue;
537
538                 if (getnameinfo(ifa->ifa_addr, salen, ip, sizeof(ip), NULL, 0,
539                                 NI_NUMERICHOST) < 0)
540                 {
541                         continue;
542                 }
543
544                 Jim_AppendString(interp, tclOutput, ip, strlen(ip));
545                 break;
546
547         }
548
549         freeifaddrs(ifp);
550 #else
551         Jim_Obj *tclOutput = Jim_NewStringObj(interp, "fixme!!!", 0);
552         LOG_ERROR("NOT IMPLEMENTED!!!");
553 #endif
554         Jim_SetResult(interp, tclOutput);
555
556         return JIM_OK;
557 }
558
559 /* not so pretty code to fish out eth0 mac address */
560 static int zylinjtag_Jim_Command_mac(Jim_Interp *interp, int argc,
561                 Jim_Obj * const *argv)
562 {
563
564
565         struct ifreq *ifr, *ifend;
566         struct ifreq ifreq;
567         struct ifconf ifc;
568         struct ifreq ifs[5];
569         int SockFD;
570
571         SockFD = socket(AF_INET, SOCK_DGRAM, 0);
572         if (SockFD < 0)
573         {
574                 return JIM_ERR;
575         }
576
577         ifc.ifc_len = sizeof(ifs);
578         ifc.ifc_req = ifs;
579         if (ioctl(SockFD, SIOCGIFCONF, &ifc) < 0)
580         {
581                 close(SockFD);
582                 return JIM_ERR;
583         }
584
585         ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
586         for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
587         {
588                 //if (ifr->ifr_addr.sa_family == AF_INET)
589                 {
590                         if (strcmp("eth0", ifr->ifr_name) != 0)
591                                 continue;
592                         strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
593                         if (ioctl(SockFD, SIOCGIFHWADDR, &ifreq) < 0)
594                         {
595                                 close(SockFD);
596                                 return JIM_ERR;
597                         }
598
599                         close(SockFD);
600
601
602                         Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
603
604                         char buffer[256];
605                         sprintf(buffer, "%02x-%02x-%02x-%02x-%02x-%02x",
606                                         ifreq.ifr_hwaddr.sa_data[0]&0xff,
607                                         ifreq.ifr_hwaddr.sa_data[1]&0xff,
608                                         ifreq.ifr_hwaddr.sa_data[2]&0xff,
609                                         ifreq.ifr_hwaddr.sa_data[3]&0xff,
610                                         ifreq.ifr_hwaddr.sa_data[4]&0xff,
611                                         ifreq.ifr_hwaddr.sa_data[5]&0xff);
612
613                         Jim_AppendString(interp, tclOutput, buffer, strlen(buffer));
614
615                         Jim_SetResult(interp, tclOutput);
616
617                         return JIM_OK;
618                 }
619         }
620         close(SockFD);
621
622         return JIM_ERR;
623
624 }
625
626 static const struct command_registration ioutil_command_handlers[] = {
627         {
628                 .name = "cat",
629                 .handler = &handle_cat_command,
630                 .mode = COMMAND_ANY,
631                 .help = "display file content",
632                 .usage= "<file_name>",
633         },
634         {
635                 .name = "trunc",
636                 .handler = &handle_trunc_command,
637                 .mode = COMMAND_ANY,
638                 .help = "truncate a file 0 size",
639                 .usage= "<file_name>",
640         },
641         {
642                 .name = "cp",
643                 .handler = &handle_cp_command,
644                 .mode = COMMAND_ANY,
645                 .help = "copy a file",
646                 .usage = "<src> <dst>",
647         },
648         {
649                 .name = "append_file",
650                 .handler = &handle_append_command,
651                 .mode = COMMAND_ANY,
652                 .help = "append a variable number of strings to a file",
653                 .usage= "<file_name> [<string> ...]",
654         },
655         {
656                 .name = "meminfo",
657                 .handler = &handle_meminfo_command,
658                 .mode = COMMAND_ANY,
659                 .help = "display available ram memory",
660         },
661         // jim handlers
662         {
663                 .name = "rm",
664                 .mode = COMMAND_ANY,
665                 .jim_handler = &zylinjtag_Jim_Command_rm,
666                 .help = "remove a file",
667                 .usage = "<file>",
668         },
669         {
670                 .name = "peek",
671                 .mode = COMMAND_ANY,
672                 .jim_handler = &zylinjtag_Jim_Command_peek,
673                 .help = "peek at a memory address",
674                 .usage = "<addr>",
675         },
676         {
677                 .name = "poke",
678                 .mode = COMMAND_ANY,
679                 .jim_handler = &zylinjtag_Jim_Command_poke,
680                 .help = "poke at a memory address",
681                 .usage = "<addr> <value>",
682         },
683         {
684                 .name = "ls",
685                 .mode = COMMAND_ANY,
686                 .jim_handler = &zylinjtag_Jim_Command_ls,
687                 .help = "show a listing of files",
688                 .usage = "<dir>",
689         },
690         {
691                 .name = "mac",
692                 .mode = COMMAND_ANY,
693                 .jim_handler = &zylinjtag_Jim_Command_mac,
694                 .help = "show MAC address",
695         },
696         {
697                 .name = "ip",
698                 .jim_handler = &zylinjtag_Jim_Command_ip,
699                 .mode = COMMAND_ANY,
700                 .help = "show IP address",
701         },
702         COMMAND_REGISTRATION_DONE
703 };
704
705 int ioutil_init(struct command_context *cmd_ctx)
706 {
707         return register_commands(cmd_ctx, NULL, ioutil_command_handlers);
708 }