do not depend on thunk.h - more log items
[qemu] / monitor.c
1 /*
2  * QEMU monitor
3  * 
4  * Copyright (c) 2003-2004 Fabrice Bellard
5  * 
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <getopt.h>
29 #include <inttypes.h>
30 #include <unistd.h>
31 #include <sys/mman.h>
32 #include <fcntl.h>
33 #include <signal.h>
34 #include <time.h>
35 #include <sys/time.h>
36 #include <malloc.h>
37 #include <termios.h>
38 #include <sys/poll.h>
39 #include <errno.h>
40 #include <ctype.h>
41
42 #include "cpu.h"
43 #include "vl.h"
44
45 //#define DEBUG
46
47 #define TERM_CMD_BUF_SIZE 4095
48 #define MAX_ARGS 64
49
50 #define IS_NORM 0
51 #define IS_ESC  1
52 #define IS_CSI  2
53
54 #define printf do_not_use_printf
55
56 static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1];
57 static int term_cmd_buf_index;
58 static int term_cmd_buf_size;
59 static int term_esc_state;
60 static int term_esc_param;
61
62 typedef struct term_cmd_t {
63     const char *name;
64     void (*handler)(int argc, const char **argv);
65     const char *params;
66     const char *help;
67 } term_cmd_t;
68
69 static term_cmd_t term_cmds[];
70 static term_cmd_t info_cmds[];
71
72 void term_printf(const char *fmt, ...)
73 {
74     va_list ap;
75     va_start(ap, fmt);
76     vprintf(fmt, ap);
77     va_end(ap);
78 }
79
80 void term_flush(void)
81 {
82     fflush(stdout);
83 }
84
85 static int compare_cmd(const char *name, const char *list)
86 {
87     const char *p, *pstart;
88     int len;
89     len = strlen(name);
90     p = list;
91     for(;;) {
92         pstart = p;
93         p = strchr(p, '|');
94         if (!p)
95             p = pstart + strlen(pstart);
96         if ((p - pstart) == len && !memcmp(pstart, name, len))
97             return 1;
98         if (*p == '\0')
99             break;
100         p++;
101     }
102     return 0;
103 }
104
105 static void help_cmd1(term_cmd_t *cmds, const char *prefix, const char *name)
106 {
107     term_cmd_t *cmd;
108
109     for(cmd = cmds; cmd->name != NULL; cmd++) {
110         if (!name || !strcmp(name, cmd->name))
111             term_printf("%s%s %s -- %s\n", prefix, cmd->name, cmd->params, cmd->help);
112     }
113 }
114
115 static void help_cmd(const char *name)
116 {
117     if (name && !strcmp(name, "info")) {
118         help_cmd1(info_cmds, "info ", NULL);
119     } else {
120         help_cmd1(term_cmds, "", name);
121         if (name && !strcmp(name, "log")) {
122             CPULogItem *item;
123             term_printf("Log items (comma separated):\n");
124             term_printf("%-10s %s\n", "none", "remove all logs");
125             for(item = cpu_log_items; item->mask != 0; item++) {
126                 term_printf("%-10s %s\n", item->name, item->help);
127             }
128         }
129     }
130 }
131
132 static void do_help(int argc, const char **argv)
133 {
134     help_cmd(argv[1]);
135 }
136
137 static void do_commit(int argc, const char **argv)
138 {
139     int i;
140
141     for (i = 0; i < MAX_DISKS; i++) {
142         if (bs_table[i])
143             bdrv_commit(bs_table[i]);
144     }
145 }
146
147 static void do_info(int argc, const char **argv)
148 {
149     term_cmd_t *cmd;
150     const char *item;
151
152     if (argc < 2)
153         goto help;
154     item = argv[1];
155     for(cmd = info_cmds; cmd->name != NULL; cmd++) {
156         if (compare_cmd(argv[1], cmd->name)) 
157             goto found;
158     }
159  help:
160     help_cmd(argv[0]);
161     return;
162  found:
163     cmd->handler(argc, argv);
164 }
165
166 static void do_info_network(int argc, const char **argv)
167 {
168     int i, j;
169     NetDriverState *nd;
170     
171     for(i = 0; i < nb_nics; i++) {
172         nd = &nd_table[i];
173         term_printf("%d: ifname=%s macaddr=", i, nd->ifname);
174         for(j = 0; j < 6; j++) {
175             if (j > 0)
176                 term_printf(":");
177             term_printf("%02x", nd->macaddr[j]);
178         }
179         term_printf("\n");
180     }
181 }
182  
183 static void do_info_block(int argc, const char **argv)
184 {
185     bdrv_info();
186 }
187
188 static void do_quit(int argc, const char **argv)
189 {
190     exit(0);
191 }
192
193 static int eject_device(BlockDriverState *bs, int force)
194 {
195     if (bdrv_is_inserted(bs)) {
196         if (!force) {
197             if (!bdrv_is_removable(bs)) {
198                 term_printf("device is not removable\n");
199                 return -1;
200             }
201             if (bdrv_is_locked(bs)) {
202                 term_printf("device is locked\n");
203                 return -1;
204             }
205         }
206         bdrv_close(bs);
207     }
208     return 0;
209 }
210
211 static void do_eject(int argc, const char **argv)
212 {
213     BlockDriverState *bs;
214     const char **parg;
215     int force;
216
217     parg = argv + 1;
218     if (!*parg) {
219     fail:
220         help_cmd(argv[0]);
221         return;
222     }
223     force = 0;
224     if (!strcmp(*parg, "-f")) {
225         force = 1;
226         parg++;
227     }
228     if (!*parg)
229         goto fail;
230     bs = bdrv_find(*parg);
231     if (!bs) {
232         term_printf("device not found\n");
233         return;
234     }
235     eject_device(bs, force);
236 }
237
238 static void do_change(int argc, const char **argv)
239 {
240     BlockDriverState *bs;
241
242     if (argc != 3) {
243         help_cmd(argv[0]);
244         return;
245     }
246     bs = bdrv_find(argv[1]);
247     if (!bs) {
248         term_printf("device not found\n");
249         return;
250     }
251     if (eject_device(bs, 0) < 0)
252         return;
253     bdrv_open(bs, argv[2], 0);
254 }
255
256 static void do_screen_dump(int argc, const char **argv)
257 {
258     if (argc != 2) {
259         help_cmd(argv[0]);
260         return;
261     }
262     vga_screen_dump(argv[1]);
263 }
264
265 static void do_log(int argc, const char **argv)
266 {
267     int mask;
268     
269     if (argc != 2)
270         goto help;
271     if (!strcmp(argv[1], "none")) {
272         mask = 0;
273     } else {
274         mask = cpu_str_to_log_mask(argv[1]);
275         if (!mask) {
276         help:
277             help_cmd(argv[0]);
278             return;
279         }
280     }
281     cpu_set_log(mask);
282 }
283
284 static term_cmd_t term_cmds[] = {
285     { "help|?", do_help, 
286       "[cmd]", "show the help" },
287     { "commit", do_commit, 
288       "", "commit changes to the disk images (if -snapshot is used)" },
289     { "info", do_info,
290       "subcommand", "show various information about the system state" },
291     { "q|quit", do_quit,
292       "", "quit the emulator" },
293     { "eject", do_eject,
294       "[-f] device", "eject a removable media (use -f to force it)" },
295     { "change", do_change,
296       "device filename", "change a removable media" },
297     { "screendump", do_screen_dump, 
298       "filename", "save screen into PPM image 'filename'" },
299     { "log", do_log,
300       "item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" }, 
301     { NULL, NULL, }, 
302 };
303
304 static term_cmd_t info_cmds[] = {
305     { "network", do_info_network,
306       "", "show the network state" },
307     { "block", do_info_block,
308       "", "show the block devices" },
309     { NULL, NULL, },
310 };
311
312 static void term_handle_command(char *cmdline)
313 {
314     char *p, *pstart;
315     int argc;
316     const char *args[MAX_ARGS + 1];
317     term_cmd_t *cmd;
318
319 #ifdef DEBUG
320     term_printf("command='%s'\n", cmdline);
321 #endif
322     
323     /* split command in words */
324     argc = 0;
325     p = cmdline;
326     for(;;) {
327         while (isspace(*p))
328             p++;
329         if (*p == '\0')
330             break;
331         pstart = p;
332         while (*p != '\0' && !isspace(*p))
333             p++;
334         args[argc] = pstart;
335         argc++;
336         if (argc >= MAX_ARGS)
337             break;
338         if (*p == '\0')
339             break;
340         *p++ = '\0';
341     }
342     args[argc] = NULL;
343 #ifdef DEBUG
344     for(i=0;i<argc;i++) {
345         term_printf(" '%s'", args[i]);
346     }
347     term_printf("\n");
348 #endif
349     if (argc <= 0)
350         return;
351     for(cmd = term_cmds; cmd->name != NULL; cmd++) {
352         if (compare_cmd(args[0], cmd->name)) 
353             goto found;
354     }
355     term_printf("unknown command: '%s'\n", args[0]);
356     return;
357  found:
358     cmd->handler(argc, args);
359 }
360
361 static void term_show_prompt(void)
362 {
363     term_printf("(qemu) ");
364     fflush(stdout);
365     term_cmd_buf_index = 0;
366     term_cmd_buf_size = 0;
367     term_esc_state = IS_NORM;
368 }
369
370 static void term_insert_char(int ch)
371 {
372     if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) {
373         memmove(term_cmd_buf + term_cmd_buf_index + 1,
374                 term_cmd_buf + term_cmd_buf_index,
375                 term_cmd_buf_size - term_cmd_buf_index);
376         term_cmd_buf[term_cmd_buf_index] = ch;
377         term_cmd_buf_size++;
378         term_printf("\033[@%c", ch);
379         term_cmd_buf_index++;
380         term_flush();
381     }
382 }
383
384 static void term_backward_char(void)
385 {
386     if (term_cmd_buf_index > 0) {
387         term_cmd_buf_index--;
388         term_printf("\033[D");
389         term_flush();
390     }
391 }
392
393 static void term_forward_char(void)
394 {
395     if (term_cmd_buf_index < term_cmd_buf_size) {
396         term_cmd_buf_index++;
397         term_printf("\033[C");
398         term_flush();
399     }
400 }
401
402 static void term_delete_char(void)
403 {
404     if (term_cmd_buf_index < term_cmd_buf_size) {
405         memmove(term_cmd_buf + term_cmd_buf_index,
406                 term_cmd_buf + term_cmd_buf_index + 1,
407                 term_cmd_buf_size - term_cmd_buf_index - 1);
408         term_printf("\033[P");
409         term_cmd_buf_size--;
410         term_flush();
411     }
412 }
413
414 static void term_backspace(void)
415 {
416     if (term_cmd_buf_index > 0) {
417         term_backward_char();
418         term_delete_char();
419     }
420 }
421
422 static void term_bol(void)
423 {
424     while (term_cmd_buf_index > 0)
425         term_backward_char();
426 }
427
428 static void term_eol(void)
429 {
430     while (term_cmd_buf_index < term_cmd_buf_size)
431         term_forward_char();
432 }
433
434 /* return true if command handled */
435 static void term_handle_byte(int ch)
436 {
437     switch(term_esc_state) {
438     case IS_NORM:
439         switch(ch) {
440         case 1:
441             term_bol();
442             break;
443         case 5:
444             term_eol();
445             break;
446         case 10:
447         case 13:
448             term_cmd_buf[term_cmd_buf_size] = '\0';
449             term_printf("\n");
450             term_handle_command(term_cmd_buf);
451             term_show_prompt();
452             break;
453         case 27:
454             term_esc_state = IS_ESC;
455             break;
456         case 127:
457         case 8:
458             term_backspace();
459             break;
460         default:
461             if (ch >= 32) {
462                 term_insert_char(ch);
463             }
464             break;
465         }
466         break;
467     case IS_ESC:
468         if (ch == '[') {
469             term_esc_state = IS_CSI;
470             term_esc_param = 0;
471         } else {
472             term_esc_state = IS_NORM;
473         }
474         break;
475     case IS_CSI:
476         switch(ch) {
477         case 'D':
478             term_backward_char();
479             break;
480         case 'C':
481             term_forward_char();
482             break;
483         case '0' ... '9':
484             term_esc_param = term_esc_param * 10 + (ch - '0');
485             goto the_end;
486         case '~':
487             switch(term_esc_param) {
488             case 1:
489                 term_bol();
490                 break;
491             case 3:
492                 term_delete_char();
493                 break;
494             case 4:
495                 term_eol();
496                 break;
497             }
498             break;
499         default:
500             break;
501         }
502         term_esc_state = IS_NORM;
503     the_end:
504         break;
505     }
506 }
507
508 /*************************************************************/
509 /* serial console support */
510
511 #define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */
512
513 static int term_got_escape, term_command;
514
515 void term_print_help(void)
516 {
517     term_printf("\n"
518                 "C-a h    print this help\n"
519                 "C-a x    exit emulatior\n"
520                 "C-a s    save disk data back to file (if -snapshot)\n"
521                 "C-a b    send break (magic sysrq)\n"
522                 "C-a c    switch between console and monitor\n"
523                 "C-a C-a  send C-a\n"
524                 );
525 }
526
527 /* called when a char is received */
528 static void term_received_byte(int ch)
529 {
530     if (!serial_console) {
531         /* if no serial console, handle every command */
532         term_handle_byte(ch);
533     } else {
534         if (term_got_escape) {
535             term_got_escape = 0;
536             switch(ch) {
537             case 'h':
538                 term_print_help();
539                 break;
540             case 'x':
541                 exit(0);
542                 break;
543             case 's': 
544                 {
545                     int i;
546                     for (i = 0; i < MAX_DISKS; i++) {
547                         if (bs_table[i])
548                             bdrv_commit(bs_table[i]);
549                     }
550                 }
551                 break;
552             case 'b':
553                 if (serial_console)
554                     serial_receive_break(serial_console);
555                 break;
556             case 'c':
557                 if (!term_command) {
558                     term_show_prompt();
559                     term_command = 1;
560                 } else {
561                     term_command = 0;
562                 }
563                 break;
564             case TERM_ESCAPE:
565                 goto send_char;
566             }
567         } else if (ch == TERM_ESCAPE) {
568             term_got_escape = 1;
569         } else {
570         send_char:
571             if (term_command) {
572                 term_handle_byte(ch);
573             } else {
574                 if (serial_console)
575                     serial_receive_byte(serial_console, ch);
576             }
577         }
578     }
579 }
580
581 static int term_can_read(void *opaque)
582 {
583     if (serial_console) {
584         return serial_can_receive(serial_console);
585     } else {
586         return 128;
587     }
588 }
589
590 static void term_read(void *opaque, const uint8_t *buf, int size)
591 {
592     int i;
593     for(i = 0; i < size; i++)
594         term_received_byte(buf[i]);
595 }
596
597 void monitor_init(void)
598 {
599     if (!serial_console) {
600         term_printf("QEMU %s monitor - type 'help' for more information\n",
601                     QEMU_VERSION);
602         term_show_prompt();
603     }
604     add_fd_read_handler(0, term_can_read, term_read, NULL);
605 }