File: drivers.ml

package info (click to toggle)
guestfs-tools 1.52.3-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 69,256 kB
  • sloc: ansic: 15,698; ml: 15,621; sh: 7,396; xml: 5,478; makefile: 3,601; perl: 1,535; lex: 135; yacc: 128; python: 80
file content (334 lines) | stat: -rw-r--r-- 12,587 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
(* virt-drivers
 * Copyright (C) 2009-2023 Red Hat Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *)

open Std_utils
open Tools_utils
open Common_gettext.Gettext
open Getopt.OptionName
open DOM

open Printf

let parse_cmdline () =
  let blocksize = ref 0 in
  let domain = ref None in
  let file = ref None in
  let libvirturi = ref "" in
  let format = ref "auto" in

  let set_file arg =
    if !file <> None then
      error (f_"--add option can only be given once");
    let uri =
      try URI.parse_uri arg
      with URI.Parse_failed ->
        error (f_"error parsing URI '%s'. \
                  Look for error messages printed above.") arg in
    file := Some uri
  and set_domain dom =
    if !domain <> None then
      error (f_"--domain option can only be given once");
    domain := Some dom
  in

  let argspec = [
    [ S 'a'; L"add" ], Getopt.String (s_"file", set_file), s_"Add disk image file";
    [ L"blocksize" ], Getopt.Set_int ("512|4096", blocksize), s_"Set disk sector size";
    [ S 'c'; L"connect" ], Getopt.Set_string (s_"uri", libvirturi), s_"Set libvirt URI";
    [ S 'd'; L"domain" ], Getopt.String (s_"domain", set_domain), s_"Set libvirt guest name";
    [ L"format" ], Getopt.Set_string (s_"format", format), s_"Format of input disk";
  ] in
  let usage_msg =
    sprintf (f_"\
%s: detect bootloader, kernel and drivers inside guest

A short summary of the options is given below.  For detailed help please
read the man page virt-drivers(1).
")
      prog in
  let opthandle = create_standard_options argspec ~key_opts:true usage_msg in
  Getopt.parse opthandle.getopt;

  (* Check -a and -d options. *)
  let file = !file in
  let domain = !domain in
  let libvirturi = match !libvirturi with "" -> None | s -> Some s in
  let add =
    match file, domain with
    | None, None ->
      error (f_"you must give either -a or -d options.  \
                Read virt-drivers(1) man page for further information.")
    | Some _, Some _ ->
      error (f_"you cannot give -a and -d options together.  \
                Read virt-drivers(1) man page for further information.")
    | None, Some dom ->
      fun (g : Guestfs.guestfs) ->
        let readonlydisk = "ignore" (* ignore CDs, data drives *) in
        ignore (g#add_domain
                  ~readonly:true ~allowuuid:true ~readonlydisk
                  ?libvirturi dom)
    | Some uri, None ->
      fun g ->
        let { URI.path; protocol; server; username; password } = uri in
        let format = match !format with "auto" -> None | s -> Some s in
        let blocksize = match !blocksize with 0 -> None | i -> Some i in
        g#add_drive
          ~readonly:true ?blocksize ?format ~protocol ?server ?username
          ?secret:password path
  in

  add, opthandle.ks

let rec do_detection g roots =
  let comment = Comment generated_by in
  let firmware, firmware_xml = do_detect_firmware g in
  let oses = List.map (fun root -> do_detect_os g root firmware) roots in
  let doc : DOM.doc =
    doc "operatingsystems" [] (comment :: firmware_xml @ oses) in
  doc

and do_detect_firmware g =
  let firmware = Firmware.detect_firmware g in
  let xml =
    match firmware with
    | Firmware.I_BIOS ->
       [ e "firmware" ["type", "bios"] [] ]
    | Firmware.I_UEFI esps ->
       List.map (fun esp -> e "firmware" ["type", "uefi"] [ PCData esp ])
         esps in
  firmware, xml

and do_detect_os g root firmware =
  let body = ref [] in

  (* Display some of the standard virt-inspector fields. *)
  List.push_back body (e "root" [] [ PCData root ]);
  let typ = g#inspect_get_type root in
  if typ <> "unknown" then
    List.push_back body (e "name" [] [ PCData typ ]);

  let adds fn field =
    let v = fn root in
    if v <> "unknown" then
      List.push_back body (e field [] [ PCData v ]);
  and addi fn field =
    let v = fn root in
    List.push_back body (e field [] [ PCData (string_of_int v) ]);
  in
  adds g#inspect_get_arch               "arch";
  adds g#inspect_get_distro             "distro";
  adds g#inspect_get_product_name       "product_name";
  adds g#inspect_get_product_variant    "product_variant";
  addi g#inspect_get_major_version      "major_version";
  addi g#inspect_get_minor_version      "minor_version";
  adds g#inspect_get_package_format     "package_format";
  adds g#inspect_get_package_management "package_management";
  adds g#inspect_get_build_id           "build_id";
  adds g#inspect_get_osinfo             "osinfo";

  (* Now mount up the disks in order to detect bootloader and kernels. *)
  let mps = g#inspect_get_mountpoints root in
  let cmp (a,_) (b,_) = compare (String.length a) (String.length b) in
  let mps = List.sort cmp mps in
  List.iter (fun (mp, dev) -> g#mount_ro dev mp) mps;

  (match typ with
   | "linux" ->
      (* XXX This shouldn't be necessary.  Linux_* modules should do it. *)
      g#aug_init "/" 1;
      let bootloader = do_detect_linux_bootloader g root firmware in
      List.push_back body bootloader
   | "windows" ->
      let drivers = do_detect_windows_drivers g root in
      List.push_back body drivers
   | _ -> ()
  );

  g#umount_all ();

  e "operatingsystem" [] !body

and do_detect_linux_bootloader g root firmware =
  let bootloader = Linux_bootloaders.detect_bootloader g root firmware in
  let bl_name = bootloader#name in
  let bl_config = bootloader#get_config_file () in
  let kernels = do_detect_linux_kernels g root bootloader in
  e "bootloader" ["type", bl_name; "config", bl_config] kernels

and do_detect_linux_kernels g root bootloader =
  let apps = g#inspect_list_applications2 root in
  let apps = Array.to_list apps in
  let kernels = Linux_kernels.detect_kernels g root bootloader apps in
  List.map kernel_info_to_xml kernels

and kernel_info_to_xml { Linux_kernels.ki_name; ki_version;
                         ki_arch; ki_vmlinuz; ki_initrd; ki_modpath;
                         ki_modules; ki_supports_virtio_blk;
                         ki_supports_virtio_net; ki_supports_virtio_rng;
                         ki_supports_virtio_balloon;
                         ki_supports_isa_pvpanic;
                         ki_supports_virtio_socket;
                         ki_is_xen_pv_only_kernel;
                         ki_is_debug; ki_config_file } =
  let body = ref [] in
  List.push_back body (e "name" []    [ PCData ki_name ]);
  List.push_back body (e "version" [] [ PCData ki_version ]);
  List.push_back body (e "arch" []    [ PCData ki_arch ]);
  List.push_back body (e "vmlinuz" [] [ PCData ki_vmlinuz ]);
  List.may_push_back body
    (Option.map (fun v -> e "initrd" [] [ PCData v ]) ki_initrd);
  List.push_back body (e "modules_path" [] [ PCData ki_modpath ]);
  List.push_back body (e "modules" []
                         (List.map (fun m -> e "module" [] [ PCData m ])
                            (List.sort compare ki_modules)));
  if ki_supports_virtio_blk then
    List.push_back body (e "supports_virtio_blk" [] []);
  if ki_supports_virtio_net then
    List.push_back body (e "supports_virtio_net" [] []);
  if ki_supports_virtio_rng then
    List.push_back body (e "supports_virtio_rng" [] []);
  if ki_supports_virtio_balloon then
    List.push_back body (e "supports_virtio_balloon" [] []);
  if ki_supports_isa_pvpanic then
    List.push_back body (e "supports_isa_pvpanic" [] []);
  if ki_supports_virtio_socket then
    List.push_back body (e "supports_virtio_socket" [] []);
  if ki_is_xen_pv_only_kernel then
    List.push_back body (e "is_xen_pv_only_kernel" [] []);
  if ki_is_debug then
    List.push_back body (e "debug_kernel" [] []);
  List.may_push_back body
    (Option.map (fun v -> e "config_file" []  [ PCData v ]) ki_config_file);

  e "kernel" [] !body

and do_detect_windows_drivers g root =
  let drivers = Windows_drivers.detect_drivers g root in
  let drivers = List.map windows_driver_to_xml drivers in
  e "drivers" [] drivers

and windows_driver_to_xml { Windows_drivers.name; hwassoc } =
  e "driver" [] (
    e "name" [] [PCData name] :: List.map windows_hardware_to_xml hwassoc
  )

and windows_hardware_to_xml = function
  | Windows_drivers.PCI { pci_class; pci_vendor; pci_device;
                          pci_subsys; pci_rev } ->
     let attrs = ref [] in
     List.may_push_back attrs
       (Option.map (fun v -> ("class", sprintf "%06LX" v)) pci_class);
     List.may_push_back attrs
       (Option.map (fun v -> ("vendor", sprintf "%04LX" v)) pci_vendor);
     let vendorname = get_pci_vendor pci_vendor in
     List.may_push_back attrs
       (Option.map (fun v -> "vendorname", v) vendorname);
     List.may_push_back attrs
       (Option.map (fun v -> ("device", sprintf "%04LX" v)) pci_device);
     let devicename = get_pci_device pci_vendor pci_device in
     List.may_push_back attrs
       (Option.map (fun v -> "devicename", v) devicename);
     List.may_push_back attrs
       (Option.map (fun v -> ("subsystem", sprintf "%08LX" v)) pci_subsys);
     List.may_push_back attrs
       (Option.map (fun v -> ("revision", sprintf "%02LX" v)) pci_rev);
     e "pci" !attrs []

  | HID { hid_vendor; hid_product; hid_rev; hid_col; hid_multi } ->
     let attrs = ref [] in
     List.may_push_back attrs
       (Option.map (fun v -> ("vendor", sprintf "%04LX" v)) hid_vendor);
     List.may_push_back attrs
       (Option.map (fun v -> ("product", sprintf "%04LX" v)) hid_product);
     List.may_push_back attrs
       (Option.map (fun v -> ("revision", sprintf "%02LX" v)) hid_rev);
     List.may_push_back attrs
       (Option.map (fun v -> ("collection", sprintf "%02LX" v)) hid_col);
     List.may_push_back attrs
       (Option.map (fun v -> ("identifier", sprintf "%02LX" v)) hid_multi);
     e "hid" !attrs []

  | USB { usb_vendor; usb_product; usb_rev; usb_multi } ->
     let attrs = ref [] in
     List.may_push_back attrs
       (Option.map (fun v -> ("vendor", sprintf "%04LX" v)) usb_vendor);
     let vendorname = get_usb_vendor usb_vendor in
     List.may_push_back attrs
       (Option.map (fun v -> "vendorname", v) vendorname);
     List.may_push_back attrs
       (Option.map (fun v -> ("product", sprintf "%04LX" v)) usb_product);
     let productname = get_usb_device usb_vendor usb_product in
     List.may_push_back attrs
       (Option.map (fun v -> "productname", v) productname);
     List.may_push_back attrs
       (Option.map (fun v -> ("revision", sprintf "%02LX" v)) usb_rev);
     List.may_push_back attrs
       (Option.map (fun v -> ("identifier", sprintf "%02LX" v)) usb_multi);
     e "usb" !attrs []

  | Other path ->
     Comment (sprintf "unknown DeviceId: %s" (String.concat "\\" path))

and get_pci_vendor v = get_hwdata'1 Hwdata.pci_vendor v
and get_pci_device v d = get_hwdata'2 Hwdata.pci_device v d
and get_usb_vendor v = get_hwdata'1 Hwdata.usb_vendor v
and get_usb_device v d = get_hwdata'2 Hwdata.usb_device v d

and get_hwdata'1 f = function
  | Some i64 when i64 >= 0_L && i64 <= 0xffff_L ->
     let i32 = Int64.to_int32 i64 in
     f i32
  | _ -> None

and get_hwdata'2 f v d =
  match v, d with
  | Some v64, Some d64 when v64 >= 0_L && v64 <= 0xffff_L &&
                            d64 >= 0_L && d64 <= 0xffff_L ->
     let v32 = Int64.to_int32 v64 and d32 = Int64.to_int32 d64 in
     f v32 d32
  | _ -> None

(* Main program. *)
let main () =
  let add, ks = parse_cmdline () in

  (* Connect to libguestfs. *)
  let g = open_guestfs () in
  add g;
  g#set_network (key_store_requires_network ks);
  g#launch ();

  (* Decrypt the disks. *)
  inspect_decrypt g ks;

  let roots = g#inspect_os () in
  let roots = Array.to_list roots in

  (* Can't call inspect_mount_root here (ie. normal processing of
   * the -i option) because it can only handle a single root.
   *)

  (* Do the detection. *)
  let doc : DOM.doc = do_detection g roots in
  DOM.doc_to_chan stdout doc;

  (* Shutdown. *)
  g#shutdown ();
  g#close ()

let () = run_main_and_handle_errors main