gbuild trace: disable flock for wsl-as-helper case
[LibreOffice.git] / solenv / lockfile / lockfile.c
blob52be40b0fb56636aa1fd787f2d4f0034940b635e
1 /*
2 * lockfile.c Safely creates a lockfile, also over NFS.
3 * This file also holds the implementation for
4 * the Svr4 maillock functions.
6 * Copyright (C) Miquel van Smoorenburg and contributors 1997-2021.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
14 #include "autoconf.h"
16 #include <sys/types.h>
17 #if HAVE_SYS_PARAM_H
18 #include <sys/param.h>
19 #endif
20 #include <sys/stat.h>
21 #include <sys/wait.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <signal.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <time.h>
30 #include <errno.h>
31 #include "lockfile.h"
32 #include "maillock.h"
34 #ifdef HAVE_UTIME
35 #include <utime.h>
36 #endif
38 #ifdef LIB
39 static char *mlockfile;
40 static int islocked = 0;
41 #endif
43 #ifdef MAILGROUP
45 * Get the id of the mailgroup, by statting the helper program.
46 * If it is setgroup-id, then the group is the mailgroup.
48 static int mailgid()
50 struct stat st;
52 if (stat(LOCKPROG, &st) != 0)
53 return (gid_t)-1;
54 if ((st.st_mode & 02000) == 0)
55 return (gid_t)-1;
56 return st.st_gid;
60 * Is this a lock for a mailbox? Check:
61 * - is the file in /path/to/USERNAME.lock format
62 * - is /path/to/USERNAME present and owned by us
63 * - is /path/to writable by group mail
65 * To be safe in a setgid program, chdir() into the lockfile
66 * directory first, then pass in the basename of the lockfile.
68 #ifdef LIB
69 static
70 #endif
71 int is_maillock(const char *lockfile)
73 struct stat st;
74 gid_t gid;
75 char tmp[1024];
76 char *p;
78 /* remove .lock suffix */
79 strncpy(tmp, lockfile, sizeof(tmp) - 1);
80 tmp[sizeof(tmp) - 1] = 0;
81 if ((p = strrchr(tmp, '.')) == NULL || strcmp(p, ".lock") != 0)
82 return 0;
83 *p = 0;
85 /* file to lock must exist, and must be owned by us */
86 if (lstat(tmp, &st) != 0 ||
87 (st.st_mode & S_IFMT) != S_IFREG || st.st_uid != getuid())
88 return 0;
90 /* Directory this file is in must be writable by group mail. */
91 if ((gid = mailgid()) == (gid_t)-1)
92 return 0;
93 if ((p = strrchr(tmp, '/')) != NULL)
94 *p = 0;
95 else
96 strncpy(tmp, ".", sizeof(tmp));
97 if (stat(tmp, &st) != 0 || st.st_gid != gid || (st.st_mode & 0020) == 0)
98 return 0;
100 return 1;
103 #ifdef LIB
105 * Call external program to do the actual locking.
107 static int run_helper(char *opt, const char *lockfile, int retries, int flags)
109 sigset_t set, oldset;
110 char buf[8];
111 pid_t pid, n;
112 int st;
115 * Better safe than sorry.
117 if (geteuid() == 0)
118 return L_ERROR;
121 * Block SIGCHLD. The main program might have installed
122 * handlers we don't want to call.
124 sigemptyset(&set);
125 sigaddset(&set, SIGCHLD);
126 sigprocmask(SIG_BLOCK, &set, &oldset);
129 * Fork, execute locking program and wait.
131 if ((pid = fork()) < 0)
132 return L_ERROR;
133 if (pid == 0) {
134 /* drop privs */
135 if (setuid(geteuid()) < 0) {
136 perror("setuid");
137 _exit(L_ERROR);
139 snprintf(buf, sizeof(buf), "%d", retries % 1000);
140 execl(LOCKPROG, LOCKPROG, opt, "-r", buf, "-q",
141 (flags & L_PID) ? "-p" : "-N", lockfile, NULL);
142 _exit(L_ERROR);
146 * Wait for return status - do something appropriate
147 * if program died or returned L_ERROR.
149 while ((n = waitpid(pid, &st, 0)) != pid)
150 if (n < 0 && errno != EINTR)
151 break;
152 if (!sigismember(&oldset, SIGCHLD))
153 sigprocmask(SIG_UNBLOCK, &set, NULL);
154 if (n < 0)
155 return L_ERROR;
156 if (!WIFEXITED(st) || WEXITSTATUS(st) == L_ERROR) {
157 errno = EINTR;
158 return L_ERROR;
161 return WEXITSTATUS(st);
163 #endif /* LIB*/
165 #endif /* MAILGROUP */
167 #define TMPLOCKSTR ".lk"
168 #define TMPLOCKSTRSZ strlen(TMPLOCKSTR)
169 #define TMPLOCKPIDSZ 5
170 #define TMPLOCKTIMESZ 1
171 #define TMPLOCKSYSNAMESZ 23
172 #define TMPLOCKFILENAMESZ (TMPLOCKSTRSZ + TMPLOCKPIDSZ + \
173 TMPLOCKTIMESZ + TMPLOCKSYSNAMESZ)
175 static int lockfilename(const char *lockfile, char *tmplock, size_t tmplocksz)
177 char sysname[256];
178 char *p;
180 #ifdef MAXPATHLEN
182 * Safety measure.
184 if (strlen(lockfile) + TMPLOCKFILENAMESZ > MAXPATHLEN) {
185 errno = ENAMETOOLONG;
186 return L_ERROR;
188 #endif
190 if (strlen(lockfile) + TMPLOCKFILENAMESZ + 1 > tmplocksz) {
191 errno = EINVAL;
192 return L_ERROR;
196 * Create a temp lockfile (hopefully unique) and write
197 * either our pid/ppid in it, or 0\0 for svr4 compatibility.
199 if (gethostname(sysname, sizeof(sysname)) < 0)
200 return L_ERROR;
201 if ((p = strchr(sysname, '.')) != NULL)
202 *p = 0;
203 /* strcpy is safe: length-check above, limited at snprintf below */
204 strcpy(tmplock, lockfile);
205 if ((p = strrchr(tmplock, '/')) == NULL)
206 p = tmplock;
207 else
208 p++;
209 if (snprintf(p, TMPLOCKFILENAMESZ, "%s%0*d%0*x%s", TMPLOCKSTR,
210 TMPLOCKPIDSZ, (int)getpid(),
211 TMPLOCKTIMESZ, (int)time(NULL) & 15,
212 sysname) < 0) {
213 // never happens but gets rid of gcc truncation warning.
214 errno = EOVERFLOW;
215 return L_ERROR;
218 return 0;
222 * Create a lockfile.
224 static int lockfile_create_save_tmplock(const char *lockfile,
225 char *tmplock, size_t tmplocksz,
226 volatile char **xtmplock,
227 int retries, int flags, const struct lockargs_s_ *args)
229 struct stat st, st1;
230 char pidbuf[40];
231 pid_t pid = 0;
232 int sleeptime = 0;
233 int statfailed = 0;
234 int fd;
235 int i, e, pidlen;
236 int dontsleep = 1;
237 int tries = retries + 1;
239 /* process optional flags that have arguments */
240 if (flags & L_INTERVAL_D_) {
241 sleeptime = args->interval;
244 /* decide which PID to write to the lockfile */
245 if (flags & L_PID)
246 pid = getpid();
247 if (flags & L_PPID) {
248 pid = getppid();
249 if (pid == 1) {
250 /* orphaned */
251 return L_ORPHANED;
254 pidlen = snprintf(pidbuf, sizeof(pidbuf), "%d\n", pid);
255 if (pidlen < 0 || pidlen > (int) sizeof(pidbuf) - 1) {
256 errno = EOVERFLOW;
257 return L_ERROR;
260 /* create temporary lockfile */
261 if ((i = lockfilename(lockfile, tmplock, tmplocksz)) != 0)
262 return i;
263 if (xtmplock)
264 *xtmplock = tmplock;
265 fd = open(tmplock, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0644);
266 if (fd < 0) {
267 /* permission denied? perhaps try suid helper */
268 #if defined(LIB) && defined(MAILGROUP)
269 if (errno == EACCES && is_maillock(lockfile))
270 return run_helper("-l", lockfile, retries, flags);
271 #endif
272 return L_TMPLOCK;
274 i = write(fd, pidbuf, pidlen);
275 e = errno;
277 if (close(fd) != 0) {
278 e = errno;
279 i = -1;
281 if (i != pidlen) {
282 unlink(tmplock);
283 tmplock[0] = 0;
284 errno = i < 0 ? e : EAGAIN;
285 return L_TMPWRITE;
289 * Now try to link the temporary lock to the lock.
291 for (i = 0; i < tries && tries > 0; i++) {
292 if (!dontsleep) {
293 if (!(flags & L_INTERVAL_D_))
294 sleeptime += 5;
296 if (sleeptime > 5) sleeptime = 5;
297 #ifdef LIB
298 sleep(sleeptime);
299 #else
300 if ((e = check_sleep(sleeptime, flags)) != 0) {
301 unlink(tmplock);
302 tmplock[0] = 0;
303 return e;
305 #endif
307 dontsleep = 0;
311 * Now lock by linking the tempfile to the lock.
313 * KLUDGE: some people say the return code of
314 * link() over NFS can't be trusted.
315 * EXTRA FIX: the value of the nlink field
316 * can't be trusted (may be cached).
318 (void)!link(tmplock, lockfile);
320 if (lstat(tmplock, &st1) < 0) {
321 tmplock[0] = 0;
322 return L_ERROR; /* Can't happen */
325 if (lstat(lockfile, &st) < 0) {
326 if (statfailed++ > 5) {
328 * Normally, this can't happen; either
329 * another process holds the lockfile or
330 * we do. So if this error pops up
331 * repeatedly, just exit...
333 e = errno;
334 (void)unlink(tmplock);
335 tmplock[0] = 0;
336 errno = e;
337 return L_MAXTRYS;
339 continue;
343 * See if we got the lock.
345 if (st.st_rdev == st1.st_rdev &&
346 st.st_ino == st1.st_ino) {
347 (void)unlink(tmplock);
348 tmplock[0] = 0;
349 return L_SUCCESS;
351 statfailed = 0;
354 * If there is a lockfile and it is invalid,
355 * remove the lockfile.
357 if (lockfile_check(lockfile, flags) == -1) {
358 if (unlink(lockfile) < 0 && errno != ENOENT) {
360 * we failed to unlink the stale
361 * lockfile, give up.
363 return L_RMSTALE;
365 dontsleep = 1;
367 * If the lockfile was invalid, then the first
368 * try wasn't valid either - make sure we
369 * try at least once more.
371 if (tries == 1) tries++;
375 (void)unlink(tmplock);
376 tmplock[0] = 0;
377 errno = EAGAIN;
378 return L_MAXTRYS;
381 #ifdef LIB
382 static
383 #endif
384 int lockfile_create_set_tmplock(const char *lockfile, volatile char **xtmplock, int retries, int flags, const struct lockargs_s_ *args)
386 char *tmplock;
387 int r, e;
388 size_t l;
390 l = strlen(lockfile)+TMPLOCKFILENAMESZ+1;
391 if ((tmplock = (char *)malloc(l)) == NULL)
392 return L_ERROR;
393 tmplock[0] = 0;
394 r = lockfile_create_save_tmplock(lockfile,
395 tmplock, l, xtmplock, retries, flags, args);
396 if (xtmplock)
397 *xtmplock = NULL;
398 e = errno;
399 free(tmplock);
400 errno = e;
401 return r;
404 #ifdef LIB
405 int lockfile_create(const char *lockfile, int retries, int flags)
407 /* check against unknown flags */
408 if (flags & ~(L_PID|L_PPID)) {
409 errno = EINVAL;
410 return L_ERROR;
412 return lockfile_create_set_tmplock(lockfile, NULL, retries, flags, NULL);
415 #ifdef STATIC
416 int lockfile_create2(const char *lockfile, int retries,
417 int flags, struct lockargs_s_ *args, int args_sz)
420 #define FLAGS_WITH_ARGS (L_INTERVAL_D_)
421 #define KNOWN_FLAGS (L_PID|L_PPID|L_INTERVAL_D_)
423 /* check if size is the same (version check) */
424 if (args != NULL && sizeof(struct lockargs_s_) != args_sz) {
425 errno = EINVAL;
426 return L_ERROR;
428 /* some flags _must_ have a non-null args */
429 if (args == NULL && (flags & FLAGS_WITH_ARGS)) {
430 errno = EINVAL;
431 return L_ERROR;
433 /* check against unknown flags */
434 if (flags & ~KNOWN_FLAGS) {
435 errno = EINVAL;
436 return L_ERROR;
438 return lockfile_create_set_tmplock(lockfile, NULL, retries, flags, args);
440 #endif
442 #endif
445 * See if a valid lockfile is present.
446 * Returns 0 if so, -1 if not.
448 int lockfile_check(const char *lockfile, int flags)
450 struct stat st, st2;
451 char buf[16];
452 time_t now;
453 pid_t pid;
454 int fd, len, r;
456 if (stat(lockfile, &st) < 0)
457 return -1;
460 * Get the contents and mtime of the lockfile.
462 time(&now);
463 pid = 0;
464 if ((fd = open(lockfile, O_RDONLY)) >= 0) {
466 * Try to use 'atime after read' as now, this is
467 * the time of the filesystem. Should not get
468 * confused by 'atime' or 'noatime' mount options.
470 len = 0;
471 if (fstat(fd, &st) == 0 &&
472 (len = read(fd, buf, sizeof(buf))) >= 0 &&
473 fstat(fd, &st2) == 0 &&
474 st.st_atime != st2.st_atime)
475 now = st.st_atime;
476 close(fd);
477 if (len > 0 && (flags & (L_PID|L_PPID))) {
478 buf[len] = 0;
479 pid = atoi(buf);
483 if (pid > 0) {
485 * If we have a pid, see if the process
486 * owning the lockfile is still alive.
488 r = kill(pid, 0);
489 if (r == 0 || errno == EPERM)
490 return 0;
491 if (r < 0 && errno == ESRCH)
492 return -1;
493 /* EINVAL - FALLTHRU */
497 * Without a pid in the lockfile, the lock
498 * is valid if it is newer than 5 mins.
501 if (now < st.st_mtime + 300)
502 return 0;
504 return -1;
508 * Remove a lock.
510 int lockfile_remove(const char *lockfile)
512 if (unlink(lockfile) < 0) {
513 #if defined(LIB) && defined(MAILGROUP)
514 if (errno == EACCES && is_maillock(lockfile))
515 return run_helper("-u", lockfile, 0, 0);
516 #endif
517 return errno == ENOENT ? 0 : -1;
519 return 0;
523 * Touch a lock.
525 int lockfile_touch(const char *lockfile)
527 #ifdef HAVE_UTIME
528 return utime(lockfile, NULL);
529 #else
530 return utimes(lockfile, NULL);
531 #endif
534 #ifdef LIB
536 * Lock a mailfile. This looks a lot like the SVR4 function.
537 * Arguments: lusername, retries.
539 int maillock(const char *name, int retries)
541 char *p, *mail;
542 char *newlock;
543 int i, e;
544 int len, newlen;
546 if (islocked) return 0;
548 #ifdef MAXPATHLEN
549 if (strlen(name) + sizeof(MAILDIR) + 6 > MAXPATHLEN) {
550 errno = ENAMETOOLONG;
551 return L_NAMELEN;
553 #endif
556 * If $MAIL is for the same username as "name"
557 * then use $MAIL instead.
560 len = strlen(name)+strlen(MAILDIR)+6;
561 mlockfile = (char *)malloc(len);
562 if (!mlockfile)
563 return L_ERROR;
564 sprintf(mlockfile, "%s%s.lock", MAILDIR, name);
565 if ((mail = getenv("MAIL")) != NULL) {
566 if ((p = strrchr(mail, '/')) != NULL)
567 p++;
568 else
569 p = mail;
570 if (strcmp(p, name) == 0) {
571 newlen = strlen(mail)+6;
572 #ifdef MAXPATHLEN
573 if (newlen > MAXPATHLEN) {
574 errno = ENAMETOOLONG;
575 return L_NAMELEN;
577 #endif
578 if (newlen > len) {
579 newlock = (char *)realloc (mlockfile, newlen);
580 if (newlock == NULL) {
581 e = errno;
582 free (mlockfile);
583 mlockfile = NULL;
584 errno = e;
585 return L_ERROR;
587 mlockfile = newlock;
589 sprintf(mlockfile, "%s.lock", mail);
592 i = lockfile_create(mlockfile, retries, 0);
593 if (i == 0) islocked = 1;
595 return i;
598 void mailunlock(void)
600 if (!islocked) return;
601 lockfile_remove(mlockfile);
602 free (mlockfile);
603 islocked = 0;
606 void touchlock(void)
608 lockfile_touch(mlockfile);
610 #endif