]>
Commit | Line | Data |
---|---|---|
6f338c34 | 1 | /* |
a2dde2f2 | 2 | * Blockdev HMP commands |
6f338c34 | 3 | * |
fce2b91f ML |
4 | * Authors: |
5 | * Anthony Liguori <aliguori@us.ibm.com> | |
6 | * | |
a1edae27 ML |
7 | * Copyright (c) 2003-2008 Fabrice Bellard |
8 | * | |
fce2b91f ML |
9 | * This work is licensed under the terms of the GNU GPL, version 2. |
10 | * See the COPYING file in the top-level directory. | |
11 | * Contributions after 2012-01-13 are licensed under the terms of the | |
12 | * GNU GPL, version 2 or (at your option) any later version. | |
a1edae27 ML |
13 | * |
14 | * This file incorporates work covered by the following copyright and | |
15 | * permission notice: | |
16 | * | |
17 | * Copyright (c) 2003-2008 Fabrice Bellard | |
6f338c34 AL |
18 | * |
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
20 | * of this software and associated documentation files (the "Software"), to deal | |
21 | * in the Software without restriction, including without limitation the rights | |
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
23 | * copies of the Software, and to permit persons to whom the Software is | |
24 | * furnished to do so, subject to the following conditions: | |
25 | * | |
26 | * The above copyright notice and this permission notice shall be included in | |
27 | * all copies or substantial portions of the Software. | |
28 | * | |
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
32 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
35 | * THE SOFTWARE. | |
36 | */ | |
37 | ||
d38ea87a | 38 | #include "qemu/osdep.h" |
1559e0d4 | 39 | #include "hw/boards.h" |
b9fe8a7a | 40 | #include "sysemu/block-backend.h" |
9c17d615 | 41 | #include "sysemu/blockdev.h" |
a1edae27 | 42 | #include "qapi/qapi-commands-block.h" |
452fcdbc | 43 | #include "qapi/qmp/qdict.h" |
c4f26c9f | 44 | #include "qapi/error.h" |
0932e3f2 | 45 | #include "qapi/qmp/qerror.h" |
1de7afc9 | 46 | #include "qemu/config-file.h" |
922a01a0 | 47 | #include "qemu/option.h" |
9c17d615 | 48 | #include "sysemu/sysemu.h" |
83c9089e | 49 | #include "monitor/monitor.h" |
0932e3f2 | 50 | #include "monitor/hmp.h" |
abb21ac3 | 51 | #include "block/block_int.h" |
a2dde2f2 | 52 | #include "block/block-hmp-cmds.h" |
6f338c34 | 53 | |
6700d3d6 | 54 | void hmp_drive_add(Monitor *mon, const QDict *qdict) |
6f338c34 | 55 | { |
c4f26c9f | 56 | Error *err = NULL; |
751c6a17 | 57 | DriveInfo *dinfo; |
9dfd7c7a | 58 | QemuOpts *opts; |
0056ae24 | 59 | MachineClass *mc; |
6700d3d6 ML |
60 | const char *optstr = qdict_get_str(qdict, "opts"); |
61 | bool node = qdict_get_try_bool(qdict, "node", false); | |
62 | ||
63 | if (node) { | |
64 | hmp_drive_add_node(mon, optstr); | |
65 | return; | |
66 | } | |
6f338c34 | 67 | |
2292ddae | 68 | opts = drive_def(optstr); |
9dfd7c7a | 69 | if (!opts) |
6700d3d6 | 70 | return; |
6f338c34 | 71 | |
0056ae24 | 72 | mc = MACHINE_GET_CLASS(current_machine); |
c4f26c9f | 73 | dinfo = drive_new(opts, mc->block_default_type, &err); |
ab638171 | 74 | if (err) { |
c4f26c9f | 75 | error_report_err(err); |
9dfd7c7a | 76 | qemu_opts_del(opts); |
6700d3d6 | 77 | goto err; |
abb21ac3 | 78 | } |
dd97aa8a | 79 | |
dd97aa8a | 80 | if (!dinfo) { |
6700d3d6 | 81 | return; |
dd97aa8a | 82 | } |
dd97aa8a | 83 | |
4dbd84e2 | 84 | switch (dinfo->type) { |
dd97aa8a AG |
85 | case IF_NONE: |
86 | monitor_printf(mon, "OK\n"); | |
87 | break; | |
88 | default: | |
f51074cd MA |
89 | monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); |
90 | goto err; | |
dd97aa8a AG |
91 | } |
92 | return; | |
93 | ||
94 | err: | |
95 | if (dinfo) { | |
efaa7c4e HR |
96 | BlockBackend *blk = blk_by_legacy_dinfo(dinfo); |
97 | monitor_remove_blk(blk); | |
98 | blk_unref(blk); | |
dd97aa8a | 99 | } |
dd97aa8a | 100 | } |
a1edae27 ML |
101 | |
102 | void hmp_drive_del(Monitor *mon, const QDict *qdict) | |
103 | { | |
104 | const char *id = qdict_get_str(qdict, "id"); | |
105 | BlockBackend *blk; | |
106 | BlockDriverState *bs; | |
107 | AioContext *aio_context; | |
108 | Error *local_err = NULL; | |
109 | ||
110 | bs = bdrv_find_node(id); | |
111 | if (bs) { | |
112 | qmp_blockdev_del(id, &local_err); | |
113 | if (local_err) { | |
114 | error_report_err(local_err); | |
115 | } | |
116 | return; | |
117 | } | |
118 | ||
119 | blk = blk_by_name(id); | |
120 | if (!blk) { | |
121 | error_report("Device '%s' not found", id); | |
122 | return; | |
123 | } | |
124 | ||
125 | if (!blk_legacy_dinfo(blk)) { | |
126 | error_report("Deleting device added with blockdev-add" | |
127 | " is not supported"); | |
128 | return; | |
129 | } | |
130 | ||
131 | aio_context = blk_get_aio_context(blk); | |
132 | aio_context_acquire(aio_context); | |
133 | ||
134 | bs = blk_bs(blk); | |
135 | if (bs) { | |
136 | if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { | |
137 | error_report_err(local_err); | |
138 | aio_context_release(aio_context); | |
139 | return; | |
140 | } | |
141 | ||
142 | blk_remove_bs(blk); | |
143 | } | |
144 | ||
145 | /* Make the BlockBackend and the attached BlockDriverState anonymous */ | |
146 | monitor_remove_blk(blk); | |
147 | ||
148 | /* | |
149 | * If this BlockBackend has a device attached to it, its refcount will be | |
150 | * decremented when the device is removed; otherwise we have to do so here. | |
151 | */ | |
152 | if (blk_get_attached_dev(blk)) { | |
153 | /* Further I/O must not pause the guest */ | |
154 | blk_set_on_error(blk, BLOCKDEV_ON_ERROR_REPORT, | |
155 | BLOCKDEV_ON_ERROR_REPORT); | |
156 | } else { | |
157 | blk_unref(blk); | |
158 | } | |
159 | ||
160 | aio_context_release(aio_context); | |
161 | } | |
162 | ||
163 | void hmp_commit(Monitor *mon, const QDict *qdict) | |
164 | { | |
165 | const char *device = qdict_get_str(qdict, "device"); | |
166 | BlockBackend *blk; | |
167 | int ret; | |
168 | ||
169 | if (!strcmp(device, "all")) { | |
170 | ret = blk_commit_all(); | |
171 | } else { | |
172 | BlockDriverState *bs; | |
173 | AioContext *aio_context; | |
174 | ||
175 | blk = blk_by_name(device); | |
176 | if (!blk) { | |
177 | error_report("Device '%s' not found", device); | |
178 | return; | |
179 | } | |
180 | if (!blk_is_available(blk)) { | |
181 | error_report("Device '%s' has no medium", device); | |
182 | return; | |
183 | } | |
184 | ||
185 | bs = blk_bs(blk); | |
186 | aio_context = bdrv_get_aio_context(bs); | |
187 | aio_context_acquire(aio_context); | |
188 | ||
189 | ret = bdrv_commit(bs); | |
190 | ||
191 | aio_context_release(aio_context); | |
192 | } | |
193 | if (ret < 0) { | |
194 | error_report("'commit' error for '%s': %s", device, strerror(-ret)); | |
195 | } | |
196 | } | |
0932e3f2 ML |
197 | |
198 | void hmp_drive_mirror(Monitor *mon, const QDict *qdict) | |
199 | { | |
200 | const char *filename = qdict_get_str(qdict, "target"); | |
201 | const char *format = qdict_get_try_str(qdict, "format"); | |
202 | bool reuse = qdict_get_try_bool(qdict, "reuse", false); | |
203 | bool full = qdict_get_try_bool(qdict, "full", false); | |
204 | Error *err = NULL; | |
205 | DriveMirror mirror = { | |
206 | .device = (char *)qdict_get_str(qdict, "device"), | |
207 | .target = (char *)filename, | |
208 | .has_format = !!format, | |
209 | .format = (char *)format, | |
210 | .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, | |
211 | .has_mode = true, | |
212 | .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS, | |
213 | .unmap = true, | |
214 | }; | |
215 | ||
216 | if (!filename) { | |
217 | error_setg(&err, QERR_MISSING_PARAMETER, "target"); | |
218 | hmp_handle_error(mon, err); | |
219 | return; | |
220 | } | |
221 | qmp_drive_mirror(&mirror, &err); | |
222 | hmp_handle_error(mon, err); | |
223 | } | |
224 | ||
225 | void hmp_drive_backup(Monitor *mon, const QDict *qdict) | |
226 | { | |
227 | const char *device = qdict_get_str(qdict, "device"); | |
228 | const char *filename = qdict_get_str(qdict, "target"); | |
229 | const char *format = qdict_get_try_str(qdict, "format"); | |
230 | bool reuse = qdict_get_try_bool(qdict, "reuse", false); | |
231 | bool full = qdict_get_try_bool(qdict, "full", false); | |
232 | bool compress = qdict_get_try_bool(qdict, "compress", false); | |
233 | Error *err = NULL; | |
234 | DriveBackup backup = { | |
235 | .device = (char *)device, | |
236 | .target = (char *)filename, | |
237 | .has_format = !!format, | |
238 | .format = (char *)format, | |
239 | .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, | |
240 | .has_mode = true, | |
241 | .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS, | |
242 | .has_compress = !!compress, | |
243 | .compress = compress, | |
244 | }; | |
245 | ||
246 | if (!filename) { | |
247 | error_setg(&err, QERR_MISSING_PARAMETER, "target"); | |
248 | hmp_handle_error(mon, err); | |
249 | return; | |
250 | } | |
251 | ||
252 | qmp_drive_backup(&backup, &err); | |
253 | hmp_handle_error(mon, err); | |
254 | } | |
6b7fbf61 ML |
255 | |
256 | void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict) | |
257 | { | |
258 | Error *error = NULL; | |
259 | const char *device = qdict_get_str(qdict, "device"); | |
260 | int64_t value = qdict_get_int(qdict, "speed"); | |
261 | ||
262 | qmp_block_job_set_speed(device, value, &error); | |
263 | ||
264 | hmp_handle_error(mon, error); | |
265 | } | |
266 | ||
267 | void hmp_block_job_cancel(Monitor *mon, const QDict *qdict) | |
268 | { | |
269 | Error *error = NULL; | |
270 | const char *device = qdict_get_str(qdict, "device"); | |
271 | bool force = qdict_get_try_bool(qdict, "force", false); | |
272 | ||
273 | qmp_block_job_cancel(device, true, force, &error); | |
274 | ||
275 | hmp_handle_error(mon, error); | |
276 | } | |
277 | ||
278 | void hmp_block_job_pause(Monitor *mon, const QDict *qdict) | |
279 | { | |
280 | Error *error = NULL; | |
281 | const char *device = qdict_get_str(qdict, "device"); | |
282 | ||
283 | qmp_block_job_pause(device, &error); | |
284 | ||
285 | hmp_handle_error(mon, error); | |
286 | } | |
287 | ||
288 | void hmp_block_job_resume(Monitor *mon, const QDict *qdict) | |
289 | { | |
290 | Error *error = NULL; | |
291 | const char *device = qdict_get_str(qdict, "device"); | |
292 | ||
293 | qmp_block_job_resume(device, &error); | |
294 | ||
295 | hmp_handle_error(mon, error); | |
296 | } | |
297 | ||
298 | void hmp_block_job_complete(Monitor *mon, const QDict *qdict) | |
299 | { | |
300 | Error *error = NULL; | |
301 | const char *device = qdict_get_str(qdict, "device"); | |
302 | ||
303 | qmp_block_job_complete(device, &error); | |
304 | ||
305 | hmp_handle_error(mon, error); | |
306 | } | |
fce2b91f ML |
307 | |
308 | void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict) | |
309 | { | |
310 | const char *device = qdict_get_str(qdict, "device"); | |
311 | const char *filename = qdict_get_try_str(qdict, "snapshot-file"); | |
312 | const char *format = qdict_get_try_str(qdict, "format"); | |
313 | bool reuse = qdict_get_try_bool(qdict, "reuse", false); | |
314 | enum NewImageMode mode; | |
315 | Error *err = NULL; | |
316 | ||
317 | if (!filename) { | |
318 | /* | |
319 | * In the future, if 'snapshot-file' is not specified, the snapshot | |
320 | * will be taken internally. Today it's actually required. | |
321 | */ | |
322 | error_setg(&err, QERR_MISSING_PARAMETER, "snapshot-file"); | |
323 | hmp_handle_error(mon, err); | |
324 | return; | |
325 | } | |
326 | ||
327 | mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS; | |
328 | qmp_blockdev_snapshot_sync(true, device, false, NULL, | |
329 | filename, false, NULL, | |
330 | !!format, format, | |
331 | true, mode, &err); | |
332 | hmp_handle_error(mon, err); | |
333 | } | |
334 | ||
335 | void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict) | |
336 | { | |
337 | const char *device = qdict_get_str(qdict, "device"); | |
338 | const char *name = qdict_get_str(qdict, "name"); | |
339 | Error *err = NULL; | |
340 | ||
341 | qmp_blockdev_snapshot_internal_sync(device, name, &err); | |
342 | hmp_handle_error(mon, err); | |
343 | } | |
344 | ||
345 | void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict) | |
346 | { | |
347 | const char *device = qdict_get_str(qdict, "device"); | |
348 | const char *name = qdict_get_str(qdict, "name"); | |
349 | const char *id = qdict_get_try_str(qdict, "id"); | |
350 | Error *err = NULL; | |
351 | ||
352 | qmp_blockdev_snapshot_delete_internal_sync(device, !!id, id, | |
353 | true, name, &err); | |
354 | hmp_handle_error(mon, err); | |
355 | } |