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