#include #include #include #include #include #include #include #include #include #include #include "qemu-gtk.h" #include "tcp.h" /* ----------------------------------------------------------------- */ static void show_error(struct qemu_window *win, char *cmd, char *reply) { GtkWidget *dialog; dialog = gtk_message_dialog_new(GTK_WINDOW(win->toplevel), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "ERROR: %s", reply); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "monitor command was:\n%s\n", cmd); g_signal_connect_swapped(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog); gtk_widget_show_all(dialog); } static void monitor_parse_vnc(struct qemu_window *win, char *reply) { char *ptr, host[64]; int vnc = 0; int tty = 0; int passwd = 0; int port; if (strcmp(reply, "VNC server disabled") == 0 || /* qemu 0.9.x */ strcmp(reply, "Server: disabled") == 0) { /* qemu 0.10.x */ if (win->sercon) tty = 1; } else if (1 == sscanf(reply, "VNC server active on: %127[^\r\n]", win->vnc_display)) { /* qemu 0.9.x */ if (strstr(reply, "No client connected")) { vnc = 1; if (strstr(win->vnc_display, ",password")) passwd = 1; } } else if (strncmp(reply, "Server:",7) == 0) { /* qemu 0.10.x */ ptr = strstr(reply, "address: "); if (2 == sscanf(ptr, "address: %63[^:]:%d", host, &port)) { snprintf(win->vnc_display, sizeof(win->vnc_display), "%s:%d", host, port - 5900); vnc = 1; if (strstr(reply, "auth: vnc")) passwd = 1; } } if (vnc) { if (passwd) { win->vnc_password = malloc(32); #if 1 /* FIXME: do something less predictable */ srand(time(NULL)+getpid()); snprintf(win->vnc_password, 32, "%x", rand() & 0xfffffff); #endif win->vnc_send_password = 1; monitor_append(win, "change vnc password"); } else { vnc_connect(win); } } else if (tty) { conn_open_term(win, "serial0", win->sercon, 0); } } static int monitor_parse(struct qemu_window *win, char *buf, int len) { char *reply, *prompt, *cmd; int off; /* handle vnc password change */ if (win->vnc_send_password && win->mrun && !strcmp(win->mrun->cmd, "change vnc password") && strstr(buf, "Password: ")) { char buf[64]; int len; if (debug) fprintf(stderr, "%s: password prompt\n", __FUNCTION__); len = snprintf(buf, sizeof(buf), "%s\n", win->vnc_password); write(win->monitor.handle, buf, len); win->vnc_send_password = 0; return 0; } /* parse buffer */ prompt = strstr(buf, "(qemu) "); if (NULL == prompt) { return 0; } off = prompt - buf; if (prompt > buf+1 && prompt[-2] == '\r' && prompt[-1] == '\n') prompt -= 2; *prompt = 0; if (!win->mrun) { if (debug) fprintf(stderr, "%s: no cmd in flight\n", __FUNCTION__); goto out; } cmd = win->mrun->cmd; reply = strstr(buf, "\r\n"); if (!reply) { reply = ""; if (debug) fprintf(stderr, "%s: empty reply for \"%s\"\n", __FUNCTION__, cmd); } else { reply += 2; if (debug) fprintf(stderr, "%s: \"%s\" -> [%s]\n", __FUNCTION__, cmd, reply); } /* parse reply */ if (0 == strcmp(cmd, "info version")) { snprintf(win->version, sizeof(win->version), "%s", reply); update_status(win); } else if (0 == strcmp(cmd, "info name")) { snprintf(win->name, sizeof(win->name), "%s", reply); update_status(win); } else if (0 == strcmp(cmd, "info chardev")) { devices_parse_info_chardev(win, reply); if (win->sercon_tab && win->sercon) conn_open_term(win, "serial0", win->sercon, 0); } else if (0 == strcmp(cmd, "info vnc")) { monitor_parse_vnc(win, reply); } else if (0 == strcmp(cmd, "change vnc password")) { vnc_connect(win); } else if (0 == strcmp(cmd, "info block")) { devices_parse_info_block(win, reply); } else if (0 == strcmp(cmd, "info usb")) { devices_parse_info_usb(win, reply); } else if (0 == strcmp(cmd, "info usbhost")) { devices_parse_info_usbhost(win, reply); } else if (0 == strncmp(cmd, "eject ", 6) || 0 == strncmp(cmd, "change ", 7) || 0 == strncmp(cmd, "usb_add ", 8) || 0 == strncmp(cmd, "usb_del ", 8)) { if (strlen(reply)) show_error(win, cmd, reply); } out: return off + 7; } /* ----------------------------------------------------------------- */ static void monitor_next(struct qemu_window *win) { char buf[256]; int len; if (win->mrun) free(win->mrun); win->mrun = win->mqueue; if (!win->mrun) return; win->mqueue = win->mrun->next; len = snprintf(buf, sizeof(buf), "%s\n", win->mrun->cmd); write(win->monitor.handle, buf, len); } void monitor_append(struct qemu_window *win, char *cmd) { struct qemu_mq *mq; int try_send = 0; if (win->mqueue) { for (mq = win->mqueue; mq->next; mq = mq->next) ; mq->next = malloc(sizeof(*mq)); mq = mq->next; if (debug) fprintf(stderr, "%s: tail: %s\n", __FUNCTION__, cmd); } else { win->mqueue = malloc(sizeof(*mq)); mq = win->mqueue; if (!win->mrun && !win->mused && win->msize) try_send = 1; if (debug) fprintf(stderr, "%s: %s: %s\n", __FUNCTION__, try_send ? "send" : "head", cmd); } mq->next = NULL; snprintf(mq->cmd, sizeof(mq->cmd), "%s", cmd); if (try_send) monitor_next(win); } static gboolean monitor_watch(GIOChannel *source, GIOCondition condition, gpointer userdata) { struct qemu_window *win = userdata; int rc; if (win->mused == win->msize) { if (!win->msize) win->msize = 4; win->msize *= 2; win->mbuf = realloc(win->mbuf, win->msize +1); } rc = read(win->monitor.handle, win->mbuf + win->mused, win->msize - win->mused); switch(rc) { case -1: if (EINTR == errno) break; perror("monitor: read"); goto close; case 0: fprintf(stderr, "monitor: EOF\n"); goto close; default: if (win->monitor.vte) vte_terminal_feed(VTE_TERMINAL(win->monitor.vte), win->mbuf + win->mused, rc); win->mused += rc; win->mbuf[win->mused] = 0; rc = monitor_parse(win, win->mbuf, win->mused); if (rc) { if (rc < win->mused) memmove(win->mbuf, win->mbuf + rc, win->mused - rc); win->mused -= rc; } break; } if (0 == win->mused) monitor_next(win); return TRUE; close: if (win->monitor.vte) vte_terminal_feed(VTE_TERMINAL(win->monitor.vte), "\r\n=== CLOSED ===", 16); close(win->monitor.handle); win->monitor.handle = -1; if (win->quit_on_shutdown) gtk_widget_destroy(win->toplevel); else update_status(win); return FALSE; } /* ----------------------------------------------------------------- */ int monitor_connect(struct qemu_window *win, char *dest) { int fd; fd = conn_init(&win->monitor, "monitor", dest); if (-1 == fd) return -1; win->monitor.ch = g_io_channel_unix_new(fd); win->monitor.id = g_io_add_watch(win->monitor.ch, G_IO_IN, monitor_watch, win); monitor_append(win, "info version"); monitor_append(win, "info name"); devices_rescan(win); monitor_append(win, "info vnc"); return fd; }