libguestfs/SOURCES/0022-generator-Add-chown-op...

399 lines
15 KiB
Diff
Raw Normal View History

2024-03-28 10:46:39 +00:00
From 64c1716c4120e8dc69d0106b6ebcf6ccb9d4fc24 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 29 Jun 2023 13:33:04 +0100
Subject: [PATCH] generator: Add --chown option for virt-customize
Also this updates the common submodule to include the changes.
Fixes: https://github.com/rwmjones/guestfs-tools/issues/12
Acked-by: Laszlo Ersek <lersek@redhat.com>
(cherry picked from commit d8e48bff212f9b0558480ffedf8158157360d0d5)
---
common | 2 +-
generator/customize.ml | 28 ++++++++++++++++++++++++++++
2 files changed, 29 insertions(+), 1 deletion(-)
Submodule common d61cd820..bbb54714:
diff --git a/common/mlcustomize/customize-options.pod b/common/mlcustomize/customize-options.pod
index 22a96e04..22724600 100644
--- a/common/mlcustomize/customize-options.pod
+++ b/common/mlcustomize/customize-options.pod
@@ -63,6 +63,30 @@ Change the permissions of C<FILE> to C<PERMISSIONS>.
I<Note>: C<PERMISSIONS> by default would be decimal, unless you prefix
it with C<0> to get octal, ie. use C<0700> not C<700>.
+=item B<--chown> UID.GID:PATH
+
+Change the owner user and group ID of a file or directory in the guest.
+Note:
+
+=over 4
+
+=item *
+
+Only numeric UIDs and GIDs will work, and these may not be the same
+inside the guest as on the host.
+
+=item *
+
+This will not work with Windows guests.
+
+=back
+
+For example:
+
+ virt-customize --chown '0.0:/var/log/audit.log'
+
+See also: I<--upload>.
+
=item B<--commands-from-file> FILENAME
Read the customize commands from a file, one (and its arguments)
diff --git a/common/mlcustomize/customize-synopsis.pod b/common/mlcustomize/customize-synopsis.pod
index d04f421e..e20b12d4 100644
--- a/common/mlcustomize/customize-synopsis.pod
+++ b/common/mlcustomize/customize-synopsis.pod
@@ -1,15 +1,15 @@
[--append-line FILE:LINE] [--chmod PERMISSIONS:FILE]
- [--commands-from-file FILENAME] [--copy SOURCE:DEST]
- [--copy-in LOCALPATH:REMOTEDIR] [--delete PATH] [--edit FILE:EXPR]
- [--firstboot SCRIPT] [--firstboot-command 'CMD+ARGS']
- [--firstboot-install PKG,PKG..] [--hostname HOSTNAME]
- [--inject-qemu-ga METHOD] [--inject-virtio-win METHOD]
- [--install PKG,PKG..] [--link TARGET:LINK[:LINK..]] [--mkdir DIR]
- [--move SOURCE:DEST] [--password USER:SELECTOR]
- [--root-password SELECTOR] [--run SCRIPT]
- [--run-command 'CMD+ARGS'] [--scrub FILE] [--sm-attach SELECTOR]
- [--sm-register] [--sm-remove] [--sm-unregister]
- [--ssh-inject USER[:SELECTOR]] [--truncate FILE]
+ [--chown UID.GID:PATH] [--commands-from-file FILENAME]
+ [--copy SOURCE:DEST] [--copy-in LOCALPATH:REMOTEDIR]
+ [--delete PATH] [--edit FILE:EXPR] [--firstboot SCRIPT]
+ [--firstboot-command 'CMD+ARGS'] [--firstboot-install PKG,PKG..]
+ [--hostname HOSTNAME] [--inject-qemu-ga METHOD]
+ [--inject-virtio-win METHOD] [--install PKG,PKG..]
+ [--link TARGET:LINK[:LINK..]] [--mkdir DIR] [--move SOURCE:DEST]
+ [--password USER:SELECTOR] [--root-password SELECTOR]
+ [--run SCRIPT] [--run-command 'CMD+ARGS'] [--scrub FILE]
+ [--sm-attach SELECTOR] [--sm-register] [--sm-remove]
+ [--sm-unregister] [--ssh-inject USER[:SELECTOR]] [--truncate FILE]
[--truncate-recursive PATH] [--timezone TIMEZONE] [--touch FILE]
[--uninstall PKG,PKG..] [--update] [--upload FILE:DEST]
[--write FILE:CONTENT] [--no-logfile]
diff --git a/common/mlcustomize/customize_cmdline.ml b/common/mlcustomize/customize_cmdline.ml
index 3c24315d..fd3074ad 100644
--- a/common/mlcustomize/customize_cmdline.ml
+++ b/common/mlcustomize/customize_cmdline.ml
@@ -41,6 +41,8 @@ and op = [
(* --append-line FILE:LINE *)
| `Chmod of string * string
(* --chmod PERMISSIONS:FILE *)
+ | `Chown of string * string
+ (* --chown UID.GID:PATH *)
| `CommandsFromFile of string
(* --commands-from-file FILENAME *)
| `Copy of string * string
@@ -187,6 +189,17 @@ let rec argspec () =
s_"Change the permissions of a file"
),
Some "PERMISSIONS:FILE", "Change the permissions of C<FILE> to C<PERMISSIONS>.\n\nI<Note>: C<PERMISSIONS> by default would be decimal, unless you prefix\nit with C<0> to get octal, ie. use C<0700> not C<700>.";
+ (
+ [ L"chown" ],
+ Getopt.String (
+ s_"UID.GID:PATH",
+ fun s ->
+ let p = split_string_pair "chown" s in
+ List.push_front (`Chown p) ops
+ ),
+ s_"Change the owner user and group ID of a file or directory"
+ ),
+ Some "UID.GID:PATH", "Change the owner user and group ID of a file or directory in the guest.\nNote:\n\n=over 4\n\n=item *\n\nOnly numeric UIDs and GIDs will work, and these may not be the same\ninside the guest as on the host.\n\n=item *\n\nThis will not work with Windows guests.\n\n=back\n\nFor example:\n\n virt-customize --chown '0.0:/var/log/audit.log'\n\nSee also: I<--upload>.";
(
[ L"commands-from-file" ],
Getopt.String (
diff --git a/common/mlcustomize/customize_cmdline.mli b/common/mlcustomize/customize_cmdline.mli
index 0cc166e6..5883bbe0 100644
--- a/common/mlcustomize/customize_cmdline.mli
+++ b/common/mlcustomize/customize_cmdline.mli
@@ -33,6 +33,8 @@ and op = [
(* --append-line FILE:LINE *)
| `Chmod of string * string
(* --chmod PERMISSIONS:FILE *)
+ | `Chown of string * string
+ (* --chown UID.GID:PATH *)
| `CommandsFromFile of string
(* --commands-from-file FILENAME *)
| `Copy of string * string
diff --git a/common/mltools/curl.ml b/common/mltools/curl.ml
index 6dba9753..73eed903 100644
--- a/common/mltools/curl.ml
+++ b/common/mltools/curl.ml
@@ -20,11 +20,13 @@ open Printf
open Std_utils
open Tools_utils
+open Common_gettext.Gettext
type t = {
curl : string;
args : args;
tmpdir : string option;
+ url : string;
}
and args = (string * string option) list
@@ -40,11 +42,17 @@ let args_of_proxy = function
| SystemProxy -> []
| ForcedProxy url -> [ "proxy", Some url; "noproxy", Some "" ]
-let create ?(curl = "curl") ?(proxy = SystemProxy) ?tmpdir args =
+let create ?(curl = "curl") ?(proxy = SystemProxy) ?tmpdir args url =
+ (* The ["url"] key must not appear in [args]. This was how the
+ * previous version of this module worked, so lets check there
+ * are no callers still doing this.
+ *)
+ List.iter (function "url", _ -> assert false | _ -> ()) args;
+
let args = safe_args @ args_of_proxy proxy @ args in
- { curl = curl; args = args; tmpdir = tmpdir }
+ { curl; args; tmpdir; url }
-let run { curl; args; tmpdir } =
+let run { curl; args; tmpdir; url } =
let config_file, chan = Filename.open_temp_file ?temp_dir:tmpdir
"guestfscurl" ".conf" in
List.iter (
@@ -67,15 +75,16 @@ let run { curl; args; tmpdir } =
| c -> output_char chan c
done;
fprintf chan "\"\n"
- ) args;
+ ) (("url", Some url) :: args);
close_out chan;
let cmd = sprintf "%s -q --config %s" (quote curl) (quote config_file) in
- let lines = external_command ~echo_cmd:false cmd in
+ let help = sprintf (f_"downloading %s") url in
+ let lines = external_command ~echo_cmd:false ~help cmd in
Unix.unlink config_file;
lines
-let to_string { curl; args } =
+let to_string { curl; args; url } =
let b = Buffer.create 128 in
bprintf b "%s -q" (quote curl);
List.iter (
@@ -85,7 +94,7 @@ let to_string { curl; args } =
| "user", Some _ -> bprintf b " --user <hidden>"
| name, Some value -> bprintf b " --%s %s" name (quote value)
) args;
- bprintf b "\n";
+ bprintf b " %s\n" (quote url);
Buffer.contents b
let print chan t = output_string chan (to_string t)
diff --git a/common/mltools/curl.mli b/common/mltools/curl.mli
index a3e98dc6..1606a79a 100644
--- a/common/mltools/curl.mli
+++ b/common/mltools/curl.mli
@@ -27,13 +27,16 @@ type proxy =
| SystemProxy (** Use the system settings. *)
| ForcedProxy of string (** The proxy is forced to the specified URL. *)
-val create : ?curl:string -> ?proxy:proxy -> ?tmpdir:string -> args -> t
+val create : ?curl:string -> ?proxy:proxy -> ?tmpdir:string -> args -> string
+ -> t
(** Create a curl command handle.
The curl arguments are a list of key, value pairs corresponding
to curl command line parameters, without leading dashes,
eg. [("user", Some "user:password")].
+ The string parameter is the URL (which is required).
+
The optional [?curl] parameter controls the name of the curl
binary (default ["curl"]).
diff --git a/common/mltools/tools_utils.ml b/common/mltools/tools_utils.ml
index 8b611e77..23f16c51 100644
--- a/common/mltools/tools_utils.ml
+++ b/common/mltools/tools_utils.ml
@@ -435,8 +435,12 @@ let create_standard_options argspec ?anon_fun ?(key_opts = false)
let getopt = Getopt.create argspec ?anon_fun usage_msg in
{ getopt; ks; debug_gc }
+let external_command_failed help cmd reason =
+ let help_prefix = match help with None -> "" | Some str -> str ^ ": " in
+ error "%s%s %s: %s" help_prefix (s_"external command") cmd reason
+
(* Run an external command, slurp up the output as a list of lines. *)
-let external_command ?(echo_cmd = true) cmd =
+let external_command ?(echo_cmd = true) ?help cmd =
if echo_cmd then
debug "%s" cmd;
let chan = Unix.open_process_in cmd in
@@ -448,15 +452,18 @@ let external_command ?(echo_cmd = true) cmd =
(match stat with
| Unix.WEXITED 0 -> ()
| Unix.WEXITED i ->
- error (f_"external command %s exited with error %d") cmd i
+ let reason = sprintf (f_"exited with error %d") i in
+ external_command_failed help cmd reason
| Unix.WSIGNALED i ->
- error (f_"external command %s killed by signal %d") cmd i
+ let reason = sprintf (f_"killed by signal %d") i in
+ external_command_failed help cmd reason
| Unix.WSTOPPED i ->
- error (f_"external command %s stopped by signal %d") cmd i
+ let reason = sprintf (f_"stopped by signal %d") i in
+ external_command_failed help cmd reason
);
lines
-let rec run_commands ?(echo_cmd = true) cmds =
+let rec run_commands ?(echo_cmd = true) ?help cmds =
let res = Array.make (List.length cmds) 0 in
let pids =
List.mapi (
@@ -482,21 +489,21 @@ let rec run_commands ?(echo_cmd = true) cmds =
let matching_pair = List.hd matching_pair in
let idx, _, app, outfd, errfd = matching_pair in
pids := new_pids;
- res.(idx) <- do_teardown app outfd errfd stat
+ res.(idx) <- do_teardown help app outfd errfd stat
);
done;
Array.to_list res
-and run_command ?(echo_cmd = true) ?stdout_fd ?stderr_fd args =
+and run_command ?(echo_cmd = true) ?help ?stdout_fd ?stderr_fd args =
let run_res = do_run args ~echo_cmd ?stdout_fd ?stderr_fd in
match run_res with
| Either (pid, app, outfd, errfd) ->
let _, stat = Unix.waitpid [] pid in
- do_teardown app outfd errfd stat
+ do_teardown help app outfd errfd stat
| Or code ->
code
-and do_run ?(echo_cmd = true) ?stdout_fd ?stderr_fd args =
+and do_run ?(echo_cmd = true) ?help ?stdout_fd ?stderr_fd args =
let app = List.hd args in
let get_fd default = function
| None ->
@@ -522,16 +529,18 @@ and do_run ?(echo_cmd = true) ?stdout_fd ?stderr_fd args =
debug "%s: %s: executable not found" app fn;
Or 127
-and do_teardown app outfd errfd exitstat =
+and do_teardown help app outfd errfd exitstat =
Option.iter Unix.close outfd;
Option.iter Unix.close errfd;
match exitstat with
| Unix.WEXITED i ->
- i
+ i
| Unix.WSIGNALED i ->
- error (f_"external command %s killed by signal %d") app i
+ let reason = sprintf (f_"killed by signal %d") i in
+ external_command_failed help app reason
| Unix.WSTOPPED i ->
- error (f_"external command %s stopped by signal %d") app i
+ let reason = sprintf (f_"stopped by signal %d") i in
+ external_command_failed help app reason
let shell_command ?(echo_cmd = true) cmd =
if echo_cmd then
diff --git a/common/mltools/tools_utils.mli b/common/mltools/tools_utils.mli
index ec900e63..193ba7b6 100644
--- a/common/mltools/tools_utils.mli
+++ b/common/mltools/tools_utils.mli
@@ -103,13 +103,17 @@ val create_standard_options : Getopt.speclist -> ?anon_fun:Getopt.anon_fun -> ?k
Returns a new {!cmdline_options} structure. *)
-val external_command : ?echo_cmd:bool -> string -> string list
+val external_command : ?echo_cmd:bool -> ?help:string -> string -> string list
(** Run an external command, slurp up the output as a list of lines.
[echo_cmd] specifies whether to output the full command on verbose
- mode, and it's on by default. *)
+ mode, and it's on by default.
-val run_commands : ?echo_cmd:bool -> (string list * Unix.file_descr option * Unix.file_descr option) list -> int list
+ [help] is an optional string which is printed as a prefix in
+ case the external command fails, eg as a hint to the user about
+ what we were trying to do. *)
+
+val run_commands : ?echo_cmd:bool -> ?help:string -> (string list * Unix.file_descr option * Unix.file_descr option) list -> int list
(** Run external commands in parallel without using a shell,
and return a list with their exit codes.
@@ -126,16 +130,24 @@ val run_commands : ?echo_cmd:bool -> (string list * Unix.file_descr option * Uni
end of the execution of the command for which it was specified.
[echo_cmd] specifies whether output the full command on verbose
- mode, and it's on by default. *)
+ mode, and it's on by default.
-val run_command : ?echo_cmd:bool -> ?stdout_fd:Unix.file_descr -> ?stderr_fd:Unix.file_descr -> string list -> int
+ [help] is an optional string which is printed as a prefix in
+ case the external command fails, eg as a hint to the user about
+ what we were trying to do. *)
+
+val run_command : ?echo_cmd:bool -> ?help:string -> ?stdout_fd:Unix.file_descr -> ?stderr_fd:Unix.file_descr -> string list -> int
(** Run an external command without using a shell, and return its exit code.
If [stdout_fd] or [stderr_fd] is specified, the file descriptor
is automatically closed after executing the command.
[echo_cmd] specifies whether output the full command on verbose
- mode, and it's on by default. *)
+ mode, and it's on by default.
+
+ [help] is an optional string which is printed as a prefix in
+ case the external command fails, eg as a hint to the user about
+ what we were trying to do. *)
val shell_command : ?echo_cmd:bool -> string -> int
(** Run an external shell command, and return its exit code.
diff --git a/generator/customize.ml b/generator/customize.ml
index aa7ac8e8..8d3dec3e 100644
--- a/generator/customize.ml
+++ b/generator/customize.ml
@@ -95,6 +95,34 @@ I<Note>: C<PERMISSIONS> by default would be decimal, unless you prefix
it with C<0> to get octal, ie. use C<0700> not C<700>.";
};
+ { op_name = "chown";
+ op_type = StringPair "UID.GID:PATH";
+ op_discrim = "`Chown";
+ op_shortdesc = "Change the owner user and group ID of a file or directory";
+ op_pod_longdesc = "\
+Change the owner user and group ID of a file or directory in the guest.
+Note:
+
+=over 4
+
+=item *
+
+Only numeric UIDs and GIDs will work, and these may not be the same
+inside the guest as on the host.
+
+=item *
+
+This will not work with Windows guests.
+
+=back
+
+For example:
+
+ virt-customize --chown '0.0:/var/log/audit.log'
+
+See also: I<--upload>.";
+ };
+
{ op_name = "commands-from-file";
op_type = StringFn ("FILENAME", "customize_read_from_file");
op_discrim = "`CommandsFromFile";