The branch main has been updated by jhb:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=7be913e00d79b3bf740049797fbc3f6ab8193995

commit 7be913e00d79b3bf740049797fbc3f6ab8193995
Author:     John Baldwin <j...@freebsd.org>
AuthorDate: 2025-08-04 19:38:07 +0000
Commit:     John Baldwin <j...@freebsd.org>
CommitDate: 2025-08-04 19:38:07 +0000

    libutil++: Add freebsd::pidfile wrapper class around struct pidfh
    
    This class wraps the pidfile_* API from libutil.  The destructor calls
    pidfile_remove() when an object is destroyed.  This class is similar
    to std::unique_ptr<> in that it retains exclusive ownership of the
    pidfh object.
    
    In addition to release and reset methods, write, close, and fileno
    methods are provided as wrappers for pidfile_*.
    
    Sponsored by:   Chelsio Communications
    Pull Request:   https://github.com/freebsd/freebsd-src/pull/1794
---
 lib/libutil++/Makefile              |   1 +
 lib/libutil++/freebsd::pidfile.3    | 110 ++++++++++++++++++++++++++++++++++++
 lib/libutil++/libutil++.hh          |  74 ++++++++++++++++++++++++
 lib/libutil++/tests/Makefile        |   3 +
 lib/libutil++/tests/pidfile_test.cc |  44 +++++++++++++++
 5 files changed, 232 insertions(+)

diff --git a/lib/libutil++/Makefile b/lib/libutil++/Makefile
index 3389e7e21b11..df3074c2278e 100644
--- a/lib/libutil++/Makefile
+++ b/lib/libutil++/Makefile
@@ -9,6 +9,7 @@ MAN+=   freebsd::FILE_up.3 \
        freebsd::fd_up.3 \
        freebsd::malloc_up.3 \
        freebsd::nvlist_up.3 \
+       freebsd::pidfile.3 \
        freebsd::stringf.3
 
 .include <src.opts.mk>
diff --git a/lib/libutil++/freebsd::pidfile.3 b/lib/libutil++/freebsd::pidfile.3
new file mode 100644
index 000000000000..fb67253f5c02
--- /dev/null
+++ b/lib/libutil++/freebsd::pidfile.3
@@ -0,0 +1,110 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2025 Chelsio Communications, Inc.
+.\" Written by: John Baldwin <j...@freebsd.org>
+.\"
+.Dd July 31, 2025
+.Dt FREEBSD::STRINGF 3
+.Os
+.Sh NAME
+.Nm freebsd::pidfile
+.Nd own a PID file handle
+.Sh LIBRARY
+.Lb libutil++
+.Sh SYNOPSIS
+.In libutil++.hh
+.Pp
+.Vt class freebsd::pidfile
+{
+.Bd -ragged -offset indent
+.Fn pidfile
+.Fn pidfile "struct pidfh *pfh"
+.Fn pidfile "pidfile &&other"
+.Fn ~pidfile
+.Ft struct pidfh *
+.Fn release
+.Ft void
+.Fn reset "struct pidfh *newpfh = nullptr"
+.Ft int
+.Fn write
+.Ft int
+.Fn close
+.Ft int
+.Fn fileno
+.Ft "pidfile &"
+.Fn operator= "pidfile &&other"
+.Ft "pidfile &"
+.Fn operator= "struct pidfh *pfh"
+.Fn "explicit operator bool"
+.Ed
+};
+.Sh DESCRIPTION
+Each instance of this class owns a PID file handle created by
+.Xr pidfile_open 3 .
+This class is patterned on std::unique_ptr;
+however,
+rather than exporting the raw pointer via a
+.Fn get
+method,
+this class provides wrapper methods for each of the other
+.Xr pidfile 3
+functions.
+The currently-owned PID file is removed by invoking
+.Xr pidfile_remove 3
+when an instance of this class is destroyed.
+The currently-owned PID file is also removed if it is replaced by the
+.Fn reset
+method or assignment operators.
+.Pp
+The
+.Fn release
+method relinquishes ownership of the current PID file handle and returns the
+value of the previously-owned PID file handle.
+.Pp
+The
+.Fn write
+method writes out the PID of the current process to the PID file via
+.Xr pidfile_write 3 .
+.Pp
+The
+.Fn close
+method closes the current PID file without removing it via
+.Xr pidfile_close 3 .
+If the close succeeds, the PID file handle is no longer valid.
+.Pp
+The
+.Fn fileno
+method returns the underlying file descriptor for the current PID file via
+.Xr pidfile_fileno 3 .
+.Pp
+The explicit
+.Vt bool
+conversion operator permits testing the validity of an object.
+The operator returns true if the instance owns a valid PID file handle.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+int
+main()
+{
+       freebsd::pidfile pf(pidfile_open("/var/run/daemon.pid",
+           0600, NULL));
+       if (!pf)
+               err(1, "pidfile_open");
+
+       if (daemon(0, 0) == -1) {
+               warn("daemon");
+               return 1;
+       }
+
+       pf->write();
+
+       for (;;) {
+               /* Do Work */
+       }
+
+       return 0;
+}
+.Ed
+.Sh SEE ALSO
+.Xr pidfile 3
diff --git a/lib/libutil++/libutil++.hh b/lib/libutil++/libutil++.hh
index 121633c4deea..ecf737f2fcb0 100644
--- a/lib/libutil++/libutil++.hh
+++ b/lib/libutil++/libutil++.hh
@@ -9,6 +9,7 @@
 #define        __LIBUTILPP_HH__
 
 #include <sys/nv.h>
