help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Question] Reliable way to clean up a directory in EXIT trap


From: Masahiro Yamada
Subject: Re: [Question] Reliable way to clean up a directory in EXIT trap
Date: Wed, 20 Jul 2022 00:24:36 +0900

Hi,

On Tue, Jul 19, 2022 at 6:56 PM Koichi Murase <myoga.murase@gmail.com> wrote:
>
> 2022年7月19日(火) 18:04 Masahiro Yamada <masahiroy@kernel.org>:
> > Here is even simpler test code.
>
> Here is a reduced case:
>
> $ bash -c 'for i in {0..999}; do (trap "rm -rf $BASHPID.tmp" EXIT; >>
> "$BASHPID.tmp") done'
> ^C

Thanks.
This also reproduced the issue.



>
> I'm not sure if we should clean up temporary files on the signals in
> general. For example, it is also possible that we would like to check
> the intermediate state of the processing after canceling the command
> by INT, for which we want to preserve the intermediate state of the
> temporary files. Naively, I would expect programs to terminate without
> changing the current status of the filesystem by default.



Actually, my main motivation is to make the Linux kernel build system
(written by GNU Make) more reliable.


GNU Make is one of programs that need to clean up files on termination.
(Of course, we cannot do anything against SIGKILL, but GNU Make traps
several signals to clean up files)

If Make is interrupted and results in producing an incomplete file,
it must be deleted so that it will be remade from scratch on the next
run of make.
Otherwise, Make will never rebuild it because the timestamp already
became newer than that of any of its prerequisites. (but the content is
still incomplete).

Actually, GNU Make supports ".DELETE_ON_ERROR" to remove
partially-updated files on any error (including interruption).  [1]

[1]: https://www.gnu.org/software/make/manual/html_node/Special-Targets.html

Seems to be a perfect story?

No, actually this does not work when Make is piped to another program because
Make dies with SIGPIPE before the cleanup.  [2]

[2] https://lists.gnu.org/archive/html/help-make/2021-06/msg00001.html


To cover this corner-case, I tried to remove the left-over files in the trap
in the recipe lines.





If you search in Google, you can find a lot of pages
that suggest the EXIT trap:


http://redsymbol.net/articles/bash-exit-traps/
https://www.putorius.net/using-trap-to-exit-bash-scripts-cleanly.html
https://blog.gripdev.xyz/2020/05/11/cleanup-in-bash-scripts/



But, I noticed this does not work reliably.  Hence, this question.







> When you
> would like to clean up the temporary files on INT, I think that means
> you actually would like to override the behavior of INT. For this
> reason, I feel you should just trap INT as
>
> for i in {0..999}; do
>   (
>     tmp=$BASHPID.tmp
>     cleanup() { rm -rf "$tmp"; }
>     trapint() { cleanup; trap - INT; kill -INT "$BASHPID"; }
>     trap cleanup EXIT
>     trap trapint INT
>     >> "$tmp"
>   )
> done


I tested this code, and did not see any left-over file.

Why does "trap trapint INT"  make the difference?


This is my understanding:

 - If INT trap is not set, the default behavior is "exit the program"
 - Bash handles SIGINT in WCE (Wait and Cooperative Exit) manner.
   It waits if there is a foreground process running at the time when
   bash receives SIGINT
 - After that, SIGINT is handled (i.e. exit the program)
 - cleanup() is executed because the EXIT trap is run just before
    the shell terminates






>
> See also https://www.cons.org/cracauer/sigint.html for proper handling of INT.

Yes, I know this page.

This is how SIGINT is handled:

bash - WCE
dash - WUE
make - WUE


My previous comment:
> [3]
> Bash (and many other shells) does not immediately handle SIGINT
> if it is running another foreground process.
> When receiving SIGINT, Bash just _remembers_ the fact that it has
> received SIGINT.
> After the foregound process finishes, Bash handles the signal.

is what I learned from that page.






>
> --
> Koichi



--
Best Regards
Masahiro Yamada



reply via email to

[Prev in Thread] Current Thread [Next in Thread]