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

399 lines
15 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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";