]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
PR27725, better objcopy -p times
authorAlan Modra <amodra@gmail.com>
Wed, 14 Apr 2021 15:06:42 +0000 (00:36 +0930)
committerAlan Modra <amodra@gmail.com>
Thu, 15 Apr 2021 08:21:17 +0000 (17:51 +0930)
Nanosecond rather than second resolution.

PR 27725
* configure.ac: Check for sys/time.h and utimensat.  Use standard
checks for mkstemp and mkdtemp.  Whitespace.  Check for nanosecond
members of struct stat.
* rename.c: Prefer sys/time.h for utimes over utime.h for utime.
(STAT_TIMESPEC, STAT_TIMESPEC_NS): Define
(get_stat_atime_ns, get_stat_mtime_ns): New inline functions.
(get_stat_atime, get_stat_mtime): Likewise.
(set_times): Choose first available of utimensat, utimes, utime.
Use above inline functions to set timespec and timeval values.
* configure: Regenerate.
* config.in: Regenerate.
* testsuite/binutils-all/objcopy.exp (objcopy_test): Add test of
file timestamp when --preserve-dates is used.

binutils/ChangeLog
binutils/config.in
binutils/configure
binutils/configure.ac
binutils/rename.c
binutils/testsuite/binutils-all/objcopy.exp

index f8a74d14620e3deb1de94acd2a7247f601842fb9..212c35327ba73627ea4bf091cad7ff8d1067a437 100644 (file)
@@ -1,3 +1,20 @@
+2021-04-15  Alan Modra  <amodra@gmail.com>
+
+       PR 27725
+       * configure.ac: Check for sys/time.h and utimensat.  Use standard
+       checks for mkstemp and mkdtemp.  Whitespace.  Check for nanosecond
+       members of struct stat.
+       * rename.c: Prefer sys/time.h for utimes over utime.h for utime.
+       (STAT_TIMESPEC, STAT_TIMESPEC_NS): Define
+       (get_stat_atime_ns, get_stat_mtime_ns): New inline functions.
+       (get_stat_atime, get_stat_mtime): Likewise.
+       (set_times): Choose first available of utimensat, utimes, utime.
+       Use above inline functions to set timespec and timeval values.
+       * configure: Regenerate.
+       * config.in: Regenerate.
+       * testsuite/binutils-all/objcopy.exp (objcopy_test): Add test of
+       file timestamp when --preserve-dates is used.
+
 2021-04-15  Alan Modra  <amodra@gmail.com>
 
        PR 27456
index cc6aad16dddb19169dab422927d69abecd285196..e79967c5c14c8311dfc2d9a74c7a70c063173c52 100644 (file)
 /* Define to 1 if you have the <string.h> header file. */
 #undef HAVE_STRING_H
 
+/* Define to 1 if `st_atimensec' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_ATIMENSEC
+
+/* Define to 1 if `st_atimespec.tv_nsec' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC
+
+/* Define to 1 if `st_atim.st__tim.tv_nsec' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC
+
+/* Define to 1 if `st_atim.tv_nsec' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+
 /* Define to 1 if you have the <sys/file.h> header file. */
 #undef HAVE_SYS_FILE_H
 
 /* Define to 1 if you have the <sys/stat.h> header file. */
 #undef HAVE_SYS_STAT_H
 
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
 /* Define to 1 if you have the <sys/types.h> header file. */
 #undef HAVE_SYS_TYPES_H
 
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
+/* Define to 1 if you have the `utimensat' function. */
+#undef HAVE_UTIMENSAT
+
 /* Define to 1 if you have the `utimes' function. */
 #undef HAVE_UTIMES
 
 /* Define to 1 if user symbol names have a leading underscore, 0 if not. */
 #undef TARGET_PREPENDS_UNDERSCORE
 
+/* Define to 1 if the type of the st_atim member of a struct stat is struct
+   timespec. */
+#undef TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC
+
 /* Use b modifier when opening binary files? */
 #undef USE_BINARY_FOPEN
 
