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>
This commit is contained in:
parent
47cdd3ba60
commit
e050b09c7e
17 changed files with 1269 additions and 1 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -183,3 +183,6 @@ LibreOffice.VC.VC.opendb
|
|||
/a.wasm
|
||||
/a.out.js
|
||||
/a.out.wasm
|
||||
|
||||
# lockfile config header
|
||||
/solenv/lockfile/autoconf.h
|
||||
|
|
|
@ -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)"
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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@
|
||||
|
|
42
configure.ac
42
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="
|
||||
|
@ -5702,6 +5709,40 @@ AC_SUBST(CC_FOR_BUILD)
|
|||
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 ===================================================================
|
||||
|
@ -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
|
||||
|
|
25
solenv/Executable_lockfile.mk
Normal file
25
solenv/Executable_lockfile.mk
Normal file
|
@ -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:
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -24,6 +24,7 @@ gb_BUILD_TOOLS_executables = \
|
|||
helpex \
|
||||
idxdict \
|
||||
javamaker \
|
||||
$(if $(filter-out ANDROID MACOSX iOS WNT,$(OS)),lockfile) \
|
||||
makedepend \
|
||||
propex \
|
||||
saxparser \
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) \
|
||||
|
|
6
solenv/lockfile/README
Normal file
6
solenv/lockfile/README
Normal file
|
@ -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;
|
30
solenv/lockfile/autoconf.h.in
Normal file
30
solenv/lockfile/autoconf.h.in
Normal file
|
@ -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
|
459
solenv/lockfile/dotlockfile.c
Normal file
459
solenv/lockfile/dotlockfile.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
614
solenv/lockfile/lockfile.c
Normal file
614
solenv/lockfile/lockfile.c
Normal file
|
@ -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
|
||||
|
65
solenv/lockfile/lockfile.h
Normal file
65
solenv/lockfile/lockfile.h
Normal file
|
@ -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 */
|
1
solenv/lockfile/maillock.h
Normal file
1
solenv/lockfile/maillock.h
Normal file
|
@ -0,0 +1 @@
|
|||
#define MAILDIR "/very/likely/doesnt/exists"
|
Loading…
Reference in a new issue