16f4ced0bSchristos /* Simple subrs.
2*cb63e24eSchristos Copyright (C) 2019-2024 Free Software Foundation, Inc.
36f4ced0bSchristos
46f4ced0bSchristos This file is part of libctf.
56f4ced0bSchristos
66f4ced0bSchristos libctf is free software; you can redistribute it and/or modify it under
76f4ced0bSchristos the terms of the GNU General Public License as published by the Free
86f4ced0bSchristos Software Foundation; either version 3, or (at your option) any later
96f4ced0bSchristos version.
106f4ced0bSchristos
116f4ced0bSchristos This program is distributed in the hope that it will be useful, but
126f4ced0bSchristos WITHOUT ANY WARRANTY; without even the implied warranty of
136f4ced0bSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
146f4ced0bSchristos See the GNU General Public License for more details.
156f4ced0bSchristos
166f4ced0bSchristos You should have received a copy of the GNU General Public License
176f4ced0bSchristos along with this program; see the file COPYING. If not see
186f4ced0bSchristos <http://www.gnu.org/licenses/>. */
196f4ced0bSchristos
206f4ced0bSchristos #include <ctf-impl.h>
216f4ced0bSchristos #ifdef HAVE_MMAP
226f4ced0bSchristos #include <sys/mman.h>
236f4ced0bSchristos #endif
246f4ced0bSchristos #include <sys/types.h>
256f4ced0bSchristos #include <stdarg.h>
266f4ced0bSchristos #include <string.h>
276f4ced0bSchristos #include <unistd.h>
286f4ced0bSchristos
294f645668Schristos #ifndef ENOTSUP
304f645668Schristos #define ENOTSUP ENOSYS
314f645668Schristos #endif
324f645668Schristos
336f4ced0bSchristos int _libctf_version = CTF_VERSION; /* Library client version. */
346f4ced0bSchristos int _libctf_debug = 0; /* Debugging messages enabled. */
356f4ced0bSchristos
366f4ced0bSchristos /* Private, read-only mmap from a file, with fallback to copying.
376f4ced0bSchristos
386f4ced0bSchristos No handling of page-offset issues at all: the caller must allow for that. */
396f4ced0bSchristos
406f4ced0bSchristos _libctf_malloc_ void *
ctf_mmap(size_t length,size_t offset,int fd)416f4ced0bSchristos ctf_mmap (size_t length, size_t offset, int fd)
426f4ced0bSchristos {
436f4ced0bSchristos void *data;
446f4ced0bSchristos
456f4ced0bSchristos #ifdef HAVE_MMAP
466f4ced0bSchristos data = mmap (NULL, length, PROT_READ, MAP_PRIVATE, fd, offset);
476f4ced0bSchristos if (data == MAP_FAILED)
486f4ced0bSchristos data = NULL;
496f4ced0bSchristos #else
506f4ced0bSchristos if ((data = malloc (length)) != NULL)
516f4ced0bSchristos {
526f4ced0bSchristos if (ctf_pread (fd, data, length, offset) <= 0)
536f4ced0bSchristos {
546f4ced0bSchristos free (data);
556f4ced0bSchristos data = NULL;
566f4ced0bSchristos }
576f4ced0bSchristos }
586f4ced0bSchristos #endif
596f4ced0bSchristos return data;
606f4ced0bSchristos }
616f4ced0bSchristos
626f4ced0bSchristos void
ctf_munmap(void * buf,size_t length _libctf_unused_)636f4ced0bSchristos ctf_munmap (void *buf, size_t length _libctf_unused_)
646f4ced0bSchristos {
656f4ced0bSchristos #ifdef HAVE_MMAP
666f4ced0bSchristos (void) munmap (buf, length);
676f4ced0bSchristos #else
686f4ced0bSchristos free (buf);
696f4ced0bSchristos #endif
706f4ced0bSchristos }
716f4ced0bSchristos
726f4ced0bSchristos ssize_t
ctf_pread(int fd,void * buf,ssize_t count,off_t offset)736f4ced0bSchristos ctf_pread (int fd, void *buf, ssize_t count, off_t offset)
746f4ced0bSchristos {
756f4ced0bSchristos ssize_t len;
766f4ced0bSchristos size_t acc = 0;
776f4ced0bSchristos char *data = (char *) buf;
786f4ced0bSchristos
796f4ced0bSchristos #ifdef HAVE_PREAD
806f4ced0bSchristos while (count > 0)
816f4ced0bSchristos {
826f4ced0bSchristos errno = 0;
836f4ced0bSchristos if (((len = pread (fd, data, count, offset)) < 0) &&
846f4ced0bSchristos errno != EINTR)
856f4ced0bSchristos return len;
866f4ced0bSchristos if (errno == EINTR)
876f4ced0bSchristos continue;
886f4ced0bSchristos
896f4ced0bSchristos acc += len;
906f4ced0bSchristos if (len == 0) /* EOF. */
916f4ced0bSchristos return acc;
926f4ced0bSchristos
936f4ced0bSchristos count -= len;
946f4ced0bSchristos offset += len;
956f4ced0bSchristos data += len;
966f4ced0bSchristos }
976f4ced0bSchristos return acc;
986f4ced0bSchristos #else
996f4ced0bSchristos off_t orig_off;
1006f4ced0bSchristos
1016f4ced0bSchristos if ((orig_off = lseek (fd, 0, SEEK_CUR)) < 0)
1026f4ced0bSchristos return -1;
1036f4ced0bSchristos if ((lseek (fd, offset, SEEK_SET)) < 0)
1046f4ced0bSchristos return -1;
1056f4ced0bSchristos
1066f4ced0bSchristos while (count > 0)
1076f4ced0bSchristos {
1086f4ced0bSchristos errno = 0;
1096f4ced0bSchristos if (((len = read (fd, data, count)) < 0) &&
1106f4ced0bSchristos errno != EINTR)
1116f4ced0bSchristos return len;
1126f4ced0bSchristos if (errno == EINTR)
1136f4ced0bSchristos continue;
1146f4ced0bSchristos
1156f4ced0bSchristos acc += len;
1166f4ced0bSchristos if (len == 0) /* EOF. */
1176f4ced0bSchristos break;
1186f4ced0bSchristos
1196f4ced0bSchristos count -= len;
1206f4ced0bSchristos data += len;
1216f4ced0bSchristos }
1226f4ced0bSchristos if ((lseek (fd, orig_off, SEEK_SET)) < 0)
1236f4ced0bSchristos return -1; /* offset is smashed. */
1246f4ced0bSchristos #endif
1256f4ced0bSchristos
1266f4ced0bSchristos return acc;
1276f4ced0bSchristos }
1286f4ced0bSchristos
1296f4ced0bSchristos /* Set the CTF library client version to the specified version. If version is
1306f4ced0bSchristos zero, we just return the default library version number. */
1316f4ced0bSchristos int
ctf_version(int version)1326f4ced0bSchristos ctf_version (int version)
1336f4ced0bSchristos {
1346f4ced0bSchristos if (version < 0)
1356f4ced0bSchristos {
1366f4ced0bSchristos errno = EINVAL;
1376f4ced0bSchristos return -1;
1386f4ced0bSchristos }
1396f4ced0bSchristos
1406f4ced0bSchristos if (version > 0)
1416f4ced0bSchristos {
1426f4ced0bSchristos /* Dynamic version switching is not presently supported. */
1436f4ced0bSchristos if (version != CTF_VERSION)
1446f4ced0bSchristos {
1456f4ced0bSchristos errno = ENOTSUP;
1466f4ced0bSchristos return -1;
1476f4ced0bSchristos }
1486f4ced0bSchristos ctf_dprintf ("ctf_version: client using version %d\n", version);
1496f4ced0bSchristos _libctf_version = version;
1506f4ced0bSchristos }
1516f4ced0bSchristos
1526f4ced0bSchristos return _libctf_version;
1536f4ced0bSchristos }
1546f4ced0bSchristos
1556f4ced0bSchristos void
libctf_init_debug(void)1566f4ced0bSchristos libctf_init_debug (void)
1576f4ced0bSchristos {
1586f4ced0bSchristos static int inited;
1596f4ced0bSchristos if (!inited)
1606f4ced0bSchristos {
1616f4ced0bSchristos _libctf_debug = getenv ("LIBCTF_DEBUG") != NULL;
1626f4ced0bSchristos inited = 1;
1636f4ced0bSchristos }
1646f4ced0bSchristos }
1656f4ced0bSchristos
ctf_setdebug(int debug)1666f4ced0bSchristos void ctf_setdebug (int debug)
1676f4ced0bSchristos {
1686f4ced0bSchristos /* Ensure that libctf_init_debug() has been called, so that we don't get our
1696f4ced0bSchristos debugging-on-or-off smashed by the next call. */
1706f4ced0bSchristos
1716f4ced0bSchristos libctf_init_debug();
1726f4ced0bSchristos _libctf_debug = debug;
1736f4ced0bSchristos ctf_dprintf ("CTF debugging set to %i\n", debug);
1746f4ced0bSchristos }
1756f4ced0bSchristos
ctf_getdebug(void)1766f4ced0bSchristos int ctf_getdebug (void)
1776f4ced0bSchristos {
1786f4ced0bSchristos return _libctf_debug;
1796f4ced0bSchristos }
1806f4ced0bSchristos
1816f4ced0bSchristos _libctf_printflike_ (1, 2)
ctf_dprintf(const char * format,...)1826f4ced0bSchristos void ctf_dprintf (const char *format, ...)
1836f4ced0bSchristos {
1844f645668Schristos if (_libctf_unlikely_ (_libctf_debug))
1856f4ced0bSchristos {
1866f4ced0bSchristos va_list alist;
1876f4ced0bSchristos
1886f4ced0bSchristos va_start (alist, format);
1896f4ced0bSchristos fflush (stdout);
1906f4ced0bSchristos (void) fputs ("libctf DEBUG: ", stderr);
1916f4ced0bSchristos (void) vfprintf (stderr, format, alist);
1926f4ced0bSchristos va_end (alist);
1936f4ced0bSchristos }
1946f4ced0bSchristos }
1954f645668Schristos
1964f645668Schristos /* This needs more attention to thread-safety later on. */
1974f645668Schristos static ctf_list_t open_errors;
1984f645668Schristos
1994f645668Schristos /* Errors and warnings. Report the warning or error to the list in FP (or the
2004f645668Schristos open errors list if NULL): if ERR is nonzero it is the errno to report to the
2014f645668Schristos debug stream instead of that recorded on fp. */
2024f645668Schristos _libctf_printflike_ (4, 5)
2034f645668Schristos extern void
ctf_err_warn(ctf_dict_t * fp,int is_warning,int err,const char * format,...)2044f645668Schristos ctf_err_warn (ctf_dict_t *fp, int is_warning, int err,
2054f645668Schristos const char *format, ...)
2064f645668Schristos {
2074f645668Schristos va_list alist;
2084f645668Schristos ctf_err_warning_t *cew;
2094f645668Schristos
2104f645668Schristos /* Don't bother reporting errors here: we can't do much about them if they
2114f645668Schristos happen. If we're so short of memory that a tiny malloc doesn't work, a
2124f645668Schristos vfprintf isn't going to work either and the caller will have to rely on the
2134f645668Schristos ENOMEM return they'll be getting in short order anyway. */
2144f645668Schristos
2154f645668Schristos if ((cew = malloc (sizeof (ctf_err_warning_t))) == NULL)
2164f645668Schristos return;
2174f645668Schristos
2184f645668Schristos cew->cew_is_warning = is_warning;
2194f645668Schristos va_start (alist, format);
2204f645668Schristos if (vasprintf (&cew->cew_text, format, alist) < 0)
2214f645668Schristos {
2224f645668Schristos free (cew);
2234f645668Schristos va_end (alist);
2244f645668Schristos return;
2254f645668Schristos }
2264f645668Schristos va_end (alist);
2274f645668Schristos
2284f645668Schristos /* Include the error code only if there is one; if this is not a warning,
2294f645668Schristos only use the error code if it was explicitly passed and is nonzero.
2304f645668Schristos (Warnings may not have a meaningful error code, since the warning may not
2314f645668Schristos lead to unwinding up to the user.) */
2324f645668Schristos if ((!is_warning && (err != 0 || (fp && ctf_errno (fp) != 0)))
2334f645668Schristos || (is_warning && err != 0))
2344f645668Schristos ctf_dprintf ("%s: %s (%s)\n", is_warning ? _("error") : _("warning"),
2354f645668Schristos cew->cew_text, err != 0 ? ctf_errmsg (err)
2364f645668Schristos : ctf_errmsg (ctf_errno (fp)));
2374f645668Schristos else
2384f645668Schristos ctf_dprintf ("%s: %s\n", is_warning ? _("error") : _("warning"),
2394f645668Schristos cew->cew_text);
2404f645668Schristos
2414f645668Schristos if (fp != NULL)
2424f645668Schristos ctf_list_append (&fp->ctf_errs_warnings, cew);
2434f645668Schristos else
2444f645668Schristos ctf_list_append (&open_errors, cew);
2454f645668Schristos }
2464f645668Schristos
2474f645668Schristos /* Move all the errors/warnings from an fp into the open_errors. */
2484f645668Schristos void
ctf_err_warn_to_open(ctf_dict_t * fp)2494f645668Schristos ctf_err_warn_to_open (ctf_dict_t *fp)
2504f645668Schristos {
2514f645668Schristos ctf_list_splice (&open_errors, &fp->ctf_errs_warnings);
2524f645668Schristos }
2534f645668Schristos
2544f645668Schristos /* Error-warning reporting: an 'iterator' that returns errors and warnings from
2554f645668Schristos the error/warning list, in order of emission. Errors and warnings are popped
2564f645668Schristos after return: the caller must free the returned error-text pointer.
2574f645668Schristos
2584f645668Schristos An fp of NULL returns CTF-open-time errors from the open_errors variable
2594f645668Schristos above.
2604f645668Schristos
2614f645668Schristos The treatment of errors from this function itself is somewhat unusual: it
2624f645668Schristos will often be called on an error path, so we don't want to overwrite the
2634f645668Schristos ctf_errno unless we have no choice. So, like ctf_bufopen et al, this
2644f645668Schristos function takes an errp pointer where errors are reported. The pointer is
2654f645668Schristos optional: if not set, errors are reported via the fp (if non-NULL). Calls
2664f645668Schristos with neither fp nor errp set are mildly problematic because there is no clear
2674f645668Schristos way to report end-of-iteration: you just have to assume that a NULL return
2684f645668Schristos means the end, and not an iterator error. */
2694f645668Schristos
2704f645668Schristos char *
ctf_errwarning_next(ctf_dict_t * fp,ctf_next_t ** it,int * is_warning,int * errp)2714f645668Schristos ctf_errwarning_next (ctf_dict_t *fp, ctf_next_t **it, int *is_warning,
2724f645668Schristos int *errp)
2734f645668Schristos {
2744f645668Schristos ctf_next_t *i = *it;
2754f645668Schristos char *ret;
2764f645668Schristos ctf_list_t *errlist;
2774f645668Schristos ctf_err_warning_t *cew;
2784f645668Schristos
2794f645668Schristos if (fp)
2804f645668Schristos errlist = &fp->ctf_errs_warnings;
2814f645668Schristos else
2824f645668Schristos errlist = &open_errors;
2834f645668Schristos
2844f645668Schristos if (!i)
2854f645668Schristos {
2864f645668Schristos if ((i = ctf_next_create ()) == NULL)
2874f645668Schristos {
2884f645668Schristos if (errp)
2894f645668Schristos *errp = ENOMEM;
2904f645668Schristos else if (fp)
2914f645668Schristos ctf_set_errno (fp, ENOMEM);
2924f645668Schristos return NULL;
2934f645668Schristos }
2944f645668Schristos
2954f645668Schristos i->cu.ctn_fp = fp;
2964f645668Schristos i->ctn_iter_fun = (void (*) (void)) ctf_errwarning_next;
2974f645668Schristos *it = i;
2984f645668Schristos }
2994f645668Schristos
3004f645668Schristos if ((void (*) (void)) ctf_errwarning_next != i->ctn_iter_fun)
3014f645668Schristos {
3024f645668Schristos if (errp)
3034f645668Schristos *errp = ECTF_NEXT_WRONGFUN;
3044f645668Schristos else if (fp)
3054f645668Schristos ctf_set_errno (fp, ECTF_NEXT_WRONGFUN);
3064f645668Schristos return NULL;
3074f645668Schristos }
3084f645668Schristos
3094f645668Schristos if (fp != i->cu.ctn_fp)
3104f645668Schristos {
3114f645668Schristos if (errp)
3124f645668Schristos *errp = ECTF_NEXT_WRONGFP;
3134f645668Schristos else if (fp)
3144f645668Schristos ctf_set_errno (fp, ECTF_NEXT_WRONGFP);
3154f645668Schristos return NULL;
3164f645668Schristos }
3174f645668Schristos
3184f645668Schristos cew = ctf_list_next (errlist);
3194f645668Schristos
3204f645668Schristos if (!cew)
3214f645668Schristos {
3224f645668Schristos ctf_next_destroy (i);
3234f645668Schristos *it = NULL;
3244f645668Schristos if (errp)
3254f645668Schristos *errp = ECTF_NEXT_END;
3264f645668Schristos else if (fp)
3274f645668Schristos ctf_set_errno (fp, ECTF_NEXT_END);
3284f645668Schristos return NULL;
3294f645668Schristos }
3304f645668Schristos
3314f645668Schristos if (is_warning)
3324f645668Schristos *is_warning = cew->cew_is_warning;
3334f645668Schristos ret = cew->cew_text;
3344f645668Schristos ctf_list_delete (errlist, cew);
3354f645668Schristos free (cew);
3364f645668Schristos return ret;
3374f645668Schristos }
3384f645668Schristos
3394f645668Schristos void
ctf_assert_fail_internal(ctf_dict_t * fp,const char * file,size_t line,const char * exprstr)3404f645668Schristos ctf_assert_fail_internal (ctf_dict_t *fp, const char *file, size_t line,
3414f645668Schristos const char *exprstr)
3424f645668Schristos {
3434f645668Schristos ctf_err_warn (fp, 0, ECTF_INTERNAL, _("%s: %lu: libctf assertion failed: %s"),
3444f645668Schristos file, (long unsigned int) line, exprstr);
3454f645668Schristos ctf_set_errno (fp, ECTF_INTERNAL);
3464f645668Schristos }
347