index 82720324e0db12679178e5d627171ef2bcb59f0d..1f855268c6fae6917fb58addef502f844de18bd0 100755 (executable)
@@ -2153,6 +2153,63 @@ rm -f conftest.val
 
 } # ac_fn_c_compute_int
 
+# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
+# ----------------------------------------------------
+# Tries to find if the field MEMBER exists in type AGGR, after including
+# INCLUDES, setting cache variable VAR accordingly.
+ac_fn_c_check_member ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
+$as_echo_n "checking for $2.$3... " >&6; }
+if eval \${$4+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$5
+int
+main ()
+{
+static $2 ac_aggr;
+if (ac_aggr.$3)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$4=yes"
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$5
+int
+main ()
+{
+static $2 ac_aggr;
+if (sizeof ac_aggr.$3)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$4=yes"
+else
+  eval "$4=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$4
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_member
+
 # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES
 # ---------------------------------------------
 # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
@@ -10874,7 +10931,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 10877 "configure"
+#line 10934 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -10980,7 +11037,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 10983 "configure"
+#line 11040 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12984,7 +13041,7 @@ _ACEOF
 # plugin-api.h tests HAVE_STDINT_H and HAVE_INTTYPES_H
 # Besides those, we need to check anything used in binutils/ not in C99.
 for ac_header in fcntl.h inttypes.h stdint.h sys/file.h \
-                sys/stat.h sys/types.h unistd.h
+                sys/stat.h sys/time.h sys/types.h unistd.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -13239,7 +13296,7 @@ $as_echo "#define HAVE_MMAP 1" >>confdefs.h
 fi
 rm -f conftest.mmap conftest.txt
 
-for ac_func in getc_unlocked sbrk utimes
+for ac_func in getc_unlocked mkdtemp mkstemp sbrk utimensat utimes
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -13251,23 +13308,10 @@ _ACEOF
 fi
 done
 
-ac_fn_c_check_func "$LINENO" "mkstemp" "ac_cv_func_mkstemp"
-if test "x$ac_cv_func_mkstemp" = xyes; then :
-
-$as_echo "#define HAVE_MKSTEMP 1" >>confdefs.h
-
-fi
-
-ac_fn_c_check_func "$LINENO" "mkdtemp" "ac_cv_func_mkdtemp"
-if test "x$ac_cv_func_mkdtemp" = xyes; then :
 
-$as_echo "#define HAVE_MKDTEMP 1" >>confdefs.h
-
-fi
-
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mbstate_t" >&5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for mbstate_t" >&5
 $as_echo_n "checking for mbstate_t... " >&6; }
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #include <wchar.h>
 int
@@ -13284,13 +13328,107 @@ else
   have_mbstate_t=no
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_mbstate_t" >&5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_mbstate_t" >&5
 $as_echo "$have_mbstate_t" >&6; }
-  if test x"$have_mbstate_t" = xyes; then
+if test x"$have_mbstate_t" = xyes; then
 
 $as_echo "#define HAVE_MBSTATE_T 1" >>confdefs.h
 
-  fi
+fi
+
+# Copied from gnulib stat-time.m4.
+# We should just switch over to using gnulib.
+ac_fn_c_check_member "$LINENO" "struct stat" "st_atim.tv_nsec" "ac_cv_member_struct_stat_st_atim_tv_nsec" "#include <sys/types.h>
+   #include <sys/stat.h>
+"
+if test "x$ac_cv_member_struct_stat_st_atim_tv_nsec" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC 1
+_ACEOF
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct stat.st_atim is of type struct timespec" >&5
+$as_echo_n "checking whether struct stat.st_atim is of type struct timespec... " >&6; }
+if ${ac_cv_typeof_struct_stat_st_atim_is_struct_timespec+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+          #include <sys/types.h>
+          #include <sys/stat.h>
+          #if HAVE_SYS_TIME_H
+          # include <sys/time.h>
+          #endif
+          #include <time.h>
+          struct timespec ts;
+          struct stat st;
+
+int
+main ()
+{
+
+          st.st_atim = ts;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_typeof_struct_stat_st_atim_is_struct_timespec=yes
+else
+  ac_cv_typeof_struct_stat_st_atim_is_struct_timespec=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_typeof_struct_stat_st_atim_is_struct_timespec" >&5
+$as_echo "$ac_cv_typeof_struct_stat_st_atim_is_struct_timespec" >&6; }
+   if test $ac_cv_typeof_struct_stat_st_atim_is_struct_timespec = yes; then
+
+$as_echo "#define TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC 1" >>confdefs.h
+
+   fi
+else
+  ac_fn_c_check_member "$LINENO" "struct stat" "st_atimespec.tv_nsec" "ac_cv_member_struct_stat_st_atimespec_tv_nsec" "#include <sys/types.h>
+      #include <sys/stat.h>
+"
+if test "x$ac_cv_member_struct_stat_st_atimespec_tv_nsec" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC 1
+_ACEOF
+
+
+else
+  ac_fn_c_check_member "$LINENO" "struct stat" "st_atimensec" "ac_cv_member_struct_stat_st_atimensec" "#include <sys/types.h>
+         #include <sys/stat.h>
+"
+if test "x$ac_cv_member_struct_stat_st_atimensec" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_ATIMENSEC 1
+_ACEOF
+
+
+else
+  ac_fn_c_check_member "$LINENO" "struct stat" "st_atim.st__tim.tv_nsec" "ac_cv_member_struct_stat_st_atim_st__tim_tv_nsec" "#include <sys/types.h>
+            #include <sys/stat.h>
+"
+if test "x$ac_cv_member_struct_stat_st_atim_st__tim_tv_nsec" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC 1
+_ACEOF
+
+
+fi
+
+fi
+
+fi
+
+fi
+
 
 # Some systems have frexp only in -lm, not in -lc.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing frexp" >&5
index 3c5a8e13da3ca88474050dd66f4c20540e2fd494..b8ab642e06880031f7db1f7afde5ba434d5dc0b9 100644 (file)
@@ -182,24 +182,57 @@ AC_CHECK_SIZEOF([long long])
 # plugin-api.h tests HAVE_STDINT_H and HAVE_INTTYPES_H
 # Besides those, we need to check anything used in binutils/ not in C99.
 AC_CHECK_HEADERS(fcntl.h inttypes.h stdint.h sys/file.h \
-                sys/stat.h sys/types.h unistd.h)
+                sys/stat.h sys/time.h sys/types.h unistd.h)
 AC_HEADER_SYS_WAIT
 AC_FUNC_MMAP
