18dffb485Schristos /* Simple subrs. 2*c9055873Schristos Copyright (C) 2019-2024 Free Software Foundation, Inc. 38dffb485Schristos 48dffb485Schristos This file is part of libctf. 58dffb485Schristos 68dffb485Schristos libctf is free software; you can redistribute it and/or modify it under 78dffb485Schristos the terms of the GNU General Public License as published by the Free 88dffb485Schristos Software Foundation; either version 3, or (at your option) any later 98dffb485Schristos version. 108dffb485Schristos 118dffb485Schristos This program is distributed in the hope that it will be useful, but 128dffb485Schristos WITHOUT ANY WARRANTY; without even the implied warranty of 138dffb485Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 148dffb485Schristos See the GNU General Public License for more details. 158dffb485Schristos 168dffb485Schristos You should have received a copy of the GNU General Public License 178dffb485Schristos along with this program; see the file COPYING. If not see 188dffb485Schristos <http://www.gnu.org/licenses/>. */ 198dffb485Schristos 208dffb485Schristos #include <ctf-impl.h> 218dffb485Schristos #ifdef HAVE_MMAP 228dffb485Schristos #include <sys/mman.h> 238dffb485Schristos #endif 248dffb485Schristos #include <sys/types.h> 258dffb485Schristos #include <stdarg.h> 268dffb485Schristos #include <string.h> 278dffb485Schristos #include <unistd.h> 288dffb485Schristos 298dffb485Schristos #ifndef ENOTSUP 308dffb485Schristos #define ENOTSUP ENOSYS 318dffb485Schristos #endif 328dffb485Schristos 338dffb485Schristos int _libctf_version = CTF_VERSION; /* Library client version. */ 348dffb485Schristos int _libctf_debug = 0; /* Debugging messages enabled. */ 358dffb485Schristos 368dffb485Schristos /* Private, read-only mmap from a file, with fallback to copying. 378dffb485Schristos 388dffb485Schristos No handling of page-offset issues at all: the caller must allow for that. */ 398dffb485Schristos 408dffb485Schristos _libctf_malloc_ void * 418dffb485Schristos ctf_mmap (size_t length, size_t offset, int fd) 428dffb485Schristos { 438dffb485Schristos void *data; 448dffb485Schristos 458dffb485Schristos #ifdef HAVE_MMAP 468dffb485Schristos data = mmap (NULL, length, PROT_READ, MAP_PRIVATE, fd, offset); 478dffb485Schristos if (data == MAP_FAILED) 488dffb485Schristos data = NULL; 498dffb485Schristos #else 508dffb485Schristos if ((data = malloc (length)) != NULL) 518dffb485Schristos { 528dffb485Schristos if (ctf_pread (fd, data, length, offset) <= 0) 538dffb485Schristos { 548dffb485Schristos free (data); 558dffb485Schristos data = NULL; 568dffb485Schristos } 578dffb485Schristos } 588dffb485Schristos #endif 598dffb485Schristos return data; 608dffb485Schristos } 618dffb485Schristos 628dffb485Schristos void 638dffb485Schristos ctf_munmap (void *buf, size_t length _libctf_unused_) 648dffb485Schristos { 658dffb485Schristos #ifdef HAVE_MMAP 668dffb485Schristos (void) munmap (buf, length); 678dffb485Schristos #else 688dffb485Schristos free (buf); 698dffb485Schristos #endif 708dffb485Schristos } 718dffb485Schristos 728dffb485Schristos ssize_t 738dffb485Schristos ctf_pread (int fd, void *buf, ssize_t count, off_t offset) 748dffb485Schristos { 758dffb485Schristos ssize_t len; 768dffb485Schristos size_t acc = 0; 778dffb485Schristos char *data = (char *) buf; 788dffb485Schristos 798dffb485Schristos #ifdef HAVE_PREAD 808dffb485Schristos while (count > 0) 818dffb485Schristos { 828dffb485Schristos errno = 0; 838dffb485Schristos if (((len = pread (fd, data, count, offset)) < 0) && 848dffb485Schristos errno != EINTR) 858dffb485Schristos return len; 868dffb485Schristos if (errno == EINTR) 878dffb485Schristos continue; 888dffb485Schristos 898dffb485Schristos acc += len; 908dffb485Schristos if (len == 0) /* EOF. */ 918dffb485Schristos return acc; 928dffb485Schristos 938dffb485Schristos count -= len; 948dffb485Schristos offset += len; 958dffb485Schristos data += len; 968dffb485Schristos } 978dffb485Schristos return acc; 988dffb485Schristos #else 998dffb485Schristos off_t orig_off; 1008dffb485Schristos 1018dffb485Schristos if ((orig_off = lseek (fd, 0, SEEK_CUR)) < 0) 1028dffb485Schristos return -1; 1038dffb485Schristos if ((lseek (fd, offset, SEEK_SET)) < 0) 1048dffb485Schristos return -1; 1058dffb485Schristos 1068dffb485Schristos while (count > 0) 1078dffb485Schristos { 1088dffb485Schristos errno = 0; 1098dffb485Schristos if (((len = read (fd, data, count)) < 0) && 1108dffb485Schristos errno != EINTR) 1118dffb485Schristos return len; 1128dffb485Schristos if (errno == EINTR) 1138dffb485Schristos continue; 1148dffb485Schristos 1158dffb485Schristos acc += len; 1168dffb485Schristos if (len == 0) /* EOF. */ 1178dffb485Schristos break; 1188dffb485Schristos 1198dffb485Schristos count -= len; 1208dffb485Schristos data += len; 1218dffb485Schristos } 1228dffb485Schristos if ((lseek (fd, orig_off, SEEK_SET)) < 0) 1238dffb485Schristos return -1; /* offset is smashed. */ 1248dffb485Schristos #endif 1258dffb485Schristos 1268dffb485Schristos return acc; 1278dffb485Schristos } 1288dffb485Schristos 1298dffb485Schristos /* Set the CTF library client version to the specified version. If version is 1308dffb485Schristos zero, we just return the default library version number. */ 1318dffb485Schristos int 1328dffb485Schristos ctf_version (int version) 1338dffb485Schristos { 1348dffb485Schristos if (version < 0) 1358dffb485Schristos { 1368dffb485Schristos errno = EINVAL; 1378dffb485Schristos return -1; 1388dffb485Schristos } 1398dffb485Schristos 1408dffb485Schristos if (version > 0) 1418dffb485Schristos { 1428dffb485Schristos /* Dynamic version switching is not presently supported. */ 1438dffb485Schristos if (version != CTF_VERSION) 1448dffb485Schristos { 1458dffb485Schristos errno = ENOTSUP; 1468dffb485Schristos return -1; 1478dffb485Schristos } 1488dffb485Schristos ctf_dprintf ("ctf_version: client using version %d\n", version); 1498dffb485Schristos _libctf_version = version; 1508dffb485Schristos } 1518dffb485Schristos 1528dffb485Schristos return _libctf_version; 1538dffb485Schristos } 1548dffb485Schristos 1558dffb485Schristos void 1568dffb485Schristos libctf_init_debug (void) 1578dffb485Schristos { 1588dffb485Schristos static int inited; 1598dffb485Schristos if (!inited) 1608dffb485Schristos { 1618dffb485Schristos _libctf_debug = getenv ("LIBCTF_DEBUG") != NULL; 1628dffb485Schristos inited = 1; 1638dffb485Schristos } 1648dffb485Schristos } 1658dffb485Schristos 1668dffb485Schristos void ctf_setdebug (int debug) 1678dffb485Schristos { 1688dffb485Schristos /* Ensure that libctf_init_debug() has been called, so that we don't get our 1698dffb485Schristos debugging-on-or-off smashed by the next call. */ 1708dffb485Schristos 1718dffb485Schristos libctf_init_debug(); 1728dffb485Schristos _libctf_debug = debug; 1738dffb485Schristos ctf_dprintf ("CTF debugging set to %i\n", debug); 1748dffb485Schristos } 1758dffb485Schristos 1768dffb485Schristos int ctf_getdebug (void) 1778dffb485Schristos { 1788dffb485Schristos return _libctf_debug; 1798dffb485Schristos } 1808dffb485Schristos 1818dffb485Schristos _libctf_printflike_ (1, 2) 1828dffb485Schristos void ctf_dprintf (const char *format, ...) 1838dffb485Schristos { 1848dffb485Schristos if (_libctf_unlikely_ (_libctf_debug)) 1858dffb485Schristos { 1868dffb485Schristos va_list alist; 1878dffb485Schristos 1888dffb485Schristos va_start (alist, format); 1898dffb485Schristos fflush (stdout); 1908dffb485Schristos (void) fputs ("libctf DEBUG: ", stderr); 1918dffb485Schristos (void) vfprintf (stderr, format, alist); 1928dffb485Schristos va_end (alist); 1938dffb485Schristos } 1948dffb485Schristos } 1958dffb485Schristos 1968dffb485Schristos /* This needs more attention to thread-safety later on. */ 1978dffb485Schristos static ctf_list_t open_errors; 1988dffb485Schristos 1998dffb485Schristos /* Errors and warnings. Report the warning or error to the list in FP (or the 2008dffb485Schristos open errors list if NULL): if ERR is nonzero it is the errno to report to the 2018dffb485Schristos debug stream instead of that recorded on fp. */ 2028dffb485Schristos _libctf_printflike_ (4, 5) 2038dffb485Schristos extern void 2044b169a6bSchristos ctf_err_warn (ctf_dict_t *fp, int is_warning, int err, 2058dffb485Schristos const char *format, ...) 2068dffb485Schristos { 2078dffb485Schristos va_list alist; 2088dffb485Schristos ctf_err_warning_t *cew; 2098dffb485Schristos 2108dffb485Schristos /* Don't bother reporting errors here: we can't do much about them if they 2118dffb485Schristos happen. If we're so short of memory that a tiny malloc doesn't work, a 2128dffb485Schristos vfprintf isn't going to work either and the caller will have to rely on the 2138dffb485Schristos ENOMEM return they'll be getting in short order anyway. */ 2148dffb485Schristos 2158dffb485Schristos if ((cew = malloc (sizeof (ctf_err_warning_t))) == NULL) 2168dffb485Schristos return; 2178dffb485Schristos 2188dffb485Schristos cew->cew_is_warning = is_warning; 2198dffb485Schristos va_start (alist, format); 2208dffb485Schristos if (vasprintf (&cew->cew_text, format, alist) < 0) 2218dffb485Schristos { 2228dffb485Schristos free (cew); 2238dffb485Schristos va_end (alist); 2248dffb485Schristos return; 2258dffb485Schristos } 2268dffb485Schristos va_end (alist); 2278dffb485Schristos 228*c9055873Schristos /* Include the error code only if there is one; if this is a warning, 2294b169a6bSchristos only use the error code if it was explicitly passed and is nonzero. 2308dffb485Schristos (Warnings may not have a meaningful error code, since the warning may not 2318dffb485Schristos lead to unwinding up to the user.) */ 2324b169a6bSchristos if ((!is_warning && (err != 0 || (fp && ctf_errno (fp) != 0))) 2334b169a6bSchristos || (is_warning && err != 0)) 2348dffb485Schristos ctf_dprintf ("%s: %s (%s)\n", is_warning ? _("error") : _("warning"), 2358dffb485Schristos cew->cew_text, err != 0 ? ctf_errmsg (err) 2368dffb485Schristos : ctf_errmsg (ctf_errno (fp))); 2378dffb485Schristos else 2388dffb485Schristos ctf_dprintf ("%s: %s\n", is_warning ? _("error") : _("warning"), 2398dffb485Schristos cew->cew_text); 2408dffb485Schristos 2418dffb485Schristos if (fp != NULL) 2428dffb485Schristos ctf_list_append (&fp->ctf_errs_warnings, cew); 2438dffb485Schristos else 2448dffb485Schristos ctf_list_append (&open_errors, cew); 2458dffb485Schristos } 2468dffb485Schristos 2478dffb485Schristos /* Move all the errors/warnings from an fp into the open_errors. */ 2488dffb485Schristos void 2494b169a6bSchristos ctf_err_warn_to_open (ctf_dict_t *fp) 2508dffb485Schristos { 2518dffb485Schristos ctf_list_splice (&open_errors, &fp->ctf_errs_warnings); 2528dffb485Schristos } 2538dffb485Schristos 2548dffb485Schristos /* Error-warning reporting: an 'iterator' that returns errors and warnings from 2558dffb485Schristos the error/warning list, in order of emission. Errors and warnings are popped 2568dffb485Schristos after return: the caller must free the returned error-text pointer. 2578dffb485Schristos 2588dffb485Schristos An fp of NULL returns CTF-open-time errors from the open_errors variable 2598dffb485Schristos above. 2608dffb485Schristos 2618dffb485Schristos The treatment of errors from this function itself is somewhat unusual: it 2628dffb485Schristos will often be called on an error path, so we don't want to overwrite the 2638dffb485Schristos ctf_errno unless we have no choice. So, like ctf_bufopen et al, this 2648dffb485Schristos function takes an errp pointer where errors are reported. The pointer is 2658dffb485Schristos optional: if not set, errors are reported via the fp (if non-NULL). Calls 2668dffb485Schristos with neither fp nor errp set are mildly problematic because there is no clear 2678dffb485Schristos way to report end-of-iteration: you just have to assume that a NULL return 2688dffb485Schristos means the end, and not an iterator error. */ 2698dffb485Schristos 2708dffb485Schristos char * 2714b169a6bSchristos ctf_errwarning_next (ctf_dict_t *fp, ctf_next_t **it, int *is_warning, 2728dffb485Schristos int *errp) 2738dffb485Schristos { 2748dffb485Schristos ctf_next_t *i = *it; 2758dffb485Schristos char *ret; 2768dffb485Schristos ctf_list_t *errlist; 2778dffb485Schristos ctf_err_warning_t *cew; 2788dffb485Schristos 2798dffb485Schristos if (fp) 2808dffb485Schristos errlist = &fp->ctf_errs_warnings; 2818dffb485Schristos else 2828dffb485Schristos errlist = &open_errors; 2838dffb485Schristos 2848dffb485Schristos if (!i) 2858dffb485Schristos { 2868dffb485Schristos if ((i = ctf_next_create ()) == NULL) 2878dffb485Schristos { 2888dffb485Schristos if (errp) 2898dffb485Schristos *errp = ENOMEM; 2908dffb485Schristos else if (fp) 2918dffb485Schristos ctf_set_errno (fp, ENOMEM); 2928dffb485Schristos return NULL; 2938dffb485Schristos } 2948dffb485Schristos 2958dffb485Schristos i->cu.ctn_fp = fp; 2968dffb485Schristos i->ctn_iter_fun = (void (*) (void)) ctf_errwarning_next; 2978dffb485Schristos *it = i; 2988dffb485Schristos } 2998dffb485Schristos 3008dffb485Schristos if ((void (*) (void)) ctf_errwarning_next != i->ctn_iter_fun) 3018dffb485Schristos { 3028dffb485Schristos if (errp) 3038dffb485Schristos *errp = ECTF_NEXT_WRONGFUN; 3048dffb485Schristos else if (fp) 3058dffb485Schristos ctf_set_errno (fp, ECTF_NEXT_WRONGFUN); 3068dffb485Schristos return NULL; 3078dffb485Schristos } 3088dffb485Schristos 3098dffb485Schristos if (fp != i->cu.ctn_fp) 3108dffb485Schristos { 3118dffb485Schristos if (errp) 3128dffb485Schristos *errp = ECTF_NEXT_WRONGFP; 3138dffb485Schristos else if (fp) 3148dffb485Schristos ctf_set_errno (fp, ECTF_NEXT_WRONGFP); 3158dffb485Schristos return NULL; 3168dffb485Schristos } 3178dffb485Schristos 3188dffb485Schristos cew = ctf_list_next (errlist); 3198dffb485Schristos 3208dffb485Schristos if (!cew) 3218dffb485Schristos { 3228dffb485Schristos ctf_next_destroy (i); 3238dffb485Schristos *it = NULL; 3248dffb485Schristos if (errp) 3258dffb485Schristos *errp = ECTF_NEXT_END; 3268dffb485Schristos else if (fp) 3278dffb485Schristos ctf_set_errno (fp, ECTF_NEXT_END); 3288dffb485Schristos return NULL; 3298dffb485Schristos } 3308dffb485Schristos 3318dffb485Schristos if (is_warning) 3328dffb485Schristos *is_warning = cew->cew_is_warning; 3338dffb485Schristos ret = cew->cew_text; 3348dffb485Schristos ctf_list_delete (errlist, cew); 3358dffb485Schristos free (cew); 3368dffb485Schristos return ret; 3378dffb485Schristos } 3388dffb485Schristos 3398dffb485Schristos void 3404b169a6bSchristos ctf_assert_fail_internal (ctf_dict_t *fp, const char *file, size_t line, 3418dffb485Schristos const char *exprstr) 3428dffb485Schristos { 3438dffb485Schristos ctf_set_errno (fp, ECTF_INTERNAL); 344*c9055873Schristos ctf_err_warn (fp, 0, 0, _("%s: %lu: libctf assertion failed: %s"), 345*c9055873Schristos file, (long unsigned int) line, exprstr); 3468dffb485Schristos } 347