When find -exec is used in build scripts, ci pipelines and similar,
it's good to have way to ensure that failed subprocess executed
by find results in non zero return code of find as whole.

Previously this was only case with -exec {} +
Added option allows to enable this behavior globally.

Signed-off-by: Tomas Mudrunka <tomas.mudru...@gmail.com>
---
 THANKS        | 1 +
 find/defs.h   | 4 ++++
 find/exec.c   | 8 ++++++++
 find/find.1   | 4 ++++
 find/parser.c | 9 +++++++++
 find/util.c   | 2 +-
 6 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/THANKS b/THANKS
index 904d72e3..b4abcb5b 100644
--- a/THANKS
+++ b/THANKS
@@ -48,6 +48,7 @@ Stephane Barizien <s...@ocegr.fr>
 Steve Revilak
 Supriya Kannery
 Tavis Ormandy
+Tomas Mudrunka
 Vin Shelton
 Vincent Danjean <vdanj...@ens-lyon.fr>
 
diff --git a/find/defs.h b/find/defs.h
index 80a22a5f..fb310c74 100644
--- a/find/defs.h
+++ b/find/defs.h
@@ -572,6 +572,10 @@ struct options
    */
   bool posixly_correct;
 
+  /* If true, fail when any of the executed commands do
+   */
+  bool fail_exec;
+
   struct timespec      start_time;              /* Time at start of execution. 
 */
 
   /* Either one day before now (the default), or the start of today (if 
-daystart is given). */
diff --git a/find/exec.c b/find/exec.c
index f281fcc0..1b34be82 100644
--- a/find/exec.c
+++ b/find/exec.c
@@ -384,6 +384,14 @@ launch (struct buildcmd_control *ctl, void *usercontext, 
int argc, char **argv)
     }
   else
     {
+      /* -fail_exec exits with failure immediately if invoked command fails.
+       */
+      if (options.fail_exec)
+        {
+          state.exit_status = EXIT_FAILURE;
+          error (1, errno, _("Exec %s returned %d"), argv[0], ex);
+        }
+
       if (execp->multiple)
         {
           /* -exec   \; just returns false if the invoked command fails.
diff --git a/find/find.1 b/find/find.1
index a5b385cb..afbb1ba1 100644
--- a/find/find.1
+++ b/find/find.1
@@ -529,6 +529,10 @@ Process each directory's contents before the directory 
itself.  The
 \-delete action also implies
 .BR \-depth .
 
+.IP \-fail_exec
+Always fail immediately when -exec, -execdir, -ok or -ok dir command
+failw with non-zero return code.
+
 .IP "\-files0\-from \fIfile\fR"
 Read the starting points from \fIfile\fR instead of getting them on the
 command line.
diff --git a/find/parser.c b/find/parser.c
index 5f447933..36338e4c 100644
--- a/find/parser.c
+++ b/find/parser.c
@@ -100,6 +100,7 @@ static bool parse_depth         (const struct 
parser_table*, char *argv[], int *
 static bool parse_empty         (const struct parser_table*, char *argv[], int 
*arg_ptr);
 static bool parse_exec          (const struct parser_table*, char *argv[], int 
*arg_ptr);
 static bool parse_execdir       (const struct parser_table*, char *argv[], int 
*arg_ptr);
+static bool parse_fail_exec     (const struct parser_table*, char *argv[], int 
*arg_ptr);
 static bool parse_false         (const struct parser_table*, char *argv[], int 
*arg_ptr);
 static bool parse_files0_from   (const struct parser_table*, char *argv[], int 
*arg_ptr);
 static bool parse_fls           (const struct parser_table*, char *argv[], int 
*arg_ptr);
@@ -309,6 +310,7 @@ static struct parser_table const parse_table[] =
   PARSE_TEST_NP    ("wholename",             wholename), /* GNU, replaced 
-path, but now -path is standardized since POSIX 2008 */
   {ARG_TEST,       "writable",               parse_accesscheck, 
pred_writable}, /* GNU, 4.3.0+ */
   PARSE_OPTION     ("xdev",                  xdev), /* POSIX */
+  PARSE_OPTION     ("fail_exec",             fail_exec),     /* GNU */
   PARSE_TEST       ("xtype",                 xtype),         /* GNU */
 #ifdef UNIMPLEMENTED_UNIX
   /* It's pretty ugly for find to know about archive formats.
@@ -2500,6 +2502,13 @@ parse_xdev (const struct parser_table* entry, char 
**argv, int *arg_ptr)
   return parse_noop (entry, argv, arg_ptr);
 }
 
+static bool
+parse_fail_exec (const struct parser_table* entry, char **argv, int *arg_ptr)
+{
+  options.fail_exec = true;
+  return parse_noop (entry, argv, arg_ptr);
+}
+
 static bool
 parse_ignore_race (const struct parser_table* entry, char **argv, int *arg_ptr)
 {
diff --git a/find/util.c b/find/util.c
index b22e6e7b..2c0ecf5f 100644
--- a/find/util.c
+++ b/find/util.c
@@ -181,7 +181,7 @@ Positional options (always true):\n\
   HTL (_("\n\
 Normal options (always true, specified before other expressions):\n\
       -depth -files0-from FILE -maxdepth LEVELS -mindepth LEVELS\n\
-      -mount -noleaf -xdev -ignore_readdir_race -noignore_readdir_race\n"));
+      -mount -noleaf -xdev -ignore_readdir_race -noignore_readdir_race 
-fail_exec\n"));
   HTL (_("\n\
 Tests (N can be +N or -N or N):\n\
       -amin N -anewer FILE -atime N -cmin N -cnewer FILE -context CONTEXT\n\
-- 
2.49.0


Reply via email to