/* * lockfile.c Safely creates a lockfile, also over NFS. * This file also holds the implementation for * the Svr4 maillock functions. * * Copyright (C) Miquel van Smoorenburg and contributors 1997-2021. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version.
*/
#ifdef MAILGROUP /* * Get the id of the mailgroup, by statting the helper program. * If it is setgroup-id, then the group is the mailgroup.
*/ staticint mailgid()
{ struct stat st;
if (stat(LOCKPROG, &st) != 0) return (gid_t)-1; if ((st.st_mode & 02000) == 0) return (gid_t)-1; return st.st_gid;
}
/* * Is this a lock for a mailbox? Check: * - is the file in /path/to/USERNAME.lock format * - is /path/to/USERNAME present and owned by us * - is /path/to writable by group mail * * To be safe in a setgid program, chdir() into the lockfile * directory first, then pass in the basename of the lockfile.
*/ #ifdef LIB static #endif int is_maillock(constchar *lockfile)
{ struct stat st;
gid_t gid; char tmp[1024]; char *p;
/* file to lock must exist, and must be owned by us */ if (lstat(tmp, &st) != 0 ||
(st.st_mode & S_IFMT) != S_IFREG || st.st_uid != getuid()) return 0;
/* Directory this file is in must be writable by group mail. */ if ((gid = mailgid()) == (gid_t)-1) return 0; if ((p = strrchr(tmp, '/')) != NULL)
*p = 0; else
strncpy(tmp, ".", sizeof(tmp)); if (stat(tmp, &st) != 0 || st.st_gid != gid || (st.st_mode & 0020) == 0) return 0;
return 1;
}
#ifdef LIB /* * Call external program to do the actual locking.
*/ staticint run_helper(char *opt, constchar *lockfile, int retries, int flags)
{
sigset_t set, oldset; char buf[8];
pid_t pid, n; int st;
/* * Better safe than sorry.
*/ if (geteuid() == 0) return L_ERROR;
/* * Block SIGCHLD. The main program might have installed * handlers we don't want to call.
*/
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_BLOCK, &set, &oldset);
/* * Fork, execute locking program and wait.
*/ if ((pid = fork()) < 0) return L_ERROR; if (pid == 0) { /* drop privs */ if (setuid(geteuid()) < 0) {
perror("setuid");
_exit(L_ERROR);
}
snprintf(buf, sizeof(buf), "%d", retries % 1000);
execl(LOCKPROG, LOCKPROG, opt, "-r", buf, "-q",
(flags & L_PID) ? "-p" : "-N", lockfile, NULL);
_exit(L_ERROR);
}
/* * Wait for return status - do something appropriate * if program died or returned L_ERROR.
*/ while ((n = waitpid(pid, &st, 0)) != pid) if (n < 0 && errno != EINTR) break; if (!sigismember(&oldset, SIGCHLD))
sigprocmask(SIG_UNBLOCK, &set, NULL); if (n < 0) return L_ERROR; if (!WIFEXITED(st) || WEXITSTATUS(st) == L_ERROR) {
errno = EINTR; return L_ERROR;
}
/* * Create a temp lockfile (hopefully unique) and write * either our pid/ppid in it, or 0\0 for svr4 compatibility.
*/ if (gethostname(sysname, sizeof(sysname)) < 0) return L_ERROR; if ((p = strchr(sysname, '.')) != NULL)
*p = 0; /* strcpy is safe: length-check above, limited at snprintf below */
strcpy(tmplock, lockfile); if ((p = strrchr(tmplock, '/')) == NULL)
p = tmplock; else
p++; if (snprintf(p, TMPLOCKFILENAMESZ, "%s%0*d%0*x%s", TMPLOCKSTR,
TMPLOCKPIDSZ, (int)getpid(),
TMPLOCKTIMESZ, (int)time(NULL) & 15,
sysname) < 0) { // never happens but gets rid of gcc truncation warning.
errno = EOVERFLOW; return L_ERROR;
}
return 0;
}
/* * Create a lockfile.
*/ staticint lockfile_create_save_tmplock(constchar *lockfile, char *tmplock, size_t tmplocksz, volatilechar **xtmplock, int retries, int flags, conststruct lockargs_s_ *args)
{ struct stat st, st1; char pidbuf[40];
pid_t pid = 0; int sleeptime = 0; int statfailed = 0; int fd; int i, e, pidlen; int dontsleep = 1; int tries = retries + 1;
/* process optional flags that have arguments */ if (flags & L_INTERVAL_D_) {
sleeptime = args->interval;
}
/* decide which PID to write to the lockfile */ if (flags & L_PID)
pid = getpid(); if (flags & L_PPID) {
pid = getppid(); if (pid == 1) { /* orphaned */ return L_ORPHANED;
}
}
pidlen = snprintf(pidbuf, sizeof(pidbuf), "%d\n", pid); if (pidlen < 0 || pidlen > (int) sizeof(pidbuf) - 1) {
errno = EOVERFLOW; return L_ERROR;
}
if (close(fd) != 0) {
e = errno;
i = -1;
} if (i != pidlen) {
unlink(tmplock);
tmplock[0] = 0;
errno = i < 0 ? e : EAGAIN; return L_TMPWRITE;
}
/* * Now try to link the temporary lock to the lock.
*/ for (i = 0; i < tries && tries > 0; i++) { if (!dontsleep) { if (!(flags & L_INTERVAL_D_))
sleeptime += 5;
/* * Now lock by linking the tempfile to the lock. * * KLUDGE: some people say the return code of * link() over NFS can't be trusted. * EXTRA FIX: the value of the nlink field * can't be trusted (may be cached).
*/
(void)!link(tmplock, lockfile);
if (lstat(lockfile, &st) < 0) { if (statfailed++ > 5) { /* * Normally, this can't happen; either * another process holds the lockfile or * we do. So if this error pops up * repeatedly, just exit...
*/
e = errno;
(void)unlink(tmplock);
tmplock[0] = 0;
errno = e; return L_MAXTRYS;
} continue;
}
/* * See if we got the lock.
*/ if (st.st_rdev == st1.st_rdev &&
st.st_ino == st1.st_ino) {
(void)unlink(tmplock);
tmplock[0] = 0; return L_SUCCESS;
}
statfailed = 0;
/* * If there is a lockfile and it is invalid, * remove the lockfile.
*/ if (lockfile_check(lockfile, flags) == -1) { if (unlink(lockfile) < 0 && errno != ENOENT) { /* * we failed to unlink the stale * lockfile, give up.
*/ return L_RMSTALE;
}
dontsleep = 1; /* * If the lockfile was invalid, then the first * try wasn't valid either - make sure we * try at least once more.
*/ if (tries == 1) tries++;
}
/* check if size is the same (version check) */ if (args != NULL && sizeof(struct lockargs_s_) != args_sz) {
errno = EINVAL; return L_ERROR;
} /* some flags _must_ have a non-null args */ if (args == NULL && (flags & FLAGS_WITH_ARGS)) {
errno = EINVAL; return L_ERROR;
} /* check against unknown flags */ if (flags & ~KNOWN_FLAGS) {
errno = EINVAL; return L_ERROR;
} return lockfile_create_set_tmplock(lockfile, NULL, retries, flags, args);
} #endif
#endif
/* * See if a valid lockfile is present. * Returns 0 if so, -1 if not.
*/ int lockfile_check(constchar *lockfile, int flags)
{ struct stat st, st2; char buf[16];
time_t now;
pid_t pid; int fd, len, r;
if (stat(lockfile, &st) < 0) return -1;
/* * Get the contents and mtime of the lockfile.
*/
time(&now);
pid = 0; if ((fd = open(lockfile, O_RDONLY)) >= 0) { /* * Try to use 'atime after read' as now, this is * the time of the filesystem. Should not get * confused by 'atime' or 'noatime' mount options.
*/
len = 0; if (fstat(fd, &st) == 0 &&
(len = read(fd, buf, sizeof(buf))) >= 0 &&
fstat(fd, &st2) == 0 &&
st.st_atime != st2.st_atime)
now = st.st_atime;
close(fd); if (len > 0 && (flags & (L_PID|L_PPID))) {
buf[len] = 0;
pid = atoi(buf);
}
}
if (pid > 0) { /* * If we have a pid, see if the process * owning the lockfile is still alive.
*/
r = kill(pid, 0); if (r == 0 || errno == EPERM) return 0; if (r < 0 && errno == ESRCH) return -1; /* EINVAL - FALLTHRU */
}
/* * Without a pid in the lockfile, the lock * is valid if it is newer than 5 mins.
*/
#ifdef LIB /* * Lock a mailfile. This looks a lot like the SVR4 function. * Arguments: lusername, retries.
*/ int maillock(constchar *name, int retries)
{ char *p, *mail; char *newlock; int i, e; int len, newlen;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.