Subject: [PATCH] [FEAT VS1822] libutil: add example for util_lockfile
From: Matthew Rosato <mjrosato@linux.ibm.com>

Summary:     ap_tools: add ap-check and the ap device type to zdev   
Description: This feature adds multiple components in support of persistent
             configuration of vfio-ap mediated devices.

             The ap-check utility is added as a callout function for the
             mdevctl utility.  This allows for meaningful error messages to be
             presented to end-users when vfio-ap configuration errors are
             detected while using mdevctl to create/modify vfio-ap mediated
             devices.

             Additionally, the 'ap' device type is added to zdev, providing a
             command-line interface for managing the apmask and aqmask, which
             determine what AP resources are available for vfio-ap usage.
             'chzdev' is updated to allow for modifying the active masks as
             well as to specify persistent mask values via a udev rule.
             'lszdev' is updated to allow for querying of the active and
             persistent mask values.
Upstream-ID: 6235a51d7c8c6019210e9be77973ff016c6d03e8
Problem-ID:  VS1822

Upstream-Description:

             libutil: add example for util_lockfile

             Adds util_lockfile_example.c, which can be used to sample
             util_lockfile support.

             To acquire a lock using a parent PID (e.g. your shell instance):
             util_lockfile_exmample -f <path> -l <retries>

             To release the lock using the parent PID:
             util_lockfile_example -f <path> -r

             To acquire the lock, sleep briefly, and then release the lock
             using the PID of the util_lockfile_example process:
             util_lockfile_example -f <path> -L <retries>

             In each example, the <path> is the location of the desired lockfile
             and <retries> is the number of times to retry acquiring the lock
             if it fails.

             GitHub-ID: https://github.com/ibm-s390-linux/s390-tools/issues/142
             Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
             Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
             Signed-off-by: Jan Hoeppner <hoeppner@linux.ibm.com>


Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
---
 libutil/util_lockfile_example.c |  232 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 232 insertions(+)

