Improved the _exit_cleanup() function to handle potential recursions
authorWayne Davison <wayned@samba.org>
Sat, 14 Oct 2006 19:58:52 +0000 (19:58 +0000)
committerWayne Davison <wayned@samba.org>
Sat, 14 Oct 2006 19:58:52 +0000 (19:58 +0000)
back to the function via the calls that it is making.  The new code
treats each recursion back into the function as an opportunity to
take up where we left off (skipping whatever step just failed).

cleanup.c

index 2f47ef7..df8c95b 100644 (file)
--- a/cleanup.c
+++ b/cleanup.c
@@ -87,8 +87,6 @@ static pid_t cleanup_pid = 0;
 
 pid_t cleanup_child_pid = -1;
 
-int in_exit_cleanup = 0;
-
 /**
  * Eventually calls exit(), passing @p code, therefore does not return.
  *
@@ -96,69 +94,116 @@ int in_exit_cleanup = 0;
  **/
 NORETURN void _exit_cleanup(int code, const char *file, int line)
 {
-       int ocode = code;
+       static int cleanup_step = 0;
+       static int exit_code = 0;
+       static int unmodified_code = 0;
 
        SIGACTION(SIGUSR1, SIG_IGN);
        SIGACTION(SIGUSR2, SIG_IGN);
 
-       in_exit_cleanup = 1;
+       if (exit_code) /* Preserve first error code when recursing. */
+               code = exit_code;
 
-       if (verbose > 3) {
-               rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): entered\n",
-                       code, file, line);
-       }
+       /* Some of our actions might cause a recursive call back here, so we
+        * keep track of where we are in the cleanup and never repeat a step. */
+       switch (cleanup_step) {
+       case 0:
+               cleanup_step++;
+
+               exit_code = unmodified_code = code;
 
-       if (cleanup_child_pid != -1) {
-               int status;
-               if (wait_process(cleanup_child_pid, &status, WNOHANG) == cleanup_child_pid) {
-                       status = WEXITSTATUS(status);
-                       if (status > code)
-                               code = status;
+               if (verbose > 3) {
+                       rprintf(FINFO,
+                               "_exit_cleanup(code=%d, file=%s, line=%d): entered\n",
+                               code, file, line);
                }
-       }
 
-       if (cleanup_got_literal && cleanup_fname && cleanup_new_fname
-        && keep_partial && handle_partial_dir(cleanup_new_fname, PDIR_CREATE)) {
-               char *fname = cleanup_fname;
-               cleanup_fname = NULL;
-               if (cleanup_fd_r != -1)
-                       close(cleanup_fd_r);
-               if (cleanup_fd_w != -1) {
-                       flush_write_file(cleanup_fd_w);
-                       close(cleanup_fd_w);
+               /* FALLTHROUGH */
+       case 1:
+               cleanup_step++;
+
+               if (cleanup_child_pid != -1) {
+                       int status;
+                       int pid = wait_process(cleanup_child_pid, &status, WNOHANG);
+                       if (pid == cleanup_child_pid) {
+                               status = WEXITSTATUS(status);
+                               if (status > code)
+                                       code = exit_code = status;
+                       }
                }
-               finish_transfer(cleanup_new_fname, fname, NULL,
-                               cleanup_file, 0, !partial_dir);
-       }
-       io_flush(FULL_FLUSH);
-       if (cleanup_fname)
-               do_unlink(cleanup_fname);
-       if (code)
-               kill_all(SIGUSR1);
-       if (cleanup_pid && cleanup_pid == getpid()) {
-               char *pidf = lp_pid_file();
-               if (pidf && *pidf)
-                       unlink(lp_pid_file());
-       }
 
-       if (code == 0) {
-               if (io_error & IOERR_DEL_LIMIT)
-                       code = RERR_DEL_LIMIT;
-               if (io_error & IOERR_VANISHED)
-                       code = RERR_VANISHED;
-               if (io_error & IOERR_GENERAL || log_got_error)
-                       code = RERR_PARTIAL;
-       }
+               /* FALLTHROUGH */
+       case 2:
+               cleanup_step++;
+
+               if (cleanup_got_literal && cleanup_fname && cleanup_new_fname
+                && keep_partial && handle_partial_dir(cleanup_new_fname, PDIR_CREATE)) {
+                       char *fname = cleanup_fname;
+                       cleanup_fname = NULL;
+                       if (cleanup_fd_r != -1)
+                               close(cleanup_fd_r);
+                       if (cleanup_fd_w != -1) {
+                               flush_write_file(cleanup_fd_w);
+                               close(cleanup_fd_w);
+                       }
+                       finish_transfer(cleanup_new_fname, fname, NULL,
+                                       cleanup_file, 0, !partial_dir);
+               }
+
+               /* FALLTHROUGH */
+       case 3:
+               cleanup_step++;
+
+               io_flush(FULL_FLUSH);
+
+               /* FALLTHROUGH */
+       case 4:
+               cleanup_step++;
+
+               if (cleanup_fname)
+                       do_unlink(cleanup_fname);
+               if (code)
+                       kill_all(SIGUSR1);
+               if (cleanup_pid && cleanup_pid == getpid()) {
+                       char *pidf = lp_pid_file();
+                       if (pidf && *pidf)
+                               unlink(lp_pid_file());
+               }
+
+               if (code == 0) {
+                       if (io_error & IOERR_DEL_LIMIT)
+                               code = exit_code = RERR_DEL_LIMIT;
+                       if (io_error & IOERR_VANISHED)
+                               code = exit_code = RERR_VANISHED;
+                       if (io_error & IOERR_GENERAL || log_got_error)
+                               code = exit_code = RERR_PARTIAL;
+               }
+
+               if (code || am_daemon || (logfile_name && (am_server || !verbose)))
+                       log_exit(code, file, line);
+
+               /* FALLTHROUGH */
+       case 5:
+               cleanup_step++;
+
+               if (verbose > 2) {
+                       rprintf(FINFO,
+                               "_exit_cleanup(code=%d, file=%s, line=%d): "
+                               "about to call exit(%d)\n",
+                               unmodified_code, file, line, code);
+               }
+
+               /* FALLTHROUGH */
+       case 6:
+               cleanup_step++;
 
-       if (code || am_daemon || (logfile_name && (am_server || !verbose)))
-               log_exit(code, file, line);
+               close_all();
 
-       if (verbose > 2) {
-               rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): about to call exit(%d)\n",
-                       ocode, file, line, code);
+               /* FALLTHROUGH */
+       default:
+               break;
        }
 
-       close_all();
        exit(code);
 }