diff options
Diffstat (limited to '')
-rw-r--r-- | solenv/lockfile/README | 6 | ||||
-rw-r--r-- | solenv/lockfile/autoconf.h.in | 30 | ||||
-rw-r--r-- | solenv/lockfile/dotlockfile.c | 457 | ||||
-rw-r--r-- | solenv/lockfile/lockfile.c | 611 | ||||
-rw-r--r-- | solenv/lockfile/lockfile.h | 72 | ||||
-rw-r--r-- | solenv/lockfile/maillock.h | 1 |
6 files changed, 1177 insertions, 0 deletions
diff --git a/solenv/lockfile/README b/solenv/lockfile/README new file mode 100644 index 000000000..94a0182c2 --- /dev/null +++ b/solenv/lockfile/README @@ -0,0 +1,6 @@ +All files (except for the dummy maillock.h) were copied from liblockfile 1.17. + +Just the max sleep time was adjusted in lockfile.c / lockfile_create_save_tmplock: + ++ if (sleeptime > 60) sleeptime = 60; +- if (sleeptime > 5) sleeptime = 5; diff --git a/solenv/lockfile/autoconf.h.in b/solenv/lockfile/autoconf.h.in new file mode 100644 index 000000000..7695d5496 --- /dev/null +++ b/solenv/lockfile/autoconf.h.in @@ -0,0 +1,30 @@ +/* autoconf.h.in. Generated automatically from configure.in by autoheader. */ +/* + +acconfig.h - template used by autoheader to create config.h.in +config.h.in - used by autoconf to create config.h +config.h - created by autoconf; contains defines generated by autoconf + +*/ + + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Is the mailspool group writable */ +#undef MAILGROUP + +/* Define if you have the utime function. */ +#undef HAVE_UTIME + +/* Define if you have the utimes function. */ +#undef HAVE_UTIMES + +/* Define if you have the <getopt.h> header file. */ +#undef HAVE_GETOPT_H + +/* Define if you have the <paths.h> header file. */ +#undef HAVE_PATHS_H + +/* Define if you have the <sys/param.h> header file. */ +#undef HAVE_SYS_PARAM_H diff --git a/solenv/lockfile/dotlockfile.c b/solenv/lockfile/dotlockfile.c new file mode 100644 index 000000000..a25cdc550 --- /dev/null +++ b/solenv/lockfile/dotlockfile.c @@ -0,0 +1,457 @@ +/* + * dotlockfile.c Command line version of liblockfile. + * Runs setgid mail so is able to lock mailboxes + * as well. Liblockfile can call this command. + * + * Copyright (C) Miquel van Smoorenburg and contributors 1999-2021 + * + * 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. + */ + +#include "autoconf.h" + +#include <sys/types.h> +#if HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif +#include <sys/wait.h> +#include <stdio.h> +#include <string.h> +#include <pwd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <time.h> +#include <errno.h> +#include "maillock.h" +#include "lockfile.h" + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +#ifndef HAVE_GETOPT_H +extern int getopt(); +extern char *optarg; +extern int optind; +#endif + +static volatile char *tmplock; +static int quiet; + +/* + * If we got SIGINT, SIGQUIT, SIGHUP, remove the + * tempfile and re-raise the signal. + */ +static void got_signal(int sig) +{ + if (tmplock && tmplock[0]) + unlink((char *)tmplock); + signal(sig, SIG_DFL); + raise(sig); +} + +static void ignore_signal(int sig) +{ + (void)sig; +} + +/* + * Install signal handler only if the signal was + * not ignored already. + */ +static int set_signal(int sig, void (*handler)(int)) +{ + struct sigaction sa; + + if (sigaction(sig, NULL, &sa) < 0) + return -1; + if (sa.sa_handler == SIG_IGN) + return 0; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; + return sigaction(sig, &sa, NULL); +} + +/* + * Sleep for an amount of time while regularly checking if + * our parent is still alive. + */ +int check_sleep(int sleeptime, int flags) +{ + int i; + int interval = 5; + static int ppid = 0; + + if (ppid == 0) ppid = getppid(); + + if (flags & L_INTERVAL_D_) + interval = 1; + + for (i = 0; i < sleeptime; i += interval) { + sleep(interval); + if (kill(ppid, 0) < 0 && errno == ESRCH) + return L_ERROR; + } + return 0; +} + +/* + * Split a filename up in file and directory. + */ +#ifdef MAILGROUP +static int fn_split(char *fn, char **fn_p, char **dir_p) +{ + static char *buf = NULL; + char *p; + + if (buf) + free (buf); + buf = (char *) malloc (strlen (fn) + 1); + if (! buf) + return L_ERROR; + strcpy(buf, fn); + if ((p = strrchr(buf, '/')) != NULL) { + *p++ = 0; + *fn_p = p; + *dir_p = buf; + } else { + *fn_p = fn; + *dir_p = "."; + } + return L_SUCCESS; +} +#endif + +/* + * Return name of lockfile for mail. + */ +static char *mlockname(char *user) +{ + static char *buf = NULL; + char *e; + + if (buf) + free(buf); + + e = getenv("MAIL"); + if (e) { + buf = (char *)malloc(strlen(e)+6); + if (!buf) + return NULL; + sprintf(buf, "%s.lock", e); + } else { + buf = (char *)malloc(strlen(MAILDIR)+strlen(user)+6); + if (!buf) + return NULL; + sprintf(buf, "%s%s.lock", MAILDIR, user); + } + return buf; +} + +static void perror_exit(const char *why) +{ + if (!quiet) { + fprintf(stderr, "dotlockfile: "); + perror(why); + } + exit(L_ERROR); +} + +/* + * Print usage message and exit. + */ +static void usage(void) +{ + fprintf(stderr, "Usage: dotlockfile -l [-r retries] [-i interval] [-p] [-q] <-m|lockfile>\n"); + fprintf(stderr, " dotlockfile -l [-r retries] [-i interval] [-p] [-q] <-m|lockfile> [-P] command args...\n"); + fprintf(stderr, " dotlockfile -u|-t\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + struct passwd *pwd; + struct lockargs_s_ args = { 0 }; + gid_t gid, egid; + char *lockfile = NULL; + char **cmd = NULL; + int c, r; + int retries = 5; + int interval = 0; + int flags = 0; + int lock = 0; + int unlock = 0; + int check = 0; + int touch = 0; + int writepid = 0; + int passthrough = 0; + int cwd_fd = -1; + int need_privs = 0; + pid_t pid = -1; + int e, wstatus; + + /* + * Remember real and effective gid, and + * drop privs for now. + */ + gid = getgid(); + egid = getegid(); + if (gid != egid) { + if (setregid(-1, gid) < 0) + perror_exit("setregid(-1, gid)"); + } + + set_signal(SIGINT, got_signal); + set_signal(SIGQUIT, got_signal); + set_signal(SIGHUP, got_signal); + set_signal(SIGTERM, got_signal); + set_signal(SIGPIPE, got_signal); + + /* + * Process the options. + */ + while ((c = getopt(argc, argv, "+qpNr:mluci:tP")) != EOF) switch(c) { + case 'q': + quiet = 1; + break; + case 'p': + writepid = 1; + break; + case 'N': + /* NOP */ + break; + case 'r': + retries = atoi(optarg); + if (retries <= 0 && + retries != -1 && strcmp(optarg, "0") != 0) { + if (!quiet) + fprintf(stderr, "dotlockfile: " + "-r %s: invalid argument\n", + optarg); + return L_ERROR; + } + if (retries == -1) { + /* 4000 years */ + retries = 0x7ffffff0; + } + break; + case 'm': + if ((pwd = getpwuid(geteuid())) == NULL) { + if (!quiet) + fprintf(stderr, "dotlockfile: You don't exist. Go away.\n"); + return L_ERROR; + } + lockfile = mlockname(pwd->pw_name); + if (!lockfile) { + if (!quiet) + perror("dotlockfile"); + return L_ERROR; + } + break; + case 'l': + lock = 1; + break; + case 'u': + unlock = 1; + break; + case 'c': + check = 1; + break; + case 'i': + interval = atoi(optarg); + if (interval <= 0 && strcmp(optarg, "0") != 0) { + fprintf(stderr, "dotlockfile: -i needs argument >= 0\n"); + return L_ERROR; + } + flags |= L_INTERVAL_D_; + args.interval = interval; + break; + case 't': + touch = 1; + break; + case 'P': + passthrough = 1; + break; + default: + usage(); + break; + } + + /* + * next argument may be lockfile name + */ + if (!lockfile) { + if (optind == argc) + usage(); + lockfile = argv[optind++]; + } + + /* + * next arguments may be command [args...] + */ + if (optind < argc) + cmd = argv + optind; + + /* + * Options sanity check + */ + if ((cmd || lock) && (touch || check || unlock)) + usage(); + + if (writepid) + flags |= (cmd ? L_PID : L_PPID); + +#ifdef MAXPATHLEN + if (strlen(lockfile) >= MAXPATHLEN) { + if (!quiet) + fprintf(stderr, "dotlockfile: %s: name too long\n", lockfile); + return L_NAMELEN; + } +#endif + + /* + * Check if we run setgid. + */ +#ifdef MAILGROUP + if (gid != egid) { + /* + * See if the requested lock is for a mailbox. + * First, remember current working directory. + */ +#ifdef O_PATH + cwd_fd = open(".", O_PATH|O_CLOEXEC); +#else + cwd_fd = open(".", O_RDONLY|O_CLOEXEC); +#endif + if (cwd_fd < 0) { + if (!quiet) + fprintf(stderr, "dotlockfile: opening \".\": %s\n", + strerror(errno)); + return L_ERROR; + } + /* + * Now change directory to the directory the lockfile is in. + */ + char *file, *dir; + r = fn_split(lockfile, &file, &dir); + if (r != L_SUCCESS) { + if (!quiet) + perror("dotlockfile"); + return L_ERROR; + } + if (chdir(dir) != 0) { + if (!quiet) + fprintf(stderr, "dotlockfile: %s: %s\n", dir, strerror(errno)); + return L_ERROR; + } + + lockfile = file; + need_privs = is_maillock(lockfile); + } +#endif + + /* + * See if we actually need to run setgid. + */ + if (need_privs) { + if (setregid(gid, egid) != 0) + perror_exit("setregid"); + } else { + if (gid != egid && setgid(gid) != 0) + perror_exit("setgid"); + } + + /* + * Simple check for a valid lockfile ? + */ + if (check) + return (lockfile_check(lockfile, flags) < 0) ? 1 : 0; + + + /* + * Touch lock ? + */ + if (touch) + return (lockfile_touch(lockfile) < 0) ? 1 : 0; + + /* + * Remove lockfile? + */ + if (unlock) + return (lockfile_remove(lockfile) == 0) ? 0 : 1; + + + /* + * No, lock. + */ + r = lockfile_create_set_tmplock(lockfile, &tmplock, retries, flags, &args); + if (r != 0 || !cmd) + return r; + + + /* + * Spawn command. + * + * Using an empty signal handler means that we ignore the + * signal, but that it's restored to SIG_DFL at execve(). + */ + set_signal(SIGINT, ignore_signal); + set_signal(SIGQUIT, ignore_signal); + set_signal(SIGHUP, ignore_signal); + set_signal(SIGALRM, ignore_signal); + + pid = fork(); + if (pid < 0) { + if (!quiet) + perror("fork"); + lockfile_remove(lockfile); + exit(L_ERROR); + } + if (pid == 0) { + /* drop setgid */ + if (gid != egid && setgid(gid) < 0) { + perror("setgid"); + exit(127); + } + /* restore current working directory */ + if (cwd_fd >= 0) { + if (fchdir(cwd_fd) < 0) { + perror("dotlockfile: restoring cwd:"); + exit(127); + } + close(cwd_fd); + } + /* exec */ + execvp(cmd[0], cmd); + perror(cmd[0]); + exit(127); + } + + /* wait for child */ + while (1) { + if (!writepid) + alarm(30); + e = waitpid(pid, &wstatus, 0); + if (e >= 0 || errno != EINTR) + break; + if (!writepid) + lockfile_touch(lockfile); + } + + alarm(0); + lockfile_remove(lockfile); + + if (passthrough) { + if (WIFEXITED(wstatus)) + return WEXITSTATUS(wstatus); + if (WIFSIGNALED(wstatus)) + return 128+WTERMSIG(wstatus); + } + return 0; +} + diff --git a/solenv/lockfile/lockfile.c b/solenv/lockfile/lockfile.c new file mode 100644 index 000000000..52be40b0f --- /dev/null +++ b/solenv/lockfile/lockfile.c @@ -0,0 +1,611 @@ +/* + * 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. + */ + +#include "autoconf.h" + +#include <sys/types.h> +#if HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <errno.h> +#include "lockfile.h" +#include "maillock.h" + +#ifdef HAVE_UTIME +#include <utime.h> +#endif + +#ifdef LIB +static char *mlockfile; +static int islocked = 0; +#endif + +#ifdef MAILGROUP +/* + * Get the id of the mailgroup, by statting the helper program. + * If it is setgroup-id, then the group is the mailgroup. + */ +static int 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(const char *lockfile) +{ + struct stat st; + gid_t gid; + char tmp[1024]; + char *p; + + /* remove .lock suffix */ + strncpy(tmp, lockfile, sizeof(tmp) - 1); + tmp[sizeof(tmp) - 1] = 0; + if ((p = strrchr(tmp, '.')) == NULL || strcmp(p, ".lock") != 0) + return 0; + *p = 0; + + /* 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. + */ +static int run_helper(char *opt, const char *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; + } + + return WEXITSTATUS(st); +} +#endif /* LIB*/ + +#endif /* MAILGROUP */ + +#define TMPLOCKSTR ".lk" +#define TMPLOCKSTRSZ strlen(TMPLOCKSTR) +#define TMPLOCKPIDSZ 5 +#define TMPLOCKTIMESZ 1 +#define TMPLOCKSYSNAMESZ 23 +#define TMPLOCKFILENAMESZ (TMPLOCKSTRSZ + TMPLOCKPIDSZ + \ + TMPLOCKTIMESZ + TMPLOCKSYSNAMESZ) + +static int lockfilename(const char *lockfile, char *tmplock, size_t tmplocksz) +{ + char sysname[256]; + char *p; + +#ifdef MAXPATHLEN + /* + * Safety measure. + */ + if (strlen(lockfile) + TMPLOCKFILENAMESZ > MAXPATHLEN) { + errno = ENAMETOOLONG; + return L_ERROR; + } +#endif + + if (strlen(lockfile) + TMPLOCKFILENAMESZ + 1 > tmplocksz) { + errno = EINVAL; + 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. + */ +static int lockfile_create_save_tmplock(const char *lockfile, + char *tmplock, size_t tmplocksz, + volatile char **xtmplock, + int retries, int flags, const struct 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; + } + + /* create temporary lockfile */ + if ((i = lockfilename(lockfile, tmplock, tmplocksz)) != 0) + return i; + if (xtmplock) + *xtmplock = tmplock; + fd = open(tmplock, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0644); + if (fd < 0) { + /* permission denied? perhaps try suid helper */ +#if defined(LIB) && defined(MAILGROUP) + if (errno == EACCES && is_maillock(lockfile)) + return run_helper("-l", lockfile, retries, flags); +#endif + return L_TMPLOCK; + } + i = write(fd, pidbuf, pidlen); + e = errno; + + 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; + + if (sleeptime > 5) sleeptime = 5; +#ifdef LIB + sleep(sleeptime); +#else + if ((e = check_sleep(sleeptime, flags)) != 0) { + unlink(tmplock); + tmplock[0] = 0; + return e; + } +#endif + } + dontsleep = 0; + + + /* + * 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(tmplock, &st1) < 0) { + tmplock[0] = 0; + return L_ERROR; /* Can't happen */ + } + + 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++; + } + + } + (void)unlink(tmplock); + tmplock[0] = 0; + errno = EAGAIN; + return L_MAXTRYS; +} + +#ifdef LIB +static +#endif +int lockfile_create_set_tmplock(const char *lockfile, volatile char **xtmplock, int retries, int flags, const struct lockargs_s_ *args) +{ + char *tmplock; + int r, e; + size_t l; + + l = strlen(lockfile)+TMPLOCKFILENAMESZ+1; + if ((tmplock = (char *)malloc(l)) == NULL) + return L_ERROR; + tmplock[0] = 0; + r = lockfile_create_save_tmplock(lockfile, + tmplock, l, xtmplock, retries, flags, args); + if (xtmplock) + *xtmplock = NULL; + e = errno; + free(tmplock); + errno = e; + return r; +} + +#ifdef LIB +int lockfile_create(const char *lockfile, int retries, int flags) +{ + /* check against unknown flags */ + if (flags & ~(L_PID|L_PPID)) { + errno = EINVAL; + return L_ERROR; + } + return lockfile_create_set_tmplock(lockfile, NULL, retries, flags, NULL); +} + +#ifdef STATIC +int lockfile_create2(const char *lockfile, int retries, + int flags, struct lockargs_s_ *args, int args_sz) +{ + + #define FLAGS_WITH_ARGS (L_INTERVAL_D_) + #define KNOWN_FLAGS (L_PID|L_PPID|L_INTERVAL_D_) + + /* 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(const char *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. + */ + + if (now < st.st_mtime + 300) + return 0; + + return -1; +} + +/* + * Remove a lock. + */ +int lockfile_remove(const char *lockfile) +{ + if (unlink(lockfile) < 0) { +#if defined(LIB) && defined(MAILGROUP) + if (errno == EACCES && is_maillock(lockfile)) + return run_helper("-u", lockfile, 0, 0); +#endif + return errno == ENOENT ? 0 : -1; + } + return 0; +} + +/* + * Touch a lock. + */ +int lockfile_touch(const char *lockfile) +{ +#ifdef HAVE_UTIME + return utime(lockfile, NULL); +#else + return utimes(lockfile, NULL); +#endif +} + +#ifdef LIB +/* + * Lock a mailfile. This looks a lot like the SVR4 function. + * Arguments: lusername, retries. + */ +int maillock(const char *name, int retries) +{ + char *p, *mail; + char *newlock; + int i, e; + int len, newlen; + + if (islocked) return 0; + +#ifdef MAXPATHLEN + if (strlen(name) + sizeof(MAILDIR) + 6 > MAXPATHLEN) { + errno = ENAMETOOLONG; + return L_NAMELEN; + } +#endif + + /* + * If $MAIL is for the same username as "name" + * then use $MAIL instead. + */ + + len = strlen(name)+strlen(MAILDIR)+6; + mlockfile = (char *)malloc(len); + if (!mlockfile) + return L_ERROR; + sprintf(mlockfile, "%s%s.lock", MAILDIR, name); + if ((mail = getenv("MAIL")) != NULL) { + if ((p = strrchr(mail, '/')) != NULL) + p++; + else + p = mail; + if (strcmp(p, name) == 0) { + newlen = strlen(mail)+6; +#ifdef MAXPATHLEN + if (newlen > MAXPATHLEN) { + errno = ENAMETOOLONG; + return L_NAMELEN; + } +#endif + if (newlen > len) { + newlock = (char *)realloc (mlockfile, newlen); + if (newlock == NULL) { + e = errno; + free (mlockfile); + mlockfile = NULL; + errno = e; + return L_ERROR; + } + mlockfile = newlock; + } + sprintf(mlockfile, "%s.lock", mail); + } + } + i = lockfile_create(mlockfile, retries, 0); + if (i == 0) islocked = 1; + + return i; +} + +void mailunlock(void) +{ + if (!islocked) return; + lockfile_remove(mlockfile); + free (mlockfile); + islocked = 0; +} + +void touchlock(void) +{ + lockfile_touch(mlockfile); +} +#endif + diff --git a/solenv/lockfile/lockfile.h b/solenv/lockfile/lockfile.h new file mode 100644 index 000000000..f2f72022f --- /dev/null +++ b/solenv/lockfile/lockfile.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 1999 Miquel van Smoorenburg + * + * 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. + * + * On Debian GNU/Linux systems, the complete text of the GNU Library + * General Public License can be found in `/usr/doc/copyright/LGPL'. + * You can also find a copy on the GNU website at http://www.gnu.org/ + */ +#ifndef LOCKFILE_H_ +#define LOCKFILE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Prototypes. + */ +int lockfile_create(const char *lockfile, int retries, int flags); +int lockfile_remove(const char *lockfile); +int lockfile_touch(const char *lockfile); +int lockfile_check(const char *lockfile, int flags); + +/* + * Return values for lockfile_create() + */ +#define L_SUCCESS 0 /* Lockfile created */ +#define L_NAMELEN 1 /* Recipient name too long */ +#define L_TMPLOCK 2 /* Error creating temp lockfile */ +#define L_TMPWRITE 3 /* Can't write pid into temp lockfile */ +#define L_MAXTRYS 4 /* Failed after max. number of attempts */ +#define L_ERROR 5 /* Unknown error; check errno */ +#define L_MANLOCK 6 /* Cannot set mandatory lock on tempfile */ +#define L_ORPHANED 7 /* Called with L_PPID but parent is gone */ +#define L_RMSTALE 8 /* Failed to remove stale lockfile */ + +/* + * Flag values for lockfile_create() + */ +#define L_PID 16 /* Put PID in lockfile */ +#define L_PPID 32 /* Put PPID in lockfile */ + +/* + * Experimental. + */ +struct lockargs_s_ { + int interval; /* Static interval between retries */ +}; +#define L_INTERVAL_D_ 64 /* Specify consistent retry interval */ +#ifdef LOCKFILE_EXPERIMENTAL +#define lockargs lockargs_s_ +#define L_INTERVAL L_INTERVAL_D_ +int lockfile_create2(const char *lockfile, int retries, + int flags, struct lockargs *args, int args_sz); +#endif + +#ifndef LIB +int check_sleep(int, int); +#endif +int is_maillock(const char *lockfile); +int lockfile_create_set_tmplock(const char *lockfile, volatile char **tmplock, + int retries, int flags, const struct lockargs_s_ *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LOCKFILE_H */ diff --git a/solenv/lockfile/maillock.h b/solenv/lockfile/maillock.h new file mode 100644 index 000000000..565e002d0 --- /dev/null +++ b/solenv/lockfile/maillock.h @@ -0,0 +1 @@ +#define MAILDIR "/very/likely/doesnt/exists" |