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