+#include <libutil.h>
 #include <netdb.h>
 #include <unistd.h>
 
@@ -145,6 +146,79 @@ namespace freebsd {
 
        using nvlist_up = std::unique_ptr<nvlist_t, nvlist_deleter>;
 
+       /*
+        * A wrapper class for the pidfile_* API.  The destructor
+        * calls pidfile_remove() when an object is destroyed.  This
+        * class is similar to std::unique_ptr<> in that it retains
+        * exclusive ownership of the pidfh object.
+        *
+        * In addition to release() and reset methods(), write(),
+        * close(), and fileno() methods are provided as wrappers for
+        * pidfile_*.
+        */
+       class pidfile {
+       public:
+               pidfile() = default;
+               pidfile(struct pidfh *pfh) : pfh(pfh) {}
+               pidfile(pidfile &&other) : pfh(other.release()) {}
+               pidfile(pidfile const &) = delete;
+
+               ~pidfile() { reset(); }
+
+               struct pidfh *release()
+               {
+                       struct pidfh *oldpfh = pfh;
+
+                       pfh = nullptr;
+                       return (oldpfh);
+               }
+
+               void reset(struct pidfh *newpfh = nullptr)
+               {
+                       if (pfh != nullptr)
+                               pidfile_remove(pfh);
+                       pfh = newpfh;
+               }
+
+               int write()
+               {
+                       return (pidfile_write(pfh));
+               }
+
+               int close()
+               {
+                       int rv = pidfile_close(pfh);
+                       if (rv == 0)
+                               pfh = nullptr;
+                       return (rv);
+               }
+
+               int fileno()
+               {
+                       return (pidfile_fileno(pfh));
+               }
+
+               pidfile &operator=(pidfile &&other) noexcept
+               {
+                       if (this == &other)
+                               return *this;
+                       reset(other.release());
+                       return *this;
+               }
+
+               pidfile &operator=(pidfile const &) = delete;
+
+               pidfile &operator=(struct pidfh *newpfh)
+               {
+                       reset(newpfh);
+                       return *this;
+               }
+
+               explicit operator bool() const { return pfh != nullptr; }
+       private:
+               struct pidfh *pfh = nullptr;
+       };
+
        /*
         * Returns a std::string containing the same output as
         * sprintf().  Throws std::bad_alloc if an error occurs.
diff --git a/lib/libutil++/tests/Makefile b/lib/libutil++/tests/Makefile
index 81b7be4f5660..e7720d122f36 100644
--- a/lib/libutil++/tests/Makefile
+++ b/lib/libutil++/tests/Makefile
@@ -1,9 +1,12 @@
 PACKAGE=       tests
 
+ATF_TESTS_CXX+=        pidfile_test
 ATF_TESTS_CXX+=        stringf_test
 ATF_TESTS_CXX+=        up_test
 
 CFLAGS+=       -I${SRCTOP}/lib/libutil++
 LIBADD+=       util++
 
+LIBADD.pidfile_test+= util
+
 .include <bsd.test.mk>
diff --git a/lib/libutil++/tests/pidfile_test.cc 
b/lib/libutil++/tests/pidfile_test.cc
new file mode 100644
index 000000000000..067f10e8fab8
--- /dev/null
+++ b/lib/libutil++/tests/pidfile_test.cc
@@ -0,0 +1,44 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Chelsio Communications, Inc.
+ * Written by: John Baldwin <j...@freebsd.org>
+ */
+
+#include <atf-c++.hpp>
+#include <sys/stat.h>
+#include <libutil.h>
+
+#include <libutil++.hh>
+
+ATF_TEST_CASE_WITHOUT_HEAD(basic);
+ATF_TEST_CASE_BODY(basic)
+{
+       pid_t other;
+       struct pidfh *pfh = pidfile_open("test_pidfile", 0600, &other);
+       ATF_REQUIRE(pfh != nullptr);
+       ATF_REQUIRE(pidfile_fileno(pfh) >= 0);
+
+       struct stat sb;
+       ATF_REQUIRE(fstat(pidfile_fileno(pfh), &sb) == 0);
+       ATF_REQUIRE_EQ(0, sb.st_size);
+
+       freebsd::pidfile pf(pfh);
+       ATF_REQUIRE_EQ(pidfile_fileno(pfh), pf.fileno());
+
+       ATF_REQUIRE(pf.write() == 0);
+       
+       ATF_REQUIRE(fstat(pf.fileno(), &sb) == 0);
+       ATF_REQUIRE(sb.st_size > 0);
+
+       ATF_REQUIRE(pf.close() == 0);
+       ATF_REQUIRE(pf.fileno() == -1);
+       ATF_REQUIRE_EQ(EDOOFUS, errno);
+
+       ATF_REQUIRE(unlink("test_pidfile") == 0);
+}
+
+ATF_INIT_TEST_CASES(tcs)
+{
+       ATF_ADD_TEST_CASE(tcs, basic);
+}

Reply via email to