mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
customize: Add --append-line.
This appends a single line to a file, with some cleverness involving guessing the right line endings to use. Also adds a test.
This commit is contained in:
@@ -70,6 +70,19 @@ $VG virt-builder phony-fedora \
|
||||
--delete /Makefile \
|
||||
--link /etc/foo/bar/baz/foo:/foo \
|
||||
--link /etc/foo/bar/baz/foo:/foo1:/foo2:/foo3 \
|
||||
--append-line '/etc/append1:hello' \
|
||||
--append-line '/etc/append2:line1' \
|
||||
--append-line '/etc/append2:line2' \
|
||||
--write '/etc/append3:line1' \
|
||||
--append-line '/etc/append3:line2' \
|
||||
--write '/etc/append4:line1
|
||||
' \
|
||||
--append-line '/etc/append4:line2' \
|
||||
--touch /etc/append5 \
|
||||
--append-line '/etc/append5:line1' \
|
||||
--write '/etc/append6:
|
||||
' \
|
||||
--append-line '/etc/append6:line2' \
|
||||
--firstboot Makefile --firstboot-command 'echo "hello"' \
|
||||
--firstboot-install "minicom,inkscape"
|
||||
|
||||
@@ -97,6 +110,24 @@ echo -----
|
||||
# Password
|
||||
is-file /etc/shadow
|
||||
cat /etc/shadow | sed -r '/^root:/!d;s,^(root:\\\$6\\\$).*,\\1,g'
|
||||
|
||||
echo -----
|
||||
# Line appending
|
||||
# Note that the guestfish 'cat' command appends a newline
|
||||
echo append1:
|
||||
cat /etc/append1
|
||||
echo append2:
|
||||
cat /etc/append2
|
||||
echo append3:
|
||||
cat /etc/append3
|
||||
echo append4:
|
||||
cat /etc/append4
|
||||
echo append5:
|
||||
cat /etc/append5
|
||||
echo append6:
|
||||
cat /etc/append6
|
||||
|
||||
echo -----
|
||||
EOF
|
||||
|
||||
if [ "$(cat test-virt-builder.out)" != "true
|
||||
@@ -113,7 +144,31 @@ true
|
||||
/usr/share/zoneinfo/Europe/London
|
||||
-----
|
||||
true
|
||||
root:\$6\$" ]; then
|
||||
root:\$6\$
|
||||
-----
|
||||
append1:
|
||||
hello
|
||||
|
||||
append2:
|
||||
line1
|
||||
line2
|
||||
|
||||
append3:
|
||||
line1
|
||||
line2
|
||||
|
||||
append4:
|
||||
line1
|
||||
line2
|
||||
|
||||
append5:
|
||||
line1
|
||||
|
||||
append6:
|
||||
|
||||
line2
|
||||
|
||||
-----" ]; then
|
||||
echo "$0: unexpected output:"
|
||||
cat test-virt-builder.out
|
||||
exit 1
|
||||
|
||||
@@ -34,6 +34,7 @@ generator_built = \
|
||||
customize-synopsis.pod
|
||||
|
||||
SOURCES_MLI = \
|
||||
append_line.mli \
|
||||
crypt.mli \
|
||||
customize_cmdline.mli \
|
||||
customize_run.mli \
|
||||
@@ -51,6 +52,7 @@ SOURCES_MLI = \
|
||||
# This list must be in dependency order.
|
||||
SOURCES_ML = \
|
||||
customize_utils.ml \
|
||||
append_line.ml \
|
||||
crypt.ml \
|
||||
firstboot.ml \
|
||||
hostname.ml \
|
||||
|
||||
71
customize/append_line.ml
Normal file
71
customize/append_line.ml
Normal file
@@ -0,0 +1,71 @@
|
||||
(* virt-customize
|
||||
* Copyright (C) 2016 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 Common_utils
|
||||
open Common_gettext.Gettext
|
||||
|
||||
module G = Guestfs
|
||||
|
||||
let append_line (g : G.guestfs) root path line =
|
||||
(* The default line ending for this guest type. This is only
|
||||
* used when we don't know anything more about the file.
|
||||
*)
|
||||
let default_newline () =
|
||||
match g#inspect_get_type root with
|
||||
| "windows" -> "\r\n"
|
||||
| _ -> "\n"
|
||||
in
|
||||
|
||||
if not (g#exists path) then (
|
||||
g#write path (line ^ default_newline ())
|
||||
)
|
||||
else (
|
||||
(* Stat the file. We want to know it's a regular file, and
|
||||
* also its size.
|
||||
*)
|
||||
let { G.st_mode = mode; st_size = size } = g#statns path in
|
||||
if Int64.logand mode 0o170000_L <> 0o100000_L then
|
||||
error (f_"append_line: %s is not a file") path;
|
||||
|
||||
(* Guess the line ending from the first part of the file, else
|
||||
* use the default for this guest type.
|
||||
*)
|
||||
let newline =
|
||||
let content = g#pread path 8192 0L in
|
||||
if String.find content "\r\n" >= 0 then "\r\n"
|
||||
else if String.find content "\n" >= 0 then "\n"
|
||||
else if String.find content "\r" >= 0 then "\r"
|
||||
else default_newline () in
|
||||
|
||||
let line = line ^ newline in
|
||||
|
||||
(* Do we need to append a newline to the existing file? *)
|
||||
let last_chars =
|
||||
let len = String.length newline in
|
||||
if size <= 0L then newline (* empty file ends in virtual newline *)
|
||||
else if size >= Int64.of_int len then
|
||||
g#pread path len (size -^ Int64.of_int len)
|
||||
else
|
||||
g#pread path len 0L in
|
||||
let line =
|
||||
if last_chars = newline then line
|
||||
else newline ^ line in
|
||||
|
||||
(* Finally, append. *)
|
||||
g#write_append path line
|
||||
)
|
||||
20
customize/append_line.mli
Normal file
20
customize/append_line.mli
Normal file
@@ -0,0 +1,20 @@
|
||||
(* virt-customize
|
||||
* Copyright (C) 2016 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 append_line : Guestfs.guestfs -> string -> string -> string -> unit
|
||||
(** append_line [g root file line] appends a single line to a text file. *)
|
||||
@@ -25,6 +25,7 @@ open Common_utils
|
||||
open Customize_utils
|
||||
open Customize_cmdline
|
||||
open Password
|
||||
open Append_line
|
||||
|
||||
let run (g : Guestfs.guestfs) root (ops : ops) =
|
||||
(* Is the host_cpu compatible with the guest arch? ie. Can we
|
||||
@@ -204,6 +205,16 @@ exec >>%s 2>&1
|
||||
(* Perform the remaining customizations in command-line order. *)
|
||||
List.iter (
|
||||
function
|
||||
| `AppendLine (path, line) ->
|
||||
(* It's an error if it's not a single line. This is
|
||||
* to prevent incorrect line endings being added to a file.
|
||||
*)
|
||||
if String.contains line '\n' then
|
||||
error (f_"--append-line: line must not contain newline characters. Use the --append-line option multiple times to add several lines.");
|
||||
|
||||
message (f_"Appending line to %s") path;
|
||||
append_line g root path line
|
||||
|
||||
| `Chmod (mode, path) ->
|
||||
message (f_"Changing permissions of %s to %s") path mode;
|
||||
(* If the mode string is octal, add the OCaml prefix for octal values
|
||||
|
||||
@@ -49,6 +49,40 @@ and op_type =
|
||||
| SMPoolSelector of string (* pool selector *)
|
||||
|
||||
let ops = [
|
||||
{ op_name = "append-line";
|
||||
op_type = StringPair "FILE:LINE";
|
||||
op_discrim = "`AppendLine";
|
||||
op_shortdesc = "Append line(s) to the file";
|
||||
op_pod_longdesc = "\
|
||||
Append a single line of text to the C<FILE>. If the file does not already
|
||||
end with a newline, then one is added before the appended
|
||||
line. Also a newline is added to the end of the C<LINE> string
|
||||
automatically.
|
||||
|
||||
For example (assuming ordinary shell quoting) this command:
|
||||
|
||||
--append-line '/etc/hosts:10.0.0.1 foo'
|
||||
|
||||
will add either C<10.0.0.1 foo⏎> or C<⏎10.0.0.1 foo⏎> to
|
||||
the file, the latter only if the existing file does not
|
||||
already end with a newline.
|
||||
|
||||
C<⏎> represents a newline character, which is guessed by
|
||||
looking at the existing content of the file, so this command
|
||||
does the right thing for files using Unix or Windows line endings.
|
||||
It also works for empty or non-existent files.
|
||||
|
||||
To insert several lines, use the same option several times:
|
||||
|
||||
--append-line '/etc/hosts:10.0.0.1 foo'
|
||||
--append-line '/etc/hosts:10.0.0.2 bar'
|
||||
|
||||
To insert a blank line before the appended line, do:
|
||||
|
||||
--append-line '/etc/hosts:'
|
||||
--append-line '/etc/hosts:10.0.0.1 foo'";
|
||||
};
|
||||
|
||||
{ op_name = "chmod";
|
||||
op_type = StringPair "PERMISSIONS:FILE";
|
||||
op_discrim = "`Chmod";
|
||||
|
||||
Reference in New Issue
Block a user