aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarel Zak <kzak@redhat.com>2024-04-29 10:25:17 +0200
committerKarel Zak <kzak@redhat.com>2024-04-29 10:25:17 +0200
commit1c04b04a552113149c05388d974e598a7db404de (patch)
treeddc2b0091edcc18d85e0f6d96d5740ad3275c567
parentb46a82a06f11ddc7a57d8bc892b3154e465f60d4 (diff)
parent8a0dc11a5b204c7d43adae9f42abcebe41c5b66e (diff)
downloadutil-linux-1c04b04a552113149c05388d974e598a7db404de.tar.gz
Merge branch 'PR/flock-fcntl' of github.com:karelzak/util-linux-work
* 'PR/flock-fcntl' of github.com:karelzak/util-linux-work: flock: add support for using fcntl() with open file description locks
-rw-r--r--sys-utils/flock.c78
1 files changed, 76 insertions, 2 deletions
diff --git a/sys-utils/flock.c b/sys-utils/flock.c
index 7d878ff810..c42c9da51c 100644
--- a/sys-utils/flock.c
+++ b/sys-utils/flock.c
@@ -48,6 +48,17 @@
#include "monotonic.h"
#include "timer.h"
+#ifndef F_OFD_GETLK
+#define F_OFD_GETLK 36
+#define F_OFD_SETLK 37
+#define F_OFD_SETLKW 38
+#endif
+
+enum {
+ API_FLOCK,
+ API_FCNTL_OFD,
+};
+
static void __attribute__((__noreturn__)) usage(void)
{
fputs(USAGE_HEADER, stdout);
@@ -70,6 +81,7 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_( " -o, --close close file descriptor before running command\n"), stdout);
fputs(_( " -c, --command <command> run a single command string through the shell\n"), stdout);
fputs(_( " -F, --no-fork execute command without forking\n"), stdout);
+ fputs(_( " --fcntl use fcntl(F_OFD_SETLK) rather than flock()\n"), stdout);
fputs(_( " --verbose increase verbosity\n"), stdout);
fputs(USAGE_SEPARATOR, stdout);
fprintf(stdout, USAGE_HELP_OPTIONS(26));
@@ -126,6 +138,49 @@ static void __attribute__((__noreturn__)) run_program(char **cmd_argv)
_exit((errno == ENOMEM) ? EX_OSERR : EX_UNAVAILABLE);
}
+static int flock_to_fcntl_type(int op)
+{
+ switch (op) {
+ case LOCK_EX:
+ return F_WRLCK;
+ case LOCK_SH:
+ return F_RDLCK;
+ case LOCK_UN:
+ return F_UNLCK;
+ default:
+ errx(EX_SOFTWARE, _("internal error, unknown operation %d"), op);
+ }
+}
+
+static int fcntl_lock(int fd, int op, int block)
+{
+ struct flock arg = {
+ .l_type = flock_to_fcntl_type(op),
+ .l_whence = SEEK_SET,
+ .l_start = 0,
+ .l_len = 0,
+ };
+ int cmd = (block & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW;
+ return fcntl(fd, cmd, &arg);
+}
+
+static int do_lock(int api, int fd, int op, int block)
+{
+ switch (api) {
+ case API_FLOCK:
+ return flock(fd, op | block);
+ case API_FCNTL_OFD:
+ return fcntl_lock(fd, op, block);
+ /*
+ * Should never happen, api can never have values other than
+ * API_*.
+ */
+ default:
+ errx(EX_SOFTWARE, _("internal error, unknown api %d"), api);
+ }
+}
+
+
int main(int argc, char *argv[])
{
struct ul_timer timer;
@@ -140,6 +195,7 @@ int main(int argc, char *argv[])
int no_fork = 0;
int status;
int verbose = 0;
+ int api = API_FLOCK;
struct timeval time_start = { 0 }, time_done = { 0 };
/*
* The default exit code for lock conflict or timeout
@@ -149,7 +205,8 @@ int main(int argc, char *argv[])
char **cmd_argv = NULL, *sh_c_argv[4];
const char *filename = NULL;
enum {
- OPT_VERBOSE = CHAR_MAX + 1
+ OPT_VERBOSE = CHAR_MAX + 1,
+ OPT_FCNTL,
};
static const struct option long_options[] = {
{"shared", no_argument, NULL, 's'},
@@ -163,6 +220,7 @@ int main(int argc, char *argv[])
{"close", no_argument, NULL, 'o'},
{"no-fork", no_argument, NULL, 'F'},
{"verbose", no_argument, NULL, OPT_VERBOSE},
+ {"fcntl", no_argument, NULL, OPT_FCNTL},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
@@ -217,6 +275,9 @@ int main(int argc, char *argv[])
if (conflict_exit_code < 0 || conflict_exit_code > 255)
errx(EX_USAGE, _("exit code out of range (expected 0 to 255)"));
break;
+ case OPT_FCNTL:
+ api = API_FCNTL_OFD;
+ break;
case OPT_VERBOSE:
verbose = 1;
break;
@@ -234,6 +295,13 @@ int main(int argc, char *argv[])
errx(EX_USAGE,
_("the --no-fork and --close options are incompatible"));
+ /*
+ * For fcntl(F_OFD_SETLK), an exclusive lock requires that the
+ * file is open for write.
+ */
+ if (api != API_FLOCK && type == LOCK_EX)
+ open_flags = O_WRONLY;
+
if (argc > optind + 1) {
/* Run command */
if (!strcmp(argv[optind + 1], "-c") ||
@@ -280,9 +348,15 @@ int main(int argc, char *argv[])
if (verbose)
gettime_monotonic(&time_start);
- while (flock(fd, type | block)) {
+ while (do_lock(api, fd, type, block)) {
switch (errno) {
case EWOULDBLOCK:
+ /*
+ * Per the man page, for fcntl(), EACCES may
+ * be returned and means the same as
+ * EAGAIN/EWOULDBLOCK.
+ */
+ case EACCES:
/* -n option set and failed to lock. */
if (verbose)
warnx(_("failed to get lock"));