--- /dev/null
+++ b/libutil/util_lockfile_example.c
@@ -0,0 +1,232 @@
+/**
+ * util_lockfile_example - Example program for util_lockfile
+ *
+ * Copyright IBM Corp. 2022
+ *
+ * s390-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+//! [code]
+#include <stdio.h>
+#include <stdlib.h>
+#include "lib/util_opt.h"
+#include "lib/util_prg.h"
+#include "lib/util_lockfile.h"
+
+static const struct util_prg prg = {
+	.desc = "Example for util_lockfile.",
+	.copyright_vec = { {
+			.owner = "IBM Corp.",
+			.pub_first = 2022,
+			.pub_last = 2022,
+		},
+		UTIL_PRG_COPYRIGHT_END }
+};
+
+static struct util_opt opt_vec[] = {
+	UTIL_OPT_SECTION("OPTIONS"),
+	{
+		.option = { "file", required_argument, NULL, 'f' },
+		.argument = "PATH",
+		.desc = "Use the specified path for a lockfile.",
+	},
+	{
+		.option = { "lock", required_argument, NULL, 'l' },
+		.argument = "RETRIES",
+		.desc = "Acquire the specified file lock using the parent "
+			"process id of this process.  If not immediately "
+			"successful, retry for the specified number of times",
+	},
+	{
+		.option = { "release", 0, NULL, 'r' },
+		.desc = "Release the specified lock file using the parent "
+			"process id",
+	},
+	{
+		.option = { "lock-and-release", required_argument, NULL, 'L' },
+		.argument = "RETRIES",
+		.desc = "Acquire the specified file lock using this process "
+			"id, then release it.  If not immediately successful, "
+			"retry for the specified number of times.",
+	},
+	UTIL_OPT_HELP,
+	UTIL_OPT_VERSION,
+	UTIL_OPT_END
+};
+
+enum prg_action {
+	ACTION_NONE = 0,
+	ACTION_LOCK,
+	ACTION_RELEASE,
+	ACTION_LOCK_AND_RELEASE,
+};
+
+static void print_single_action_error(void)
+{
+	warnx("Only a single action (--lock, --lock-parent, --release, "
+	      "--release-parent) is allowed");
+}
+
+int main(int argc, char *argv[])
+{
+	enum prg_action action_id = ACTION_NONE;
+	int opt, rc, retries = 0;
+	char *endp, *path = NULL;
+
+	util_prg_init(&prg);
+	util_opt_init(opt_vec, NULL);
+
+	while (1) {
+		opt = util_opt_getopt_long(argc, argv);
+		if (opt == -1)
+			break;
+		switch (opt) {
+		case 'f':
+			path = optarg;
+			break;
+		case 'L':
+			if (action_id != ACTION_NONE) {
+				print_single_action_error();
+				return EXIT_FAILURE;
+			}
+			retries = strtol(optarg, &endp, 0);
+			if (*optarg == '\0' || *endp != '\0' || retries < 0) {
+				warnx("Invalid retry value for "
+				      "--lock-and-release: '%s'",
+				      optarg);
+				util_prg_print_parse_error();
+				return EXIT_FAILURE;
+			}
+			action_id = ACTION_LOCK_AND_RELEASE;
+			break;
+		case 'l':
+			if (action_id != ACTION_NONE) {
+				print_single_action_error();
+				return EXIT_FAILURE;
+			}
+			retries = strtol(optarg, &endp, 0);
+			if (*optarg == '\0' || *endp != '\0' || retries < 0) {
+				warnx("Invalid retry value for "
+				      "--parent-lock: '%s'",
+				      optarg);
+				util_prg_print_parse_error();
+				return EXIT_FAILURE;
+			}
+			action_id = ACTION_LOCK;
+			break;
+		case 'r':
+			if (action_id != ACTION_NONE) {
+				print_single_action_error();
+				return EXIT_FAILURE;
+			}
+			action_id = ACTION_RELEASE;
+			break;
+		case 'h':
+			util_prg_print_help();
+			util_opt_print_help();
+			exit(EXIT_SUCCESS);
+		case 'v':
+			util_prg_print_version();
+			exit(EXIT_SUCCESS);
+		default:
+			util_opt_print_parse_error(opt, argv);
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (!path) {
+		warnx("--file is required, see --help for more information");
+		return EXIT_FAILURE;
+	}
+
+	if (action_id == ACTION_NONE) {
+		warnx("One of the following actions must be specified: "
+		      "--lock, --release, --lock-and-release");
+		return EXIT_FAILURE;
+	}
+
+	/* Determine which util_lockfile function to call */
+	switch (action_id) {
+	case ACTION_LOCK:
+		rc = util_lockfile_parent_lock(path, retries);
+		switch (rc) {
+		case UTIL_LOCKFILE_OK:
+			printf("lock acquired successfully\n");
+			break;
+		case UTIL_LOCKFILE_LOCK_FAIL:
+			warnx("lock was not acquired; already held");
+			return EXIT_FAILURE;
+		case UTIL_LOCKFILE_ERR:
+			warnx("lock was not acquired; file error");
+			return EXIT_FAILURE;
+		default:
+			warnx("Unknown util_lockfile rc %d", rc);
+			return EXIT_FAILURE;
+		}
+		break;
+	case ACTION_RELEASE:
+		rc = util_lockfile_parent_release(path);
+		switch (rc) {
+		case UTIL_LOCKFILE_OK:
+			printf("lock released successfully\n");
+			break;
+		case UTIL_LOCKFILE_RELEASE_NONE:
+			warnx("lock file did not exist");
+			return EXIT_FAILURE;
+		case UTIL_LOCKFILE_RELEASE_FAIL:
+			warnx("lock was not held by the specified pid");
+			return EXIT_FAILURE;
+		case UTIL_LOCKFILE_ERR:
+			warnx("lock was not released; file error");
+			return EXIT_FAILURE;
+		default:
+			warnx("Unknown util_lockfile rc %d", rc);
+			return EXIT_FAILURE;
+		}
+		break;
+	case ACTION_LOCK_AND_RELEASE:
+		rc = util_lockfile_lock(path, retries);
+		switch (rc) {
+		case UTIL_LOCKFILE_OK:
+			printf("lock acquired successfully, holding for 2 seconds\n");
+			break;
+		case UTIL_LOCKFILE_LOCK_FAIL:
+			warnx("lock was not acquired; already held");
+			return EXIT_FAILURE;
+		case UTIL_LOCKFILE_ERR:
+			warnx("lock was not acquired; file error");
+			return EXIT_FAILURE;
+		default:
+			warnx("Unknown util_lockfile rc %d", rc);
+			return EXIT_FAILURE;
+		}
+		/* Briefly sleep while holding the lock */
+		sleep(2);
+		rc = util_lockfile_release(path);
+		switch (rc) {
+		case UTIL_LOCKFILE_OK:
+			printf("lock released successfully\n");
+			break;
+		case UTIL_LOCKFILE_RELEASE_NONE:
+			warnx("lock file did not exist");
+			return EXIT_FAILURE;
+		case UTIL_LOCKFILE_RELEASE_FAIL:
+			warnx("lock was not held by the specified pid");
+			return EXIT_FAILURE;
+		case UTIL_LOCKFILE_ERR:
+			warnx("lock was not released; file error");
+			return EXIT_FAILURE;
+		default:
+			warnx("Unknown util_lockfile rc %d", rc);
+			return EXIT_FAILURE;
+		}
+		break;
+	default:
+		warnx("Unknown util_lockfile action");
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}
+//! [code]
