inspect: Split code into separate files.
[libguestfs.git] / src / inspect.c
1 /* libguestfs
2  * Copyright (C) 2010-2011 Red Hat Inc.
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 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, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <inttypes.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30 #include <endian.h>
31
32 #ifdef HAVE_PCRE
33 #include <pcre.h>
34 #endif
35
36 #ifdef HAVE_HIVEX
37 #include <hivex.h>
38 #endif
39
40 #include "c-ctype.h"
41 #include "ignore-value.h"
42 #include "xstrtol.h"
43
44 #include "guestfs.h"
45 #include "guestfs-internal.h"
46 #include "guestfs-internal-actions.h"
47 #include "guestfs_protocol.h"
48
49 #if defined(HAVE_PCRE) && defined(HAVE_HIVEX)
50
51 /* The main inspection code. */
52 char **
53 guestfs__inspect_os (guestfs_h *g)
54 {
55   /* Remove any information previously stored in the handle. */
56   guestfs___free_inspect_info (g);
57
58   if (guestfs_umount_all (g) == -1)
59     return NULL;
60
61   /* Iterate over all possible devices.  Try to mount each
62    * (read-only).  Examine ones which contain filesystems and add that
63    * information to the handle.
64    */
65   /* Look to see if any devices directly contain filesystems (RHBZ#590167). */
66   char **devices;
67   devices = guestfs_list_devices (g);
68   if (devices == NULL)
69     return NULL;
70
71   size_t i;
72   for (i = 0; devices[i] != NULL; ++i) {
73     if (guestfs___check_for_filesystem_on (g, devices[i], 1, 0) == -1) {
74       guestfs___free_string_list (devices);
75       guestfs___free_inspect_info (g);
76       return NULL;
77     }
78   }
79   guestfs___free_string_list (devices);
80
81   /* Look at all partitions. */
82   char **partitions;
83   partitions = guestfs_list_partitions (g);
84   if (partitions == NULL) {
85     guestfs___free_inspect_info (g);
86     return NULL;
87   }
88
89   for (i = 0; partitions[i] != NULL; ++i) {
90     if (guestfs___check_for_filesystem_on (g, partitions[i], 0, i+1) == -1) {
91       guestfs___free_string_list (partitions);
92       guestfs___free_inspect_info (g);
93       return NULL;
94     }
95   }
96   guestfs___free_string_list (partitions);
97
98   /* Look at all LVs. */
99   if (guestfs___feature_available (g, "lvm2")) {
100     char **lvs;
101     lvs = guestfs_lvs (g);
102     if (lvs == NULL) {
103       guestfs___free_inspect_info (g);
104       return NULL;
105     }
106
107     for (i = 0; lvs[i] != NULL; ++i) {
108       if (guestfs___check_for_filesystem_on (g, lvs[i], 0, 0) == -1) {
109         guestfs___free_string_list (lvs);
110         guestfs___free_inspect_info (g);
111         return NULL;
112       }
113     }
114     guestfs___free_string_list (lvs);
115   }
116
117   /* At this point we have, in the handle, a list of all filesystems
118    * found and data about each one.  Now we assemble the list of
119    * filesystems which are root devices and return that to the user.
120    * Fall through to guestfs__inspect_get_roots to do that.
121    */
122   char **ret = guestfs__inspect_get_roots (g);
123   if (ret == NULL)
124     guestfs___free_inspect_info (g);
125   return ret;
126 }
127
128 char **
129 guestfs__inspect_get_roots (guestfs_h *g)
130 {
131   /* NB. Doesn't matter if g->nr_fses == 0.  We just return an empty
132    * list in this case.
133    */
134
135   size_t i;
136   size_t count = 0;
137   for (i = 0; i < g->nr_fses; ++i)
138     if (g->fses[i].is_root)
139       count++;
140
141   char **ret = calloc (count+1, sizeof (char *));
142   if (ret == NULL) {
143     perrorf (g, "calloc");
144     return NULL;
145   }
146
147   count = 0;
148   for (i = 0; i < g->nr_fses; ++i) {
149     if (g->fses[i].is_root) {
150       ret[count] = safe_strdup (g, g->fses[i].device);
151       count++;
152     }
153   }
154   ret[count] = NULL;
155
156   return ret;
157 }
158
159 char *
160 guestfs__inspect_get_type (guestfs_h *g, const char *root)
161 {
162   struct inspect_fs *fs = guestfs___search_for_root (g, root);
163   if (!fs)
164     return NULL;
165
166   char *ret;
167   switch (fs->type) {
168   case OS_TYPE_LINUX: ret = safe_strdup (g, "linux"); break;
169   case OS_TYPE_WINDOWS: ret = safe_strdup (g, "windows"); break;
170   case OS_TYPE_FREEBSD: ret = safe_strdup (g, "freebsd"); break;
171   case OS_TYPE_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
172   }
173
174   return ret;
175 }
176
177 char *
178 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
179 {
180   struct inspect_fs *fs = guestfs___search_for_root (g, root);
181   if (!fs)
182     return NULL;
183
184   return safe_strdup (g, fs->arch ? : "unknown");
185 }
186
187 char *
188 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
189 {
190   struct inspect_fs *fs = guestfs___search_for_root (g, root);
191   if (!fs)
192     return NULL;
193
194   char *ret;
195   switch (fs->distro) {
196   case OS_DISTRO_ARCHLINUX: ret = safe_strdup (g, "archlinux"); break;
197   case OS_DISTRO_DEBIAN: ret = safe_strdup (g, "debian"); break;
198   case OS_DISTRO_FEDORA: ret = safe_strdup (g, "fedora"); break;
199   case OS_DISTRO_GENTOO: ret = safe_strdup (g, "gentoo"); break;
200   case OS_DISTRO_LINUX_MINT: ret = safe_strdup (g, "linuxmint"); break;
201   case OS_DISTRO_MANDRIVA: ret = safe_strdup (g, "mandriva"); break;
202   case OS_DISTRO_MEEGO: ret = safe_strdup (g, "meego"); break;
203   case OS_DISTRO_PARDUS: ret = safe_strdup (g, "pardus"); break;
204   case OS_DISTRO_REDHAT_BASED: ret = safe_strdup (g, "redhat-based"); break;
205   case OS_DISTRO_RHEL: ret = safe_strdup (g, "rhel"); break;
206   case OS_DISTRO_SLACKWARE: ret = safe_strdup (g, "slackware"); break;
207   case OS_DISTRO_WINDOWS: ret = safe_strdup (g, "windows"); break;
208   case OS_DISTRO_UBUNTU: ret = safe_strdup (g, "ubuntu"); break;
209   case OS_DISTRO_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
210   }
211
212   return ret;
213 }
214
215 int
216 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
217 {
218   struct inspect_fs *fs = guestfs___search_for_root (g, root);
219   if (!fs)
220     return -1;
221
222   return fs->major_version;
223 }
224
225 int
226 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
227 {
228   struct inspect_fs *fs = guestfs___search_for_root (g, root);
229   if (!fs)
230     return -1;
231
232   return fs->minor_version;
233 }
234
235 char *
236 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
237 {
238   struct inspect_fs *fs = guestfs___search_for_root (g, root);
239   if (!fs)
240     return NULL;
241
242   return safe_strdup (g, fs->product_name ? : "unknown");
243 }
244
245 char *
246 guestfs__inspect_get_product_variant (guestfs_h *g, const char *root)
247 {
248   struct inspect_fs *fs = guestfs___search_for_root (g, root);
249   if (!fs)
250     return NULL;
251
252   return safe_strdup (g, fs->product_variant ? : "unknown");
253 }
254
255 char *
256 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
257 {
258   struct inspect_fs *fs = guestfs___search_for_root (g, root);
259   if (!fs)
260     return NULL;
261
262   if (!fs->windows_systemroot) {
263     error (g, _("not a Windows guest, or systemroot could not be determined"));
264     return NULL;
265   }
266
267   return safe_strdup (g, fs->windows_systemroot);
268 }
269
270 char *
271 guestfs__inspect_get_windows_current_control_set (guestfs_h *g,
272                                                   const char *root)
273 {
274   struct inspect_fs *fs = guestfs___search_for_root (g, root);
275   if (!fs)
276     return NULL;
277
278   if (!fs->windows_current_control_set) {
279     error (g, _("not a Windows guest, or CurrentControlSet could not be determined"));
280     return NULL;
281   }
282
283   return safe_strdup (g, fs->windows_current_control_set);
284 }
285
286 char *
287 guestfs__inspect_get_format (guestfs_h *g, const char *root)
288 {
289   struct inspect_fs *fs = guestfs___search_for_root (g, root);
290   if (!fs)
291     return NULL;
292
293   char *ret;
294   switch (fs->format) {
295   case OS_FORMAT_INSTALLED: ret = safe_strdup (g, "installed"); break;
296   case OS_FORMAT_INSTALLER: ret = safe_strdup (g, "installer"); break;
297   case OS_FORMAT_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
298   }
299
300   return ret;
301 }
302
303 int
304 guestfs__inspect_is_live (guestfs_h *g, const char *root)
305 {
306   struct inspect_fs *fs = guestfs___search_for_root (g, root);
307   if (!fs)
308     return -1;
309
310   return fs->is_live_disk;
311 }
312
313 int
314 guestfs__inspect_is_netinst (guestfs_h *g, const char *root)
315 {
316   struct inspect_fs *fs = guestfs___search_for_root (g, root);
317   if (!fs)
318     return -1;
319
320   return fs->is_netinst_disk;
321 }
322
323 int
324 guestfs__inspect_is_multipart (guestfs_h *g, const char *root)
325 {
326   struct inspect_fs *fs = guestfs___search_for_root (g, root);
327   if (!fs)
328     return -1;
329
330   return fs->is_multipart_disk;
331 }
332
333 char **
334 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
335 {
336   struct inspect_fs *fs = guestfs___search_for_root (g, root);
337   if (!fs)
338     return NULL;
339
340   char **ret;
341
342   /* If no fstab information (Windows) return just the root. */
343   if (fs->nr_fstab == 0) {
344     ret = calloc (3, sizeof (char *));
345     ret[0] = safe_strdup (g, "/");
346     ret[1] = safe_strdup (g, root);
347     ret[2] = NULL;
348     return ret;
349   }
350
351 #define CRITERION fs->fstab[i].mountpoint[0] == '/'
352   size_t i, count = 0;
353   for (i = 0; i < fs->nr_fstab; ++i)
354     if (CRITERION)
355       count++;
356
357   /* Hashtables have 2N+1 entries. */
358   ret = calloc (2*count+1, sizeof (char *));
359   if (ret == NULL) {
360     perrorf (g, "calloc");
361     return NULL;
362   }
363
364   count = 0;
365   for (i = 0; i < fs->nr_fstab; ++i)
366     if (CRITERION) {
367       ret[2*count] = safe_strdup (g, fs->fstab[i].mountpoint);
368       ret[2*count+1] = safe_strdup (g, fs->fstab[i].device);
369       count++;
370     }
371 #undef CRITERION
372
373   return ret;
374 }
375
376 char **
377 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
378 {
379   struct inspect_fs *fs = guestfs___search_for_root (g, root);
380   if (!fs)
381     return NULL;
382
383   char **ret;
384
385   /* If no fstab information (Windows) return just the root. */
386   if (fs->nr_fstab == 0) {
387     ret = calloc (2, sizeof (char *));
388     ret[0] = safe_strdup (g, root);
389     ret[1] = NULL;
390     return ret;
391   }
392
393   ret = calloc (fs->nr_fstab + 1, sizeof (char *));
394   if (ret == NULL) {
395     perrorf (g, "calloc");
396     return NULL;
397   }
398
399   size_t i;
400   for (i = 0; i < fs->nr_fstab; ++i)
401     ret[i] = safe_strdup (g, fs->fstab[i].device);
402
403   return ret;
404 }
405
406 char **
407 guestfs__inspect_get_drive_mappings (guestfs_h *g, const char *root)
408 {
409   char **ret;
410   size_t i, count;
411   struct inspect_fs *fs;
412
413   fs = guestfs___search_for_root (g, root);
414   if (!fs)
415     return NULL;
416
417   /* If no drive mappings, return an empty hashtable. */
418   if (!fs->drive_mappings)
419     count = 0;
420   else {
421     for (count = 0; fs->drive_mappings[count] != NULL; count++)
422       ;
423   }
424
425   ret = calloc (count+1, sizeof (char *));
426   if (ret == NULL) {
427     perrorf (g, "calloc");
428     return NULL;
429   }
430
431   /* We need to make a deep copy of the hashtable since the caller
432    * will free it.
433    */
434   for (i = 0; i < count; ++i)
435     ret[i] = safe_strdup (g, fs->drive_mappings[i]);
436
437   ret[count] = NULL;
438
439   return ret;
440 }
441
442 char *
443 guestfs__inspect_get_package_format (guestfs_h *g, const char *root)
444 {
445   struct inspect_fs *fs = guestfs___search_for_root (g, root);
446   if (!fs)
447     return NULL;
448
449   char *ret;
450   switch (fs->package_format) {
451   case OS_PACKAGE_FORMAT_RPM: ret = safe_strdup (g, "rpm"); break;
452   case OS_PACKAGE_FORMAT_DEB: ret = safe_strdup (g, "deb"); break;
453   case OS_PACKAGE_FORMAT_PACMAN: ret = safe_strdup (g, "pacman"); break;
454   case OS_PACKAGE_FORMAT_EBUILD: ret = safe_strdup (g, "ebuild"); break;
455   case OS_PACKAGE_FORMAT_PISI: ret = safe_strdup (g, "pisi"); break;
456   case OS_PACKAGE_FORMAT_UNKNOWN:
457   default:
458     ret = safe_strdup (g, "unknown");
459     break;
460   }
461
462   return ret;
463 }
464
465 char *
466 guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
467 {
468   struct inspect_fs *fs = guestfs___search_for_root (g, root);
469   if (!fs)
470     return NULL;
471
472   char *ret;
473   switch (fs->package_management) {
474   case OS_PACKAGE_MANAGEMENT_YUM: ret = safe_strdup (g, "yum"); break;
475   case OS_PACKAGE_MANAGEMENT_UP2DATE: ret = safe_strdup (g, "up2date"); break;
476   case OS_PACKAGE_MANAGEMENT_APT: ret = safe_strdup (g, "apt"); break;
477   case OS_PACKAGE_MANAGEMENT_PACMAN: ret = safe_strdup (g, "pacman"); break;
478   case OS_PACKAGE_MANAGEMENT_PORTAGE: ret = safe_strdup (g, "portage"); break;
479   case OS_PACKAGE_MANAGEMENT_PISI: ret = safe_strdup (g, "pisi"); break;
480   case OS_PACKAGE_MANAGEMENT_URPMI: ret = safe_strdup (g, "urpmi"); break;
481   case OS_PACKAGE_MANAGEMENT_UNKNOWN:
482   default:
483     ret = safe_strdup (g, "unknown");
484     break;
485   }
486
487   return ret;
488 }
489
490 char *
491 guestfs__inspect_get_hostname (guestfs_h *g, const char *root)
492 {
493   struct inspect_fs *fs = guestfs___search_for_root (g, root);
494   if (!fs)
495     return NULL;
496
497   return safe_strdup (g, fs->hostname ? : "unknown");
498 }
499
500 /* Download a guest file to a local temporary file.  The file is
501  * downloaded into g->tmpdir, unless it already exists in g->tmpdir.
502  * The final name will be g->tmpdir + "/" + basename.  Refuse to
503  * download the guest file if it is larger than max_size.  The caller
504  * does not need to delete the temporary file after use: it will be
505  * deleted when the handle is cleaned up.
506  */
507 int
508 guestfs___download_to_tmp (guestfs_h *g, const char *filename,
509                            const char *basename, int64_t max_size)
510 {
511   int tmpdirfd, fd, r = -1;
512   char buf[32];
513   int64_t size;
514
515   tmpdirfd = open (g->tmpdir, O_RDONLY);
516   if (tmpdirfd == -1) {
517     perrorf (g, _("%s: temporary directory not found"), g->tmpdir);
518     return -1;
519   }
520
521   /* If the file has already been downloaded, return. */
522   if (faccessat (tmpdirfd, basename, R_OK, 0) == 0) {
523     r = 0;
524     goto out;
525   }
526
527   /* Check size of remote file. */
528   size = guestfs_filesize (g, filename);
529   if (size == -1)
530     /* guestfs_filesize failed and has already set error in handle */
531     goto out;
532   if (size > max_size) {
533     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
534            filename, size);
535     goto out;
536   }
537
538   fd = openat (tmpdirfd, basename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0600);
539   if (fd == -1) {
540     perrorf (g, "openat: %s/%s", g->tmpdir, basename);
541     goto out;
542   }
543
544   snprintf (buf, sizeof buf, "/dev/fd/%d", fd);
545
546   if (guestfs_download (g, filename, buf) == -1) {
547     unlinkat (tmpdirfd, basename, 0);
548     close (fd);
549     goto out;
550   }
551
552   if (close (fd) == -1) {
553     perrorf (g, "close: %s/%s", g->tmpdir, basename);
554     unlinkat (tmpdirfd, basename, 0);
555     goto out;
556   }
557
558   r = 0;
559  out:
560   if (tmpdirfd >= 0)
561     close (tmpdirfd);
562
563   return r;
564 }
565
566 struct inspect_fs *
567 guestfs___search_for_root (guestfs_h *g, const char *root)
568 {
569   if (g->nr_fses == 0) {
570     error (g, _("no inspection data: call guestfs_inspect_os first"));
571     return NULL;
572   }
573
574   size_t i;
575   struct inspect_fs *fs;
576   for (i = 0; i < g->nr_fses; ++i) {
577     fs = &g->fses[i];
578     if (fs->is_root && STREQ (root, fs->device))
579       return fs;
580   }
581
582   error (g, _("%s: root device not found: only call this function with a root device previously returned by guestfs_inspect_os"),
583          root);
584   return NULL;
585 }
586
587 #else /* no PCRE or hivex at compile time */
588
589 /* XXX These functions should be in an optgroup. */
590
591 #define NOT_IMPL(r)                                                     \
592   error (g, _("inspection API not available since this version of libguestfs was compiled without PCRE or hivex libraries")); \
593   return r
594
595 char **
596 guestfs__inspect_os (guestfs_h *g)
597 {
598   NOT_IMPL(NULL);
599 }
600
601 char **
602 guestfs__inspect_get_roots (guestfs_h *g)
603 {
604   NOT_IMPL(NULL);
605 }
606
607 char *
608 guestfs__inspect_get_type (guestfs_h *g, const char *root)
609 {
610   NOT_IMPL(NULL);
611 }
612
613 char *
614 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
615 {
616   NOT_IMPL(NULL);
617 }
618
619 char *
620 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
621 {
622   NOT_IMPL(NULL);
623 }
624
625 int
626 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
627 {
628   NOT_IMPL(-1);
629 }
630
631 int
632 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
633 {
634   NOT_IMPL(-1);
635 }
636
637 char *
638 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
639 {
640   NOT_IMPL(NULL);
641 }
642
643 char *
644 guestfs__inspect_get_product_variant (guestfs_h *g, const char *root)
645 {
646   NOT_IMPL(NULL);
647 }
648
649 char *
650 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
651 {
652   NOT_IMPL(NULL);
653 }
654
655 char *
656 guestfs__inspect_get_windows_current_control_set (guestfs_h *g,
657                                                   const char *root)
658 {
659   NOT_IMPL(NULL);
660 }
661
662 char **
663 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
664 {
665   NOT_IMPL(NULL);
666 }
667
668 char **
669 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
670 {
671   NOT_IMPL(NULL);
672 }
673
674 char **
675 guestfs__inspect_get_drive_mappings (guestfs_h *g, const char *root)
676 {
677   NOT_IMPL(NULL);
678 }
679
680 char *
681 guestfs__inspect_get_package_format (guestfs_h *g, const char *root)
682 {
683   NOT_IMPL(NULL);
684 }
685
686 char *
687 guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
688 {
689   NOT_IMPL(NULL);
690 }
691
692 char *
693 guestfs__inspect_get_hostname (guestfs_h *g, const char *root)
694 {
695   NOT_IMPL(NULL);
696 }
697
698 char *
699 guestfs__inspect_get_format (guestfs_h *g, const char *root)
700 {
701   NOT_IMPL(NULL);
702 }
703
704 int
705 guestfs__inspect_is_live (guestfs_h *g, const char *root)
706 {
707   NOT_IMPL(-1);
708 }
709
710 int
711 guestfs__inspect_is_netinst (guestfs_h *g, const char *root)
712 {
713   NOT_IMPL(-1);
714 }
715
716 int
717 guestfs__inspect_is_multipart (guestfs_h *g, const char *root)
718 {
719   NOT_IMPL(-1);
720 }
721
722 #endif /* no PCRE or hivex at compile time */
723
724 void
725 guestfs___free_inspect_info (guestfs_h *g)
726 {
727   size_t i;
728   for (i = 0; i < g->nr_fses; ++i) {
729     free (g->fses[i].device);
730     free (g->fses[i].product_name);
731     free (g->fses[i].product_variant);
732     free (g->fses[i].arch);
733     free (g->fses[i].hostname);
734     free (g->fses[i].windows_systemroot);
735     free (g->fses[i].windows_current_control_set);
736     size_t j;
737     for (j = 0; j < g->fses[i].nr_fstab; ++j) {
738       free (g->fses[i].fstab[j].device);
739       free (g->fses[i].fstab[j].mountpoint);
740     }
741     free (g->fses[i].fstab);
742     if (g->fses[i].drive_mappings)
743       guestfs___free_string_list (g->fses[i].drive_mappings);
744   }
745   free (g->fses);
746   g->nr_fses = 0;
747   g->fses = NULL;
748 }
749
750 /* In the Perl code this is a public function. */
751 int
752 guestfs___feature_available (guestfs_h *g, const char *feature)
753 {
754   /* If there's an error we should ignore it, so to do that we have to
755    * temporarily replace the error handler with a null one.
756    */
757   guestfs_error_handler_cb old_error_cb = g->error_cb;
758   g->error_cb = NULL;
759
760   const char *groups[] = { feature, NULL };
761   int r = guestfs_available (g, (char * const *) groups);
762
763   g->error_cb = old_error_cb;
764
765   return r == 0 ? 1 : 0;
766 }