-AC_CHECK_FUNCS(getc_unlocked sbrk utimes)
-AC_CHECK_FUNC([mkstemp],
-             AC_DEFINE([HAVE_MKSTEMP], 1,
-             [Define to 1 if you have the `mkstemp' function.]))
-AC_CHECK_FUNC([mkdtemp],
-              AC_DEFINE([HAVE_MKDTEMP], 1,
-              [Define to 1 if you have the `mkdtemp' function.]))
-  AC_MSG_CHECKING([for mbstate_t])
-  AC_TRY_COMPILE([#include <wchar.h>],
-  [mbstate_t teststate;],
-  have_mbstate_t=yes, have_mbstate_t=no)
-  AC_MSG_RESULT($have_mbstate_t)
-  if test x"$have_mbstate_t" = xyes; then
-    AC_DEFINE(HAVE_MBSTATE_T,1,[Define if mbstate_t exists in wchar.h.])
-  fi
+AC_CHECK_FUNCS(getc_unlocked mkdtemp mkstemp sbrk utimensat utimes)
+
+AC_MSG_CHECKING([for mbstate_t])
+AC_TRY_COMPILE([#include <wchar.h>],
+[mbstate_t teststate;],
+have_mbstate_t=yes, have_mbstate_t=no)
+AC_MSG_RESULT($have_mbstate_t)
+if test x"$have_mbstate_t" = xyes; then
+  AC_DEFINE(HAVE_MBSTATE_T,1,[Define if mbstate_t exists in wchar.h.])
+fi
+
+# Copied from gnulib stat-time.m4.
+# We should just switch over to using gnulib.
+AC_CHECK_MEMBERS([struct stat.st_atim.tv_nsec],
+  [AC_CACHE_CHECK([whether struct stat.st_atim is of type struct timespec],
+     [ac_cv_typeof_struct_stat_st_atim_is_struct_timespec],
+     [AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+        [[
+          #include <sys/types.h>
+          #include <sys/stat.h>
+          #if HAVE_SYS_TIME_H
+          # include <sys/time.h>
+          #endif
+          #include <time.h>
+          struct timespec ts;
+          struct stat st;
+        ]],
+        [[
+          st.st_atim = ts;
+        ]])],
+        [ac_cv_typeof_struct_stat_st_atim_is_struct_timespec=yes],
+        [ac_cv_typeof_struct_stat_st_atim_is_struct_timespec=no])])
+   if test $ac_cv_typeof_struct_stat_st_atim_is_struct_timespec = yes; then
+     AC_DEFINE([TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC], [1],
+       [Define to 1 if the type of the st_atim member of a struct stat is
+        struct timespec.])
+   fi],
+  [AC_CHECK_MEMBERS([struct stat.st_atimespec.tv_nsec], [],
+     [AC_CHECK_MEMBERS([struct stat.st_atimensec], [],
+        [AC_CHECK_MEMBERS([struct stat.st_atim.st__tim.tv_nsec], [], [],
+           [#include <sys/types.h>
+            #include <sys/stat.h>])],
+        [#include <sys/types.h>
+         #include <sys/stat.h>])],
+     [#include <sys/types.h>
+      #include <sys/stat.h>])],
+  [#include <sys/types.h>
+   #include <sys/stat.h>])
 
 # Some systems have frexp only in -lm, not in -lc.
 AC_SEARCH_LIBS(frexp, m)
index 969acc12b30600f6bc5825678a4716e39d61d91f..2cbe46497aba00a2469cbc1346a429607483fc8c 100644 (file)
 #include "bfd.h"
 #include "bucomm.h"
 
-#ifdef HAVE_GOOD_UTIME_H
-#include <utime.h>
-#elif defined HAVE_UTIMES
+#if defined HAVE_UTIMES
 #include <sys/time.h>
+#elif defined HAVE_GOOD_UTIME_H
+#include <utime.h>
 #endif
 
 /* The number of bytes to copy at once.  */
@@ -86,6 +86,77 @@ simple_copy (int fromfd, const char *to,
   return 0;
 }
 
+/* The following defines and inline functions are copied from gnulib.
+   FIXME: Use a gnulib import and stat-time.h instead.  */
+#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+# if defined TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC
+#  define STAT_TIMESPEC(st, st_xtim) ((st)->st_xtim)
+# else
+#  define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim.tv_nsec)
+# endif
+#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC
+# define STAT_TIMESPEC(st, st_xtim) ((st)->st_xtim##espec)
+#elif defined HAVE_STRUCT_STAT_ST_ATIMENSEC
+# define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim##ensec)
+#elif defined HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC
+# define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim.st__tim.tv_nsec)
+#endif
+
+/* Return the nanosecond component of *ST's access time.  */
+inline long int
+get_stat_atime_ns (struct stat const *st)
+{
+# if defined STAT_TIMESPEC
+  return STAT_TIMESPEC (st, st_atim).tv_nsec;
+# elif defined STAT_TIMESPEC_NS
+  return STAT_TIMESPEC_NS (st, st_atim);
+# else
+  return 0;
+# endif
+}
+
+/* Return the nanosecond component of *ST's data modification time.  */
+inline long int
+get_stat_mtime_ns (struct stat const *st)
+{
+# if defined STAT_TIMESPEC
+  return STAT_TIMESPEC (st, st_mtim).tv_nsec;
+# elif defined STAT_TIMESPEC_NS
+  return STAT_TIMESPEC_NS (st, st_mtim);
+# else
+  return 0;
+# endif
+}
+
+/* Return *ST's access time.  */
+inline struct timespec
+get_stat_atime (struct stat const *st)
+{
+#ifdef STAT_TIMESPEC
+  return STAT_TIMESPEC (st, st_atim);
+#else
+  struct timespec t;
+  t.tv_sec = st->st_atime;
+  t.tv_nsec = get_stat_atime_ns (st);
+  return t;
+#endif
+}
+
+/* Return *ST's data modification time.  */
+inline struct timespec
+get_stat_mtime (struct stat const *st)
+{
+#ifdef STAT_TIMESPEC
+  return STAT_TIMESPEC (st, st_mtim);
+#else
+  struct timespec t;
+  t.tv_sec = st->st_mtime;
+  t.tv_nsec = get_stat_mtime_ns (st);
+  return t;
+#endif
+}
+/* End FIXME.  */
+
 /* Set the times of the file DESTINATION to be the same as those in
    STATBUF.  */
 
@@ -93,20 +164,25 @@ void
 set_times (const char *destination, const struct stat *statbuf)
 {
   int result;
-#ifdef HAVE_GOOD_UTIME_H
-  struct utimbuf tb;
-
-  tb.actime = statbuf->st_atime;
-  tb.modtime = statbuf->st_mtime;
-  result = utime (destination, &tb);
+#if defined HAVE_UTIMENSAT
+  struct timespec times[2];
+  times[0] = get_stat_atime (statbuf);
+  times[1] = get_stat_mtime (statbuf);
+  result = utimensat (AT_FDCWD, destination, times, 0);
 #elif defined HAVE_UTIMES
   struct timeval tv[2];
 
   tv[0].tv_sec = statbuf->st_atime;
-  tv[0].tv_usec = 0;
+  tv[0].tv_usec = get_stat_atime_ns (statbuf) / 1000;
   tv[1].tv_sec = statbuf->st_mtime;
-  tv[1].tv_usec = 0;
+  tv[1].tv_usec = get_stat_mtime_ns (statbuf) / 1000;
   result = utimes (destination, tv);
+#elif defined HAVE_GOOD_UTIME_H
+  struct utimbuf tb;
+
+  tb.actime = statbuf->st_atime;
+  tb.modtime = statbuf->st_mtime;
+  result = utime (destination, &tb);
 #else
   long tb[2];
 
index ef251876a0234f8cedb6abccccb32bd250eea39e..e1df9ff748d098e8dafb988701a2a1f8c8aa9ab1 100644 (file)
@@ -77,6 +77,7 @@ proc objcopy_test {testname srcfile type asflags ldflags} {
            return
        }
        set xflags "--preserve-dates"
+       sleep 1
     }
 
     set got [binutils_run $OBJCOPY "$OBJCOPYFLAGS $xflags $t_tempfile $t_copyfile"]
@@ -102,6 +103,17 @@ proc objcopy_test {testname srcfile type asflags ldflags} {
 
        if [string equal "" $exec_output] then {
            pass "objcopy $type ($testname)"
+           if { $type == "executable" } {
+               set dir [file dirname $t_copyfile]
+               set f2 [file tail $t_copyfile]
+               set status [remote_exec host find "$dir -name $f2 -newer $t_tempfile -print"]
+               set exec_output [lindex $status 1]
+               if [string equal "" $exec_output] then {
+                   pass "objcopy $type ($testname) timestamp"
+               } else {
+                   fail "objcopy $type ($testname) timestamp"
+               }
+           }
        } else {
            send_log "$exec_output\n"
            verbose "$exec_output" 1