Attempt to create .deps directory every time we build objects.
[doas.git] / wrappers.h
blob2f4d97e0894c48acb8b8445a79fb09a4f3cd4186
1 /*
2 * Copyright (c) 2021-2022 Sergey Sushilin <sergeysushilin@protonmail.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #ifndef _WRAPPERS_H
18 #define _WRAPPERS_H 1
20 #include <err.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <grp.h>
24 #include <limits.h>
25 #include <pwd.h>
26 #if defined(USE_SHADOW)
27 # include <shadow.h>
28 #endif
29 #include <stdarg.h>
30 #include <stddef.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #if !defined(NDEBUG)
35 # include <string.h>
36 #endif
37 #include <sys/stat.h>
38 #include <unistd.h>
40 #include "compat.h"
42 #define streq(s1, s2) (strcmp((s1), (s2)) == 0)
43 #define strneq(s1, s2, n) (strncmp((s1), (s2), (n)) == 0)
45 static inline __malloc __alloc_size((1)) __returns_nonnull __warn_unused_result void *xmalloc(size_t size)
47 void *pointer;
49 if (size == 0)
50 errc(EXIT_FAILURE, EINVAL, "xmalloc: zero-size allocation");
52 pointer = malloc(size);
54 if (pointer == NULL)
55 err(EXIT_FAILURE, "malloc: allocating %zu bytes", size);
57 return pointer;
59 static inline __alloc_size((1, 2)) __returns_nonnull __warn_unused_result void *xcalloc(size_t count, size_t size)
61 void *pointer;
63 if (count == 0 || size == 0)
64 errc(EXIT_FAILURE, EINVAL, "xcalloc: zero-size allocation");
66 pointer = calloc(count, size);
68 if (pointer == NULL)
69 err(EXIT_FAILURE, "calloc: allocating %zu * %zu bytes", count, size);
71 return pointer;
73 static inline __alloc_size((2)) __returns_nonnull __warn_unused_result void *xrealloc(void *pointer, size_t size)
75 if (pointer == NULL && size == 0)
76 errc(EXIT_FAILURE, EINVAL, "realloc(NULL, 0)");
78 if (size == 0)
79 warnx("reallocating to zero size (use free() instead)");
81 * if (pointer == NULL)
82 * warnx("reallocating NULL to %zu (use malloc() instead)", size);
85 pointer = realloc(pointer, size);
87 if (pointer == NULL)
88 err(EXIT_FAILURE, "realloc");
90 return pointer;
92 static inline __alloc_size((2, 3)) __returns_nonnull __warn_unused_result void *xreallocarray(void *pointer, size_t count, size_t size)
94 if (pointer == NULL && (count == 0 || size == 0))
95 errc(EXIT_FAILURE, EINVAL, "reallocarray(NULL, 0)");
97 if (count == 0 || size == 0)
98 warnx("reallocating to zero size (use free() instead)");
100 * Do not warn since reallocarray(NULL, count, size) can be used
101 * for safe allocating memory with size equal to count * size.
103 * if (pointer == NULL)
104 * warnx("reallocating NULL to %zu (use malloc() instead)", size);
107 pointer = reallocarray(pointer, count, size);
109 if (pointer == NULL)
110 err(EXIT_FAILURE, "reallocarray");
112 return pointer;
114 static inline __nonnull((1)) void xfree(const void *pointer)
116 void **p = (void **)pointer;
117 if (*p != NULL) {
118 free(*p);
119 *p = NULL;
122 #define xfree(p) xfree(&p)
124 static inline __malloc __alloc_size((2)) __returns_nonnull __warn_unused_result __nonnull((1)) void *xmemdup(const void *pointer, size_t size)
126 return memcpy(xmalloc(size), pointer, size);
128 static inline __malloc __returns_nonnull __warn_unused_result __nonnull((1)) char *xstrdup(char const *string)
130 return xmemdup(string, strlen(string) + 1);
132 static inline __malloc __returns_nonnull __warn_unused_result __nonnull((1)) char *xstrndup(char const *string, size_t size)
134 char *s = memchr(string, '\0', size);
136 if (s != NULL)
137 size = s - string;
139 s = memcpy(xmalloc(size + 1), string, size);
140 s[size] = '\0';
142 return s;
145 static inline __warn_unused_result __nonnull((1)) long long int safe_strtonum(char const *s, long long int minval, long long int maxval, bool *succeed)
147 char const *ep = NULL;
148 long long int num = strtonum(s, minval, maxval, &ep);
150 if (ep != NULL)
151 warn("strtonum(%s): %s", s, ep);
153 if (succeed != NULL)
154 *succeed = (ep == NULL);
156 return num;
158 static inline __warn_unused_result __nonnull((1)) unsigned long long int safe_strtounum(char const *s, unsigned long long int maxval, bool *succeed)
160 char const *ep = NULL;
161 unsigned long long int num = strtounum(s, maxval, &ep);
163 if (ep != NULL)
164 warn("strtounum(%s): %s", s, ep);
166 if (succeed != NULL)
167 *succeed = (ep == NULL);
169 return num;
172 static inline __format_printf(2, 0) __nonnull((1, 2)) int xvfprintf(FILE *restrict fp, char const *restrict fmt, va_list ap)
174 int n = vfprintf(fp, fmt, ap);
176 if (n < 0)
177 err(EXIT_FAILURE, "vfprintf");
179 return n;
182 static inline __format_printf(2, 3) __nonnull((1, 2)) int xfprintf(FILE *restrict fp, char const *restrict fmt, ...)
184 va_list ap;
186 va_start(ap, fmt);
187 int n = xvfprintf(fp, fmt, ap);
188 va_end(ap);
190 return n;
193 static inline __format_printf(2, 0) __nonnull((1, 2)) int xvsprintf(char *restrict buf, char const *restrict fmt, va_list ap)
195 int n = vsprintf(buf, fmt, ap);
197 if (n < 0)
198 err(EXIT_FAILURE, "vsprintf");
200 return n;
202 static inline __format_printf(2, 3) __nonnull((1, 2)) int xsprintf(char *restrict buf, char const *restrict fmt, ...)
204 va_list ap;
205 int n;
207 va_start(ap, fmt);
208 n = xvsprintf(buf, fmt, ap);
209 va_end(ap);
211 return n;
214 static inline __format_printf(3, 0) __nonnull((1, 3)) int safe_vsnprintf(char *restrict buf, size_t size, char const *restrict fmt, va_list ap)
216 int n;
218 if (size > INT_MAX) {
219 warnc(EINVAL, "vsnprintf");
220 return -1;
223 n = vsnprintf(buf, size, fmt, ap);
225 if (n < 0) {
226 warn("vsnprintf");
227 return -1;
229 if ((size_t)n >= size) {
230 warnc(EOVERFLOW, "vsnprintf");
231 return -1;
234 return n;
236 static inline __format_printf(3, 4) __nonnull((1, 3)) int safe_snprintf(char *restrict buf, size_t size, char const *restrict fmt, ...)
238 va_list ap;
239 int n;
241 va_start(ap, fmt);
242 n = safe_vsnprintf(buf, size, fmt, ap);
243 va_end(ap);
245 return n;
248 static inline __warn_unused_result __format_printf(3, 0) __nonnull((1, 3)) int xvsnprintf(char *restrict buf, size_t size, char const *restrict fmt, va_list ap)
250 int n = safe_vsnprintf(buf, size, fmt, ap);
252 if (n < 0)
253 exit(EXIT_FAILURE);
255 return n;
257 static inline __format_printf(3, 4) __nonnull((1, 3)) int xsnprintf(char *restrict buf, size_t size, char const *restrict fmt, ...)
259 va_list ap;
260 int n;
262 va_start(ap, fmt);
263 n = xvsnprintf(buf, size, fmt, ap);
264 va_end(ap);
266 return n;
269 static inline __nonnull((1)) void safe_pledge(char const *restrict promises,
270 char const *const restrict *restrict paths)
272 /* Pledge is available only on OpenBSD. */
273 #if defined(__OpenBSD__)
274 if (pledge(promises, paths) < 0)
275 err(EXIT_FAILURE, "pledge(%s)", promises);
276 #else
277 (void)promises;
278 (void)paths;
279 return;
280 #endif
283 #pragma GCC poison pledge
285 static inline __nonnull((1)) int safe_open(char const *file, int flags, mode_t mode)
287 int fd = open(file, flags, mode);
289 if (fd < 0)
290 err(EXIT_FAILURE, "open: %s", file);
292 return fd;
294 static inline void safe_close(int fd)
296 if (close(fd) < 0)
297 err(EXIT_FAILURE, "close");
300 static inline void safe_fstat(int fd, struct stat *sb)
302 if (fstat(fd, sb) != 0)
303 err(EXIT_FAILURE, "fstat");
307 * DATA -- Available information about user (uid or name),
308 * the XX entry we are searching for.
309 * STRUCT_SIZE -- the size of the struct keeping entry data.
310 * F -- pointer to reentrant function to access the entry data.
311 * NAME -- name of function F.
313 static inline void *safe_getxxyyy(const void *restrict data,
314 size_t struct_size,
315 void *restrict f,
316 char const *restrict name,
317 char const *restrict error_format)
318 __format_printf(5, 0) __nonnull((1, 3, 4, 5)) __warn_unused_result __malloc;
319 static inline void *safe_getxxyyy(const void *restrict data,
320 size_t struct_size,
321 void *restrict f,
322 char const *restrict name,
323 char const *restrict error_format)
325 int s;
326 size_t size = 64;
327 char *p = xmalloc(struct_size + size);
328 void *result = NULL;
330 while (true) {
331 int (*g)(const void *, void *, char *, size_t, void **) = f;
332 s = g(data, p, p + struct_size, size, &result);
334 if (s == EINTR)
335 continue; /* Try again. */
337 if (s == ERANGE) {
338 if (__builtin_mul_overflow(2, size, &size))
339 errc(EXIT_FAILURE, ENOMEM, "%s", name);
341 p = xrealloc(p, struct_size + size);
342 continue;
345 /* We either got an error, or we succeeded and the
346 returned name fit in the buffer. */
347 break;
350 if (s != 0) {
351 free(p);
352 result = NULL;
353 errno = s;
354 warn("%s", name);
355 } else if (result == NULL) {
356 #if !defined(NDEBUG)
357 /* Verify the error format string... Just for safety,
358 no more and yet no less. */
359 char *s = strchr(error_format, '%');
361 if (s == NULL || (*(s + 1) != 's' && (*(s + 1) != 'l' && *(s + 2) != 'u')) || strchr(s + 1, '%') != NULL)
362 errx(EXIT_FAILURE, "wrong error format in getxxyyy_r: %s", error_format);
363 #endif
364 free(p);
365 warnx(error_format, data);
368 return result;
371 #define SAFE_GETXXNAM_GENERATE_STATIC(struct_name, function_name, error_format) \
372 static inline __malloc __nonnull((1)) struct struct_name *safe_##function_name(char const *name) \
374 return safe_getxxyyy(name, \
375 sizeof(struct struct_name), \
376 function_name##_r, \
377 #function_name"_r", \
378 error_format); \
380 static inline __malloc __nonnull((1)) __returns_nonnull struct struct_name *x##function_name(char const *name) \
382 struct struct_name *p = safe_##function_name(name); \
383 if (p == NULL) \
384 exit(EXIT_FAILURE); \
385 return p; \
387 #define SAFE_GETXXYID_GENERATE_STATIC(struct_name, function_name, xid_t, error_format) \
388 static inline __malloc struct struct_name *safe_##function_name(xid_t id) \
390 return safe_getxxyyy((void *)(uintptr_t)id, \
391 sizeof(struct struct_name), \
392 function_name##_r, \
393 #function_name"_r", \
394 error_format); \
396 static inline __malloc __returns_nonnull struct struct_name *x##function_name(xid_t id) \
398 struct struct_name *p = safe_##function_name(id); \
399 if (p == NULL) \
400 exit(EXIT_FAILURE); \
401 return p; \
404 SAFE_GETXXNAM_GENERATE_STATIC(passwd, getpwnam, "no matching password record was found for '%s' user");
405 SAFE_GETXXNAM_GENERATE_STATIC(group, getgrnam, "no matching group record was found for '%s' group");
406 SAFE_GETXXYID_GENERATE_STATIC(passwd, getpwuid, uid_t, "no matching password record was found for user with %lu UID");
407 SAFE_GETXXYID_GENERATE_STATIC(group, getgrgid, gid_t, "no matching group record was found for group with %lu GID");
409 #if USE_SHADOW
410 SAFE_GETXXNAM_GENERATE_STATIC(spwd, getspnam, "no matching shadow record was found for '%s' user");
411 #endif
413 /* Poison all these functions to do not allow to use them in code below
414 instead of safe_getxxyyy functions. */
415 #pragma GCC poison getpwnam getgrnam getpwnam_r getgrnam_r
416 #pragma GCC poison getpwuid getgruid getpwuid_r getgrgid_r
417 #if USE_SHADOW
418 # pragma GCC poison getspnam getspnam_r
419 #endif
421 #if defined(FLEX_SCANNER) || defined(YYBISON) || defined(YYBYACC)
422 # define malloc xmalloc
423 # define calloc xcalloc
424 # define realloc xrealloc
425 # define reallocarray xreallocarray
426 # define strdup xstrdup
427 # define strndup xstrndup
428 #else
429 # undef malloc
430 # undef calloc
431 # undef realloc
432 # undef reallocarray
433 # undef free
434 # undef strdup
435 # undef strndup
436 # pragma GCC poison malloc calloc realloc reallocarray strdup strndup free
437 #endif
439 #endif /* _WRAPPERS_H */