]> git.sur5r.net Git - openocd/blob - src/openocd.c
- fix typo in memory.tcl
[openocd] / src / openocd.c
1 /***************************************************************************
2  *   Copyright (C) 2005 by Dominic Rath                                    *
3  *   Dominic.Rath@gmx.de                                                   *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #define OPENOCD_VERSION "Open On-Chip Debugger " VERSION " (" PKGBLDDATE ") svn:" PKGBLDREV
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "log.h"
28 #include "types.h"
29 #include "jtag.h"
30 #include "configuration.h"
31 #include "interpreter.h"
32 #include "xsvf.h"
33 #include "target.h"
34 #include "flash.h"
35 #include "nand.h"
36 #include "pld.h"
37
38 #include "command.h"
39 #include "server.h"
40 #include "telnet_server.h"
41 #include "gdb_server.h"
42 #include "tcl_server.h"
43
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include <strings.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <errno.h>
52
53 #ifdef _WIN32
54 #include <malloc.h>
55 #else
56 #include <alloca.h>
57 #endif
58
59 #ifdef __ECOS
60 /* Jim is provied by eCos */
61 #include <cyg/jimtcl/jim.h>
62 #else
63 #define JIM_EMBEDDED
64 #include "jim.h"
65 #endif
66
67 #include "replacements.h"
68
69 int launchTarget(struct command_context_s *cmd_ctx)
70 {
71         int retval;
72         /* Try to examine & validate jtag chain, though this may require a reset first
73          * in which case we continue setup */
74         jtag_init(cmd_ctx);
75
76         /* try to examine target at this point. If it fails, perhaps a reset will
77          * bring it up later on via a telnet/gdb session */
78         target_examine(cmd_ctx);
79
80         retval=flash_init_drivers(cmd_ctx);
81         if (retval!=ERROR_OK)
82                 return retval;
83         LOG_DEBUG("flash init complete");
84
85         retval=nand_init(cmd_ctx);
86         if (retval!=ERROR_OK)
87                 return retval;
88         LOG_DEBUG("NAND init complete");
89
90         retval=pld_init(cmd_ctx);
91         if (retval!=ERROR_OK)
92                 return retval;
93         LOG_DEBUG("pld init complete");
94         return retval;
95 }
96
97 /* Give TELNET a way to find out what version this is */
98 int handle_version_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
99 {
100         command_print(cmd_ctx, OPENOCD_VERSION);
101
102         return ERROR_OK;
103 }
104
105 static int daemon_startup = 0;
106
107 int handle_daemon_startup_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
108 {
109         if (argc==0)
110                 return ERROR_OK;
111         if (argc > 1 )
112                 return ERROR_COMMAND_SYNTAX_ERROR;
113         
114         daemon_startup = strcmp("reset", args[0])==0;
115         
116         command_print(cmd_ctx, OPENOCD_VERSION);
117
118         return ERROR_OK;
119 }
120
121 void exit_handler(void)
122 {
123         /* close JTAG interface */
124         if (jtag && jtag->quit)
125                 jtag->quit();
126 }
127
128 /* OpenOCD can't really handle failure of this command. Patches welcome! :-) */
129 int handle_init_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
130 {
131         int retval;
132         static int initialized=0;
133         if (initialized)
134                 return ERROR_OK;
135         
136         initialized=1;
137         
138         command_set_output_handler(cmd_ctx, configuration_output_handler, NULL);
139
140         atexit(exit_handler);
141         
142         if (target_init(cmd_ctx) != ERROR_OK)
143                 return ERROR_FAIL;
144         LOG_DEBUG("target init complete");
145
146         if ((retval=jtag_interface_init(cmd_ctx)) != ERROR_OK)
147         {
148                 /* we must be able to set up the jtag interface */
149                 return retval;
150         }
151         LOG_DEBUG("jtag interface init complete");
152
153         /* Try to initialize & examine the JTAG chain at this point, but
154          * continue startup regardless
155          */
156         if (jtag_init(cmd_ctx) == ERROR_OK)
157         {
158                 LOG_DEBUG("jtag init complete");
159                 if (target_examine(cmd_ctx) == ERROR_OK)
160                 {
161                         LOG_DEBUG("jtag examine complete");
162                 }
163         }
164         
165         if (flash_init_drivers(cmd_ctx) != ERROR_OK)
166                 return ERROR_FAIL;
167         LOG_DEBUG("flash init complete");
168
169         if (nand_init(cmd_ctx) != ERROR_OK)
170                 return ERROR_FAIL;
171         LOG_DEBUG("NAND init complete");
172
173         if (pld_init(cmd_ctx) != ERROR_OK)
174                 return ERROR_FAIL;
175         LOG_DEBUG("pld init complete");
176
177         /* initialize tcp server */
178         server_init();
179
180         /* initialize telnet subsystem */
181         telnet_init("Open On-Chip Debugger");
182         gdb_init();
183         tcl_init(); /* allows tcl to just connect without going thru telnet */
184
185         return ERROR_OK;
186 }
187
188 void lockBigLock();
189 void unlockBigLock();
190
191 Jim_Interp *interp;
192 command_context_t *active_cmd_ctx;
193
194 static int new_int_array_element(Jim_Interp * interp, const char *varname, int idx, u32 val)
195 {
196         char *namebuf;
197         Jim_Obj *nameObjPtr, *valObjPtr;
198         int result;
199
200         namebuf = alloc_printf("%s(%d)", varname, idx );
201         
202     nameObjPtr = Jim_NewStringObj(interp, namebuf, -1);
203     valObjPtr = Jim_NewIntObj(interp, val );
204     Jim_IncrRefCount(nameObjPtr);
205     Jim_IncrRefCount(valObjPtr);
206     result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
207     Jim_DecrRefCount(interp, nameObjPtr);
208     Jim_DecrRefCount(interp, valObjPtr);
209     free(namebuf);
210         // printf( "%s = 0%08x\n", namebuf, val );
211     return result;
212 }
213
214 static int Jim_Command_mem2array(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
215 {
216         target_t *target;
217         long l;
218         u32 width;
219         u32 len;
220         u32 addr;
221         u32 count;
222         u32 v;
223         const char *varname;
224         u8 buffer[4096];
225         int  i,n,e,retval;
226
227         /* argv[1] = name of array to receive the data
228          * argv[2] = desired width
229          * argv[3] = memory address 
230          * argv[4] = length in bytes to read
231          */
232         if( argc != 5 ){
233                 Jim_WrongNumArgs( interp, 1, argv, "varname width addr nelems" );
234                 return JIM_ERR;
235         }
236         varname = Jim_GetString( argv[1], &len );
237         /* given "foo" get space for worse case "foo(%d)" .. add 20 */
238
239         e = Jim_GetLong( interp, argv[2], &l );
240         width = l;
241         if( e != JIM_OK ){
242                 return e;
243         }
244         
245         e = Jim_GetLong( interp, argv[3], &l );
246         addr = l;
247         if( e != JIM_OK ){
248                 return e;
249         }
250         e = Jim_GetLong( interp, argv[4], &l );
251         len = l;
252         if( e != JIM_OK ){
253                 return e;
254         }
255         switch(width){
256         case 8:
257                 width = 1;
258                 break;
259         case 16:
260                 width = 2;
261                 break;
262         case 32:
263                 width = 4;
264                 break;
265         default:
266                 Jim_SetResult(interp, 
267                                           Jim_NewEmptyStringObj(interp));
268                 Jim_AppendStrings( interp, Jim_GetResult(interp),
269                                                    "Invalid width param, must be 8/16/32", NULL );
270                 return JIM_ERR;
271         }
272         if( len == 0 ){
273                 Jim_SetResult(interp, 
274                                           Jim_NewEmptyStringObj(interp));
275                 Jim_AppendStrings( interp, Jim_GetResult(interp),
276                                                    "mem2array: zero width read?", NULL );
277                 return JIM_ERR;
278         }
279         if( (addr + (len * width)) < addr ){
280                 Jim_SetResult(interp, 
281                                           Jim_NewEmptyStringObj(interp));
282                 Jim_AppendStrings( interp, Jim_GetResult(interp),
283                                                    "mem2array: addr + len - wraps to zero?", NULL );
284                 return JIM_ERR;
285         }
286         /* absurd transfer size? */
287         if( len > 65536 ){
288                 Jim_SetResult(interp, 
289                                           Jim_NewEmptyStringObj(interp));
290                 Jim_AppendStrings( interp, Jim_GetResult(interp),
291                                                    "mem2array: absurd > 64K item request", NULL );
292                 return JIM_ERR;
293         }               
294                 
295         if( (width == 1) ||
296                 ((width == 2) && ((addr & 1) == 0)) ||
297                 ((width == 4) && ((addr & 3) == 0)) ){
298                 /* all is well */
299         } else {
300                 char buf[100];
301                 Jim_SetResult(interp, 
302                                           Jim_NewEmptyStringObj(interp));
303                 sprintf( buf, 
304                                  "mem2array address: 0x%08x is not aligned for %d byte reads",
305                                  addr, width );
306                                  
307                 Jim_AppendStrings( interp, Jim_GetResult(interp),
308                                                    buf , NULL );
309                 return JIM_ERR;
310         }
311
312         target = get_current_target( active_cmd_ctx );
313         
314         /* Transfer loop */
315
316         /* index counter */
317         n = 0;
318         /* assume ok */
319         e = JIM_OK;
320         while( len ){
321
322                 /* Slurp... in buffer size chunks */
323                 
324                 count = len; /* in objects.. */
325                 if( count > (sizeof(buffer)/width)){
326                         count = (sizeof(buffer)/width);
327                 }
328                 
329                 retval = target->type->read_memory( target, 
330                                                                                         addr, 
331                                                                                         width, 
332                                                                                         count,
333                                                                                         buffer );
334
335                 if( retval != ERROR_OK ){
336                         /* BOO !*/
337                         LOG_ERROR("mem2array: Read @ 0x%08x, w=%d, cnt=%d, failed",
338                                           addr, width, count );
339                         Jim_SetResult(interp, 
340                                                   Jim_NewEmptyStringObj(interp));
341                         Jim_AppendStrings( interp, Jim_GetResult(interp),
342                                                    "mem2array: cannot read memory", NULL );
343                         e = JIM_ERR;
344                         len = 0;
345                 } else {
346                         v = 0; /* shut up gcc */
347                         for( i = 0 ; i < count ; i++, n++ ){
348                                 switch(width){
349                                 case 4:
350                                         v = target_buffer_get_u32( target, &buffer[i*width] );
351                                         break;
352                                 case 2:
353                                         v = target_buffer_get_u16( target, &buffer[i*width] );
354                                         break;
355                                 case 1:
356                                         v = buffer[i] & 0x0ff;
357                                         break;
358                                 }
359                                 new_int_array_element( interp, varname, n, v );
360                         }
361                         len -= count;
362                 }
363         }
364         Jim_SetResult(interp, 
365                                   Jim_NewEmptyStringObj(interp));
366
367         return JIM_OK;
368 }
369
370 static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string)
371 {               
372         Jim_Obj *tclOutput=(Jim_Obj *)privData;
373
374         Jim_AppendString(interp, tclOutput, string, strlen(string));
375 }
376
377 /* try to execute as Jim command, otherwise fall back to standard command.
378
379         Note that even if the Jim command caused an error, then we succeeded
380         to execute it, hence this fn pretty much always returns ERROR_OK. 
381
382  */
383 int jim_command(command_context_t *context, char *line)
384 {
385         int retval=ERROR_OK;
386         /* FIX!!!! in reality there is only one cmd_ctx handler, but consider
387         what might happen here if there are multiple handlers w/reentrant callback
388         fn's... shudder!  */
389         active_cmd_ctx=context;
390         int retcode=Jim_Eval(interp, line);
391         
392         const char *result;
393         int reslen;
394     result = Jim_GetString(Jim_GetResult(interp), &reslen);
395     if (retcode == JIM_ERR) {
396             int len, i;
397         
398             LOG_USER_N("Runtime error, file \"%s\", line %d:" JIM_NL,
399                     interp->errorFileName, interp->errorLine);
400             LOG_USER_N("    %s" JIM_NL,
401                     Jim_GetString(interp->result, NULL));
402             Jim_ListLength(interp, interp->stackTrace, &len);
403             for (i = 0; i < len; i+= 3) {
404                 Jim_Obj *objPtr;
405                 const char *proc, *file, *line;
406         
407                 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
408                 proc = Jim_GetString(objPtr, NULL);
409                 Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
410                         JIM_NONE);
411                 file = Jim_GetString(objPtr, NULL);
412                 Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
413                         JIM_NONE);
414                 line = Jim_GetString(objPtr, NULL);
415                 LOG_USER_N("In procedure '%s' called at file \"%s\", line %s" JIM_NL,
416                         proc, file, line);
417             }
418     } else if (retcode == JIM_EXIT) {
419         // ignore.
420         //exit(Jim_GetExitCode(interp));
421     } else {
422         if (reslen) {
423                 int i;
424                 char buff[256+1];
425                 for (i=0; i<reslen; i+=256)
426                 {
427                         int chunk;
428                         chunk=reslen-i;
429                         if (chunk>256)
430                                 chunk=256;
431                         strncpy(buff, result, chunk);
432                         buff[chunk]=0; 
433                 LOG_USER_N("%s", buff);
434                 }
435                 LOG_USER_N("%s", "\n");
436         }
437     }
438         return retval;
439 }
440
441 int startLoop=0;
442
443 static int Jim_Command_openocd_ignore(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int ignore)
444 {
445         int retval;
446     char *cmd = (char*)Jim_GetString(argv[1], NULL);
447
448         lockBigLock();
449         
450     Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
451     
452     if (startLoop)
453     {
454         // We don't know whether or not the telnet/gdb server is running...
455         target_call_timer_callbacks_now();
456     }
457         
458         log_add_callback(tcl_output, tclOutput);
459     retval=command_run_line_internal(active_cmd_ctx, cmd);
460     
461     if (startLoop)
462     {
463         target_call_timer_callbacks_now();
464     }
465         log_remove_callback(tcl_output, tclOutput);
466     
467         Jim_SetResult(interp, tclOutput);
468     unlockBigLock();
469         
470     return (ignore||(retval==ERROR_OK))?JIM_OK:JIM_ERR;
471 }
472
473 static int Jim_Command_openocd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
474 {
475         return Jim_Command_openocd_ignore(interp, argc, argv, 1); 
476 }
477
478 static int Jim_Command_openocd_throw(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
479 {
480         return Jim_Command_openocd_ignore(interp, argc, argv, 0); 
481 }
482
483 /* find full path to file */
484 static int Jim_Command_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
485 {
486         if (argc!=2)
487                 return JIM_ERR;
488         char *file = (char*)Jim_GetString(argv[1], NULL);
489         char *full_path=find_file(file);
490         if (full_path==NULL)
491                 return JIM_ERR;
492     Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
493     free(full_path);
494     
495         Jim_SetResult(interp, result);
496         return JIM_OK;
497 }
498
499 static int Jim_Command_echo(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
500 {
501         if (argc!=2)
502                 return JIM_ERR;
503         char *str = (char*)Jim_GetString(argv[1], NULL);
504         LOG_USER("%s", str);
505         return JIM_OK;
506 }
507
508 static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *cookie)
509 {
510         size_t nbytes;
511         const char *ptr;
512
513         /* make it a char easier to read code */
514         ptr = _ptr;
515
516         nbytes = size * n;
517         if( nbytes == 0 ){
518                 return 0;
519         }
520
521         if( !active_cmd_ctx ){
522                 /* FIXME: Where should this go? */              
523                 return n;
524         }
525
526         /* do we have to chunk it? */
527         if( ptr[ nbytes ] == 0 ){
528                 /* no it is a C style string */
529                 command_output_text( active_cmd_ctx, ptr );
530                 return strlen(ptr);
531         }
532         /* GRR we must chunk - not null terminated */
533         while( nbytes ){
534                 char chunk[128+1];
535                 int x;
536
537                 x = nbytes;
538                 if( x > 128 ){
539                         x = 128;
540                 }
541                 /* copy it */
542                 memcpy( chunk, ptr, x );
543                 /* terminate it */
544                 chunk[n] = 0;
545                 /* output it */
546                 command_output_text( active_cmd_ctx, chunk );
547                 ptr += x;
548                 nbytes -= x;
549         }
550         
551         return n;
552 }
553
554 static size_t openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie )
555 {
556         /* TCL wants to read... tell him no */
557         return 0;
558 }
559
560 static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
561 {
562         char *cp;
563         int n;
564         
565         n = -1;
566         if( active_cmd_ctx ){
567                 cp = alloc_vprintf( fmt, ap );
568                 if( cp ){
569                         command_output_text( active_cmd_ctx, cp );
570                         n = strlen(cp);
571                         free(cp);
572                 }
573         }
574         return n;
575 }
576
577 static int openocd_jim_fflush(void *cookie)
578 {
579         /* nothing to flush */
580         return 0;
581 }
582
583 static char* openocd_jim_fgets(char *s, int size, void *cookie)
584 {
585         /* not supported */
586         errno = ENOTSUP;
587         return NULL;
588 }
589
590 void initJim(void)
591 {
592     Jim_CreateCommand(interp, "openocd", Jim_Command_openocd, NULL, NULL);
593     Jim_CreateCommand(interp, "openocd_throw", Jim_Command_openocd_throw, NULL, NULL);
594     Jim_CreateCommand(interp, "find", Jim_Command_find, NULL, NULL);
595     Jim_CreateCommand(interp, "echo", Jim_Command_echo, NULL, NULL);
596     Jim_CreateCommand(interp, "mem2array", Jim_Command_mem2array, NULL, NULL );
597
598         /* Set Jim's STDIO */
599         interp->cookie_stdin  = NULL;
600         interp->cookie_stdout = NULL;
601         interp->cookie_stderr = NULL;
602         interp->cb_fwrite     = openocd_jim_fwrite;
603         interp->cb_fread      = openocd_jim_fread ;
604         interp->cb_vfprintf   = openocd_jim_vfprintf;
605         interp->cb_fflush     = openocd_jim_fflush;
606         interp->cb_fgets      = openocd_jim_fgets;
607 }
608
609 /* after command line parsing */
610 void initJim2(void)
611 {
612         Jim_Eval(interp, "source [find tcl/commands.tcl]");
613 }
614
615 command_context_t *setup_command_handler()
616 {
617         command_context_t *cmd_ctx;
618         
619         cmd_ctx = command_init();
620
621         register_command(cmd_ctx, NULL, "version", handle_version_command,
622                                          COMMAND_EXEC, "show OpenOCD version");
623         register_command(cmd_ctx, NULL, "daemon_startup", handle_daemon_startup_command, COMMAND_CONFIG, 
624                         "deprecated - use \"init\" and \"reset\" at end of startup script instead");
625         
626         /* register subsystem commands */
627         server_register_commands(cmd_ctx);
628         telnet_register_commands(cmd_ctx);
629         gdb_register_commands(cmd_ctx);
630         tcl_register_commands(cmd_ctx); /* tcl server commands */
631         log_register_commands(cmd_ctx);
632         jtag_register_commands(cmd_ctx);
633         interpreter_register_commands(cmd_ctx);
634         xsvf_register_commands(cmd_ctx);
635         target_register_commands(cmd_ctx);
636         flash_register_commands(cmd_ctx);
637         nand_register_commands(cmd_ctx);
638         pld_register_commands(cmd_ctx);
639         
640         if (log_init(cmd_ctx) != ERROR_OK)
641         {
642                 exit(-1);
643         }
644         LOG_DEBUG("log init complete");
645
646         LOG_OUTPUT( OPENOCD_VERSION "\n" );
647         
648         
649         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
650         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
651         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
652         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
653         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
654         LOG_OUTPUT( "$URL$\n");
655         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
656         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
657         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
658         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
659         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
660
661         register_command(cmd_ctx, NULL, "init", handle_init_command,
662                                          COMMAND_ANY, "initializes target and servers - nop on subsequent invocations");
663
664         return cmd_ctx;
665 }
666
667 /*
668 normally this is the main() function entry, but if OpenOCD is linked
669 into application, then this fn will not be invoked, but rather that
670 application will have it's own implementation of main().
671 */
672 int openocd_main(int argc, char *argv[])
673 {
674 #ifdef JIM_EMBEDDED
675         Jim_InitEmbedded();
676     /* Create an interpreter */
677     interp = Jim_CreateInterp();
678     /* Add all the Jim core commands */
679     Jim_RegisterCoreCommands(interp);
680 #endif
681     
682         initJim();
683         
684         /* initialize commandline interface */
685         command_context_t *cmd_ctx;
686         cmd_ctx=setup_command_handler();
687         
688         command_context_t *cfg_cmd_ctx;
689         cfg_cmd_ctx = copy_command_context(cmd_ctx);
690         cfg_cmd_ctx->mode = COMMAND_CONFIG;
691         command_set_output_handler(cfg_cmd_ctx, configuration_output_handler, NULL);
692         
693         if (parse_cmdline_args(cfg_cmd_ctx, argc, argv) != ERROR_OK)
694                 return EXIT_FAILURE;
695
696         initJim2();
697
698         if (parse_config_file(cfg_cmd_ctx) != ERROR_OK)
699                 return EXIT_FAILURE;
700         
701         command_done(cfg_cmd_ctx);
702
703         if (command_run_line(cmd_ctx, "init")!=ERROR_OK)
704                 return EXIT_FAILURE;
705         
706         if (daemon_startup)
707                 command_run_line(cmd_ctx, "reset");
708         
709         startLoop=1;
710
711         /* handle network connections */
712         server_loop(cmd_ctx);
713
714         /* shut server down */
715         server_quit();
716
717         unregister_all_commands(cmd_ctx);
718         
719         /* free commandline interface */
720         command_done(cmd_ctx);
721
722         return EXIT_SUCCESS;
723 }