diff options
Diffstat (limited to 'sysprep')
-rw-r--r-- | sysprep/Makefile.am | 40 | ||||
-rw-r--r-- | sysprep/firstboot.ml | 102 | ||||
-rw-r--r-- | sysprep/firstboot.mli | 27 | ||||
-rw-r--r-- | sysprep/sysprep_operation_firstboot.ml | 86 | ||||
-rw-r--r-- | sysprep/sysprep_operation_script.ml | 6 | ||||
-rw-r--r-- | sysprep/utils.ml | 16 | ||||
-rw-r--r-- | sysprep/utils.mli | 3 | ||||
-rwxr-xr-x | sysprep/virt-sysprep.pod | 26 |
8 files changed, 299 insertions, 7 deletions
diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am index 50c6e114..5551c2d5 100644 --- a/sysprep/Makefile.am +++ b/sysprep/Makefile.am @@ -39,16 +39,43 @@ CLEANFILES = \ # Filenames sysprep_operation_<name>.ml in alphabetical order. operations = \ - abrt_data bash_history blkid_tab ca_certificates cron_spool \ - dhcp_client_state dhcp_server_state dovecot_data flag_reconfiguration \ - hostname kerberos_data lvm_uuids logfiles machine_id mail_spool \ - net_hwaddr pacct_log package_manager_cache pam_data puppet_data_log \ - random_seed rhn_systemid samba_db_log script smolt_uuid ssh_hostkeys \ - ssh_userdir sssd_db_log udev_persistent_net user_account \ + abrt_data \ + bash_history \ + blkid_tab \ + ca_certificates \ + cron_spool \ + dhcp_client_state \ + dhcp_server_state \ + dovecot_data \ + flag_reconfiguration \ + firstboot \ + hostname \ + kerberos_data \ + lvm_uuids \ + logfiles \ + machine_id \ + mail_spool \ + net_hwaddr \ + pacct_log \ + package_manager_cache \ + pam_data \ + puppet_data_log \ + random_seed \ + rhn_systemid \ + samba_db_log \ + script \ + smolt_uuid \ + ssh_hostkeys \ + ssh_userdir \ + sssd_db_log \ + udev_persistent_net \ + user_account \ utmp yum_uuid # Alphabetical order. SOURCES = \ + firstboot.ml \ + firstboot.mli \ main.ml \ sysprep_gettext.ml \ sysprep_operation.ml \ @@ -63,6 +90,7 @@ if HAVE_OCAML OBJECTS = \ sysprep_gettext.cmx \ utils.cmx \ + firstboot.cmx \ sysprep_operation.cmx \ $(patsubst %,sysprep_operation_%.cmx,$(operations)) \ main.cmx diff --git a/sysprep/firstboot.ml b/sysprep/firstboot.ml new file mode 100644 index 00000000..97cd8a91 --- /dev/null +++ b/sysprep/firstboot.ml @@ -0,0 +1,102 @@ +(* 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 Utils +open Sysprep_operation +open Sysprep_gettext.Gettext + +(* For Linux guests. *) +let firstboot_dir = "/usr/lib/virt-sysprep" + +let firstboot_sh = sprintf "\ +#!/bin/sh - + +d=%s/scripts +logfile=~root/virt-sysprep-firstboot.log + +for f in $d/* ; do + echo '=== Running' $f '===' >>$logfile + $f >>$logfile 2>&1 + rm $f +done +" firstboot_dir + +let firstboot_service = sprintf "\ +[Unit] +Description=virt-sysprep firstboot service +After=syslog.target network.target +Before=prefdm.service + +[Service] +Type=oneshot +ExecStart=%s/firstboot.sh +RemainAfterExit=yes + +[Install] +WantedBy=default.target +" firstboot_dir + +let failed fs = + ksprintf (fun msg -> failwith (s_"firstboot: failed: " ^ msg)) fs + +let rec install_service g root = + g#mkdir_p firstboot_dir; + g#mkdir_p (sprintf "%s/scripts" firstboot_dir); + g#write (sprintf "%s/firstboot.sh" firstboot_dir) firstboot_sh; + g#chmod 0o755 (sprintf "%s/firstboot.sh" firstboot_dir); + + (* systemd, else assume sysvinit *) + if g#is_dir "/etc/systemd" then + install_systemd_service g root + else + install_sysvinit_service g root + +(* Install the systemd firstboot service, if not installed already. *) +and install_systemd_service g root = + g#write (sprintf "%s/firstboot.service" firstboot_dir) firstboot_service; + g#mkdir_p "/etc/systemd/system/default.target.wants"; + g#ln_sf (sprintf "%s/firstboot.service" firstboot_dir) + "/etc/systemd/system/default.target.wants" + +and install_sysvinit_service g root = + g#mkdir_p "/etc/rc.d/rc2.d"; + g#mkdir_p "/etc/rc.d/rc3.d"; + g#mkdir_p "/etc/rc.d/rc5.d"; + g#ln_sf (sprintf "%s/firstboot.sh" firstboot_dir) + "/etc/rc.d/rc2.d/99virt-sysprep-firstboot"; + g#ln_sf (sprintf "%s/firstboot.sh" firstboot_dir) + "/etc/rc.d/rc3.d/99virt-sysprep-firstboot"; + g#ln_sf (sprintf "%s/firstboot.sh" firstboot_dir) + "/etc/rc.d/rc5.d/99virt-sysprep-firstboot" + +let add_firstboot_script g root id content = + let typ = g#inspect_get_type root in + let distro = g#inspect_get_distro root in + match typ, distro with + | "linux", _ -> + install_service g root; + let t = Int64.of_float (Unix.time ()) in + let r = string_random8 () in + let filename = sprintf "%s/scripts/%Ld-%s-%s" firstboot_dir t r id in + g#write filename content; + g#chmod 0o755 filename + + | _ -> + failed "guest type %s/%s is not supported" typ distro diff --git a/sysprep/firstboot.mli b/sysprep/firstboot.mli new file mode 100644 index 00000000..910dd751 --- /dev/null +++ b/sysprep/firstboot.mli @@ -0,0 +1,27 @@ +(* 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. + *) + +val add_firstboot_script : Guestfs.guestfs -> string -> string -> string -> unit + (** [add_firstboot_script g root id content] adds a firstboot + script called [shortname] containing [content]. + + NB. [content] is the contents of the script, {b not} a filename. + + [id] should be a short name containing only 7 bit ASCII [-a-z0-9]. + + You should make sure the filesystem is relabelled after calling this. *) diff --git a/sysprep/sysprep_operation_firstboot.ml b/sysprep/sysprep_operation_firstboot.ml new file mode 100644 index 00000000..d0f32938 --- /dev/null +++ b/sysprep/sysprep_operation_firstboot.ml @@ -0,0 +1,86 @@ +(* 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 Utils +open Sysprep_operation +open Sysprep_gettext.Gettext + +module G = Guestfs + +let files = ref [] + +let make_id_from_filename filename = + let ret = String.copy filename in + for i = 0 to String.length ret - 1 do + let c = String.unsafe_get ret i in + if not ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9')) then + String.unsafe_set ret i '-' + done; + ret + +let firstboot_perform g root = + (* Read the files and add them using the {!Firstboot} module. *) + List.iter ( + fun filename -> + let content = read_whole_file filename in + let basename = Filename.basename filename in + let id = make_id_from_filename basename in + Firstboot.add_firstboot_script g root id content + ) !files; + [ `Created_files ] + +let firstboot_op = { + name = "firstboot"; + + (* enabled_by_default because we only do anything if the + * --firstboot parameter is used. + *) + enabled_by_default = true; + + heading = s_"Add scripts to run once at next boot"; + pod_description = Some (s_"\ +Supply one of more shell scripts (using the I<--firstboot> option). + +These are run the first time the guest boots, and then are +deleted. So these are useful for performing last minute +configuration that must run in the context of the guest +operating system, for example C<yum update>. + +Output or errors from the scripts are written to +C<~root/virt-sysprep-firstboot.log> (in the guest). + +Currently this is only implemented for Linux guests using +either System V init, or systemd."); + + extra_args = [ + ("--firstboot", Arg.String (fun s -> files := s :: !files), + s_"script" ^ " " ^ s_"run script once next time guest boots"), + s_"\ +Run script(s) once next time the guest boots. You can supply +the I<--firstboot> option as many times as needed." + ]; + + perform_on_filesystems = Some firstboot_perform; + perform_on_devices = None; +} + +let () = register_operation firstboot_op diff --git a/sysprep/sysprep_operation_script.ml b/sysprep/sysprep_operation_script.ml index 9337701f..a49bc3ce 100644 --- a/sysprep/sysprep_operation_script.ml +++ b/sysprep/sysprep_operation_script.ml @@ -134,7 +134,11 @@ guest's DNS configuration file, but C<rm /etc/resolv.conf> would (try to) remove the host's file. Normally a temporary mount point for the guest is used, but you -can choose a specific one by using the I<--scriptdir> parameter."); +can choose a specific one by using the I<--scriptdir> parameter. + +B<Note:> This is different from I<--firstboot> scripts (which run +in the context of the guest when it is booting first time). +I<--script> scripts run on the host, not in the guest."); extra_args = [ ("--scriptdir", Arg.String set_scriptdir, s_"dir" ^ " " ^ s_"Mount point on host"), s_"\ diff --git a/sysprep/utils.ml b/sysprep/utils.ml index 3b3ad8af..81877249 100644 --- a/sysprep/utils.ml +++ b/sysprep/utils.ml @@ -86,3 +86,19 @@ let skip_dashes str = let compare_command_line_args a b = compare (String.lowercase (skip_dashes a)) (String.lowercase (skip_dashes b)) + +let read_whole_file path = + let buf = Buffer.create 16384 in + let chan = open_in path in + let maxlen = 16384 in + let s = String.create maxlen in + let rec loop () = + let r = input chan s 0 maxlen in + if r > 0 then ( + Buffer.add_substring buf s 0 r; + loop () + ) + in + loop (); + close_in chan; + Buffer.contents buf diff --git a/sysprep/utils.mli b/sysprep/utils.mli index 0ecb8da4..351b936b 100644 --- a/sysprep/utils.mli +++ b/sysprep/utils.mli @@ -52,3 +52,6 @@ val compare_command_line_args : string -> string -> int (** Compare two command line arguments (eg. ["-a"] and ["--V"]), ignoring leading dashes and case. Note this assumes the strings are 7 bit ASCII. *) + +val read_whole_file : string -> string +(** Read whole file into memory. *) diff --git a/sysprep/virt-sysprep.pod b/sysprep/virt-sysprep.pod index 66bc7104..71900ca8 100755 --- a/sysprep/virt-sysprep.pod +++ b/sysprep/virt-sysprep.pod @@ -383,6 +383,32 @@ to pay for disk space), then instead of copying the template, you can run L<virt-resize(1)>. Virt-resize performs a copy and resize, and thus is ideal for cloning guests from a template. +=head1 FIRSTBOOT VS SCRIPT + +The two options I<--firstboot> and I<--script> both supply shell +scripts that are run against the guest. However these two options are +significantly different. + +I<--firstboot script> uploads the file C<script> into the guest +and arranges that it will run, in the guest, when the guest is +next booted. (The script will only run once, at the "first boot"). + +I<--script script> runs the shell C<script> I<on the host>, with its +current directory inside the guest filesystem. + +If you needed, for example, to C<yum install> new packages, then you +I<must not> use I<--script> for this, since that would (a) run the +C<yum> command on the host and (b) wouldn't have access to the same +resources (repositories, keys, etc.) as the guest. Any command that +needs to run on the guest I<must> be run via I<--firstboot>. + +On the other hand if you need to make adjustments to the guest +filesystem (eg. copying in files), then I<--script> is ideal since (a) +it has access to the host filesystem and (b) you will get immediate +feedback on errors. + +Either or both options can be used multiple times on the command line. + =head1 SECURITY Although virt-sysprep removes some sensitive information from the |