resetting manifest requested domain to floor
[platform/upstream/ccache.git] / lockfile.c
1 /*
2  * Copyright (C) 2010-2011 Joel Rosdahl
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 3 of the License, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program; if not, write to the Free Software Foundation, Inc., 51
16  * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include "ccache.h"
20
21 /*
22  * This function acquires a lockfile for the given path. Returns true if the
23  * lock was acquired, otherwise false. If the lock has been considered stale
24  * for the number of microseconds specified by staleness_limit, the function
25  * will (if possible) break the lock and then try to acquire it again. The
26  * staleness limit should be reasonably larger than the longest time the lock
27  * can be expected to be held, and the updates of the locked path should
28  * probably be made with an atomic rename(2) to avoid corruption in the rare
29  * case that the lock is broken by another process.
30  */
31 bool
32 lockfile_acquire(const char *path, unsigned staleness_limit)
33 {
34         char *lockfile = format("%s.lock", path);
35         char *my_content = NULL, *content = NULL, *initial_content = NULL;
36         const char *hostname = get_hostname();
37         bool acquired = false;
38 #ifdef _WIN32
39         const size_t bufsize = 1024;
40         int fd, len;
41 #else
42         int ret;
43 #endif
44         unsigned to_sleep = 1000, slept = 0; /* Microseconds. */
45
46         while (1) {
47                 free(my_content);
48                 my_content = format("%s:%d:%d", hostname, (int)getpid(), (int)time(NULL));
49
50 #ifdef _WIN32
51                 fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0666);
52                 if (fd == -1) {
53                         cc_log("lockfile_acquire: open WRONLY %s: %s", lockfile, strerror(errno));
54                         if (errno != EEXIST) {
55                                 /* Directory doesn't exist or isn't writable? */
56                                 goto out;
57                         }
58                         /* Someone else has the lock. */
59                         fd = open(lockfile, O_RDONLY|O_BINARY);
60                         if (fd == -1) {
61                                 if (errno == ENOENT) {
62                                         /*
63                                          * The file was removed after the open() call above, so retry
64                                          * acquiring it.
65                                          */
66                                         continue;
67                                 } else {
68                                         cc_log("lockfile_acquire: open RDONLY %s: %s",
69                                                lockfile, strerror(errno));
70                                         goto out;
71                                 }
72                         }
73                         free(content);
74                         content = x_malloc(bufsize);
75                         if ((len = read(fd, content, bufsize - 1)) == -1) {
76                                 cc_log("lockfile_acquire: read %s: %s", lockfile, strerror(errno));
77                                 close(fd);
78                                 goto out;
79                         }
80                         close(fd);
81                         content[len] = '\0';
82                 } else {
83                         /* We got the lock. */
84                         if (write(fd, my_content, strlen(my_content)) == -1) {
85                                 cc_log("lockfile_acquire: write %s: %s", lockfile, strerror(errno));
86                                 close(fd);
87                                 x_unlink(lockfile);
88                                 goto out;
89                         }
90                         close(fd);
91                         acquired = true;
92                         goto out;
93                 }
94 #else
95                 ret = symlink(my_content, lockfile);
96                 if (ret == 0) {
97                         /* We got the lock. */
98                         acquired = true;
99                         goto out;
100                 }
101                 cc_log("lockfile_acquire: symlink %s: %s", lockfile, strerror(errno));
102                 if (errno == EPERM) {
103                         /*
104                          * The file system does not support symbolic links. We have no choice but
105                          * to grant the lock anyway.
106                          */
107                         acquired = true;
108                         goto out;
109                 }
110                 if (errno != EEXIST) {
111                         /* Directory doesn't exist or isn't writable? */
112                         goto out;
113                 }
114                 free(content);
115                 content = x_readlink(lockfile);
116                 if (!content) {
117                         if (errno == ENOENT) {
118                                 /*
119                                  * The symlink was removed after the symlink() call above, so retry
120                                  * acquiring it.
121                                  */
122                                 continue;
123                         } else {
124                                 cc_log("lockfile_acquire: readlink %s: %s", lockfile, strerror(errno));
125                                 goto out;
126                         }
127                 }
128 #endif
129
130                 if (str_eq(content, my_content)) {
131                         /* Lost NFS reply? */
132                         cc_log("lockfile_acquire: symlink %s failed but we got the lock anyway",
133                                lockfile);
134                         acquired = true;
135                         goto out;
136                 }
137                 /*
138                  * A possible improvement here would be to check if the process holding the
139                  * lock is still alive and break the lock early if it isn't.
140                  */
141                 cc_log("lockfile_acquire: lock info for %s: %s", lockfile, content);
142                 if (!initial_content) {
143                         initial_content = x_strdup(content);
144                 }
145                 if (slept > staleness_limit) {
146                         if (str_eq(content, initial_content)) {
147                                 /* The lock seems to be stale -- break it. */
148                                 cc_log("lockfile_acquire: breaking %s", lockfile);
149                                 if (lockfile_acquire(lockfile, staleness_limit)) {
150                                         lockfile_release(path);
151                                         lockfile_release(lockfile);
152                                         to_sleep = 1000;
153                                         slept = 0;
154                                         continue;
155                                 }
156                         }
157                         cc_log("lockfile_acquire: gave up acquiring %s", lockfile);
158                         goto out;
159                 }
160                 cc_log("lockfile_acquire: failed to acquire %s; sleeping %u microseconds",
161                        lockfile, to_sleep);
162                 usleep(to_sleep);
163                 slept += to_sleep;
164                 to_sleep *= 2;
165         }
166
167 out:
168         if (acquired) {
169                 cc_log("Acquired lock %s", lockfile);
170         } else {
171                 cc_log("Failed to acquire lock %s", lockfile);
172         }
173         free(lockfile);
174         free(my_content);
175         free(initial_content);
176         free(content);
177         return acquired;
178 }
179
180 /*
181  * Release the lockfile for the given path. Assumes that we are the legitimate
182  * owner.
183  */
184 void
185 lockfile_release(const char *path)
186 {
187         char *lockfile = format("%s.lock", path);
188         cc_log("Releasing lock %s", lockfile);
189         tmp_unlink(lockfile);
190         free(lockfile);
191 }
192
193 #ifdef TEST_LOCKFILE
194 int
195 main(int argc, char **argv)
196 {
197         extern char *cache_logfile;
198         cache_logfile = "/dev/stdout";
199         if (argc == 4) {
200                 unsigned staleness_limit = atoi(argv[1]);
201                 if (str_eq(argv[2], "acquire")) {
202                         return lockfile_acquire(argv[3], staleness_limit) == 0;
203                 } else if (str_eq(argv[2], "release")) {
204                         lockfile_release(argv[3]);
205                         return 0;
206                 }
207         }
208         fprintf(stderr,
209                 "Usage: testlockfile <staleness_limit> <acquire|release> <path>\n");
210         return 1;
211 }
212 #endif