1*0d6140beSAndroid Build Coastguard Worker /* Copyright 2016, Google Inc.
2*0d6140beSAndroid Build Coastguard Worker * All rights reserved.
3*0d6140beSAndroid Build Coastguard Worker *
4*0d6140beSAndroid Build Coastguard Worker * Redistribution and use in source and binary forms, with or without
5*0d6140beSAndroid Build Coastguard Worker * modification, are permitted provided that the following conditions are
6*0d6140beSAndroid Build Coastguard Worker * met:
7*0d6140beSAndroid Build Coastguard Worker *
8*0d6140beSAndroid Build Coastguard Worker * * Redistributions of source code must retain the above copyright
9*0d6140beSAndroid Build Coastguard Worker * notice, this list of conditions and the following disclaimer.
10*0d6140beSAndroid Build Coastguard Worker * * Redistributions in binary form must reproduce the above
11*0d6140beSAndroid Build Coastguard Worker * copyright notice, this list of conditions and the following
12*0d6140beSAndroid Build Coastguard Worker * disclaimer in the documentation and/or other materials provided
13*0d6140beSAndroid Build Coastguard Worker * with the distribution.
14*0d6140beSAndroid Build Coastguard Worker * * Neither the name of Google Inc. nor the names of its
15*0d6140beSAndroid Build Coastguard Worker * contributors may be used to endorse or promote products derived
16*0d6140beSAndroid Build Coastguard Worker * from this software without specific prior written permission.
17*0d6140beSAndroid Build Coastguard Worker *
18*0d6140beSAndroid Build Coastguard Worker * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19*0d6140beSAndroid Build Coastguard Worker * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20*0d6140beSAndroid Build Coastguard Worker * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21*0d6140beSAndroid Build Coastguard Worker * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22*0d6140beSAndroid Build Coastguard Worker * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23*0d6140beSAndroid Build Coastguard Worker * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24*0d6140beSAndroid Build Coastguard Worker * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25*0d6140beSAndroid Build Coastguard Worker * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26*0d6140beSAndroid Build Coastguard Worker * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27*0d6140beSAndroid Build Coastguard Worker * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28*0d6140beSAndroid Build Coastguard Worker * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29*0d6140beSAndroid Build Coastguard Worker *
30*0d6140beSAndroid Build Coastguard Worker * Alternatively, this software may be distributed under the terms of the
31*0d6140beSAndroid Build Coastguard Worker * GNU General Public License ("GPL") version 2 as published by the Free
32*0d6140beSAndroid Build Coastguard Worker * Software Foundation.
33*0d6140beSAndroid Build Coastguard Worker *
34*0d6140beSAndroid Build Coastguard Worker * file_lock.c: Implementation for a binary semaphore using a file lock.
35*0d6140beSAndroid Build Coastguard Worker *
36*0d6140beSAndroid Build Coastguard Worker * Warning: This relies on flock() which is known to be broken on NFS.
37*0d6140beSAndroid Build Coastguard Worker *
38*0d6140beSAndroid Build Coastguard Worker * The file will remain persistent once the lock has been used. Unfortunately,
39*0d6140beSAndroid Build Coastguard Worker * unlinking the file can introduce a race condition so we leave the file
40*0d6140beSAndroid Build Coastguard Worker * in place.
41*0d6140beSAndroid Build Coastguard Worker *
42*0d6140beSAndroid Build Coastguard Worker * The current process's PID will be written to the file for debug purposes.
43*0d6140beSAndroid Build Coastguard Worker */
44*0d6140beSAndroid Build Coastguard Worker
45*0d6140beSAndroid Build Coastguard Worker #include <errno.h>
46*0d6140beSAndroid Build Coastguard Worker #include <fcntl.h>
47*0d6140beSAndroid Build Coastguard Worker #include <inttypes.h>
48*0d6140beSAndroid Build Coastguard Worker #include <limits.h>
49*0d6140beSAndroid Build Coastguard Worker #include <stdio.h>
50*0d6140beSAndroid Build Coastguard Worker #include <stdlib.h>
51*0d6140beSAndroid Build Coastguard Worker #include <string.h>
52*0d6140beSAndroid Build Coastguard Worker #include <time.h>
53*0d6140beSAndroid Build Coastguard Worker #include <unistd.h>
54*0d6140beSAndroid Build Coastguard Worker #include <sys/file.h>
55*0d6140beSAndroid Build Coastguard Worker #include <sys/types.h>
56*0d6140beSAndroid Build Coastguard Worker #include <sys/stat.h>
57*0d6140beSAndroid Build Coastguard Worker
58*0d6140beSAndroid Build Coastguard Worker #include "flash.h"
59*0d6140beSAndroid Build Coastguard Worker #include "ipc_lock.h"
60*0d6140beSAndroid Build Coastguard Worker
61*0d6140beSAndroid Build Coastguard Worker #define SLEEP_INTERVAL_MS 50
62*0d6140beSAndroid Build Coastguard Worker
msecs_to_timespec(int msecs,struct timespec * tmspec)63*0d6140beSAndroid Build Coastguard Worker static void msecs_to_timespec(int msecs, struct timespec *tmspec)
64*0d6140beSAndroid Build Coastguard Worker {
65*0d6140beSAndroid Build Coastguard Worker tmspec->tv_sec = msecs / 1000;
66*0d6140beSAndroid Build Coastguard Worker tmspec->tv_nsec = (msecs % 1000) * 1000 * 1000;
67*0d6140beSAndroid Build Coastguard Worker }
68*0d6140beSAndroid Build Coastguard Worker
lock_is_held(struct ipc_lock * lock)69*0d6140beSAndroid Build Coastguard Worker static int lock_is_held(struct ipc_lock *lock)
70*0d6140beSAndroid Build Coastguard Worker {
71*0d6140beSAndroid Build Coastguard Worker return lock->is_held;
72*0d6140beSAndroid Build Coastguard Worker }
73*0d6140beSAndroid Build Coastguard Worker
test_dir(const char * path)74*0d6140beSAndroid Build Coastguard Worker static int test_dir(const char *path)
75*0d6140beSAndroid Build Coastguard Worker {
76*0d6140beSAndroid Build Coastguard Worker struct stat s;
77*0d6140beSAndroid Build Coastguard Worker
78*0d6140beSAndroid Build Coastguard Worker if (lstat(path, &s) < 0) {
79*0d6140beSAndroid Build Coastguard Worker msg_gerr("Cannot stat %s.\n", path);
80*0d6140beSAndroid Build Coastguard Worker return -1;
81*0d6140beSAndroid Build Coastguard Worker }
82*0d6140beSAndroid Build Coastguard Worker
83*0d6140beSAndroid Build Coastguard Worker if (!S_ISDIR(s.st_mode)) {
84*0d6140beSAndroid Build Coastguard Worker msg_gerr("%s is not a directory.\n", path);
85*0d6140beSAndroid Build Coastguard Worker return -1;
86*0d6140beSAndroid Build Coastguard Worker }
87*0d6140beSAndroid Build Coastguard Worker
88*0d6140beSAndroid Build Coastguard Worker return 0;
89*0d6140beSAndroid Build Coastguard Worker }
90*0d6140beSAndroid Build Coastguard Worker
file_lock_try_open_or_create(const char * dir,struct ipc_lock * lock)91*0d6140beSAndroid Build Coastguard Worker static int file_lock_try_open_or_create(const char *dir, struct ipc_lock *lock)
92*0d6140beSAndroid Build Coastguard Worker {
93*0d6140beSAndroid Build Coastguard Worker char path[PATH_MAX];
94*0d6140beSAndroid Build Coastguard Worker if (test_dir(dir))
95*0d6140beSAndroid Build Coastguard Worker return -1;
96*0d6140beSAndroid Build Coastguard Worker
97*0d6140beSAndroid Build Coastguard Worker if (snprintf(path, sizeof(path), "%s/%s", dir, lock->filename) < 0)
98*0d6140beSAndroid Build Coastguard Worker return -1;
99*0d6140beSAndroid Build Coastguard Worker
100*0d6140beSAndroid Build Coastguard Worker /* 0666: User/Group/Others: read, write. */
101*0d6140beSAndroid Build Coastguard Worker mode_t permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
102*0d6140beSAndroid Build Coastguard Worker lock->fd = open(path, O_RDWR | O_CREAT, permissions);
103*0d6140beSAndroid Build Coastguard Worker if (lock->fd < 0) {
104*0d6140beSAndroid Build Coastguard Worker msg_gerr("Cannot open lockfile %s\n", path);
105*0d6140beSAndroid Build Coastguard Worker return -1;
106*0d6140beSAndroid Build Coastguard Worker }
107*0d6140beSAndroid Build Coastguard Worker /* Ignore potential fchmod() failures since we might not be the owner. */
108*0d6140beSAndroid Build Coastguard Worker if (fchmod(lock->fd, permissions) == -1)
109*0d6140beSAndroid Build Coastguard Worker msg_gdbg("Cannot change the permissions of lockfile %s\n", path);
110*0d6140beSAndroid Build Coastguard Worker
111*0d6140beSAndroid Build Coastguard Worker msg_gdbg("Opened file lock \"%s\"\n", path);
112*0d6140beSAndroid Build Coastguard Worker return 0;
113*0d6140beSAndroid Build Coastguard Worker }
114*0d6140beSAndroid Build Coastguard Worker
file_lock_open_or_create(struct ipc_lock * lock)115*0d6140beSAndroid Build Coastguard Worker static int file_lock_open_or_create(struct ipc_lock *lock)
116*0d6140beSAndroid Build Coastguard Worker {
117*0d6140beSAndroid Build Coastguard Worker const char *dirs[] = {
118*0d6140beSAndroid Build Coastguard Worker #ifndef __ANDROID__
119*0d6140beSAndroid Build Coastguard Worker // Default modern Linux lock path.
120*0d6140beSAndroid Build Coastguard Worker "/run/lock",
121*0d6140beSAndroid Build Coastguard Worker // Fallback to temporary directory.
122*0d6140beSAndroid Build Coastguard Worker "/tmp",
123*0d6140beSAndroid Build Coastguard Worker #else
124*0d6140beSAndroid Build Coastguard Worker // flashrom called as a subprocess with its own SELinux context.
125*0d6140beSAndroid Build Coastguard Worker "/data/vendor/flashrom/tmp",
126*0d6140beSAndroid Build Coastguard Worker // Same as above but for case when there is no tmpfs.
127*0d6140beSAndroid Build Coastguard Worker "/data/vendor/flashrom",
128*0d6140beSAndroid Build Coastguard Worker // flashrom called from the console/shell. Comes last as a fallback.
129*0d6140beSAndroid Build Coastguard Worker "/data/local/tmp",
130*0d6140beSAndroid Build Coastguard Worker #endif
131*0d6140beSAndroid Build Coastguard Worker };
132*0d6140beSAndroid Build Coastguard Worker
133*0d6140beSAndroid Build Coastguard Worker if (file_lock_try_open_or_create(dirs[0], lock) == 0)
134*0d6140beSAndroid Build Coastguard Worker return 0;
135*0d6140beSAndroid Build Coastguard Worker
136*0d6140beSAndroid Build Coastguard Worker for (size_t i = 1; i < ARRAY_SIZE(dirs); ++i) {
137*0d6140beSAndroid Build Coastguard Worker msg_gwarn("Trying fallback directory: %s\n", dirs[i]);
138*0d6140beSAndroid Build Coastguard Worker if (file_lock_try_open_or_create(dirs[i], lock) == 0)
139*0d6140beSAndroid Build Coastguard Worker return 0;
140*0d6140beSAndroid Build Coastguard Worker }
141*0d6140beSAndroid Build Coastguard Worker
142*0d6140beSAndroid Build Coastguard Worker msg_gerr("Failed to find usable directory for file lock\n");
143*0d6140beSAndroid Build Coastguard Worker return -1;
144*0d6140beSAndroid Build Coastguard Worker }
145*0d6140beSAndroid Build Coastguard Worker
file_lock_get(struct ipc_lock * lock,int timeout_msecs)146*0d6140beSAndroid Build Coastguard Worker static int file_lock_get(struct ipc_lock *lock, int timeout_msecs)
147*0d6140beSAndroid Build Coastguard Worker {
148*0d6140beSAndroid Build Coastguard Worker int remaining_msecs;
149*0d6140beSAndroid Build Coastguard Worker struct timespec sleep_interval;
150*0d6140beSAndroid Build Coastguard Worker int ret;
151*0d6140beSAndroid Build Coastguard Worker
152*0d6140beSAndroid Build Coastguard Worker if (timeout_msecs == 0)
153*0d6140beSAndroid Build Coastguard Worker return flock(lock->fd, LOCK_EX | LOCK_NB);
154*0d6140beSAndroid Build Coastguard Worker
155*0d6140beSAndroid Build Coastguard Worker if (timeout_msecs < 0)
156*0d6140beSAndroid Build Coastguard Worker remaining_msecs = SLEEP_INTERVAL_MS;
157*0d6140beSAndroid Build Coastguard Worker else
158*0d6140beSAndroid Build Coastguard Worker remaining_msecs = timeout_msecs;
159*0d6140beSAndroid Build Coastguard Worker
160*0d6140beSAndroid Build Coastguard Worker while ((ret = flock(lock->fd, LOCK_EX | LOCK_NB)) != 0) {
161*0d6140beSAndroid Build Coastguard Worker struct timespec rem;
162*0d6140beSAndroid Build Coastguard Worker
163*0d6140beSAndroid Build Coastguard Worker if (errno != EWOULDBLOCK) {
164*0d6140beSAndroid Build Coastguard Worker msg_gerr("Error obtaining lock\n");
165*0d6140beSAndroid Build Coastguard Worker return -1;
166*0d6140beSAndroid Build Coastguard Worker }
167*0d6140beSAndroid Build Coastguard Worker
168*0d6140beSAndroid Build Coastguard Worker msecs_to_timespec(MIN(remaining_msecs, SLEEP_INTERVAL_MS),
169*0d6140beSAndroid Build Coastguard Worker &sleep_interval);
170*0d6140beSAndroid Build Coastguard Worker
171*0d6140beSAndroid Build Coastguard Worker while (nanosleep(&sleep_interval, &rem) != 0) {
172*0d6140beSAndroid Build Coastguard Worker if (errno == EINTR) {
173*0d6140beSAndroid Build Coastguard Worker sleep_interval = rem;
174*0d6140beSAndroid Build Coastguard Worker continue;
175*0d6140beSAndroid Build Coastguard Worker }
176*0d6140beSAndroid Build Coastguard Worker msg_gerr("nanosleep() failed\n");
177*0d6140beSAndroid Build Coastguard Worker return -1;
178*0d6140beSAndroid Build Coastguard Worker }
179*0d6140beSAndroid Build Coastguard Worker
180*0d6140beSAndroid Build Coastguard Worker if (timeout_msecs < 0)
181*0d6140beSAndroid Build Coastguard Worker continue;
182*0d6140beSAndroid Build Coastguard Worker
183*0d6140beSAndroid Build Coastguard Worker remaining_msecs -= SLEEP_INTERVAL_MS;
184*0d6140beSAndroid Build Coastguard Worker if (remaining_msecs < 0)
185*0d6140beSAndroid Build Coastguard Worker break;
186*0d6140beSAndroid Build Coastguard Worker }
187*0d6140beSAndroid Build Coastguard Worker
188*0d6140beSAndroid Build Coastguard Worker if (ret != 0) {
189*0d6140beSAndroid Build Coastguard Worker msg_gerr("Timed out waiting for file lock.\n");
190*0d6140beSAndroid Build Coastguard Worker return -1;
191*0d6140beSAndroid Build Coastguard Worker }
192*0d6140beSAndroid Build Coastguard Worker
193*0d6140beSAndroid Build Coastguard Worker return 0;
194*0d6140beSAndroid Build Coastguard Worker }
195*0d6140beSAndroid Build Coastguard Worker
file_lock_write_pid(struct ipc_lock * lock)196*0d6140beSAndroid Build Coastguard Worker static int file_lock_write_pid(struct ipc_lock *lock)
197*0d6140beSAndroid Build Coastguard Worker {
198*0d6140beSAndroid Build Coastguard Worker ssize_t len;
199*0d6140beSAndroid Build Coastguard Worker /* PIDs are usually 5 digits, but we'll reserve enough room for
200*0d6140beSAndroid Build Coastguard Worker a value of 2^32 (10 digits) out of paranoia. */
201*0d6140beSAndroid Build Coastguard Worker char pid_str[11];
202*0d6140beSAndroid Build Coastguard Worker
203*0d6140beSAndroid Build Coastguard Worker if (ftruncate(lock->fd, 0) < 0) {
204*0d6140beSAndroid Build Coastguard Worker msg_gerr("Cannot truncate lockfile\n");
205*0d6140beSAndroid Build Coastguard Worker return -1;
206*0d6140beSAndroid Build Coastguard Worker }
207*0d6140beSAndroid Build Coastguard Worker
208*0d6140beSAndroid Build Coastguard Worker snprintf(pid_str, sizeof(pid_str), "%lu", (unsigned long)getpid());
209*0d6140beSAndroid Build Coastguard Worker len = write(lock->fd, pid_str, strlen(pid_str));
210*0d6140beSAndroid Build Coastguard Worker if (len < 0) {
211*0d6140beSAndroid Build Coastguard Worker msg_gerr("Cannot write PID to lockfile\n");
212*0d6140beSAndroid Build Coastguard Worker return -1;
213*0d6140beSAndroid Build Coastguard Worker }
214*0d6140beSAndroid Build Coastguard Worker
215*0d6140beSAndroid Build Coastguard Worker return 0;
216*0d6140beSAndroid Build Coastguard Worker }
217*0d6140beSAndroid Build Coastguard Worker
file_lock_release(struct ipc_lock * lock)218*0d6140beSAndroid Build Coastguard Worker static void file_lock_release(struct ipc_lock *lock)
219*0d6140beSAndroid Build Coastguard Worker {
220*0d6140beSAndroid Build Coastguard Worker if (flock(lock->fd, LOCK_UN) < 0)
221*0d6140beSAndroid Build Coastguard Worker msg_gerr("Cannot release lock\n");
222*0d6140beSAndroid Build Coastguard Worker
223*0d6140beSAndroid Build Coastguard Worker if (close(lock->fd) < 0)
224*0d6140beSAndroid Build Coastguard Worker msg_gerr("Cannot close lockfile\n");
225*0d6140beSAndroid Build Coastguard Worker }
226*0d6140beSAndroid Build Coastguard Worker
227*0d6140beSAndroid Build Coastguard Worker /*
228*0d6140beSAndroid Build Coastguard Worker * timeout <0 = no timeout (try forever)
229*0d6140beSAndroid Build Coastguard Worker * timeout 0 = do not wait (return immediately)
230*0d6140beSAndroid Build Coastguard Worker * timeout >0 = wait up to $timeout milliseconds
231*0d6140beSAndroid Build Coastguard Worker *
232*0d6140beSAndroid Build Coastguard Worker * returns 0 to indicate lock acquired
233*0d6140beSAndroid Build Coastguard Worker * returns >0 to indicate lock was already held
234*0d6140beSAndroid Build Coastguard Worker * returns <0 to indicate failed to acquire lock
235*0d6140beSAndroid Build Coastguard Worker */
acquire_lock(struct ipc_lock * lock,int timeout_msecs)236*0d6140beSAndroid Build Coastguard Worker int acquire_lock(struct ipc_lock *lock, int timeout_msecs)
237*0d6140beSAndroid Build Coastguard Worker {
238*0d6140beSAndroid Build Coastguard Worker /* check if it is already held */
239*0d6140beSAndroid Build Coastguard Worker if (lock_is_held(lock))
240*0d6140beSAndroid Build Coastguard Worker return 1;
241*0d6140beSAndroid Build Coastguard Worker
242*0d6140beSAndroid Build Coastguard Worker if (file_lock_open_or_create(lock))
243*0d6140beSAndroid Build Coastguard Worker return -1;
244*0d6140beSAndroid Build Coastguard Worker
245*0d6140beSAndroid Build Coastguard Worker if (file_lock_get(lock, timeout_msecs)) {
246*0d6140beSAndroid Build Coastguard Worker lock->is_held = 0;
247*0d6140beSAndroid Build Coastguard Worker close(lock->fd);
248*0d6140beSAndroid Build Coastguard Worker return -1;
249*0d6140beSAndroid Build Coastguard Worker } else {
250*0d6140beSAndroid Build Coastguard Worker lock->is_held = 1;
251*0d6140beSAndroid Build Coastguard Worker }
252*0d6140beSAndroid Build Coastguard Worker
253*0d6140beSAndroid Build Coastguard Worker /*
254*0d6140beSAndroid Build Coastguard Worker * Write PID to lockfile for debug purposes. Failure to write to
255*0d6140beSAndroid Build Coastguard Worker * the file should not be considered fatal. There might be something
256*0d6140beSAndroid Build Coastguard Worker * bad happening with the filesystem, but the lock has already been
257*0d6140beSAndroid Build Coastguard Worker * obtained and we may need our tools for diagnostics and repairs
258*0d6140beSAndroid Build Coastguard Worker * so we should continue anyway.
259*0d6140beSAndroid Build Coastguard Worker */
260*0d6140beSAndroid Build Coastguard Worker file_lock_write_pid(lock);
261*0d6140beSAndroid Build Coastguard Worker return 0;
262*0d6140beSAndroid Build Coastguard Worker }
263*0d6140beSAndroid Build Coastguard Worker
264*0d6140beSAndroid Build Coastguard Worker /*
265*0d6140beSAndroid Build Coastguard Worker * returns 0 if lock was released successfully
266*0d6140beSAndroid Build Coastguard Worker * returns -1 if lock had not been held before the call
267*0d6140beSAndroid Build Coastguard Worker */
release_lock(struct ipc_lock * lock)268*0d6140beSAndroid Build Coastguard Worker int release_lock(struct ipc_lock *lock)
269*0d6140beSAndroid Build Coastguard Worker {
270*0d6140beSAndroid Build Coastguard Worker if (lock_is_held(lock)) {
271*0d6140beSAndroid Build Coastguard Worker file_lock_release(lock);
272*0d6140beSAndroid Build Coastguard Worker lock->is_held = 0;
273*0d6140beSAndroid Build Coastguard Worker return 0;
274*0d6140beSAndroid Build Coastguard Worker }
275*0d6140beSAndroid Build Coastguard Worker
276*0d6140beSAndroid Build Coastguard Worker msg_ginfo("%s called but lock was not held on %s.\n",
277*0d6140beSAndroid Build Coastguard Worker __func__, lock->filename);
278*0d6140beSAndroid Build Coastguard Worker return -1;
279*0d6140beSAndroid Build Coastguard Worker }
280