]> git.proxmox.com Git - qemu.git/blame - monitor.c
added a command line monitor
[qemu.git] / monitor.c
CommitLineData
9dc39cba
FB
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
56static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1];
57static int term_cmd_buf_index;
58static int term_cmd_buf_size;
59static int term_esc_state;
60static int term_esc_param;
61
62typedef 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
69static term_cmd_t term_cmds[];
70static term_cmd_t info_cmds[];
71
72void 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
80void term_flush(void)
81{
82 fflush(stdout);
83}
84
85static 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
105static 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
115static 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 }
122}
123
124static void do_help(int argc, const char **argv)
125{
126 help_cmd(argv[1]);
127}
128
129static void do_commit(int argc, const char **argv)
130{
131 int i;
132
133 for (i = 0; i < MAX_DISKS; i++) {
134 if (bs_table[i])
135 bdrv_commit(bs_table[i]);
136 }
137}
138
139static void do_info(int argc, const char **argv)
140{
141 term_cmd_t *cmd;
142 const char *item;
143
144 if (argc < 2)
145 goto help;
146 item = argv[1];
147 for(cmd = info_cmds; cmd->name != NULL; cmd++) {
148 if (compare_cmd(argv[1], cmd->name))
149 goto found;
150 }
151 help:
152 help_cmd(argv[0]);
153 return;
154 found:
155 cmd->handler(argc, argv);
156}
157
158static void do_info_network(int argc, const char **argv)
159{
160 int i, j;
161 NetDriverState *nd;
162
163 for(i = 0; i < nb_nics; i++) {
164 nd = &nd_table[i];
165 term_printf("%d: ifname=%s macaddr=", i, nd->ifname);
166 for(j = 0; j < 6; j++) {
167 if (j > 0)
168 term_printf(":");
169 term_printf("%02x", nd->macaddr[j]);
170 }
171 term_printf("\n");
172 }
173}
174
175static void do_info_block(int argc, const char **argv)
176{
177 bdrv_info();
178}
179
180static void do_quit(int argc, const char **argv)
181{
182 exit(0);
183}
184
185static int eject_device(BlockDriverState *bs, int force)
186{
187 if (bdrv_is_inserted(bs)) {
188 if (!force) {
189 if (!bdrv_is_removable(bs)) {
190 term_printf("device is not removable\n");
191 return -1;
192 }
193 if (bdrv_is_locked(bs)) {
194 term_printf("device is locked\n");
195 return -1;
196 }
197 }
198 bdrv_close(bs);
199 }
200 return 0;
201}
202
203static void do_eject(int argc, const char **argv)
204{
205 BlockDriverState *bs;
206 const char **parg;
207 int force;
208
209 parg = argv + 1;
210 if (!*parg) {
211 fail:
212 help_cmd(argv[0]);
213 return;
214 }
215 force = 0;
216 if (!strcmp(*parg, "-f")) {
217 force = 1;
218 parg++;
219 }
220 if (!*parg)
221 goto fail;
222 bs = bdrv_find(*parg);
223 if (!bs) {
224 term_printf("device not found\n");
225 return;
226 }
227 eject_device(bs, force);
228}
229
230static void do_change(int argc, const char **argv)
231{
232 BlockDriverState *bs;
233
234 if (argc != 3) {
235 help_cmd(argv[0]);
236 return;
237 }
238 bs = bdrv_find(argv[1]);
239 if (!bs) {
240 term_printf("device not found\n");
241 return;
242 }
243 if (eject_device(bs, 0) < 0)
244 return;
245 bdrv_open(bs, argv[2], 0);
246}
247
248static term_cmd_t term_cmds[] = {
249 { "help|?", do_help,
250 "[cmd]", "show the help" },
251 { "commit", do_commit,
252 "", "commit changes to the disk images (if -snapshot is used)" },
253 { "info", do_info,
254 "subcommand", "show various information about the system state" },
255 { "q|quit", do_quit,
256 "", "quit the emulator" },
257 { "eject", do_eject,
258 "[-f] device", "eject a removable media (use -f to force it)" },
259 { "change", do_change,
260 "device filename", "change a removable media" },
261 { NULL, NULL, },
262};
263
264static term_cmd_t info_cmds[] = {
265 { "network", do_info_network,
266 "", "show the network state" },
267 { "block", do_info_block,
268 "", "show the block devices" },
269 { NULL, NULL, },
270};
271
272static void term_handle_command(char *cmdline)
273{
274 char *p, *pstart;
275 int argc;
276 const char *args[MAX_ARGS + 1];
277 term_cmd_t *cmd;
278
279#ifdef DEBUG
280 term_printf("command='%s'\n", cmdline);
281#endif
282
283 /* split command in words */
284 argc = 0;
285 p = cmdline;
286 for(;;) {
287 while (isspace(*p))
288 p++;
289 if (*p == '\0')
290 break;
291 pstart = p;
292 while (*p != '\0' && !isspace(*p))
293 p++;
294 args[argc] = pstart;
295 argc++;
296 if (argc >= MAX_ARGS)
297 break;
298 if (*p == '\0')
299 break;
300 *p++ = '\0';
301 }
302 args[argc] = NULL;
303#ifdef DEBUG
304 for(i=0;i<argc;i++) {
305 term_printf(" '%s'", args[i]);
306 }
307 term_printf("\n");
308#endif
309 if (argc <= 0)
310 return;
311 for(cmd = term_cmds; cmd->name != NULL; cmd++) {
312 if (compare_cmd(args[0], cmd->name))
313 goto found;
314 }
315 term_printf("unknown command: '%s'\n", args[0]);
316 return;
317 found:
318 cmd->handler(argc, args);
319}
320
321static void term_show_prompt(void)
322{
323 term_printf("(qemu) ");
324 fflush(stdout);
325 term_cmd_buf_index = 0;
326 term_cmd_buf_size = 0;
327 term_esc_state = IS_NORM;
328}
329
330static void term_insert_char(int ch)
331{
332 if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) {
333 memmove(term_cmd_buf + term_cmd_buf_index + 1,
334 term_cmd_buf + term_cmd_buf_index,
335 term_cmd_buf_size - term_cmd_buf_index);
336 term_cmd_buf[term_cmd_buf_index] = ch;
337 term_cmd_buf_size++;
338 term_printf("\033[@%c", ch);
339 term_cmd_buf_index++;
340 term_flush();
341 }
342}
343
344static void term_backward_char(void)
345{
346 if (term_cmd_buf_index > 0) {
347 term_cmd_buf_index--;
348 term_printf("\033[D");
349 term_flush();
350 }
351}
352
353static void term_forward_char(void)
354{
355 if (term_cmd_buf_index < term_cmd_buf_size) {
356 term_cmd_buf_index++;
357 term_printf("\033[C");
358 term_flush();
359 }
360}
361
362static void term_delete_char(void)
363{
364 if (term_cmd_buf_index < term_cmd_buf_size) {
365 memmove(term_cmd_buf + term_cmd_buf_index,
366 term_cmd_buf + term_cmd_buf_index + 1,
367 term_cmd_buf_size - term_cmd_buf_index - 1);
368 term_printf("\033[P");
369 term_cmd_buf_size--;
370 term_flush();
371 }
372}
373
374static void term_backspace(void)
375{
376 if (term_cmd_buf_index > 0) {
377 term_backward_char();
378 term_delete_char();
379 }
380}
381
382static void term_bol(void)
383{
384 while (term_cmd_buf_index > 0)
385 term_backward_char();
386}
387
388static void term_eol(void)
389{
390 while (term_cmd_buf_index < term_cmd_buf_size)
391 term_forward_char();
392}
393
394/* return true if command handled */
395static void term_handle_byte(int ch)
396{
397 switch(term_esc_state) {
398 case IS_NORM:
399 switch(ch) {
400 case 1:
401 term_bol();
402 break;
403 case 5:
404 term_eol();
405 break;
406 case 10:
407 case 13:
408 term_cmd_buf[term_cmd_buf_size] = '\0';
409 term_printf("\n");
410 term_handle_command(term_cmd_buf);
411 term_show_prompt();
412 break;
413 case 27:
414 term_esc_state = IS_ESC;
415 break;
416 case 127:
417 case 8:
418 term_backspace();
419 break;
420 default:
421 if (ch >= 32) {
422 term_insert_char(ch);
423 }
424 break;
425 }
426 break;
427 case IS_ESC:
428 if (ch == '[') {
429 term_esc_state = IS_CSI;
430 term_esc_param = 0;
431 } else {
432 term_esc_state = IS_NORM;
433 }
434 break;
435 case IS_CSI:
436 switch(ch) {
437 case 'D':
438 term_backward_char();
439 break;
440 case 'C':
441 term_forward_char();
442 break;
443 case '0' ... '9':
444 term_esc_param = term_esc_param * 10 + (ch - '0');
445 goto the_end;
446 case '~':
447 switch(term_esc_param) {
448 case 1:
449 term_bol();
450 break;
451 case 3:
452 term_delete_char();
453 break;
454 case 4:
455 term_eol();
456 break;
457 }
458 break;
459 default:
460 break;
461 }
462 term_esc_state = IS_NORM;
463 the_end:
464 break;
465 }
466}
467
468/*************************************************************/
469/* serial console support */
470
471#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */
472
473static int term_got_escape, term_command;
474
475void term_print_help(void)
476{
477 term_printf("\n"
478 "C-a h print this help\n"
479 "C-a x exit emulatior\n"
480 "C-a d switch on/off debug log\n"
481 "C-a s save disk data back to file (if -snapshot)\n"
482 "C-a b send break (magic sysrq)\n"
483 "C-a c switch between console and monitor\n"
484 "C-a C-a send C-a\n"
485 );
486}
487
488/* called when a char is received */
489static void term_received_byte(int ch)
490{
491 if (!serial_console) {
492 /* if no serial console, handle every command */
493 term_handle_byte(ch);
494 } else {
495 if (term_got_escape) {
496 term_got_escape = 0;
497 switch(ch) {
498 case 'h':
499 term_print_help();
500 break;
501 case 'x':
502 exit(0);
503 break;
504 case 's':
505 {
506 int i;
507 for (i = 0; i < MAX_DISKS; i++) {
508 if (bs_table[i])
509 bdrv_commit(bs_table[i]);
510 }
511 }
512 break;
513 case 'b':
514 if (serial_console)
515 serial_receive_break(serial_console);
516 break;
517 case 'c':
518 if (!term_command) {
519 term_show_prompt();
520 term_command = 1;
521 } else {
522 term_command = 0;
523 }
524 break;
525 case 'd':
526 cpu_set_log(CPU_LOG_ALL);
527 break;
528 case TERM_ESCAPE:
529 goto send_char;
530 }
531 } else if (ch == TERM_ESCAPE) {
532 term_got_escape = 1;
533 } else {
534 send_char:
535 if (term_command) {
536 term_handle_byte(ch);
537 } else {
538 if (serial_console)
539 serial_receive_byte(serial_console, ch);
540 }
541 }
542 }
543}
544
545static int term_can_read(void *opaque)
546{
547 if (serial_console) {
548 return serial_can_receive(serial_console);
549 } else {
550 return 1;
551 }
552}
553
554static void term_read(void *opaque, const uint8_t *buf, int size)
555{
556 int i;
557 for(i = 0; i < size; i++)
558 term_received_byte(buf[i]);
559}
560
561void monitor_init(void)
562{
563 if (!serial_console) {
564 term_printf("QEMU %s monitor - type 'help' for more information\n",
565 QEMU_VERSION);
566 term_show_prompt();
567 }
568 add_fd_read_handler(0, term_can_read, term_read, NULL);
569}