1 /*
2 * virsh-util.c: helpers for virsh
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see
16 * <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include "virsh-util.h"
22
23 #include "virfile.h"
24 #include "virstring.h"
25 #include "viralloc.h"
26 #include "virxml.h"
27
28 static virDomainPtr
virshLookupDomainInternal(vshControl * ctl,const char * cmdname,const char * name,unsigned int flags)29 virshLookupDomainInternal(vshControl *ctl,
30 const char *cmdname,
31 const char *name,
32 unsigned int flags)
33 {
34 virDomainPtr dom = NULL;
35 int id;
36 virshControl *priv = ctl->privData;
37
38 virCheckFlags(VIRSH_BYID | VIRSH_BYUUID | VIRSH_BYNAME, NULL);
39
40 /* try it by ID */
41 if (flags & VIRSH_BYID) {
42 if (virStrToLong_i(name, NULL, 10, &id) == 0 && id >= 0) {
43 vshDebug(ctl, VSH_ERR_DEBUG, "%s: <domain> looks like ID\n",
44 cmdname);
45 dom = virDomainLookupByID(priv->conn, id);
46 }
47 }
48
49 /* try it by UUID */
50 if (!dom && (flags & VIRSH_BYUUID) &&
51 strlen(name) == VIR_UUID_STRING_BUFLEN-1) {
52 vshDebug(ctl, VSH_ERR_DEBUG, "%s: <domain> trying as domain UUID\n",
53 cmdname);
54 dom = virDomainLookupByUUIDString(priv->conn, name);
55 }
56
57 /* try it by NAME */
58 if (!dom && (flags & VIRSH_BYNAME)) {
59 vshDebug(ctl, VSH_ERR_DEBUG, "%s: <domain> trying as domain NAME\n",
60 cmdname);
61 dom = virDomainLookupByName(priv->conn, name);
62 }
63
64 vshResetLibvirtError();
65
66 if (!dom)
67 vshError(ctl, _("failed to get domain '%s'"), name);
68
69 return dom;
70 }
71
72
73 virDomainPtr
virshLookupDomainBy(vshControl * ctl,const char * name,unsigned int flags)74 virshLookupDomainBy(vshControl *ctl,
75 const char *name,
76 unsigned int flags)
77 {
78 return virshLookupDomainInternal(ctl, "unknown", name, flags);
79 }
80
81
82 virDomainPtr
virshCommandOptDomainBy(vshControl * ctl,const vshCmd * cmd,const char ** name,unsigned int flags)83 virshCommandOptDomainBy(vshControl *ctl,
84 const vshCmd *cmd,
85 const char **name,
86 unsigned int flags)
87 {
88 const char *n = NULL;
89 const char *optname = "domain";
90
91 if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0)
92 return NULL;
93
94 vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n",
95 cmd->def->name, optname, n);
96
97 if (name)
98 *name = n;
99
100 return virshLookupDomainInternal(ctl, cmd->def->name, n, flags);
101 }
102
103
104 virDomainPtr
virshCommandOptDomain(vshControl * ctl,const vshCmd * cmd,const char ** name)105 virshCommandOptDomain(vshControl *ctl,
106 const vshCmd *cmd,
107 const char **name)
108 {
109 return virshCommandOptDomainBy(ctl, cmd, name,
110 VIRSH_BYID | VIRSH_BYUUID | VIRSH_BYNAME);
111 }
112
113
114 int
virshDomainState(vshControl * ctl,virDomainPtr dom,int * reason)115 virshDomainState(vshControl *ctl,
116 virDomainPtr dom,
117 int *reason)
118 {
119 virDomainInfo info;
120 virshControl *priv = ctl->privData;
121
122 if (reason)
123 *reason = -1;
124
125 if (!priv->useGetInfo) {
126 int state;
127 if (virDomainGetState(dom, &state, reason, 0) < 0) {
128 if (virGetLastErrorCode() == VIR_ERR_NO_SUPPORT)
129 priv->useGetInfo = true;
130 else
131 return -1;
132 } else {
133 return state;
134 }
135 }
136
137 /* fall back to virDomainGetInfo if virDomainGetState is not supported */
138 if (virDomainGetInfo(dom, &info) < 0)
139 return -1;
140 return info.state;
141 }
142
143
144 int
virshStreamSink(virStreamPtr st G_GNUC_UNUSED,const char * bytes,size_t nbytes,void * opaque)145 virshStreamSink(virStreamPtr st G_GNUC_UNUSED,
146 const char *bytes,
147 size_t nbytes,
148 void *opaque)
149 {
150 virshStreamCallbackData *cbData = opaque;
151
152 return safewrite(cbData->fd, bytes, nbytes);
153 }
154
155
156 int
virshStreamSource(virStreamPtr st G_GNUC_UNUSED,char * bytes,size_t nbytes,void * opaque)157 virshStreamSource(virStreamPtr st G_GNUC_UNUSED,
158 char *bytes,
159 size_t nbytes,
160 void *opaque)
161 {
162 virshStreamCallbackData *cbData = opaque;
163 int fd = cbData->fd;
164
165 return saferead(fd, bytes, nbytes);
166 }
167
168
169 int
virshStreamSourceSkip(virStreamPtr st G_GNUC_UNUSED,long long offset,void * opaque)170 virshStreamSourceSkip(virStreamPtr st G_GNUC_UNUSED,
171 long long offset,
172 void *opaque)
173 {
174 virshStreamCallbackData *cbData = opaque;
175 int fd = cbData->fd;
176
177 if (lseek(fd, offset, SEEK_CUR) == (off_t) -1)
178 return -1;
179
180 return 0;
181 }
182
183
184 int
virshStreamSkip(virStreamPtr st G_GNUC_UNUSED,long long offset,void * opaque)185 virshStreamSkip(virStreamPtr st G_GNUC_UNUSED,
186 long long offset,
187 void *opaque)
188 {
189 virshStreamCallbackData *cbData = opaque;
190 off_t cur;
191
192 if (cbData->isBlock) {
193 g_autofree char * buf = NULL;
194 const size_t buflen = 1 * 1024 * 1024; /* 1MiB */
195
196 /* While for files it's enough to lseek() and ftruncate() to create
197 * a hole which would emulate zeroes on read(), for block devices
198 * we have to write zeroes to read() zeroes. And we have to write
199 * @got bytes of zeroes. Do that in smaller chunks though.*/
200
201 buf = g_new0(char, buflen);
202
203 while (offset) {
204 size_t count = MIN(offset, buflen);
205 ssize_t r;
206
207 if ((r = safewrite(cbData->fd, buf, count)) < 0)
208 return -1;
209
210 offset -= r;
211 }
212 } else {
213 if ((cur = lseek(cbData->fd, offset, SEEK_CUR)) == (off_t) -1)
214 return -1;
215
216 if (ftruncate(cbData->fd, cur) < 0)
217 return -1;
218 }
219
220 return 0;
221 }
222
223
224 int
virshStreamInData(virStreamPtr st G_GNUC_UNUSED,int * inData,long long * offset,void * opaque)225 virshStreamInData(virStreamPtr st G_GNUC_UNUSED,
226 int *inData,
227 long long *offset,
228 void *opaque)
229 {
230 virshStreamCallbackData *cbData = opaque;
231 vshControl *ctl = cbData->ctl;
232 int fd = cbData->fd;
233
234 if (cbData->isBlock) {
235 /* Block devices are always in data section by definition. The
236 * @sectionLen is slightly more tricky. While we could try and get
237 * how much bytes is there left until EOF, we can pretend there is
238 * always X bytes left and let the saferead() below hit EOF (which
239 * is then handled gracefully anyway). Worst case scenario, this
240 * branch is called more than once.
241 * X was chosen to be 1MiB but it has ho special meaning. */
242 *inData = 1;
243 *offset = 1 * 1024 * 1024;
244 } else {
245 if (virFileInData(fd, inData, offset) < 0) {
246 vshError(ctl, "%s", _("Unable to get current position in stream"));
247 return -1;
248 }
249 }
250
251 return 0;
252 }
253
254
255 void
virshDomainFree(virDomainPtr dom)256 virshDomainFree(virDomainPtr dom)
257 {
258 if (!dom)
259 return;
260
261 vshSaveLibvirtHelperError();
262 virDomainFree(dom); /* sc_prohibit_obj_free_apis_in_virsh */
263 }
264
265
266 void
virshDomainCheckpointFree(virDomainCheckpointPtr chk)267 virshDomainCheckpointFree(virDomainCheckpointPtr chk)
268 {
269 if (!chk)
270 return;
271
272 vshSaveLibvirtHelperError();
273 virDomainCheckpointFree(chk); /* sc_prohibit_obj_free_apis_in_virsh */
274 }
275
276
277 void
virshDomainSnapshotFree(virDomainSnapshotPtr snap)278 virshDomainSnapshotFree(virDomainSnapshotPtr snap)
279 {
280 if (!snap)
281 return;
282
283 vshSaveLibvirtHelperError();
284 virDomainSnapshotFree(snap); /* sc_prohibit_obj_free_apis_in_virsh */
285 }
286
287
288 void
virshInterfaceFree(virInterfacePtr iface)289 virshInterfaceFree(virInterfacePtr iface)
290 {
291 if (!iface)
292 return;
293
294 vshSaveLibvirtHelperError();
295 virInterfaceFree(iface); /* sc_prohibit_obj_free_apis_in_virsh */
296 }
297
298
299 void
virshNetworkFree(virNetworkPtr network)300 virshNetworkFree(virNetworkPtr network)
301 {
302 if (!network)
303 return;
304
305 vshSaveLibvirtHelperError();
306 virNetworkFree(network); /* sc_prohibit_obj_free_apis_in_virsh */
307 }
308
309
310 void
virshNodeDeviceFree(virNodeDevicePtr device)311 virshNodeDeviceFree(virNodeDevicePtr device)
312 {
313 if (!device)
314 return;
315
316 vshSaveLibvirtHelperError();
317 virNodeDeviceFree(device); /* sc_prohibit_obj_free_apis_in_virsh */
318 }
319
320
321 void
virshNWFilterFree(virNWFilterPtr nwfilter)322 virshNWFilterFree(virNWFilterPtr nwfilter)
323 {
324 if (!nwfilter)
325 return;
326
327 vshSaveLibvirtHelperError();
328 virNWFilterFree(nwfilter); /* sc_prohibit_obj_free_apis_in_virsh */
329 }
330
331
332 void
virshSecretFree(virSecretPtr secret)333 virshSecretFree(virSecretPtr secret)
334 {
335 if (!secret)
336 return;
337
338 vshSaveLibvirtHelperError();
339 virSecretFree(secret); /* sc_prohibit_obj_free_apis_in_virsh */
340 }
341
342
343 void
virshStoragePoolFree(virStoragePoolPtr pool)344 virshStoragePoolFree(virStoragePoolPtr pool)
345 {
346 if (!pool)
347 return;
348
349 vshSaveLibvirtHelperError();
350 virStoragePoolFree(pool); /* sc_prohibit_obj_free_apis_in_virsh */
351 }
352
353
354 void
virshStorageVolFree(virStorageVolPtr vol)355 virshStorageVolFree(virStorageVolPtr vol)
356 {
357 if (!vol)
358 return;
359
360 vshSaveLibvirtHelperError();
361 virStorageVolFree(vol); /* sc_prohibit_obj_free_apis_in_virsh */
362 }
363
364
365
366 void
virshStreamFree(virStreamPtr stream)367 virshStreamFree(virStreamPtr stream)
368 {
369 if (!stream)
370 return;
371
372 vshSaveLibvirtHelperError();
373 virStreamFree(stream); /* sc_prohibit_obj_free_apis_in_virsh */
374 }
375
376
377 int
virshDomainGetXMLFromDom(vshControl * ctl,virDomainPtr dom,unsigned int flags,xmlDocPtr * xml,xmlXPathContextPtr * ctxt)378 virshDomainGetXMLFromDom(vshControl *ctl,
379 virDomainPtr dom,
380 unsigned int flags,
381 xmlDocPtr *xml,
382 xmlXPathContextPtr *ctxt)
383 {
384 g_autofree char *desc = NULL;
385
386 if (!(desc = virDomainGetXMLDesc(dom, flags))) {
387 vshError(ctl, _("Failed to get domain description xml"));
388 return -1;
389 }
390
391 *xml = virXMLParseStringCtxt(desc, _("(domain_definition)"), ctxt);
392
393 if (!(*xml)) {
394 vshError(ctl, _("Failed to parse domain description xml"));
395 return -1;
396 }
397
398 return 0;
399 }
400
401
402 int
virshDomainGetXML(vshControl * ctl,const vshCmd * cmd,unsigned int flags,xmlDocPtr * xml,xmlXPathContextPtr * ctxt)403 virshDomainGetXML(vshControl *ctl,
404 const vshCmd *cmd,
405 unsigned int flags,
406 xmlDocPtr *xml,
407 xmlXPathContextPtr *ctxt)
408 {
409 virDomainPtr dom;
410 int ret;
411
412 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
413 return -1;
414
415 ret = virshDomainGetXMLFromDom(ctl, dom, flags, xml, ctxt);
416
417 virshDomainFree(dom);
418
419 return ret;
420 }
421