Once a temporary file is created, a program must release it when finished with it. Otherwise, the temporary file directory will fill with many abandoned files over time. Calling unlink(2) is trivial, but making sure it is done when the program prematurely exits is more of a challenge.
One way to make sure that the temporary file is released is to release it immediately after it is created and opened. This looks illogical to those who are new to UNIX, but a UNIX file can exist after it has been unlinked, as long as the file remains open. When the last open file descriptor for the file is closed, the disk space is reclaimed by the UNIX kernel.
Recall function tmpfile(3), which creates temporary files with no pathname. It uses this general procedure:
Generate a unique temporary filename.
Create and open the file.
Call unlink(2) on the temporary filename. This effectively makes the file nameless, but the file itself exists as long as it remains open.
Call fdopen(3) to open a FILE stream, using the open file descriptor from step 2.
Return the FILE stream pointer to the caller.
This temporary but nameless file has two advantages:
The file has already been released. No temporary file cleanup is required.
No other process can subsequently open and tamper with the temporary file. This also provides a measure of privacy.
The second point is still subject to a window of opportunity, since the file must be created and then passed to unlink(2). However, the main advantage presented here is that no matter how your program exits or aborts, the temporary file will not be left in a directory, since it has already been unlinked.
There are situations in which the unlink(2) approach is not convenient. If the file must be closed and then reopened, then you have no choice but to keep a name associated with the temporary file. For this reason, the C programmer must rely on other methods, such as the atexit(3) function.
The C library function atexit(3) allows the programmer to register a function that can be used for all types of cleanup tasks. Of primary interest here is the removal of temporary files. The function synopsis for atexit(3) is as follows:
#include <stdlib.h> int atexit(void (*func)(void));
The argument provided to atexit(3) is simply the function pointer to a function, declared as follows:
void func(void) { /* My cleanup code… */ }
The function atexit(3) returns 0 when it registers the function successfully and returns non-zero when it fails. FreeBSD returns -1 and an error code in errno when atexit(3) fails, but be sure to read the Warning in this section about this. For maximum portability, it is best to test for zero to see if atexit(3) succeeded.
The functions registered by atexit(3) are called in the reverse order from which they are registered.
Note
FreeBSD and UnixWare 7 document that a minimum of 32 functions may be registered. Additional entries are limited only by available memory. Linux appears to support as many registrations as remaining memory permits.
HPUX 11 and IBM's AIX 4.3 state that the atexit(3) function is limited to a maximum of ATEXIT_MAX registered functions. For HPUX, this is defined by the include file <limits.h>; for AIX, it is <sys/limits.h>.
The limit for Solaris 8 is defined by sysconf(3C) using the parameter _SC_ATEXIT_MAX.
Warning
FreeBSD documents that atexit(3) sets errno when -1 is returned (ENOMEM is one documented error returned). Linux (Red Hat 6.0) documentation states that atexit(3) returns -1 if it fails, and errno is not set.
SGI's IRIX 6.5, UnixWare 7, and HPUX 11 document that they return "non-zero when [they] fail." No error codes for errno are documented.
For these reasons, always test for a successful return (a 0 return) of atexit(3) for maximum portability. Additionally, the errno code should be ignored unless the specific platform is taken into account.
The program in Listing 8.6 shows an example that calls on the atexit(3) function. This causes a cleanup function to be called upon program termination.
An examination of the program shows that first the mr_clean() function is registered with atexit(3), in line 49. Lines 54–72 create a temporary file, write to it, and then close it. The program takes a normal exit in line 73.
Exiting causes the registered function mr_clean() to be called to release the temporary file that was created. This is demonstrated by the compile and run session shown, as follows:
$ make atexit cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall atexit.c cc atexit.o -o atexit $ ./atexit Temp. file is /tmp/tmp-D52582 mr_clean() started: unlinking temp. file /tmp/tmp-D52582 mr_clean() ended. $
The program announces (line 58 of Listing 8.6) that it has created the temporary file /tmp/tmp-D52582 and then silently returns from the main() program (line 73). This causes the registered cleanup function mr_clean() to be called, which then produces the last three lines of output, indicating that it has called unlink(2) to remove the temporary file.
One of the major portability concerns that you should bear in mind is that some platforms will limit the number of registered functions to a maximum of 32. This is especially critical if you are designing a C library, where you have no direct control over how the user is using atexit(3). If the caller of your library has already used up all 32 possible registrations, then your library will be out of luck.
One way that this problem can be circumvented is by registering one special function, which can then invoke as many additional cleanup functions as you choose.
The C++ programmer has the capability to rely on destructors for cleanup operations. Listing 8.7 shows a very simple example of a class named Temp that makes use of a temporary file.
The program shown in Listing 8.7 declares a class Temp in lines 20–29. The class method Temp::printf() allows the caller to format a text line to be written to the temporary file. Method Temp::rewind() rewinds the temporary file, and method Temp::gets() allows the caller to retrieve one text line from the temporary file.
The constructor is implemented in lines 36–51. Note the call to the C function tempnam(3) in line 41, and the call to fopen(3) in line 47 to create and open the file. The pathname is stored in private member tf_path, and the open FILE is saved in private member tf (declared in lines 21 and 22).
When the Temp object is destroyed, the destructor, which is implemented in lines 57–63, is called upon. The destructor closes the temporary file, deletes the pathname of the file, and then frees the pathname string (lines 58–60).
The main() program constructs one instance of the Temp class in line 119 (the object is named tf). The object is destroyed when the main() program exits in line 145 (the return statement).
Lines 129–142 simply exercise some of the methods of the object tf. One text line is written to the temporary file, the file is rewound, and the one text line is read back.
Compiling and running this program should yield results similar to the following:
$ make destruct cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall -fhandle-exceptions destruct.cc cc destruct.o -o destruct -lstdc++ $ ./destruct PID 52982 started: Read back: Created temp file: /tmp/tmp-Q52982 Now exiting.. Temp::~Temp() called. $
The line starting with Read back: shows how the temporary file was being exercised. The line Temp::~Temp() called. shows the output from the write(2) call in line 62 of the destructor, proving that the destructor was called. In fact, if the pathname is checked, it will be nonexistent:
$ ls -l /tmp/tmp-Q52982
ls: /tmp/tmp-Q52982: No such file or directory
$
This proves that the destructor did its job.
While this technique seems to address the cleanup issue, you should be aware that pitfalls still exist. For example, if you change the statement in line 145 that now reads return 0; to read exit(0);, you will discover that the destructor for the object tf is not called. If your application has calls to exit(3) sprinkled throughout, you may still wish to use the services of the atexit(3) function.
Sometimes it is necessary for a program to exit without invoking any cleanup at all. This is highly desirable when something has gone wrong and you want your program to leave things as they are. This allows you to keep all temporary files around so that they can be inspected for troubleshooting purposes. This can be done with the _exit(2) function:
#include <unistd.h> void _exit(int status);
The function is called in the same manner as exit(3), except that no atexit(3) processing is invoked when _exit(2) is called.
18.218.156.35