summaryrefslogtreecommitdiff
path: root/src/cleanup.c
blob: 1e12e83a4fd4f509e99e6ec034617de9d093b506 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* -*- c-file-style: "java"; indent-tabs-mode: nil; tab-width: 4; fill-column: 78 -*-
 *
 * distcc -- A simple distributed compiler system
 *
 * Copyright (C) 2002, 2003, 2004 by Martin Pool
 * Copyright 2007 Google Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 */

        /* "I have not come through fire and death to bandy crooked
         * words with a serving-man until the lightning falls!"
         *      -- Gandalf (BBC LoTR radio play) */


#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>

#include <sys/stat.h>
#include <sys/types.h>

#include "distcc.h"
#include "exitcode.h"
#include "trace.h"
#include "util.h"

/**
 * A list of files that need to be cleaned up on exit.
 *
 * Volatile because it can be read from signal handlers.
 **/
char *volatile *volatile cleanups = 0;   /* Dynamically allocated array. */
volatile int cleanups_size = 0; /* The length of the array. */
volatile int n_cleanups = 0;    /* The number of entries used. */

static void dcc_cleanup_tempfiles_inner(int from_signal_handler);

void dcc_cleanup_tempfiles(void)
{
    dcc_cleanup_tempfiles_inner(0);
}

void dcc_cleanup_tempfiles_from_signal_handler(void)
{
    dcc_cleanup_tempfiles_inner(1);
}

/*
 * You can call this at any time, or hook it into atexit().  It is
 * safe to call repeatedly.
 *
 * If from_signal_handler (a boolean) is non-zero, it means that
 * we're being called from a signal handler, so we need to be
 * careful not to call malloc() or free() or any other functions
 * that might not be async-signal-safe.
 * (We do call the tracing functions, which is perhaps unsafe
 * because they call sprintf() if DISCC_VERBOSE=1 is enabled.
 * But in that case it is probably worth the very small risk
 * of a crash to get the full tracing output.)
 *
 * If $DISTCC_SAVE_TEMPS is set to "1", then files are not actually
 * deleted, which can be good for debugging.  However, we still need
 * to remove them from the list, otherwise it will eventually overflow
 * in prefork mode.
 */
static void dcc_cleanup_tempfiles_inner(int from_signal_handler)
{
    int i;
    int done = 0;
    int save = dcc_getenv_bool("DISTCC_SAVE_TEMPS", 0);

    /* do the unlinks from the last to the first file.
     * This way, directories get deleted after their files. */

     /* tempus fugit */
    for (i = n_cleanups - 1; i >= 0; i--) {
        if (save) {
            rs_trace("skip cleanup of %s", cleanups[i]);
        } else {
            /* Try removing it as a directory first, and
             * if that fails, try removing is as a file.
             * Report the error from removing-as-a-file
             * if both fail. */
            if ((rmdir(cleanups[i]) == -1) &&
                (unlink(cleanups[i]) == -1) &&
                (errno != ENOENT)) {
                rs_log_notice("cleanup %s failed: %s", cleanups[i],
                              strerror(errno));
            }
            done++;
        }
        n_cleanups = i;
        if (from_signal_handler) {
            /* It's not safe to call free() in this case.
             * Don't worry about the memory leak - we're about
             * to exit the process anyway. */
        } else {
            free(cleanups[i]);
        }
        cleanups[i] = NULL;
    }

    rs_trace("deleted %d temporary files", done);
}


/**
 * Add to the list of files to delete on exit.
 * If it runs out of memory, it returns non-zero.
 */
int dcc_add_cleanup(const char *filename)
{
    char *new_filename;
    int new_n_cleanups = n_cleanups + 1;

    /* Increase the size of the cleanups array, if needed.
     * We avoid using realloc() here, to ensure that 'cleanups' remains
     * valid at all times - we might get a signal in the middle here
     * that could call dcc_cleanup_tempfiles_from_signal_handler(). */
    if (new_n_cleanups > cleanups_size) {
        char **old_cleanups;
        int new_cleanups_size = (cleanups_size == 0 ? 10 : cleanups_size * 3);
        char **new_cleanups = malloc(new_cleanups_size * sizeof(char *));
        if (new_cleanups == NULL) {
            rs_log_crit("malloc failed - too many cleanups");
            return EXIT_OUT_OF_MEMORY;
        }
        memcpy(new_cleanups, (char **)cleanups, cleanups_size * sizeof(char *));
        old_cleanups = (char **)cleanups;
        cleanups = new_cleanups;           /* Atomic assignment. */
        cleanups_size = new_cleanups_size; /* Atomic assignment. */
        free(old_cleanups);
    }

    new_filename = strdup(filename);
    if (new_filename == NULL) {
        rs_log_crit("strdup failed - too many cleanups");
        return EXIT_OUT_OF_MEMORY;
    }

    cleanups[new_n_cleanups - 1] = new_filename; /* Atomic assignment. */
    n_cleanups = new_n_cleanups;                 /* Atomic assignment. */

    return 0;
}