ctf-subr.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. /* Simple subrs.
  2. Copyright (C) 2019-2022 Free Software Foundation, Inc.
  3. This file is part of libctf.
  4. libctf is free software; you can redistribute it and/or modify it under
  5. the terms of the GNU General Public License as published by the Free
  6. Software Foundation; either version 3, or (at your option) any later
  7. version.
  8. This program is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. See the GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; see the file COPYING. If not see
  14. <http://www.gnu.org/licenses/>. */
  15. #include <ctf-impl.h>
  16. #ifdef HAVE_MMAP
  17. #include <sys/mman.h>
  18. #endif
  19. #include <sys/types.h>
  20. #include <stdarg.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #ifndef ENOTSUP
  24. #define ENOTSUP ENOSYS
  25. #endif
  26. int _libctf_version = CTF_VERSION; /* Library client version. */
  27. int _libctf_debug = 0; /* Debugging messages enabled. */
  28. /* Private, read-only mmap from a file, with fallback to copying.
  29. No handling of page-offset issues at all: the caller must allow for that. */
  30. _libctf_malloc_ void *
  31. ctf_mmap (size_t length, size_t offset, int fd)
  32. {
  33. void *data;
  34. #ifdef HAVE_MMAP
  35. data = mmap (NULL, length, PROT_READ, MAP_PRIVATE, fd, offset);
  36. if (data == MAP_FAILED)
  37. data = NULL;
  38. #else
  39. if ((data = malloc (length)) != NULL)
  40. {
  41. if (ctf_pread (fd, data, length, offset) <= 0)
  42. {
  43. free (data);
  44. data = NULL;
  45. }
  46. }
  47. #endif
  48. return data;
  49. }
  50. void
  51. ctf_munmap (void *buf, size_t length _libctf_unused_)
  52. {
  53. #ifdef HAVE_MMAP
  54. (void) munmap (buf, length);
  55. #else
  56. free (buf);
  57. #endif
  58. }
  59. ssize_t
  60. ctf_pread (int fd, void *buf, ssize_t count, off_t offset)
  61. {
  62. ssize_t len;
  63. size_t acc = 0;
  64. char *data = (char *) buf;
  65. #ifdef HAVE_PREAD
  66. while (count > 0)
  67. {
  68. errno = 0;
  69. if (((len = pread (fd, data, count, offset)) < 0) &&
  70. errno != EINTR)
  71. return len;
  72. if (errno == EINTR)
  73. continue;
  74. acc += len;
  75. if (len == 0) /* EOF. */
  76. return acc;
  77. count -= len;
  78. offset += len;
  79. data += len;
  80. }
  81. return acc;
  82. #else
  83. off_t orig_off;
  84. if ((orig_off = lseek (fd, 0, SEEK_CUR)) < 0)
  85. return -1;
  86. if ((lseek (fd, offset, SEEK_SET)) < 0)
  87. return -1;
  88. while (count > 0)
  89. {
  90. errno = 0;
  91. if (((len = read (fd, data, count)) < 0) &&
  92. errno != EINTR)
  93. return len;
  94. if (errno == EINTR)
  95. continue;
  96. acc += len;
  97. if (len == 0) /* EOF. */
  98. break;
  99. count -= len;
  100. data += len;
  101. }
  102. if ((lseek (fd, orig_off, SEEK_SET)) < 0)
  103. return -1; /* offset is smashed. */
  104. #endif
  105. return acc;
  106. }
  107. /* Set the CTF library client version to the specified version. If version is
  108. zero, we just return the default library version number. */
  109. int
  110. ctf_version (int version)
  111. {
  112. if (version < 0)
  113. {
  114. errno = EINVAL;
  115. return -1;
  116. }
  117. if (version > 0)
  118. {
  119. /* Dynamic version switching is not presently supported. */
  120. if (version != CTF_VERSION)
  121. {
  122. errno = ENOTSUP;
  123. return -1;
  124. }
  125. ctf_dprintf ("ctf_version: client using version %d\n", version);
  126. _libctf_version = version;
  127. }
  128. return _libctf_version;
  129. }
  130. void
  131. libctf_init_debug (void)
  132. {
  133. static int inited;
  134. if (!inited)
  135. {
  136. _libctf_debug = getenv ("LIBCTF_DEBUG") != NULL;
  137. inited = 1;
  138. }
  139. }
  140. void ctf_setdebug (int debug)
  141. {
  142. /* Ensure that libctf_init_debug() has been called, so that we don't get our
  143. debugging-on-or-off smashed by the next call. */
  144. libctf_init_debug();
  145. _libctf_debug = debug;
  146. ctf_dprintf ("CTF debugging set to %i\n", debug);
  147. }
  148. int ctf_getdebug (void)
  149. {
  150. return _libctf_debug;
  151. }
  152. _libctf_printflike_ (1, 2)
  153. void ctf_dprintf (const char *format, ...)
  154. {
  155. if (_libctf_unlikely_ (_libctf_debug))
  156. {
  157. va_list alist;
  158. va_start (alist, format);
  159. fflush (stdout);
  160. (void) fputs ("libctf DEBUG: ", stderr);
  161. (void) vfprintf (stderr, format, alist);
  162. va_end (alist);
  163. }
  164. }
  165. /* This needs more attention to thread-safety later on. */
  166. static ctf_list_t open_errors;
  167. /* Errors and warnings. Report the warning or error to the list in FP (or the
  168. open errors list if NULL): if ERR is nonzero it is the errno to report to the
  169. debug stream instead of that recorded on fp. */
  170. _libctf_printflike_ (4, 5)
  171. extern void
  172. ctf_err_warn (ctf_dict_t *fp, int is_warning, int err,
  173. const char *format, ...)
  174. {
  175. va_list alist;
  176. ctf_err_warning_t *cew;
  177. /* Don't bother reporting errors here: we can't do much about them if they
  178. happen. If we're so short of memory that a tiny malloc doesn't work, a
  179. vfprintf isn't going to work either and the caller will have to rely on the
  180. ENOMEM return they'll be getting in short order anyway. */
  181. if ((cew = malloc (sizeof (ctf_err_warning_t))) == NULL)
  182. return;
  183. cew->cew_is_warning = is_warning;
  184. va_start (alist, format);
  185. if (vasprintf (&cew->cew_text, format, alist) < 0)
  186. {
  187. free (cew);
  188. va_end (alist);
  189. return;
  190. }
  191. va_end (alist);
  192. /* Include the error code only if there is one; if this is not a warning,
  193. only use the error code if it was explicitly passed and is nonzero.
  194. (Warnings may not have a meaningful error code, since the warning may not
  195. lead to unwinding up to the user.) */
  196. if ((!is_warning && (err != 0 || (fp && ctf_errno (fp) != 0)))
  197. || (is_warning && err != 0))
  198. ctf_dprintf ("%s: %s (%s)\n", is_warning ? _("error") : _("warning"),
  199. cew->cew_text, err != 0 ? ctf_errmsg (err)
  200. : ctf_errmsg (ctf_errno (fp)));
  201. else
  202. ctf_dprintf ("%s: %s\n", is_warning ? _("error") : _("warning"),
  203. cew->cew_text);
  204. if (fp != NULL)
  205. ctf_list_append (&fp->ctf_errs_warnings, cew);
  206. else
  207. ctf_list_append (&open_errors, cew);
  208. }
  209. /* Move all the errors/warnings from an fp into the open_errors. */
  210. void
  211. ctf_err_warn_to_open (ctf_dict_t *fp)
  212. {
  213. ctf_list_splice (&open_errors, &fp->ctf_errs_warnings);
  214. }
  215. /* Error-warning reporting: an 'iterator' that returns errors and warnings from
  216. the error/warning list, in order of emission. Errors and warnings are popped
  217. after return: the caller must free the returned error-text pointer.
  218. An fp of NULL returns CTF-open-time errors from the open_errors variable
  219. above.
  220. The treatment of errors from this function itself is somewhat unusual: it
  221. will often be called on an error path, so we don't want to overwrite the
  222. ctf_errno unless we have no choice. So, like ctf_bufopen et al, this
  223. function takes an errp pointer where errors are reported. The pointer is
  224. optional: if not set, errors are reported via the fp (if non-NULL). Calls
  225. with neither fp nor errp set are mildly problematic because there is no clear
  226. way to report end-of-iteration: you just have to assume that a NULL return
  227. means the end, and not an iterator error. */
  228. char *
  229. ctf_errwarning_next (ctf_dict_t *fp, ctf_next_t **it, int *is_warning,
  230. int *errp)
  231. {
  232. ctf_next_t *i = *it;
  233. char *ret;
  234. ctf_list_t *errlist;
  235. ctf_err_warning_t *cew;
  236. if (fp)
  237. errlist = &fp->ctf_errs_warnings;
  238. else
  239. errlist = &open_errors;
  240. if (!i)
  241. {
  242. if ((i = ctf_next_create ()) == NULL)
  243. {
  244. if (errp)
  245. *errp = ENOMEM;
  246. else if (fp)
  247. ctf_set_errno (fp, ENOMEM);
  248. return NULL;
  249. }
  250. i->cu.ctn_fp = fp;
  251. i->ctn_iter_fun = (void (*) (void)) ctf_errwarning_next;
  252. *it = i;
  253. }
  254. if ((void (*) (void)) ctf_errwarning_next != i->ctn_iter_fun)
  255. {
  256. if (errp)
  257. *errp = ECTF_NEXT_WRONGFUN;
  258. else if (fp)
  259. ctf_set_errno (fp, ECTF_NEXT_WRONGFUN);
  260. return NULL;
  261. }
  262. if (fp != i->cu.ctn_fp)
  263. {
  264. if (errp)
  265. *errp = ECTF_NEXT_WRONGFP;
  266. else if (fp)
  267. ctf_set_errno (fp, ECTF_NEXT_WRONGFP);
  268. return NULL;
  269. }
  270. cew = ctf_list_next (errlist);
  271. if (!cew)
  272. {
  273. ctf_next_destroy (i);
  274. *it = NULL;
  275. if (errp)
  276. *errp = ECTF_NEXT_END;
  277. else if (fp)
  278. ctf_set_errno (fp, ECTF_NEXT_END);
  279. return NULL;
  280. }
  281. if (is_warning)
  282. *is_warning = cew->cew_is_warning;
  283. ret = cew->cew_text;
  284. ctf_list_delete (errlist, cew);
  285. free (cew);
  286. return ret;
  287. }
  288. void
  289. ctf_assert_fail_internal (ctf_dict_t *fp, const char *file, size_t line,
  290. const char *exprstr)
  291. {
  292. ctf_err_warn (fp, 0, ECTF_INTERNAL, _("%s: %lu: libctf assertion failed: %s"),
  293. file, (long unsigned int) line, exprstr);
  294. ctf_set_errno (fp, ECTF_INTERNAL);
  295. }