algo-geeks
algo-geeks

Reputation: 5438

Remove all files before application exits

I am working on some code which is the back-end for a tool. This tool creates all its output in a fixed directory. Now suppose at any time a client wants to kill all its applications, then all files should be deleted. So I use the system command (rm -rf) to remove the directory, but files which are open are not deleted, and hence the directory is also not deleted. How can I do this smartly?

One option seems to be to use a table maintaining all the open files and closing them all before firing rm -rf on the directory. However, this may slow down the whole application.

Another option I explored is using lsof to find a list of open files and close them all before rm -rf.

An additional constraint on my issue is that I have open several different files in a process, and one of them is in use by another process. Using the above methods, if I close all the file descriptors and try to delete the directory, logically it should not be deleted because there are still files opened by another process.

Any suggestions would be appreciated.

Upvotes: 3

Views: 2004

Answers (8)

jim mcnamara
jim mcnamara

Reputation: 16389

use getdtablesize, assuming you do not want to close stdin, stdout, stderr:

for(i=3;i<getdtablesize(); i++)
     ckerr(close(i));

where ckerr is an error check function. Note this conforms to old standards:

        SVr4, 4.4BSD (the getdtablesize() function first appeared in 4.2BSD). 
    It is not specified in POSIX.1-2001; portable applications should employ
 sysconf(_SC_OPEN_MAX) instead of this call.

So this is purely a poor man's alternative.... but it is not deprecated, yet.

Consider calling nftw() or ftw() to clean up your working director(ies), you can execute a remove() in the callback function.

Upvotes: 0

ams
ams

Reputation: 25579

The tmpfile function might help you? This creates a temporary file that is automatically deleted on program exit (I think abort might override this though).

If that doesn't work, note that (on Linux, at least) it is possible to delete files before they are closed. They disappear from the directory listing, but any program that has the file open can still access it. As soon as all the programs close the file it's gone for good. Until then, the only way to reopen them is to look in /proc/<pid>/fd/<num>. This is a trick used by the flash plugin to make it harder to grab video streams.

Upvotes: 0

In addition to the other answers, if you are sure that all the files are created in a known temporary directory, and if the program terminates normally (by calling exit or returning from main) your could use atexit to register a clean-up function which would use opendir and readdir to collect all the file names and remove them, then rmdir the directory itself.

I'm doing similar things in my gcc/melt-runtime.c file, function do_finalize_melt near lines 10390.

You could also register with atexit a function calling system("rm -rf /path/to/your/dir") or even perhaps running a script which would queue to the batch system another script doing the rm -rf or just calling atexit(myexit_handler) with

void myexit_handler(void)
{
   FILE* batchf = popen("/usr/bin/batch", "w");
   /* get some sleep to be sure our process had time to exit */
   fprintf (batchf, "/bin/sleep 5\n"); 
   fprintf (batchf, "/bin/rm -rf %s\n", yourtempdir);
   pclose (batchf);
}

I am assuming yourtempdir has no naughty characters inside (e.g. no spaces or quotes or backslashes ...)

The Linux semantics is that files which are opened but have been removed are deleted when the program exits. If these are NFS files (but putting a temporary directory on the network is IMHO a mistake, because you want it to be fast!) then indeed some .nfs* files are remaining on the NFS server (and you need some way to remove them, perhaps by ssh).

You should not try to remove your directory before the application exits, but just after it exits, otherwise still opened files will not be removed inside.

Upvotes: 1

sirgeorge
sirgeorge

Reputation: 6531

From 'fcloseall' man page:

#define _GNU_SOURCE
/* See feature_test_macros(7) *
/#include <stdio.h>
int fcloseall(void);

This should work assuming that your application uses 'fopen' and friends :)

Upvotes: 1

mark4o
mark4o

Reputation: 60863

There should only be an issue with removing the entire directory if some process has files in the directory open and the directory is on a remote disk. In that case, removing a file that is open may cause it to be renamed, so that the remote system can maintain its reference to the file. If the file is on a local disk there is no issue with maintaining a reference to the deleted file.

When the process that has the files open is terminated, the files will be closed automatically by the OS. If no other process has any of the files open then there should be no issue removing the directory (assuming no other issues such as file permissions). So you just need to ensure that the processes that have the files open are terminated before the directory is removed.

Upvotes: 1

Dan Fego
Dan Fego

Reputation: 14004

I think using a script is a good idea for your instance, and Coren's answer is precise. That being said, I've seen programs just loop through a substantial range of file descriptors to close all of them. It has the potential for error if you have more than the range you loop through, but something like this shouldn't hurt an application if you're intending to close all your descriptors, provided they are within this limit:

for (int i = 0; i < 4096; i++) {
    close(i);
}

In order to have this done before your application exits, you can set a signal handler, as mentioned and linked by Coren. Make sure if you use signals and a handler that you do as little as possible in the signal handler, i.e. just set a flag and have your program do its cleanup.

Edit: Also, this answer from another post on StackOverflow gives both examples of how to do this (#3 is like Coren's and is for Linux), but also has a couple of links to actual source code that handle this stuff. You might want to check it out!

Upvotes: 0

Coren
Coren

Reputation: 5637

On *nix system, you can catch the kill signal of an application with a mask and signal system call. See this question for sample code.

In order to list open files, you can simply take a look at /proc/**PID**/fd. You'll have the same content than lsof :

lr-x------ 1 user user 64 2012-02-09 08:40 0 -> /dev/null
l-wx------ 1 user user 64 2012-02-09 08:40 1 -> /home/user/.xsession-errors
lrwx------ 1 user user 64 2012-02-09 08:40 11 -> /etc/passwd
l-wx------ 1 user user 64 2012-02-09 08:40 2 -> /home/user/.xsession-errors
lrwx------ 1 user user 64 2012-02-09 08:40 3 -> socket:[15449]
lr-x------ 1 user user 64 2012-02-09 08:40 4 -> socket:[15450]
l-wx------ 1 user user 64 2012-02-09 08:40 5 -> pipe:[11740]
l-wx------ 1 user user 64 2012-02-09 08:40 6 -> socket:[15448]
lrwx------ 1 user user 64 2012-02-09 08:40 7 -> anon_inode:[eventfd]
lrwx------ 1 user user 64 2012-02-09 08:40 8 -> pipe:[11740]
lrwx------ 1 user user 64 2012-02-09 08:40 9 -> /dev/urandom

With this list, you'll be able to grep files interesting you and close them by fd in the signal handler, just before leaving.

Upvotes: 2

Ed Heal
Ed Heal

Reputation: 60007

Why not wrap the program in a script?

e.g.

#!/bin/sh

mkdir <some directory>
/path/to/program <some directory to let the program know where to put stuff>
rm -fr <the same directory>

And run this instead of running the program directly

Upvotes: 3

Related Questions