[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [[UNIKRAFT RFC PATCH] v2 1/1] Implementation of isrlib string.c



Hi Christian,

thanks a lot for the update. I would remove some functions from the library, especially the ones that are not thread- and reentrance-safe (use variables on stack only). Otherwise, it looks good. In the patch description you should briefly describe why you introduce this library and where you got the code from. As subject I would choose something like:
        lib/isrlib: Introduce libc-like interrupt-context-safe routines

Thanks,

Simon

On 21.08.20 09:57, cristianvijelie@xxxxxxxxx wrote:
From: Cristian Vijelie <cristianvijelie@xxxxxxxxx>

Signed-off-by: Cristian Vijelie <cristianvijelie@xxxxxxxxx>
---
  include/uk/isr/string.h |  76 +++++
  lib/isrlib/Config.uk    |   7 +
  lib/isrlib/Makefile.uk  |   3 +
  lib/isrlib/string.c     | 633 ++++++++++++++++++++++++++++++++++++++++
  4 files changed, 719 insertions(+)
  create mode 100644 include/uk/isr/string.h
  create mode 100644 lib/isrlib/Config.uk
  create mode 100644 lib/isrlib/Makefile.uk
  create mode 100644 lib/isrlib/string.c

diff --git a/include/uk/isr/string.h b/include/uk/isr/string.h
new file mode 100644
index 0000000..7ce9fea
--- /dev/null
+++ b/include/uk/isr/string.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Simon Kuenzer <simon.kuenzer@xxxxxxxxx>

You can add your name here, too.

+ *
+ *
+ * Copyright (c) 2017, NEC Europe Ltd., NEC Corporation. All rights reserved.
+ *

Probably you can add your university to the copyright too (depends on your university and if there is a contract). Do the same also for the license header in string.c.

+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __UK_ISR_STRING_H__
+#define __UK_ISR_STRING_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <string.h>
+
+void *memcpy_isr(void *dst, const void *src, size_t len);
+void *memset_isr(void *ptr, int val, size_t len);
+void *memchr_isr(const void *ptr, int val, size_t len);
+void *memrchr_isr(const void *m, int c, size_t n);
+int memcmp_isr(const void *ptr1, const void *ptr2, size_t len);
+void *memmove_isr(void *dst, const void *src, size_t len);
+
+char *strncpy_isr(char *dst, const char *src, size_t len);
+char *strcpy_isr(char *dst, const char *src);
+size_t strlcpy_isr(char *d, const char *s, size_t n);
+size_t strlcat_isr(char *d, const char *s, size_t n);
+size_t strnlen_isr(const char *str, size_t maxlen);
+size_t strlen_isr(const char *str);
+char *strchrnul_isr(const char *s, int c);
+char *strchr_isr(const char *str, int c);
+char *strrchr_isr(const char *s, int c);
+int strncmp_isr(const char *str1, const char *str2, size_t len);
+int strcmp_isr(const char *str1, const char *str2);
+size_t strcspn_isr(const char *s, const char *c);
+size_t strspn_isr(const char *s, const char *c);

Functions that depend on a global variable are a bad idea for being interrupt-context-safe (breaks thread-safety, reentrancy property). For interrupts this will break as soon as we support SMP or have nested interrupts. Please remove those functions from the lib. As far as I briefly see, this is at least true for strtok_isr() and strerror_isr(). You could then rename strtok_isr_r() to strtok_isr().

+char *strtok_isr(char *restrict s, const char *restrict sep);
+char *strtok_isr_r(char *restrict s, const char *restrict sep, char **restrict 
p);

Please, also remove any function that uses malloc (like these two). It is in general not recommended to alloc memory during interrupt context. Since we can swap allocators, you don't know if malloc is interrupt-context-safe. All your functions in the isrlib should not call any library-external function, otherwise they are not interrupt-context-safe anymore. In general, I was thinking if we could do an offline analysis by using a function call tree in order to make sure that our interrupt handlers fulfill the properties...

+char *strndup_isr(const char *str, size_t len);
+char *strdup_isr(const char *str);
+

I think strerror is not needed at all. This is done for formatting an error message which is in general not a good idea to do within interrupt context. I would remove them. We also do not want to keep maintaining two error number lists (nolibc):

+char *strerror_isr_r(int errnum, char *buf, size_t buflen);
+char *strerror_isr(int errnum);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UK_ISR_STRING_H__ */
diff --git a/lib/isrlib/Config.uk b/lib/isrlib/Config.uk
new file mode 100644
index 0000000..c6be13c
--- /dev/null
+++ b/lib/isrlib/Config.uk
@@ -0,0 +1,7 @@
+menuconfig LIBISRLIB
+    bool "isrlib: ISR helper library"
+    default n
+

Since we do not have any options - and I do not expect any for now, use `config` instead of `menuconfig`. The if-case can be removed.

+if LIBISRLIB
+
+endif
diff --git a/lib/isrlib/Makefile.uk b/lib/isrlib/Makefile.uk
new file mode 100644
index 0000000..df3616d
--- /dev/null
+++ b/lib/isrlib/Makefile.uk
@@ -0,0 +1,3 @@
+$(eval $(call addlib_s,libisrlib,$(CONFIG_LIBISRLIB)))
+
+LIBISRLIB_SRCS-y += $(LIBISRLIB_BASE)/string.c|isr

I think you need to add the library include folder to the glocal C and C++ scope.

diff --git a/lib/isrlib/string.c b/lib/isrlib/string.c
new file mode 100644
index 0000000..6cb0981
--- /dev/null
+++ b/lib/isrlib/string.c
@@ -0,0 +1,633 @@

Please keep the license headers in string.c. Copy them from nolibc and add your name and your university as mentioned to string.h.

+#include <uk/isr/string.h>

You can remove this comment:

+/* I included types.h because VSCode was complaining that __u8 and other
+ * types are not defined, but the build is succesfull without it.
+ */
+#include <uk/arch/types.h>
+#include <stdint.h>
+#include <limits.h>

Is errno still needed?:

+#include <errno.h>
+
+void *memcpy_isr(void *dst, const void *src, size_t len)
+{
+       size_t p;
+
+       for (p = 0; p < len; ++p)
+               *((__u8 *)(((__uptr)dst) + p)) = *((__u8 *)(((__uptr)src) + p));
+
+       return dst;
+}
+
+void *memset_isr(void *ptr, int val, size_t len)
+{
+       __u8 *p = (__u8 *) ptr;
+
+       for (; len > 0; --len)
+               *(p++) = (__u8)val;
+
+       return ptr;
+}
+
+void *memchr_isr(const void *ptr, int val, size_t len)
+{
+       uintptr_t o = 0;
+
+       for (o = 0; o < (uintptr_t)len; ++o)
+               if (*((const uint8_t *)(((uintptr_t)ptr) + o)) == (uint8_t)val)
+                       return (void *)((uintptr_t)ptr + o);
+
+       return NULL; /* did not find val */
+}
+
+void *memrchr_isr(const void *m, int c, size_t n)
+{
+       const unsigned char *s = m;
+
+       c = (unsigned char) c;
+       while (n--)
+               if (s[n] == c)
+                       return (void *) (s + n);
+       return 0;
+}
+
+void *memmove_isr(void *dst, const void *src, size_t len)
+{
+       uint8_t *d = dst;
+       const uint8_t *s = src;
+
+       if (src > dst) {
+               for (; len > 0; --len)
+                       *(d++) = *(s++);
+       } else {
+               s += len;
+               d += len;
+
+               for (; len > 0; --len)
+                       *(d--) = *(s--);
+       }
+
+       return dst;
+}
+
+int memcmp_isr(const void *ptr1, const void *ptr2, size_t len)
+{
+       const unsigned char *c1 = (const unsigned char *)ptr1;
+       const unsigned char *c2 = (const unsigned char *)ptr2;
+
+       for (; len > 0; --len, ++c1, ++c2) {
+               if ((*c1) != (*c2))
+                       return ((*c1) - (*c2));
+       }
+
+       return 0;
+}
+
+size_t strlen_isr(const char *str)
+{
+       return strnlen_isr(str, SIZE_MAX);
+}
+
+size_t strnlen_isr(const char *str, size_t len)
+{
+       const char *p = memchr_isr(str, 0, len);
+       return p ? (size_t) (p - str) : len;
+}
+
+char *strncpy_isr(char *dst, const char *src, size_t len)
+{
+       size_t clen;
+
+       clen = strnlen_isr(src, len);
+       memcpy_isr(dst, src, clen);
+
+       /* instead of filling up the rest of left space with zeros,
+        * append a termination character if we did not copy one
+        */
+       if (clen < len && dst[clen - 1] != '\0')
+               dst[clen] = '\0';
+       return dst;
+}
+
+char *strcpy_isr(char *dst, const char *src)
+{
+       return strncpy_isr(dst, src, SIZE_MAX);
+}
+
+int strncmp_isr(const char *str1, const char *str2, size_t len)
+{
+       const char *c1 = (const char *)str1;
+       const char *c2 = (const char *)str2;
+
+       for (; len > 0; --len, ++c1, ++c2) {
+               if ((*c1) != (*c2))
+                       return (int)((*c1) - (*c2));
+               if ((*c1) == '\0')
+                       break;
+       }
+       return 0;
+}
+
+int strcmp_isr(const char *str1, const char *str2)
+{
+       register signed char __res;
+
+       while ((__res = *str1 - *str2++) == 0 && *str1++)
+               ;
+
+       return __res;
+}
+
+/* The following code is taken from musl libc */
+#define ALIGN (sizeof(size_t))
+#define ONES ((size_t) -1 / UCHAR_MAX)
+#define HIGHS (ONES * (UCHAR_MAX / 2 + 1))
+#define HASZERO(x) (((x) - ONES) & ~(x) & HIGHS)
+#define BITOP(a, b, op) \
+               ((a)[(size_t)(b) / (8*sizeof *(a))] op \
+               (size_t)1 << ((size_t)(b) % (8 * sizeof *(a))))
+
+char *strchrnul_isr(const char *s, int c)
+{
+       size_t *w, k;
+
+       c = (unsigned char)c;
+       if (!c)
+               return (char *)s + strlen_isr(s);
+
+       for (; (uintptr_t)s % ALIGN; s++)
+               if (!*s || *(unsigned char *)s == c)
+                       return (char *)s;
+       k = ONES * c;
+       for (w = (void *)s; !HASZERO(*w) && !HASZERO(*w ^ k); w++)
+               ;
+       for (s = (void *)w; *s && *(unsigned char *)s != c; s++)
+               ;
+       return (char *)s;
+}
+
+char *strchr_isr(const char *str, int c)
+{
+       char *r = strchrnul_sr(str, c);
+       return *(unsigned char *)r == (unsigned char)c ? r : 0;
+}
+
+char *strrchr_isr(const char *s, int c)
+{
+       return memrchr_isr(s, c, strlen_isr(s) + 1);
+}
+
+size_t strcspn_isr(const char *s, const char *c)
+{
+       const char *a = s;
+       size_t byteset[32 / sizeof(size_t)];
+
+       if (!c[0] || !c[1])
+               return strchrnul_isr(s, *c)-a;
+
+       memset_isr(byteset, 0, sizeof(byteset));
+       for (; *c && BITOP(byteset, *(unsigned char *)c, |=); c++)
+               ;
+       for (; *s && !BITOP(byteset, *(unsigned char *)s, &); s++)
+               ;
+       return s-a;
+}
+
+size_t strspn_isr(const char *s, const char *c)
+{
+       const char *a = s;
+       size_t byteset[32 / sizeof(size_t)] = { 0 };
+
+       if (!c[0])
+               return 0;
+       if (!c[1]) {
+               for (; *s == *c; s++)
+                       ;
+               return s-a;
+       }
+
+       for (; *c && BITOP(byteset, *(unsigned char *)c, |=); c++)
+               ;
+       for (; *s && BITOP(byteset, *(unsigned char *)s, &); s++)
+               ;
+       return s-a;
+}
+
+char *strtok_isr(char *restrict s, const char *restrict sep)
+{
+       static char *p;
+
+       if (!s && !(s = p))
+               return NULL;
+       s += strspn_isr(s, sep);
+       if (!*s)
+               return p = 0;
+       p = s + strcspn_isr(s, sep);
+       if (*p)
+               *p++ = 0;
+       else
+               p = 0;
+       return s;
+}
+
+char *strtok_isr_r(char *restrict s, const char *restrict sep, char **restrict 
p)
+{
+       if (!s && !(s = *p))
+               return NULL;
+       s += strspn_isr(s, sep);
+       if (!*s)
+               return *p = 0;
+       *p = s + strcspn_isr(s, sep);
+       if (**p)
+               *(*p)++ = 0;
+       else
+               *p = 0;
+       return s;
+}
+
+char *strndup_isr(const char *str, size_t len)
+{
+       char *__res;
+       int __len;
+
+       __len = strnlen(str, len);
+
+       __res = malloc(__len + 1);
+       if (__res) {
+               memcpy(__res, str, __len);
+               __res[__len] = '\0';
+       }
+
+       return __res;
+}
+
+char *strdup_isr(const char *str)
+{
+       return strndup_isr(str, SIZE_MAX);
+}
+
+/* strlcpy has different ALIGN */
+#undef ALIGN
+#define ALIGN (sizeof(size_t)-1)
+size_t strlcpy_isr(char *d, const char *s, size_t n)
+{
+       char *d0 = d;
+       size_t *wd;
+       const size_t *ws;
+
+       if (!n--)
+               goto finish;
+
+       if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) {
+               for (; ((uintptr_t) s & ALIGN) && n && (*d = *s);
+                    n--, s++, d++)
+                       ;
+
+               if (n && *s) {
+                       wd = (void *)d; ws = (const void *)s;
+                       for (; n >= sizeof(size_t) && !HASZERO(*ws);
+                            n -= sizeof(size_t), ws++, wd++)
+                               *wd = *ws;
+
+                       d = (void *)wd; s = (const void *)ws;
+               }
+       }
+
+       for (; n && (*d = *s); n--, s++, d++)
+               ;
+       *d = 0;
+finish:
+       return d-d0 + strlen_isr(s);
+}
+
+size_t strlcat_isr(char *d, const char *s, size_t n)
+{
+       size_t l = strnlen_isr(d, n);
+       if (l == n)
+               return l + strlen_isr(s);
+       return l + strlcpy_isr(d+l, s, n-l);
+}
+
+/* GNU-specific version of strerror_r */
+char *strerror_isr_r(int errnum, char *buf, size_t buflen)
+{
+       const char *strerr;
+
+       switch (errnum) {
+       case EPERM:
+               strerr = "Operation not permitted";
+               break;
+       case ENOENT:
+               strerr = "No such file or directory";
+               break;
+       case ESRCH:
+               strerr = "No such process";
+               break;
+       case EINTR:
+               strerr = "Interrupted system call";
+               break;
+       case EIO:
+               strerr = "Input/output error";
+               break;
+       case ENXIO:
+               strerr = "Device not configured";
+               break;
+       case E2BIG:
+               strerr = "Argument list too long";
+               break;
+       case ENOEXEC:
+               strerr = "Exec format error";
+               break;
+       case EBADF:
+               strerr = "Bad file descriptor";
+               break;
+       case ECHILD:
+               strerr = "No child processes";
+               break;
+       case EDEADLK:
+               strerr = "Resource deadlock avoided";
+               break;
+       case ENOMEM:
+               strerr = "Cannot allocate memory";
+               break;
+       case EACCES:
+               strerr = "Permission denied";
+               break;
+       case EFAULT:
+               strerr = "Bad address";
+               break;
+       case ENOTBLK:
+               strerr = "Block device required";
+               break;
+       case EBUSY:
+               strerr = "Device busy";
+               break;
+       case EEXIST:
+               strerr = "File exists";
+               break;
+       case EXDEV:
+               strerr = "Cross-device link";
+               break;
+       case ENODEV:
+               strerr = "Operation not supported by device";
+               break;
+       case ENOTDIR:
+               strerr = "Not a directory";
+               break;
+       case EISDIR:
+               strerr = "Is a directory";
+               break;
+       case EINVAL:
+               strerr = "Invalid argument";
+               break;
+       case ENFILE:
+               strerr = "Too many open files in system";
+               break;
+       case EMFILE:
+               strerr = "Too many open files";
+               break;
+       case ENOTTY:
+               strerr = "Inappropriate ioctl for device";
+               break;
+       case ETXTBSY:
+               strerr = "Text file busy";
+               break;
+       case EFBIG:
+               strerr = "File too large";
+               break;
+       case ENOSPC:
+               strerr = "No space left on device";
+               break;
+       case ESPIPE:
+               strerr = "Illegal seek";
+               break;
+       case EROFS:
+               strerr = "Read-only file system";
+               break;
+       case EMLINK:
+               strerr = "Too many links";
+               break;
+       case EPIPE:
+               strerr = "Broken pipe";
+               break;
+       case EDOM:
+               strerr = "Numerical argument out of domain";
+               break;
+       case ERANGE:
+               strerr = "Result too large";
+               break;
+       case EAGAIN:
+               strerr = "Resource temporarily unavailable";
+               break;
+       case EINPROGRESS:
+               strerr = "Operation now in progress";
+               break;
+       case EALREADY:
+               strerr = "Operation already in progress";
+               break;
+       case ENOTSOCK:
+               strerr = "Socket operation on non-socket";
+               break;
+       case EDESTADDRREQ:
+               strerr = "Destination address required";
+               break;
+       case EMSGSIZE:
+               strerr = "Message too long";
+               break;
+       case EPROTOTYPE:
+               strerr = "Protocol wrong type for socket";
+               break;
+       case ENOPROTOOPT:
+               strerr = "Protocol not available";
+               break;
+       case EPROTONOSUPPORT:
+               strerr = "Protocol not supported";
+               break;
+       case ESOCKTNOSUPPORT:
+               strerr = "Socket type not supported";
+               break;
+       case EOPNOTSUPP:
+               strerr = "Operation not supported on socket";
+               break;
+       case EPFNOSUPPORT:
+               strerr = "Protocol family not supported";
+               break;
+       case EAFNOSUPPORT:
+               strerr = "Address family not supported by protocol family";
+               break;
+       case EADDRINUSE:
+               strerr = "Address already in use";
+               break;
+       case EADDRNOTAVAIL:
+               strerr = "Can't assign requested address";
+               break;
+       case ENETDOWN:
+               strerr = "Network is down";
+               break;
+       case ENETUNREACH:
+               strerr = "Network is unreachable";
+               break;
+       case ENETRESET:
+               strerr = "Network dropped connection on reset";
+               break;
+       case ECONNABORTED:
+               strerr = "Software caused connection abort";
+               break;
+       case ECONNRESET:
+               strerr = "Connection reset by peer";
+               break;
+       case ENOBUFS:
+               strerr = "No buffer space available";
+               break;
+       case EISCONN:
+               strerr = "Socket is already connected";
+               break;
+       case ENOTCONN:
+               strerr = "Socket is not connected";
+               break;
+       case ESHUTDOWN:
+               strerr = "Can't send after socket shutdown";
+               break;
+       case ETIMEDOUT:
+               strerr = "Operation timed out";
+               break;
+       case ECONNREFUSED:
+               strerr = "Connection refused";
+               break;
+       case ELOOP:
+               strerr = "Too many levels of symbolic links";
+               break;
+       case ENAMETOOLONG:
+               strerr = "File name too long";
+               break;
+       case EHOSTDOWN:
+               strerr = "Host is down";
+               break;
+       case EHOSTUNREACH:
+               strerr = "No route to host";
+               break;
+       case ENOTEMPTY:
+               strerr = "Directory not empty";
+               break;
+       case EPROCLIM:
+               strerr = "Too many processes";
+               break;
+       case EUSERS:
+               strerr = "Too many users";
+               break;
+       case EDQUOT:
+               strerr = "Disc quota exceeded";
+               break;
+       case ESTALE:
+               strerr = "Stale NFS file handle";
+               break;
+       case EBADRPC:
+               strerr = "RPC struct is bad";
+               break;
+       case ERPCMISMATCH:
+               strerr = "RPC version wrong";
+               break;
+       case EPROGUNAVAIL:
+               strerr = "RPC prog";
+               break;
+       case EPROGMISMATCH:
+               strerr = "Program version wrong";
+               break;
+       case EPROCUNAVAIL:
+               strerr = "Bad procedure for program";
+               break;
+       case ENOLCK:
+               strerr = "No locks available";
+               break;
+       case ENOSYS:
+               strerr = "Function not implemented";
+               break;
+       case EFTYPE:
+               strerr = "Inappropriate file type or format";
+               break;
+       case EAUTH:
+               strerr = "Authentication error";
+               break;
+       case ENEEDAUTH:
+               strerr = "Need authenticator";
+               break;
+       case EIDRM:
+               strerr = "Identifier removed";
+               break;
+       case ENOMSG:
+               strerr = "No message of desired type";
+               break;
+       case EOVERFLOW:
+               strerr = "Value too large to be stored in data type";
+               break;
+       case ECANCELED:
+               strerr = "Operation canceled";
+               break;
+       case EILSEQ:
+               strerr = "Illegal byte sequence";
+               break;
+       case ENOATTR:
+               strerr = "Attribute not found";
+               break;
+       case EDOOFUS:
+               strerr = "Programming error";
+               break;
+       case EBADMSG:
+               strerr = "Bad message";
+               break;
+       case EMULTIHOP:
+               strerr = "Multihop attempted";
+               break;
+       case ENOLINK:
+               strerr = "Link has been severed";
+               break;
+       case EPROTO:
+               strerr = "Protocol error";
+               break;
+       case ENOTCAPABLE:
+               strerr = "Capabilities insufficient";
+               break;
+       case ECAPMODE:
+               strerr = "Not permitted in capability mode";
+               break;
+       case ENOTRECOVERABLE:
+               strerr = "State not recoverable";
+               break;
+       case EOWNERDEAD:
+               strerr = "Previous owner died";
+               break;
+       case ENOTSUP:
+               strerr = "Not supported";
+               break;
+       default:
+               strerr = NULL;
+               errno = EINVAL; /* Unknown errnum requires errno to be set */
+               break;
+       }
+
+       if (!buflen)
+               return buf;
+
+       /*
+        * NOTE: If target buffer is too small, we are supposed to set
+        *       errno to ERANGE. We ignore this case for simplification.
+        */
+       if (strerr)
+               strncpy_isr(buf, strerr, buflen);
+       else
+               snprintf_isr(buf, buflen, "Unknown error %d", errnum);
+
+       /* ensure null termination */
+       buf[buflen - 1] = '\0';
+       return buf;
+}
+
+/* NOTE: strerror() is not thread-safe, nor reentrant-safe */
+char *strerror_isr(int errnum)
+{
+       /* NOTE: Our longest message is currently 48 characters. With
+        *       64 characters we should have room for minor changes
+        *       in the future.
+        */
+       static char buf[64];
+
+       return strerror_isr_r(errnum, buf, sizeof(buf));
+}




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.