xref: /freebsd/lib/libutil++/libutil++.hh (revision ca0d182dfeb68405cfba002622787adb10cfe7a0)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2025 Chelsio Communications, Inc.
5  * Written by: John Baldwin <jhb@FreeBSD.org>
6  */
7 
8 #ifndef __LIBUTILPP_HH__
9 #define	__LIBUTILPP_HH__
10 
11 #include <sys/nv.h>
12 #include <libutil.h>
13 #include <netdb.h>
14 #include <unistd.h>
15 
16 #include <cstdarg>
17 #include <cstdio>
18 #include <cstdlib>
19 #include <memory>
20 
21 namespace freebsd {
22 	/*
23 	 * FILE_up is a std::unique_ptr<> for FILE objects which uses
24 	 * fclose() to destroy the wrapped pointer.
25 	 */
26 	struct fclose_deleter {
operator ()freebsd::fclose_deleter27 		void operator() (std::FILE *fp) const
28 		{
29 			std::fclose(fp);
30 		}
31 	};
32 
33 	using FILE_up = std::unique_ptr<std::FILE, fclose_deleter>;
34 
35 	/*
36 	 * addrinfo_up is a std::unique_ptr<> which uses
37 	 * freeaddrinfo() to destroy the wrapped pointer.  It is
38 	 * intended to wrap arrays allocated by getaddrinfo().
39 	 */
40 	struct freeaddrinfo_deleter {
operator ()freebsd::freeaddrinfo_deleter41 		void operator() (struct addrinfo *ai) const
42 		{
43 			freeaddrinfo(ai);
44 		}
45 	};
46 
47 	using addrinfo_up = std::unique_ptr<addrinfo, freeaddrinfo_deleter>;
48 
49 	/*
50 	 * This class is intended to function similar to unique_ptr<>,
51 	 * but it contains a file descriptor rather than a pointer to
52 	 * an object.  On destruction the descriptor is closed via
53 	 * close(2).
54 	 *
55 	 * Similar to unique_ptr<>, release() returns ownership of the
56 	 * file descriptor to the caller.  reset() closes the current
57 	 * file descriptor and takes ownership of a new one.  A move
58 	 * constructor permits ownership to be transferred via
59 	 * std::move().  An integer file descriptor can be assigned
60 	 * directly which is equivalent to calling reset().
61 	 *
62 	 * An explicit bool conversion operator permits testing this
63 	 * class in logical expressions.  It returns true if it
64 	 * contains a valid descriptor.
65 	 *
66 	 * An implicit int conversion operator returns the underlying
67 	 * file descriptor allowing objects of this type to be passed
68 	 * directly to APIs such as connect(), listen(), etc.
69 	 */
70 	class fd_up {
71 	public:
fd_up()72 		fd_up() : fd(-1) {}
fd_up(int _fd)73 		fd_up(int _fd) : fd(_fd) {}
fd_up(fd_up && other)74 		fd_up(fd_up &&other) : fd(other.release()) {}
75 		fd_up(fd_up const &) = delete;
76 
~fd_up()77 		~fd_up() { reset(); }
78 
get() const79 		int get() const { return (fd); }
80 
release()81 		int release()
82 		{
83 			int oldfd = fd;
84 
85 			fd = -1;
86 			return (oldfd);
87 		}
88 
reset(int newfd=-1)89 		void reset(int newfd = -1)
90 		{
91 			if (fd >= 0)
92 				close(fd);
93 			fd = newfd;
94 		}
95 
operator =(fd_up && other)96 		fd_up &operator=(fd_up &&other) noexcept
97 		{
98 			if (this == &other)
99 				return *this;
100 
101 			reset(other.release());
102 			return *this;
103 		}
104 
105 		fd_up &operator=(fd_up const &) = delete;
106 
operator =(int newfd)107 		fd_up &operator=(int newfd)
108 		{
109 			reset(newfd);
110 			return *this;
111 		}
112 
operator bool() const113 		explicit operator bool() const { return fd >= 0; }
operator int() const114 		operator int() const { return fd; }
115 	private:
116 		int	fd;
117 	};
118 
119 	/*
120 	 * malloc_up<T> is a std::unique_ptr<> which uses free() to
121 	 * destroy the wrapped pointer.  This can be used to wrap
122 	 * pointers allocated implicitly by malloc() such as those
123 	 * returned by strdup().
124 	 */
125 	template <class T>
126 	struct free_deleter {
operator ()freebsd::free_deleter127 		void operator() (T *p) const
128 		{
129 			std::free(p);
130 		}
131 	};
132 
133 	template <class T>
134 	using malloc_up = std::unique_ptr<T, free_deleter<T>>;
135 
136 	/*
137 	 * nvlist_up is a std::unique_ptr<> for nvlist_t objects which
138 	 * uses nvlist_destroy() to destroy the wrapped pointer.
139 	 */
140 	struct nvlist_deleter {
operator ()freebsd::nvlist_deleter141 		void operator() (nvlist_t *nvl) const
142 		{
143 			nvlist_destroy(nvl);
144 		}
145 	};
146 
147 	using nvlist_up = std::unique_ptr<nvlist_t, nvlist_deleter>;
148 
149 	/*
150 	 * A wrapper class for the pidfile_* API.  The destructor
151 	 * calls pidfile_remove() when an object is destroyed.  This
152 	 * class is similar to std::unique_ptr<> in that it retains
153 	 * exclusive ownership of the pidfh object.
154 	 *
155 	 * In addition to release() and reset methods(), write(),
156 	 * close(), and fileno() methods are provided as wrappers for
157 	 * pidfile_*.
158 	 */
159 	class pidfile {
160 	public:
161 		pidfile() = default;
pidfile(struct pidfh * _pfh)162 		pidfile(struct pidfh *_pfh) : pfh(_pfh) {}
pidfile(pidfile && other)163 		pidfile(pidfile &&other) : pfh(other.release()) {}
164 		pidfile(pidfile const &) = delete;
165 
~pidfile()166 		~pidfile() { reset(); }
167 
release()168 		struct pidfh *release()
169 		{
170 			struct pidfh *oldpfh = pfh;
171 
172 			pfh = nullptr;
173 			return (oldpfh);
174 		}
175 
reset(struct pidfh * newpfh=nullptr)176 		void reset(struct pidfh *newpfh = nullptr)
177 		{
178 			if (pfh != nullptr)
179 				pidfile_remove(pfh);
180 			pfh = newpfh;
181 		}
182 
write()183 		int write()
184 		{
185 			return (pidfile_write(pfh));
186 		}
187 
close()188 		int close()
189 		{
190 			int rv = pidfile_close(pfh);
191 			if (rv == 0)
192 				pfh = nullptr;
193 			return (rv);
194 		}
195 
fileno()196 		int fileno()
197 		{
198 			return (pidfile_fileno(pfh));
199 		}
200 
operator =(pidfile && other)201 		pidfile &operator=(pidfile &&other) noexcept
202 		{
203 			if (this == &other)
204 				return *this;
205 			reset(other.release());
206 			return *this;
207 		}
208 
209 		pidfile &operator=(pidfile const &) = delete;
210 
operator =(struct pidfh * newpfh)211 		pidfile &operator=(struct pidfh *newpfh)
212 		{
213 			reset(newpfh);
214 			return *this;
215 		}
216 
operator bool() const217 		explicit operator bool() const { return pfh != nullptr; }
218 	private:
219 		struct pidfh *pfh = nullptr;
220 	};
221 
222 	/*
223 	 * Returns a std::string containing the same output as
224 	 * sprintf().  Throws std::bad_alloc if an error occurs.
225 	 */
226 	std::string stringf(const char *fmt, ...) __printflike(1, 2);
227 	std::string stringf(const char *fmt, std::va_list ap);
228 }
229 
230 #endif /* !__LIBUTILPP_HH__ */
231