1/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10** GNU General Public License for more details.
11*/
12
13#include "android/utils/tempfile.h"
14#include "android/utils/bufprint.h"
15#include "android/utils/debug.h"
16
17#include <stdlib.h>
18#include <string.h>
19#include <fcntl.h>
20
21#ifdef _WIN32
22#  define WIN32_LEAN_AND_MEAN
23#  include <windows.h>
24#else
25#  include <unistd.h>
26#endif
27
28#define  D(...)  ((void)0)
29
30/** TEMP FILE SUPPORT
31 **
32 ** simple interface to create an empty temporary file on the system.
33 **
34 ** create the file with tempfile_create(), which returns a reference to a TempFile
35 ** object, or NULL if your system is so weird it doesn't have a temporary directory.
36 **
37 ** you can then call tempfile_path() to retrieve the TempFile's real path to open
38 ** it. the returned path is owned by the TempFile object and should not be freed.
39 **
40 ** all temporary files are destroyed when the program quits, unless you explicitely
41 ** close them before that with tempfile_close()
42 **/
43
44struct TempFile
45{
46    const char*  name;
47    TempFile*    next;
48};
49
50static void       tempfile_atexit();
51static TempFile*  _all_tempfiles;
52
53TempFile*
54tempfile_create( void )
55{
56    TempFile*    tempfile;
57    const char*  tempname = NULL;
58
59#ifdef _WIN32
60    char  temp_namebuff[MAX_PATH];
61    char  temp_dir[MAX_PATH];
62    char  *p = temp_dir, *end = p + sizeof(temp_dir);
63    UINT  retval;
64
65    p = bufprint_temp_dir( p, end );
66    if (p >= end) {
67        D( "TEMP directory path is too long" );
68        return NULL;
69    }
70
71    retval = GetTempFileName(temp_dir, "TMP", 0, temp_namebuff);
72    if (retval == 0) {
73        D( "can't create temporary file in '%s'", temp_dir );
74        return NULL;
75    }
76
77    tempname = temp_namebuff;
78#else
79#define  TEMPLATE  "/tmp/.android-emulator-XXXXXX"
80    int   tempfd = -1;
81    char  template[512];
82    char  *p = template, *end = p + sizeof(template);
83
84    p = bufprint_temp_file( p, end, "emulator-XXXXXX" );
85    if (p >= end) {
86        D( "Xcannot create temporary file in /tmp/android !!" );
87        return NULL;
88    }
89
90    D( "template: %s", template );
91    tempfd = mkstemp( template );
92    if (tempfd < 0) {
93        D("cannot create temporary file in /tmp/android !!");
94        return NULL;
95    }
96    close(tempfd);
97    tempname = template;
98#endif
99    tempfile = malloc( sizeof(*tempfile) + strlen(tempname) + 1 );
100    tempfile->name = (char*)(tempfile + 1);
101    strcpy( (char*)tempfile->name, tempname );
102
103    tempfile->next = _all_tempfiles;
104    _all_tempfiles = tempfile;
105
106    if ( !tempfile->next ) {
107        atexit( tempfile_atexit );
108    }
109
110    return tempfile;
111}
112
113const char*
114tempfile_path(TempFile*  temp)
115{
116    return temp ? temp->name : NULL;
117}
118
119void
120tempfile_close(TempFile*  tempfile)
121{
122#ifdef _WIN32
123    DeleteFile(tempfile->name);
124#else
125    unlink(tempfile->name);
126#endif
127}
128
129/** TEMP FILE CLEANUP
130 **
131 **/
132
133/* we don't expect to use many temporary files */
134#define MAX_ATEXIT_FDS  16
135
136typedef struct {
137    int   count;
138    int   fds[ MAX_ATEXIT_FDS ];
139} AtExitFds;
140
141static void
142atexit_fds_add( AtExitFds*  t, int  fd )
143{
144    if (t->count < MAX_ATEXIT_FDS)
145        t->fds[t->count++] = fd;
146    else {
147        dwarning("%s: over %d calls. Program exit may not cleanup all temporary files",
148            __FUNCTION__, MAX_ATEXIT_FDS);
149    }
150}
151
152static void
153atexit_fds_del( AtExitFds*  t, int  fd )
154{
155    int  nn;
156    for (nn = 0; nn < t->count; nn++)
157        if (t->fds[nn] == fd) {
158            /* move the last element to the current position */
159            t->count  -= 1;
160            t->fds[nn] = t->fds[t->count];
161            break;
162        }
163}
164
165static void
166atexit_fds_close_all( AtExitFds*  t )
167{
168    int  nn;
169    for (nn = 0; nn < t->count; nn++)
170        close(t->fds[nn]);
171}
172
173static AtExitFds   _atexit_fds[1];
174
175void
176atexit_close_fd(int  fd)
177{
178    if (fd >= 0)
179        atexit_fds_add(_atexit_fds, fd);
180}
181
182void
183atexit_close_fd_remove(int  fd)
184{
185    if (fd >= 0)
186        atexit_fds_del(_atexit_fds, fd);
187}
188
189static void
190tempfile_atexit( void )
191{
192    TempFile*  tempfile;
193
194    atexit_fds_close_all( _atexit_fds );
195
196    for (tempfile = _all_tempfiles; tempfile; tempfile = tempfile->next)
197        tempfile_close(tempfile);
198}
199