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