diff options
-rw-r--r-- | Makefile.android | 1 | ||||
-rw-r--r-- | android/console.c | 135 | ||||
-rw-r--r-- | monitor.c | 6 | ||||
-rw-r--r-- | monitor.h | 2 | ||||
-rw-r--r-- | outputchannel.c | 61 | ||||
-rw-r--r-- | outputchannel.h | 38 | ||||
-rw-r--r-- | savevm.c | 99 | ||||
-rw-r--r-- | sysemu.h | 5 |
8 files changed, 303 insertions, 44 deletions
diff --git a/Makefile.android b/Makefile.android index 69538b7..1cc904a 100644 --- a/Makefile.android +++ b/Makefile.android @@ -650,6 +650,7 @@ CORE_MISC_SOURCES = vl-android.c \ monitor.c \ readline.c \ qemu-char-android.c \ + outputchannel.c \ qemu-error.c \ qerror.c \ disas.c \ diff --git a/android/console.c b/android/console.c index af22fc6..4f1a976 100644 --- a/android/console.c +++ b/android/console.c @@ -36,6 +36,7 @@ #include "android/utils/bufprint.h" #include "android/utils/debug.h" #include "android/utils/stralloc.h" +#include "android/config/config.h" #include "tcpdump.h" #include "net.h" #include "monitor.h" @@ -251,18 +252,25 @@ static void control_control_write( ControlClient client, const char* buff, in } } -static void control_write( ControlClient client, const char* format, ... ) +static int control_vwrite( ControlClient client, const char* format, va_list args ) { static char temp[1024]; - va_list args; + int ret = vsnprintf( temp, sizeof(temp), format, args ); + temp[ sizeof(temp)-1 ] = 0; + control_control_write( client, temp, -1 ); + + return ret; +} +static int control_write( ControlClient client, const char* format, ... ) +{ + int ret; + va_list args; va_start(args, format); - vsnprintf( temp, sizeof(temp), format, args ); + ret = control_vwrite(client, format, args); va_end(args); - temp[ sizeof(temp)-1 ] = 0; - - control_control_write( client, temp, -1 ); + return ret; } @@ -1929,6 +1937,109 @@ static const CommandDefRec event_commands[] = { NULL, NULL, NULL, NULL, NULL, NULL } }; +#if CONFIG_ANDROID_SNAPSHOTS + + +/********************************************************************************************/ +/********************************************************************************************/ +/***** ******/ +/***** S N A P S H O T C O M M A N D S ******/ +/***** ******/ +/********************************************************************************************/ +/********************************************************************************************/ + +static int +control_write_out_cb(void* opaque, const char* fmt, va_list ap) +{ + ControlClient client = opaque; + int ret = control_vwrite(client, fmt, ap); + return ret; +} + +static int +control_write_err_cb(void* opaque, const char* fmt, va_list ap) +{ + int ret = 0; + ControlClient client = opaque; + ret += control_write(client, "KO: "); + ret += control_vwrite(client, fmt, ap); + return ret; +} + +static int +do_snapshot_list( ControlClient client, char* args ) +{ + int ret; + OutputChannel *out = output_channel_alloc(client, control_write_out_cb); + OutputChannel *err = output_channel_alloc(client, control_write_err_cb); + do_info_snapshots_oc(out, err); + ret = output_channel_written(err); + output_channel_free(out); + output_channel_free(err); + + return ret > 0; +} + +static int +do_snapshot_save( ControlClient client, char* args ) +{ + int ret; + OutputChannel *err = output_channel_alloc(client, control_write_err_cb); + do_savevm_oc(err, args); + ret = output_channel_written(err); + output_channel_free(err); + + return ret > 0; // no output on error channel indicates success +} + +static int +do_snapshot_load( ControlClient client, char* args ) +{ + int ret; + OutputChannel *err = output_channel_alloc(client, control_write_err_cb); + do_loadvm_oc(err, args); + ret = output_channel_written(err); + output_channel_free(err); + + return ret > 0; +} + +static int +do_snapshot_del( ControlClient client, char* args ) +{ + int ret; + OutputChannel *err = output_channel_alloc(client, control_write_err_cb); + do_delvm_oc(err, args); + ret = output_channel_written(err); + output_channel_free(err); + + return ret > 0; +} + +static const CommandDefRec snapshot_commands[] = +{ + { "list", "list available state snapshots", + "'avd snapshot list' will show a list of all state snapshots that can be loaded\r\n", + NULL, do_snapshot_list, NULL }, + + { "save", "save state snapshot", + "'avd snapshot save <name>' will save the current (run-time) state to a snapshot with the given name\r\n", + NULL, do_snapshot_save, NULL }, + + { "load", "load state snapshot", + "'avd snapshot load <name>' will load the state snapshot of the given name\r\n", + NULL, do_snapshot_load, NULL }, + + { "del", "delete state snapshot", + "'avd snapshot del <name>' will delete the state snapshot with the given name\r\n", + NULL, do_snapshot_del, NULL }, + + { NULL, NULL, NULL, NULL, NULL, NULL } +}; + + +#endif + /********************************************************************************************/ /********************************************************************************************/ @@ -1985,13 +2096,19 @@ static const CommandDefRec vm_commands[] = NULL, do_avd_start, NULL }, { "status", "query virtual device status", - "'avd status' will indicate wether the virtual device is running or not\r\n", + "'avd status' will indicate whether the virtual device is running or not\r\n", NULL, do_avd_status, NULL }, { "name", "query virtual device name", "'avd name' will return the name of this virtual device\r\n", NULL, do_avd_name, NULL }, +#if CONFIG_ANDROID_SNAPSHOTS + { "snapshot", "state snapshot commands", + "allows you to save and restore the virtual device state in snapshots\r\n", + NULL, NULL, snapshot_commands }, +#endif + { NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -2313,8 +2430,8 @@ static const CommandDefRec main_commands[] = "allows you to simulate an inbound SMS\r\n", NULL, NULL, sms_commands }, - { "avd", "manager virtual device state", - "allows to change (e.g. start/stop) the virtual device state\r\n", NULL, + { "avd", "control virtual device execution", + "allows you to control (e.g. start/stop) the execution of the virtual device\r\n", NULL, NULL, vm_commands }, { "window", "manage emulator window", @@ -165,11 +165,13 @@ static void monitor_puts(Monitor *mon, const char *str) } } -void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) +int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { char buf[4096]; - vsnprintf(buf, sizeof(buf), fmt, ap); + int ret = vsnprintf(buf, sizeof(buf), fmt, ap); monitor_puts(mon, buf); + + return ret; } void monitor_printf(Monitor *mon, const char *fmt, ...) @@ -53,7 +53,7 @@ int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs, int monitor_get_fd(Monitor *mon, const char *fdname); -void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap); +int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap); void monitor_printf(Monitor *mon, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); void monitor_print_filename(Monitor *mon, const char *filename); diff --git a/outputchannel.c b/outputchannel.c new file mode 100644 index 0000000..a58c767 --- /dev/null +++ b/outputchannel.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "outputchannel.h" +#include "qemu-common.h" + +struct OutputChannel { + void* opaque; /* caller-specific information */ + OutputChannelPrintf printf; /* callback function to do the printing */ + unsigned int written; /* number of bytes written to the channel */ +}; + +OutputChannel* output_channel_alloc(void* opaque, OutputChannelPrintf cb) +{ + OutputChannel* oc = qemu_mallocz(sizeof(oc)); + oc->printf = cb; + oc->opaque = opaque; + oc->written = 0; + + return oc; +} + +int output_channel_printf(OutputChannel* oc, const char* fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = oc->printf(oc->opaque, fmt, ap); + va_end(ap); + + /* Don't count errors and no-ops towards number of bytes written */ + if (ret > 0) { + oc->written += ret; + } + + return ret; +} + +void output_channel_free(OutputChannel* oc) +{ + free(oc); +} + +unsigned int output_channel_written(OutputChannel* oc) +{ + return oc->written; +} diff --git a/outputchannel.h b/outputchannel.h new file mode 100644 index 0000000..4dece8d --- /dev/null +++ b/outputchannel.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _OUTPUTCHANNEL_H +#define _OUTPUTCHANNEL_H + +#include <stdarg.h> + +/* Callback function to print a printf-formatted string to a channel */ +typedef int (*OutputChannelPrintf) (void* opaque, const char* fmt, va_list ap); + +typedef struct OutputChannel OutputChannel; + +/* Allocates a new output channel */ +OutputChannel* output_channel_alloc(void* opaque, OutputChannelPrintf cb); + +/* Prints a printf-formatted string to the output channel */ +int output_channel_printf(OutputChannel* oc, const char* fmt, ...); + +/* Frees an output channel */ +void output_channel_free(OutputChannel* oc); + +/* Returns the number of bytes written to the channel */ +unsigned int output_channel_written(OutputChannel* oc); + +#endif /* _OUTPUTCHANNEL_H */ @@ -86,6 +86,7 @@ #include "sysemu.h" #include "qemu-timer.h" #include "qemu-char.h" +#include "outputchannel.h" #include "block.h" #include "audio/audio.h" #include "migration.h" @@ -1162,8 +1163,22 @@ static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, return ret; } +static int +monitor_output_channel_cb(void* opaque, const char* fmt, va_list ap) +{ + return monitor_vprintf((Monitor*) opaque, fmt, ap); +} + + void do_savevm(Monitor *mon, const char *name) { + OutputChannel *oc = output_channel_alloc(mon, monitor_output_channel_cb); + do_savevm_oc(oc, name); + output_channel_free(oc); +} + +void do_savevm_oc(OutputChannel *err, const char *name) +{ BlockDriverState *bs, *bs1; QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1; int must_delete, ret, i; @@ -1179,7 +1194,7 @@ void do_savevm(Monitor *mon, const char *name) bs = get_bs_snapshots(); if (!bs) { - monitor_printf(mon, "No block device can accept snapshots\n"); + output_channel_printf(err, "No block device can accept snapshots\n"); return; } @@ -1218,22 +1233,22 @@ void do_savevm(Monitor *mon, const char *name) sn->vm_clock_nsec = qemu_get_clock(vm_clock); if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) { - monitor_printf(mon, "Device %s does not support VM state snapshots\n", - bdrv_get_device_name(bs)); + output_channel_printf(err, "Device %s does not support VM state snapshots\n", + bdrv_get_device_name(bs)); goto the_end; } /* save the VM state */ f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 1); if (!f) { - monitor_printf(mon, "Could not open VM state file\n"); + output_channel_printf(err, "Could not open VM state file\n"); goto the_end; } ret = qemu_savevm_state(f); vm_state_size = qemu_ftell(f); qemu_fclose(f); if (ret < 0) { - monitor_printf(mon, "Error %d while writing VM\n", ret); + output_channel_printf(err, "Error %d while writing VM\n", ret); goto the_end; } @@ -1245,17 +1260,17 @@ void do_savevm(Monitor *mon, const char *name) if (must_delete) { ret = bdrv_snapshot_delete(bs1, old_sn->id_str); if (ret < 0) { - monitor_printf(mon, - "Error while deleting snapshot on '%s'\n", - bdrv_get_device_name(bs1)); + output_channel_printf(err, + "Error while deleting snapshot on '%s'\n", + bdrv_get_device_name(bs1)); } } /* Write VM state size only to the image that contains the state */ sn->vm_state_size = (bs == bs1 ? vm_state_size : 0); ret = bdrv_snapshot_create(bs1, sn); if (ret < 0) { - monitor_printf(mon, "Error while creating snapshot on '%s'\n", - bdrv_get_device_name(bs1)); + output_channel_printf(err, "Error while creating snapshot on '%s'\n", + bdrv_get_device_name(bs1)); } } } @@ -1267,6 +1282,13 @@ void do_savevm(Monitor *mon, const char *name) void do_loadvm(Monitor *mon, const char *name) { + OutputChannel *oc = output_channel_alloc(mon, monitor_output_channel_cb); + do_loadvm_oc(oc, name); + output_channel_free(oc); +} + +void do_loadvm_oc(OutputChannel *err, const char *name) +{ BlockDriverState *bs, *bs1; BlockDriverInfo bdi1, *bdi = &bdi1; QEMUSnapshotInfo sn; @@ -1276,7 +1298,7 @@ void do_loadvm(Monitor *mon, const char *name) bs = get_bs_snapshots(); if (!bs) { - monitor_printf(mon, "No block device supports snapshots\n"); + output_channel_printf(err, "No block device supports snapshots\n"); return; } @@ -1292,20 +1314,20 @@ void do_loadvm(Monitor *mon, const char *name) ret = bdrv_snapshot_goto(bs1, name); if (ret < 0) { if (bs != bs1) - monitor_printf(mon, "Warning: "); + output_channel_printf(err, "Warning: "); switch(ret) { case -ENOTSUP: - monitor_printf(mon, + output_channel_printf(err, "Snapshots not supported on device '%s'\n", bdrv_get_device_name(bs1)); break; case -ENOENT: - monitor_printf(mon, "Could not find snapshot '%s' on " + output_channel_printf(err, "Could not find snapshot '%s' on " "device '%s'\n", name, bdrv_get_device_name(bs1)); break; default: - monitor_printf(mon, "Error %d while activating snapshot on" + output_channel_printf(err, "Error %d while activating snapshot on" " '%s'\n", ret, bdrv_get_device_name(bs1)); break; } @@ -1317,7 +1339,7 @@ void do_loadvm(Monitor *mon, const char *name) } if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) { - monitor_printf(mon, "Device %s does not support VM state snapshots\n", + output_channel_printf(err, "Device %s does not support VM state snapshots\n", bdrv_get_device_name(bs)); return; } @@ -1330,13 +1352,13 @@ void do_loadvm(Monitor *mon, const char *name) /* restore the VM state */ f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 0); if (!f) { - monitor_printf(mon, "Could not open VM state file\n"); + output_channel_printf(err, "Could not open VM state file\n"); goto the_end; } ret = qemu_loadvm_state(f); qemu_fclose(f); if (ret < 0) { - monitor_printf(mon, "Error %d while loading VM state\n", ret); + output_channel_printf(err, "Error %d while loading VM state\n", ret); } the_end: if (saved_vm_running) @@ -1345,12 +1367,18 @@ void do_loadvm(Monitor *mon, const char *name) void do_delvm(Monitor *mon, const char *name) { + OutputChannel *oc = output_channel_alloc(mon, monitor_output_channel_cb); + do_delvm_oc(oc, name); + output_channel_free(oc); +} +void do_delvm_oc(OutputChannel *err, const char *name) +{ BlockDriverState *bs, *bs1; int i, ret; bs = get_bs_snapshots(); if (!bs) { - monitor_printf(mon, "No block device supports snapshots\n"); + output_channel_printf(err, "No block device supports snapshots\n"); return; } @@ -1360,12 +1388,12 @@ void do_delvm(Monitor *mon, const char *name) ret = bdrv_snapshot_delete(bs1, name); if (ret < 0) { if (ret == -ENOTSUP) - monitor_printf(mon, - "Snapshots not supported on device '%s'\n", - bdrv_get_device_name(bs1)); + output_channel_printf(err, + "Snapshots not supported on device '%s'\n", + bdrv_get_device_name(bs1)); else - monitor_printf(mon, "Error %d while deleting snapshot on " - "'%s'\n", ret, bdrv_get_device_name(bs1)); + output_channel_printf(err, "Error %d while deleting snapshot on " + "'%s'\n", ret, bdrv_get_device_name(bs1)); } } } @@ -1373,6 +1401,13 @@ void do_delvm(Monitor *mon, const char *name) void do_info_snapshots(Monitor *mon) { + OutputChannel *oc = output_channel_alloc(mon, monitor_output_channel_cb); + do_info_snapshots_oc(oc, oc); + output_channel_free(oc); +} + +void do_info_snapshots_oc(OutputChannel *out, OutputChannel *err) +{ BlockDriverState *bs, *bs1; QEMUSnapshotInfo *sn_tab, *sn; int nb_sns, i; @@ -1380,30 +1415,30 @@ void do_info_snapshots(Monitor *mon) bs = get_bs_snapshots(); if (!bs) { - monitor_printf(mon, "No available block device supports snapshots\n"); + output_channel_printf(err, "No available block device supports snapshots\n"); return; } - monitor_printf(mon, "Snapshot devices:"); + output_channel_printf(out, "Snapshot devices:"); for(i = 0; i <= nb_drives; i++) { bs1 = drives_table[i].bdrv; if (bdrv_can_snapshot(bs1)) { if (bs == bs1) - monitor_printf(mon, " %s", bdrv_get_device_name(bs1)); + output_channel_printf(out, " %s", bdrv_get_device_name(bs1)); } } - monitor_printf(mon, "\n"); + output_channel_printf(out, "\n"); nb_sns = bdrv_snapshot_list(bs, &sn_tab); if (nb_sns < 0) { - monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns); + output_channel_printf(err, "bdrv_snapshot_list: error %d\n", nb_sns); return; } - monitor_printf(mon, "Snapshot list (from %s):\n", + output_channel_printf(out, "Snapshot list (from %s):\n", bdrv_get_device_name(bs)); - monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); + output_channel_printf(out, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); for(i = 0; i < nb_sns; i++) { sn = &sn_tab[i]; - monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); + output_channel_printf(out, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); } qemu_free(sn_tab); } @@ -8,6 +8,7 @@ #include "qemu-timer.h" #include "qdict.h" #include "qerror.h" +#include "outputchannel.h" #ifdef _WIN32 #include <windows.h> @@ -61,9 +62,13 @@ void qemu_system_powerdown(void); void qemu_system_reset(void); void do_savevm(Monitor *mon, const char *name); +void do_savevm_oc(OutputChannel *err, const char *name); void do_loadvm(Monitor *mon, const char *name); +void do_loadvm_oc(OutputChannel *err, const char *name); void do_delvm(Monitor *mon, const char *name); +void do_delvm_oc(OutputChannel *err, const char *name); void do_info_snapshots(Monitor *mon); +void do_info_snapshots_oc(OutputChannel *out, OutputChannel *err); void qemu_announce_self(void); |