summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan-Marek Glogowski <glogow@fbihome.de>2021-05-14 15:26:39 +0200
committerJan-Marek Glogowski <glogow@fbihome.de>2021-12-01 13:06:55 +0100
commite050b09c7ef193da9da4441d9984c793e35b5a8a (patch)
tree4dd5b57161afc89adc8399a8aaca9387cf397310
parent47cdd3ba605324072db617c6ae2d1a647b8bdce6 (diff)
gbuild: serialize dynamic link for static builds
This is a hack, because make has no way to serialize processing of a target (just .NOTPARALLEL for the whole Makefile). It uses the lockfile tool / liblockfile 1.17. Since that polls the file, I adjusted the poll timeout to 5s max, because I found the 60s wait much too long. Guess even 1s would be ok... Since it's just a small build tool, I simply copied and patched its source, instead of creating an external project. And there is --with-system-lockfile=... to use an external binary instead. Change-Id: I16bc4579a273dcf1aac811ae4723ca325a0b9eba Reviewed-on: https://gerrit.libreoffice.org/c/core/+/126152 Tested-by: Jenkins Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
-rw-r--r--.gitignore3
-rw-r--r--Makefile.gbuild4
-rw-r--r--Repository.mk1
-rw-r--r--config_host.mk.in1
-rw-r--r--configure.ac42
-rw-r--r--solenv/Executable_lockfile.mk25
-rw-r--r--solenv/Module_solenv.mk1
-rw-r--r--solenv/clang-format/excludelist5
-rw-r--r--solenv/gbuild/extensions/pre_BuildTools.mk1
-rw-r--r--solenv/gbuild/partial_build.mk4
-rw-r--r--solenv/gbuild/platform/unxgcc.mk8
-rw-r--r--solenv/lockfile/README6
-rw-r--r--solenv/lockfile/autoconf.h.in30
-rw-r--r--solenv/lockfile/dotlockfile.c459
-rw-r--r--solenv/lockfile/lockfile.c614
-rw-r--r--solenv/lockfile/lockfile.h65
-rw-r--r--solenv/lockfile/maillock.h1
17 files changed, 1269 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index c0a41f579713..418108746918 100644
--- a/.gitignore
+++ b/.gitignore
@@ -183,3 +183,6 @@ LibreOffice.VC.VC.opendb
/a.wasm
/a.out.js
/a.out.wasm
+
+# lockfile config header
+/solenv/lockfile/autoconf.h
diff --git a/Makefile.gbuild b/Makefile.gbuild
index cd7fc973c287..9e7f205e22d4 100644
--- a/Makefile.gbuild
+++ b/Makefile.gbuild
@@ -20,6 +20,10 @@ include $(SRCDIR)/solenv/gbuild/gbuild.mk
$(eval $(call gb_Module_make_global_targets,$(SRCDIR)/RepositoryModule_$(gb_Side).mk))
+ifneq (,$(DISABLE_DYNLOADING))
+$(if $(gb_LinkTarget__Lock),$(shell rm -f $(gb_LinkTarget__Lock)))
+endif
+
upload-symbols:
bin/upload_symbols.py $(WORKDIR)/symbols.zip $(BREAKPAD_SYMBOL_CONFIG) "$(LIBO_VERSION_MAJOR).$(LIBO_VERSION_MINOR).$(LIBO_VERSION_MICRO).$(LIBO_VERSION_PATCH)$(LIBO_VERSION_SUFFIX)$(LIBO_VERSION_SUFFIX_SUFFIX)"
diff --git a/Repository.mk b/Repository.mk
index f2cebecb278d..629f95354422 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -44,6 +44,7 @@ $(eval $(call gb_Helper_register_executables,NONE, \
$(if $(filter iOS,$(OS)),LibreOffice) \
lngconvex \
localize \
+ $(if $(filter-out ANDROID MACOSX iOS WNT,$(OS)),lockfile) \
makedepend \
mbsdiff \
osl_process_child \
diff --git a/config_host.mk.in b/config_host.mk.in
index fc1a4b442e82..a931d947f2d3 100644
--- a/config_host.mk.in
+++ b/config_host.mk.in
@@ -404,6 +404,7 @@ export LIBXML_JAR=@LIBXML_JAR@
export LIBXML_LIBS=$(gb_SPACE)@LIBXML_LIBS@
export LIBXSLT_CFLAGS=$(gb_SPACE)@LIBXSLT_CFLAGS@
export LIBXSLT_LIBS=$(gb_SPACE)@LIBXSLT_LIBS@
+export LOCKFILE=@LOCKFILE@
export LO_CLANG_CC=@LO_CLANG_CC@
export LO_CLANG_CXX=@LO_CLANG_CXX@
export LO_CLANG_CXXFLAGS_INTRINSICS_SSE2=@LO_CLANG_CXXFLAGS_INTRINSICS_SSE2@
diff --git a/configure.ac b/configure.ac
index ac76f505ea8a..ce0f76f5c27e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2413,6 +2413,10 @@ AC_ARG_WITH(system-liblangtag,
[Use liblangtag library already on system.]),,
[with_system_liblangtag="$with_system_libs"])
+AC_ARG_WITH(system-lockfile,
+ AS_HELP_STRING([--with-system-lockfile[=file]],
+ [Detect a system lockfile program or use the \$file argument.]))
+
AC_ARG_WITH(webdav,
AS_HELP_STRING([--with-webdav],
[Specify which library to use for webdav implementation.
@@ -5455,6 +5459,7 @@ if test "$cross_compiling" = "yes"; then
config_host/*.in \
sysui/desktop/macosx/Info.plist.in \
.vscode/vs-code-template.code-workspace.in \
+ solenv/lockfile/autoconf.h.in \
) \
| (cd CONF-FOR-BUILD && tar xf -)
cp configure CONF-FOR-BUILD
@@ -5504,6 +5509,7 @@ if test "$cross_compiling" = "yes"; then
test -n "$with_help" -a "$with_help" != "no" && sub_conf_opts="$sub_conf_opts --with-help=$with_help"
test "$enable_extensions" = yes || sub_conf_opts="$sub_conf_opts --disable-extensions"
test "$enable_wasm_strip" = "yes" && sub_conf_opts="$sub_conf_opts --enable-wasm-strip"
+ test "${with_system_lockfile+set}" = set && sub_conf_opts="$sub_conf_opts --with-system-lockfile=${with_system_lockfile}"
# Don't bother having configure look for stuff not needed for the build platform anyway
sub_conf_defaults=" \
@@ -5620,6 +5626,7 @@ if test "$cross_compiling" = "yes"; then
JAVACOMPILER
JAVADOC
JAVADOCISGJDOC
+ LOCKFILE
"
# these need some special handling
EXTRA_HANDLED_SETTINGS="
@@ -5703,6 +5710,40 @@ AC_SUBST(CXX_FOR_BUILD)
AC_SUBST(CPPU_ENV_FOR_BUILD)
dnl ===================================================================
+dnl Check for lockfile deps
+dnl ===================================================================
+if test -z "$CROSS_COMPILING"; then
+ test -n "$LOCKFILE" -a "${with_system_lockfile+set}" != set && with_system_lockfile="$LOCKFILE"
+ test "${with_system_lockfile+set}" = set || with_system_lockfile=no
+ AC_MSG_CHECKING([whick lockfile binary to use])
+ case "$with_system_lockfile" in
+ yes)
+ AC_MSG_RESULT([external])
+ AC_PATH_PROGS([LOCKFILE],[dotlockfile lockfile])
+ ;;
+ no)
+ AC_MSG_RESULT([internal])
+ ;;
+ *)
+ if test -x "$with_system_lockfile"; then
+ LOCKFILE="$with_system_lockfile"
+ else
+ AC_MSG_ERROR(['$with_system_lockfile' is not executable.])
+ fi
+ AC_MSG_RESULT([$with_system_lockfile])
+ ;;
+ esac
+fi
+
+if test -n "$LOCKFILE" -a "$DISABLE_DYNLOADING" = TRUE; then
+ add_warning "The default system lockfile has increasing poll intervals up to 60s, so linking executables may be delayed."
+fi
+
+AC_CHECK_HEADERS([getopt.h paths.h sys/param.h])
+AC_CHECK_FUNCS([utime utimes])
+AC_SUBST(LOCKFILE)
+
+dnl ===================================================================
dnl Check for syslog header
dnl ===================================================================
AC_CHECK_HEADER(syslog.h, AC_DEFINE(HAVE_SYSLOG_H))
@@ -14627,6 +14668,7 @@ AC_CONFIG_HEADERS([config_host/config_oauth2.h])
AC_CONFIG_HEADERS([config_host/config_poppler.h])
AC_CONFIG_HEADERS([config_host/config_python.h])
AC_CONFIG_HEADERS([config_host/config_writerperfect.h])
+AC_CONFIG_HEADERS([solenv/lockfile/autoconf.h])
AC_OUTPUT
if test "$CROSS_COMPILING" = TRUE; then
diff --git a/solenv/Executable_lockfile.mk b/solenv/Executable_lockfile.mk
new file mode 100644
index 000000000000..78784147a020
--- /dev/null
+++ b/solenv/Executable_lockfile.mk
@@ -0,0 +1,25 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Executable_Executable,lockfile))
+
+$(eval $(call gb_Executable_set_warnings_not_errors,lockfile))
+
+$(eval $(call gb_Executable_set_include,lockfile, \
+ -I$(SRCDIR)/solenv/lockfile \
+ -I$(BUILDDIR)/solenv/lockfile \
+))
+
+$(eval $(call gb_Executable_add_cobjects,lockfile, \
+ solenv/lockfile/dotlockfile \
+ solenv/lockfile/lockfile \
+ , $(gb_COMPILEROPTFLAGS) \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/solenv/Module_solenv.mk b/solenv/Module_solenv.mk
index 70eb5f9fb7c3..bbe6b5a97f32 100644
--- a/solenv/Module_solenv.mk
+++ b/solenv/Module_solenv.mk
@@ -12,6 +12,7 @@ $(eval $(call gb_Module_Module,solenv))
$(eval $(call gb_Module_add_targets_for_build,solenv,\
Executable_concat-deps \
Executable_gbuildtojson \
+ $(if $(filter-out ANDROID MACOSX iOS WNT,$(OS)),Executable_lockfile) \
))
ifeq ($(COM),MSC)
diff --git a/solenv/clang-format/excludelist b/solenv/clang-format/excludelist
index 456d168f500d..2448a00b08f9 100644
--- a/solenv/clang-format/excludelist
+++ b/solenv/clang-format/excludelist
@@ -10907,6 +10907,11 @@ smoketest/smoketest.cxx
solenv/bin/concat-deps.c
solenv/gbuildtojson/gbuildtojson.cxx
solenv/gcc-wrappers/wrapper.cxx
+solenv/lockfile/autoconf.h.in
+solenv/lockfile/dotlockfile.c
+solenv/lockfile/lockfile.c
+solenv/lockfile/lockfile.h
+solenv/lockfile/maillock.h
soltools/cpp/_eval.c
soltools/cpp/_getopt.c
soltools/cpp/_include.c
diff --git a/solenv/gbuild/extensions/pre_BuildTools.mk b/solenv/gbuild/extensions/pre_BuildTools.mk
index 84e2ea533673..8481a3785637 100644
--- a/solenv/gbuild/extensions/pre_BuildTools.mk
+++ b/solenv/gbuild/extensions/pre_BuildTools.mk
@@ -24,6 +24,7 @@ gb_BUILD_TOOLS_executables = \
helpex \
idxdict \
javamaker \
+ $(if $(filter-out ANDROID MACOSX iOS WNT,$(OS)),lockfile) \
makedepend \
propex \
saxparser \
diff --git a/solenv/gbuild/partial_build.mk b/solenv/gbuild/partial_build.mk
index 3b4478f23154..bc0e4a261b7b 100644
--- a/solenv/gbuild/partial_build.mk
+++ b/solenv/gbuild/partial_build.mk
@@ -37,4 +37,8 @@ include $(SRCDIR)/solenv/gbuild/gbuild.mk
$(eval $(call gb_Module_make_global_targets,$(wildcard $(module_directory)Module*.mk)))
+ifeq ($(DISABLE_DYNLOADING),TRUE)
+$(if $(gb_LinkTarget__Lock),$(shell rm -f $(gb_LinkTarget__Lock)))
+endif
+
# vim: set noet sw=4 ts=4:
diff --git a/solenv/gbuild/platform/unxgcc.mk b/solenv/gbuild/platform/unxgcc.mk
index 0cec2ecfbc0c..60e63f36cd3a 100644
--- a/solenv/gbuild/platform/unxgcc.mk
+++ b/solenv/gbuild/platform/unxgcc.mk
@@ -99,6 +99,10 @@ gb_LinkTarget__RPATHS := \
gb_LinkTarget_CFLAGS := $(gb_CFLAGS)
gb_LinkTarget_CXXFLAGS := $(gb_CXXFLAGS)
+gb_LinkTarget__cmd_lockfile = $(if $(LOCKFILE),$(LOCKFILE),$(call gb_Executable_get_command,lockfile))
+gb_LinkTarget__Lock := $(WORKDIR)/LinkTarget/link.lock
+gb_LinkTarget__WantLock = $(if $(and $(filter-out ANDROID MACOSX iOS WNT,$(OS)),$(filter TRUE,$(DISABLE_DYNLOADING)),$(filter CppunitTest Executable,$(TARGETTYPE))),$(true))
+
# note that `cat $(extraobjectlist)` is needed to build with older gcc versions, e.g. 4.1.2 on SLED10
# we want to use @$(extraobjectlist) in the long run
# link with C compiler if there are no C++ files (pyuno_wrapper depends on this)
@@ -107,6 +111,7 @@ gb_LinkTarget_CXXFLAGS := $(gb_CXXFLAGS)
# libclang_rt.ubsan_cxx-x86_64.a, and oosplash links against sal but itself only
# contains .c sources:
define gb_LinkTarget__command_dynamiclink
+$(if $(gb_LinkTarget__WantLock),$(gb_LinkTarget__cmd_lockfile) -r -1 $(gb_LinkTarget__Lock))
$(call gb_Helper_abbreviate_dirs,\
$(if $(CXXOBJECTS)$(GENCXXOBJECTS)$(EXTRAOBJECTLISTS)$(filter-out XTRUE,X$(ENABLE_RUNTIME_OPTIMIZATIONS)),$(or $(T_CXX),$(gb_CXX)) $(gb_CXX_LINKFLAGS),$(or $(T_CC),$(gb_CC))) \
$(if $(filter Library CppunitTest,$(TARGETTYPE)),$(gb_Library_TARGETTYPEFLAGS)) \
@@ -141,7 +146,8 @@ $(call gb_Helper_abbreviate_dirs,\
$(patsubst lib%.a,-l%,$(patsubst lib%.so,-l%,$(patsubst %.$(gb_Library_UDK_MAJORVER),%,$(foreach lib,$(LINKED_LIBS),$(call gb_Library_get_filename,$(lib)))))) \
) \
-o $(1) \
- $(if $(SOVERSIONSCRIPT),&& ln -sf ../../program/$(notdir $(1)) $(ILIBTARGET)))
+ $(if $(SOVERSIONSCRIPT),&& ln -sf ../../program/$(notdir $(1)) $(ILIBTARGET)) \
+ $(if $(gb_LinkTarget__WantLock),; RC=$$? ; rm -f $(gb_LinkTarget__Lock); if test $$RC -ne 0; then exit $$RC; fi))
$(if $(filter Library,$(TARGETTYPE)), $(call gb_Helper_abbreviate_dirs,\
$(READELF) -d $(1) | grep SONAME > $(WORKDIR)/LinkTarget/$(2).exports.tmp; \
$(NM) $(gb_LTOPLUGINFLAGS) --dynamic --extern-only --defined-only --format=posix $(1) \
diff --git a/solenv/lockfile/README b/solenv/lockfile/README
new file mode 100644
index 000000000000..94a0182c2244
--- /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 000000000000..7695d549649e
--- /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 000000000000..3670ecc238a6
--- /dev/null
+++ b/solenv/lockfile/dotlockfile.c
@@ -0,0 +1,459 @@
+/*
+ * 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
+
+extern int is_maillock(const char *lockfile);
+extern int lockfile_create_set_tmplock(const char *lockfile,
+ volatile char **tmplock, int retries, int flags, struct __lockargs *);
+
+static volatile char *tmplock;
+static int quiet;
+
+/*
+ * If we got SIGINT, SIGQUIT, SIGHUP, remove the
+ * tempfile and re-raise the signal.
+ */
+void got_signal(int sig)
+{
+ if (tmplock && tmplock[0])
+ unlink((char *)tmplock);
+ signal(sig, SIG_DFL);
+ raise(sig);
+}
+
+void ignore_signal(int sig)
+{
+}
+
+/*
+ * Install signal handler only if the signal was
+ * not ignored already.
+ */
+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 regulary 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)
+ 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.
+ */
+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;
+}
+
+
+/*
+ * Return name of lockfile for mail.
+ */
+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;
+}
+
+void perror_exit(const char *why) {
+ if (!quiet) {
+ fprintf(stderr, "dotlockfile: ");
+ perror(why);
+ }
+ exit(L_ERROR);
+}
+
+/*
+ * Print usage mesage and exit.
+ */
+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 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;
+
+ /*
+ * Remember real and effective gid, and
+ * drop privs for now.
+ */
+ if ((gid = getgid()) < 0)
+ perror_exit("getgid");
+ if ((egid = getegid()) < 0)
+ perror_exit("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;
+ 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.
+ */
+ int cwd_fd = -1;
+ int need_privs = 0;
+#ifdef MAILGROUP
+ if (gid != egid) {
+ /*
+ * See if the requested lock is for a mailbox.
+ * First, remember currect 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_t 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 */
+ int e, wstatus;
+ 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 000000000000..d67050a52cee
--- /dev/null
+++ b/solenv/lockfile/lockfile.c
@@ -0,0 +1,614 @@
+/*
+ * 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
+
+#ifndef LIB
+extern int check_sleep(int, int);
+#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, int 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, int tmplocksz,
+ volatile char **xtmplock,
+ int retries, int flags, struct __lockargs *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) {
+ 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 > 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))
+ 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, struct __lockargs *args)
+{
+ char *tmplock;
+ int l, r, e;
+
+ 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 *args, int args_sz)
+{
+
+ #define FLAGS_WITH_ARGS (__L_INTERVAL)
+ #define KNOWN_FLAGS (L_PID|L_PPID|__L_INTERVAL)
+
+ /* check if size is the same (version check) */
+ if (args != NULL && sizeof(struct __lockargs) != 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 000000000000..12e7d494bb05
--- /dev/null
+++ b/solenv/lockfile/lockfile.h
@@ -0,0 +1,65 @@
+/*
+ * 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 {
+ int interval; /* Static interval between retries */
+};
+#define __L_INTERVAL 64 /* Specify consistent retry interval */
+#ifdef LOCKFILE_EXPERIMENTAL
+#define lockargs __lockargs
+#define L_INTERVAL __L_INTERVAL
+int lockfile_create2(const char *lockfile, int retries,
+ int flags, struct lockargs *args, int args_sz);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LOCKFILE_H */
diff --git a/solenv/lockfile/maillock.h b/solenv/lockfile/maillock.h
new file mode 100644
index 000000000000..565e002d0f2b
--- /dev/null
+++ b/solenv/lockfile/maillock.h
@@ -0,0 +1 @@
+#define MAILDIR "/very/likely/doesnt/exists"