File: sysprep_operation.ml

package info (click to toggle)
libguestfs 1%3A1.44.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 118,932 kB
  • sloc: ansic: 458,017; ml: 51,424; sh: 13,191; java: 9,578; makefile: 7,931; cs: 6,328; haskell: 5,674; python: 3,871; perl: 3,528; erlang: 2,446; xml: 1,347; ruby: 350; pascal: 257; javascript: 157; lex: 135; yacc: 128; cpp: 10
file content (321 lines) | stat: -rw-r--r-- 9,465 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
(* virt-sysprep
 * Copyright (C) 2012 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 Printf

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

class filesystem_side_effects =
object
  val mutable m_created_file = false
  val mutable m_changed_file = false
  val mutable m_update_system_ca_store = false
  method created_file () = m_created_file <- true
  method get_created_file = m_created_file
  method changed_file () = m_changed_file <- true
  method get_changed_file = m_changed_file
  method update_system_ca_store () = m_update_system_ca_store <- true
  method get_update_system_ca_store = m_update_system_ca_store
end

class device_side_effects = object end

type 'a callback = Guestfs.guestfs -> string -> 'a -> unit

type operation = {
  order : int;
  name : string;
  enabled_by_default : bool;
  heading : string;
  pod_description : string option;
  pod_notes : string option;
  extra_args : extra_arg list;
  not_enabled_check_args : unit -> unit;
  perform_on_filesystems : filesystem_side_effects callback option;
  perform_on_devices : device_side_effects callback option;
}
and extra_arg = {
  extra_argspec : Getopt.keys * Getopt.spec * Getopt.doc;
  extra_pod_argval : string option;
  extra_pod_description : string;
}

let defaults = {
  order = 0;
  name = "";
  enabled_by_default = false;
  heading = "";
  pod_description = None;
  pod_notes = None;
  extra_args = [];
  not_enabled_check_args = (fun () -> ());
  perform_on_filesystems = None;
  perform_on_devices = None;
}

let all_operations = ref []
let enabled_by_default_operations = ref []

module OperationSet = Set.Make (
  struct
    type t = operation
    let compare a b = compare a.name b.name
  end
)
type set = OperationSet.t

let empty_set = OperationSet.empty

let opset_of_oplist li =
  List.fold_left (
    fun acc elem ->
      OperationSet.add elem acc
  ) empty_set li

let add_to_set name set =
  let op = List.find (fun { name = n } -> name = n) !all_operations in
  OperationSet.add op set

let add_defaults_to_set set =
  OperationSet.union set (opset_of_oplist !enabled_by_default_operations)

let add_all_to_set set =
  opset_of_oplist !all_operations

let remove_from_set name set =
  let name_filter = fun { name = n } -> name = n in
  if List.exists name_filter !all_operations <> true then
    raise Not_found;
  OperationSet.diff set (OperationSet.filter name_filter set)

let remove_defaults_from_set set =
  OperationSet.diff set (opset_of_oplist !enabled_by_default_operations)

let remove_all_from_set set =
  empty_set

let register_operation op =
  List.push_front op all_operations;
  if op.enabled_by_default then
    List.push_front op enabled_by_default_operations

let baked = ref false
let rec bake () =
  (* Note we actually want all_operations to be sorted by name,
   * ignoring the order field.
   *)
  let ops =
    List.sort (fun { name = a } { name = b } -> compare a b) !all_operations in
  check_no_dupes ops;
  List.iter check ops;
  all_operations := ops;
  baked := true
and check_no_dupes ops =
  ignore (
    List.fold_left (
      fun opset op ->
        if OperationSet.mem op opset then
          error (f_"duplicate operation name (%s)") op.name;
        add_to_set op.name opset
    ) empty_set ops
  )
and check op =
  let n = String.length op.name in
  if n = 0 then
    error (f_"operation name is an empty string");
  for i = 0 to n-1 do
    match String.unsafe_get op.name i with
    | 'a'..'z' | 'A'..'Z' | '0'..'9' | '-' -> ()
    | c ->
      error (f_"disallowed character (%c) in operation name") c
  done;
  let n = String.length op.heading in
  if n = 0 then
    error (f_"operation %s has no heading") op.name;
  if op.heading.[n-1] = '\n' || op.heading.[n-1] = '.' then
    error (f_"heading for %s must not end with newline or period") op.name;
  (match op.pod_description with
  | None -> ()
  | Some description ->
    let n = String.length description in
    if n = 0 then
      error (f_"operation %s has no POD") op.name;
    if description.[n-1] = '\n' then
      error (f_"POD for %s must not end with newline") op.name;
  );
  (match op.pod_notes with
  | None -> ()
  | Some notes ->
    let n = String.length notes in
    if n = 0 then
      error (f_"operation %s has no POD notes") op.name;
    if notes.[n-1] = '\n' then
      error (f_"POD notes for %s must not end with newline") op.name;
  )

let extra_args () =
  assert !baked;

  List.flatten (
    List.map (fun { extra_args } ->
      List.map (fun { extra_argspec } -> extra_argspec) extra_args
    ) !all_operations
  )

(* These internal functions are used to generate the man page. *)
let dump_pod () =
  assert !baked;

  List.iter (
    fun op ->
      printf "=head2 B<%s>\n" op.name;
      if op.enabled_by_default then printf "*\n";
      printf "\n";
      printf "%s.\n\n" op.heading;
      Option.may (printf "%s\n\n") op.pod_description;
      Option.may (fun notes ->
          printf "=head3 ";
          printf (f_"Notes on %s") op.name;
          printf "\n\n";
          printf "%s\n\n" notes
      ) op.pod_notes;
  ) !all_operations

let dump_pod_options () =
  assert !baked;

  let args = List.map (
    fun { name = op_name; extra_args } ->
      List.map (fun ea -> op_name, ea) extra_args
  ) !all_operations in
  let args = List.flatten args in
  let args = List.map (
    function
    | (op_name,
       { extra_argspec = (arg_names,
                          (Getopt.Unit _ | Getopt.Set _ | Getopt.Clear _),
                          _);
         extra_pod_argval = None;
         extra_pod_description = pod }) ->
      List.map (
        fun arg_name ->
          let heading =
            sprintf "B<%s>" (Getopt.string_of_option_name arg_name) in
          arg_name, (op_name, heading, pod)
      ) arg_names

    | (op_name,
       { extra_argspec = (arg_names,
                          (Getopt.String _ | Getopt.Set_string _ | Getopt.Int _ |
                           Getopt.Set_int _ | Getopt.Symbol _),
                          _);
         extra_pod_argval = Some arg_val;
         extra_pod_description = pod }) ->
      List.map (
        fun arg_name ->
          let heading =
            sprintf "B<%s> %s"
                    (Getopt.string_of_option_name arg_name) arg_val in
          arg_name, (op_name, heading, pod)
      ) arg_names

    | _ ->
      failwith "sysprep_operation.ml: argument type not implemented"
  ) args in
  let args = List.flatten args in

  let args =
    List.sort (fun (a, _) (b, _) -> Getopt.compare_command_line_args a b) args in

  List.iter (
    fun (arg_name, (op_name, heading, pod)) ->
      printf "=item %s\n" heading;
      printf "(see C<%s> below)\n" op_name;
      printf "\n";
      printf "%s\n\n" pod
  ) args

let list_operations () =
  assert !baked;

  List.iter (
    fun op ->
      printf "%s %s %s\n" op.name
        (if op.enabled_by_default then "*" else " ")
        op.heading
  ) !all_operations

let not_enabled_check_args ?operations () =
  let enabled_ops =
    match operations with
    | None -> !enabled_by_default_operations
    | Some opset -> (* just the operation names listed *)
      OperationSet.elements opset in
  let all_ops = opset_of_oplist !all_operations in
  let enabled_ops = opset_of_oplist enabled_ops in
  let disabled_ops = OperationSet.diff all_ops enabled_ops in
  OperationSet.iter (fun op -> op.not_enabled_check_args ()) disabled_ops

let compare_operations { order = o1; name = n1 } { order = o2; name = n2 } =
  let i = compare o1 o2 in
  if i <> 0 then i else compare n1 n2

let perform_operations_on_filesystems ?operations g root
    side_effects =
  assert !baked;

  let ops =
    match operations with
    | None -> !enabled_by_default_operations
    | Some opset -> (* just the operation names listed *)
      OperationSet.elements opset in

  (* Perform the operations in alphabetical, rather than random order. *)
  let ops = List.sort compare_operations ops in

  List.iter (
    function
    | { name; perform_on_filesystems = Some fn } ->
      message (f_"Performing %S ...") name;
      fn g root side_effects
    | { perform_on_filesystems = None } -> ()
  ) ops

let perform_operations_on_devices ?operations g root
    side_effects =
  assert !baked;

  let ops =
    match operations with
    | None -> !enabled_by_default_operations
    | Some opset -> (* just the operation names listed *)
      OperationSet.elements opset in

  (* Perform the operations in alphabetical, rather than random order. *)
  let ops = List.sort compare_operations ops in

  List.iter (
    function
    | { name; perform_on_devices = Some fn } ->
      message (f_"Performing %S ...") name;
      fn g root side_effects
    | { perform_on_devices = None } -> ()
  ) ops