summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorattilio <attilio@FreeBSD.org>2013-02-28 00:24:13 +0000
committerattilio <attilio@FreeBSD.org>2013-02-28 00:24:13 +0000
commitbcc31f462ba76b89e3789cb24393126466e3775d (patch)
tree1d327faf4ac955ce717c24887b97073fcbb8ef2c
parent9c0acdc6918f8dc4e438e9eacfea645d87119a06 (diff)
parent98a0da7498845f515692f5d3913c7369c4740cfe (diff)
downloadFreeBSD-src-bcc31f462ba76b89e3789cb24393126466e3775d.zip
FreeBSD-src-bcc31f462ba76b89e3789cb24393126466e3775d.tar.gz
MFC
-rw-r--r--UPDATING7
-rw-r--r--include/stdio.h1
-rw-r--r--include/wchar.h1
-rw-r--r--lib/libc/stdio/Makefile.inc4
-rw-r--r--lib/libc/stdio/Symbol.map2
-rw-r--r--lib/libc/stdio/open_memstream.3155
-rw-r--r--lib/libc/stdio/open_memstream.c209
-rw-r--r--lib/libc/stdio/open_wmemstream.c271
-rw-r--r--sys/boot/common/load_elf.c2
-rw-r--r--sys/dev/mfi/mfi.c16
-rw-r--r--sys/dev/watchdog/watchdog.c242
-rw-r--r--sys/netinet/sctputil.c3
-rw-r--r--sys/sys/watchdog.h25
-rw-r--r--tools/regression/lib/libc/stdio/Makefile4
-rw-r--r--tools/regression/lib/libc/stdio/test-open_memstream.c203
-rw-r--r--tools/regression/lib/libc/stdio/test-open_memstream.t10
-rw-r--r--tools/regression/lib/libc/stdio/test-open_wmemstream.c203
-rw-r--r--tools/regression/lib/libc/stdio/test-open_wmemstream.t10
-rwxr-xr-xtools/test/iconv/tablegen/cmp.sh2
-rw-r--r--usr.sbin/watchdogd/watchdogd.8108
-rw-r--r--usr.sbin/watchdogd/watchdogd.c333
21 files changed, 1763 insertions, 48 deletions
diff --git a/UPDATING b/UPDATING
index 20e375c..8be2f46 100644
--- a/UPDATING
+++ b/UPDATING
@@ -26,6 +26,13 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 10.x IS SLOW:
disable the most expensive debugging functionality run
"ln -s 'abort:false,junk:false' /etc/malloc.conf".)
+20130208:
+ A new compression method (lz4) has been merged to -HEAD. Please
+ refer to zpool-features(7) for more information.
+
+ Please refer to the "ZFS notes" section of this file for information
+ on upgrading boot ZFS pools.
+
20130129:
A BSD-licensed patch(1) variant has been added and is installed
as bsdpatch, being the GNU version the default patch.
diff --git a/include/stdio.h b/include/stdio.h
index 4fc78b8..19b1e20 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -346,6 +346,7 @@ char *tempnam(const char *, const char *);
FILE *fmemopen(void * __restrict, size_t, const char * __restrict);
ssize_t getdelim(char ** __restrict, size_t * __restrict, int,
FILE * __restrict);
+FILE *open_memstream(char **, size_t *);
int renameat(int, const char *, int, const char *);
int vdprintf(int, const char * __restrict, __va_list);
diff --git a/include/wchar.h b/include/wchar.h
index 9e8d807..4a3fe53 100644
--- a/include/wchar.h
+++ b/include/wchar.h
@@ -207,6 +207,7 @@ int wcwidth(wchar_t);
#if __POSIX_VISIBLE >= 200809 || __BSD_VISIBLE
size_t mbsnrtowcs(wchar_t * __restrict, const char ** __restrict, size_t,
size_t, mbstate_t * __restrict);
+FILE *open_wmemstream(wchar_t **, size_t *);
wchar_t *wcpcpy(wchar_t * __restrict, const wchar_t * __restrict);
wchar_t *wcpncpy(wchar_t * __restrict, const wchar_t * __restrict, size_t);
wchar_t *wcsdup(const wchar_t *) __malloc_like;
diff --git a/lib/libc/stdio/Makefile.inc b/lib/libc/stdio/Makefile.inc
index bc3b9e5..0062d3d 100644
--- a/lib/libc/stdio/Makefile.inc
+++ b/lib/libc/stdio/Makefile.inc
@@ -14,6 +14,7 @@ SRCS+= _flock_stub.c asprintf.c clrerr.c dprintf.c \
ftell.c funopen.c fvwrite.c fwalk.c fwide.c fwprintf.c fwscanf.c \
fwrite.c getc.c getchar.c getdelim.c getline.c \
gets.c getw.c getwc.c getwchar.c makebuf.c mktemp.c \
+ open_memstream.c open_wmemstream.c \
perror.c printf.c printf-pos.c putc.c putchar.c \
puts.c putw.c putwc.c putwchar.c \
refill.c remove.c rewind.c rget.c scanf.c setbuf.c setbuffer.c \
@@ -36,7 +37,7 @@ MAN+= fclose.3 ferror.3 fflush.3 fgetln.3 fgets.3 fgetwln.3 fgetws.3 \
flockfile.3 \
fopen.3 fputs.3 \
fputws.3 fread.3 fseek.3 funopen.3 fwide.3 getc.3 \
- getline.3 getwc.3 mktemp.3 \
+ getline.3 getwc.3 mktemp.3 open_memstream.3 \
printf.3 printf_l.3 putc.3 putwc.3 remove.3 scanf.3 scanf_l.3 setbuf.3 \
stdio.3 tmpnam.3 \
ungetc.3 ungetwc.3 wprintf.3 wscanf.3
@@ -60,6 +61,7 @@ MLINKS+=getc.3 fgetc.3 getc.3 getc_unlocked.3 getc.3 getchar.3 \
MLINKS+=getline.3 getdelim.3
MLINKS+=getwc.3 fgetwc.3 getwc.3 getwchar.3
MLINKS+=mktemp.3 mkdtemp.3 mktemp.3 mkstemp.3 mktemp.3 mkstemps.3
+MLINKS+=open_memstream.3 open_wmemstream.3
MLINKS+=printf.3 asprintf.3 printf.3 dprintf.3 printf.3 fprintf.3 \
printf.3 snprintf.3 printf.3 sprintf.3 \
printf.3 vasprintf.3 printf.3 vdprintf.3 \
diff --git a/lib/libc/stdio/Symbol.map b/lib/libc/stdio/Symbol.map
index b78de5a..538b29a 100644
--- a/lib/libc/stdio/Symbol.map
+++ b/lib/libc/stdio/Symbol.map
@@ -156,6 +156,8 @@ FBSD_1.3 {
putwc_l;
putwchar_l;
fmemopen;
+ open_memstream;
+ open_wmemstream;
};
FBSDprivate_1.0 {
diff --git a/lib/libc/stdio/open_memstream.3 b/lib/libc/stdio/open_memstream.3
new file mode 100644
index 0000000..9396830
--- /dev/null
+++ b/lib/libc/stdio/open_memstream.3
@@ -0,0 +1,155 @@
+.\" Copyright (c) 2013 Advanced Computing Technologies LLC
+.\" Written by: John H. Baldwin <jhb@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 27, 2013
+.Dt OPEN_MEMSTREAM 3
+.Os
+.Sh NAME
+.Nm open_memstream ,
+.Nm open_wmemstream
+.Nd dynamic memory buffer stream open functions
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In stdio.h
+.Ft FILE *
+.Fn open_memstream "char **bufp" "size_t **sizep"
+.In wchar.h
+.Ft FILE *
+.Fn open_wmemstream "wchar_t **bufp" "size_t **sizep"
+.Sh DESCRIPTION
+The
+.Fn open_memstream
+and
+.Fn open_wmemstream
+functions create a write-only, seekable stream backed by a dynamically
+allocated memory buffer.
+The
+.Fn open_memstream
+function creates a byte-oriented stream,
+while the
+.Fn open_wmemstream
+function creates a wide-oriented stream.
+.Pp
+Each stream maintains a current position and size.
+Initially,
+the position and size are set to zero.
+Each write begins at the current position and advances it the number of
+successfully written bytes for
+.Fn open_memstream
+or wide characters for
+.Fn open_wmemstream .
+If a write moves the current position beyond the length of the buffer,
+the length of the buffer is extended and a null character is appended to the
+buffer.
+.Pp
+A stream's buffer always contains a null character at the end of the buffer
+that is not included in the current length.
+.Pp
+If a stream's current position is moved beyond the current length via a
+seek operation and a write is performed,
+the characters between the current length and the current position are filled
+with null characters before the write is performed.
+.Pp
+After a successful call to
+.Xr fclose 3
+or
+.Xr fflush 3 ,
+the pointer referenced by
+.Fa bufp
+will contain the start of the memory buffer and the variable referenced by
+.Fa sizep
+will contain the smaller of the current position and the current buffer length.
+.Pp
+After a successful call to
+.Xr fflush 3,
+the pointer referenced by
+.Fa bufp
+and the variable referenced by
+.Fa sizep
+are only valid until the next write operation or a call to
+.Xr fclose 3.
+.Pp
+Once a stream is closed,
+the allocated buffer referenced by
+.Fa bufp
+should be released via a call to
+.Xr free 3
+when it is no longer needed.
+.Sh IMPLEMENTATION NOTES
+Internally all I/O streams are effectively byte-oriented,
+so using wide-oriented operations to write to a stream opened via
+.Fn open_wmemstream
+results in wide characters being expanded to a stream of multibyte characters
+in stdio's internal buffers.
+These multibyte characters are then converted back to wide characters when
+written into the stream.
+As a result,
+the wide-oriented streams maintain an internal multibyte character conversion
+state that is cleared on any seek opertion that changes the current position.
+This should have no effect as long as wide-oriented output operations are used
+on a wide-oriented stream.
+.Sh RETURN VALUES
+Upon successful completion,
+.Fn open_memstream
+and
+.Fn open_wmemstream
+return a
+.Tn FILE
+pointer.
+Otherwise,
+.Dv NULL
+is returned and the global variable
+.Va errno
+is set to indicate the error.
+.Sh ERRORS
+.Bl -tag -width Er
+.It Bq Er EINVAL
+The
+.Fa bufp
+or
+.Fa sizep
+argument was
+.Dv NULL .
+.It Bq Er ENOMEM
+Memory for the stream or buffer could not be allocated.
+.El
+.Sh SEE ALSO
+.Xr fclose 3 ,
+.Xr fflush 3 ,
+.Xr fopen 3 ,
+.Xr free 3 ,
+.Xr fseek 3 ,
+.Xr sbuf 3 ,
+.Xr stdio 3
+.Sh STANDARDS
+The
+.Fn open_memstream
+and
+.Fn open_wmemstream
+functions conform to
+.St -p1003.1-2008 .
diff --git a/lib/libc/stdio/open_memstream.c b/lib/libc/stdio/open_memstream.c
new file mode 100644
index 0000000..aa50822
--- /dev/null
+++ b/lib/libc/stdio/open_memstream.c
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 2013 Advanced Computing Technologies LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "namespace.h"
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include "un-namespace.h"
+
+/* XXX: There is no FPOS_MAX. This assumes fpos_t is an off_t. */
+#define FPOS_MAX OFF_MAX
+
+struct memstream {
+ char **bufp;
+ size_t *sizep;
+ ssize_t len;
+ fpos_t offset;
+};
+
+static int
+memstream_grow(struct memstream *ms, fpos_t newoff)
+{
+ char *buf;
+ ssize_t newsize;
+
+ if (newoff < 0 || newoff >= SSIZE_MAX)
+ newsize = SSIZE_MAX - 1;
+ else
+ newsize = newoff;
+ if (newsize > ms->len) {
+ buf = realloc(*ms->bufp, newsize + 1);
+ if (buf != NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "MS: %p growing from %zd to %zd\n",
+ ms, ms->len, newsize);
+#endif
+ memset(buf + ms->len + 1, 0, newsize - ms->len);
+ *ms->bufp = buf;
+ ms->len = newsize;
+ return (1);
+ }
+ return (0);
+ }
+ return (1);
+}
+
+static void
+memstream_update(struct memstream *ms)
+{
+
+ assert(ms->len >= 0 && ms->offset >= 0);
+ *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset;
+}
+
+static int
+memstream_write(void *cookie, const char *buf, int len)
+{
+ struct memstream *ms;
+ ssize_t tocopy;
+
+ ms = cookie;
+ if (!memstream_grow(ms, ms->offset + len))
+ return (-1);
+ tocopy = ms->len - ms->offset;
+ if (len < tocopy)
+ tocopy = len;
+ memcpy(*ms->bufp + ms->offset, buf, tocopy);
+ ms->offset += tocopy;
+ memstream_update(ms);
+#ifdef DEBUG
+ fprintf(stderr, "MS: write(%p, %d) = %zd\n", ms, len, tocopy);
+#endif
+ return (tocopy);
+}
+
+static fpos_t
+memstream_seek(void *cookie, fpos_t pos, int whence)
+{
+ struct memstream *ms;
+#ifdef DEBUG
+ fpos_t old;
+#endif
+
+ ms = cookie;
+#ifdef DEBUG
+ old = ms->offset;
+#endif
+ switch (whence) {
+ case SEEK_SET:
+ /* _fseeko() checks for negative offsets. */
+ assert(pos >= 0);
+ ms->offset = pos;
+ break;
+ case SEEK_CUR:
+ /* This is only called by _ftello(). */
+ assert(pos == 0);
+ break;
+ case SEEK_END:
+ if (pos < 0) {
+ if (pos + ms->len < 0) {
+#ifdef DEBUG
+ fprintf(stderr,
+ "MS: bad SEEK_END: pos %jd, len %zd\n",
+ (intmax_t)pos, ms->len);
+#endif
+ errno = EINVAL;
+ return (-1);
+ }
+ } else {
+ if (FPOS_MAX - ms->len < pos) {
+#ifdef DEBUG
+ fprintf(stderr,
+ "MS: bad SEEK_END: pos %jd, len %zd\n",
+ (intmax_t)pos, ms->len);
+#endif
+ errno = EOVERFLOW;
+ return (-1);
+ }
+ }
+ ms->offset = ms->len + pos;
+ break;
+ }
+ memstream_update(ms);
+#ifdef DEBUG
+ fprintf(stderr, "MS: seek(%p, %jd, %d) %jd -> %jd\n", ms, (intmax_t)pos,
+ whence, (intmax_t)old, (intmax_t)ms->offset);
+#endif
+ return (ms->offset);
+}
+
+static int
+memstream_close(void *cookie)
+{
+
+ free(cookie);
+ return (0);
+}
+
+FILE *
+open_memstream(char **bufp, size_t *sizep)
+{
+ struct memstream *ms;
+ int save_errno;
+ FILE *fp;
+
+ if (bufp == NULL || sizep == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ *bufp = calloc(1, 1);
+ if (*bufp == NULL)
+ return (NULL);
+ ms = malloc(sizeof(*ms));
+ if (ms == NULL) {
+ save_errno = errno;
+ free(*bufp);
+ *bufp = NULL;
+ errno = save_errno;
+ return (NULL);
+ }
+ ms->bufp = bufp;
+ ms->sizep = sizep;
+ ms->len = 0;
+ ms->offset = 0;
+ memstream_update(ms);
+ fp = funopen(ms, NULL, memstream_write, memstream_seek,
+ memstream_close);
+ if (fp == NULL) {
+ save_errno = errno;
+ free(ms);
+ free(*bufp);
+ *bufp = NULL;
+ errno = save_errno;
+ return (NULL);
+ }
+ fwide(fp, -1);
+ return (fp);
+}
diff --git a/lib/libc/stdio/open_wmemstream.c b/lib/libc/stdio/open_wmemstream.c
new file mode 100644
index 0000000..cf2968a
--- /dev/null
+++ b/lib/libc/stdio/open_wmemstream.c
@@ -0,0 +1,271 @@
+/*-
+ * Copyright (c) 2013 Advanced Computing Technologies LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "namespace.h"
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include "un-namespace.h"
+
+/* XXX: There is no FPOS_MAX. This assumes fpos_t is an off_t. */
+#define FPOS_MAX OFF_MAX
+
+struct wmemstream {
+ wchar_t **bufp;
+ size_t *sizep;
+ ssize_t len;
+ fpos_t offset;
+ mbstate_t mbstate;
+};
+
+static int
+wmemstream_grow(struct wmemstream *ms, fpos_t newoff)
+{
+ wchar_t *buf;
+ ssize_t newsize;
+
+ if (newoff < 0 || newoff >= SSIZE_MAX / sizeof(wchar_t))
+ newsize = SSIZE_MAX / sizeof(wchar_t) - 1;
+ else
+ newsize = newoff;
+ if (newsize > ms->len) {
+ buf = realloc(*ms->bufp, (newsize + 1) * sizeof(wchar_t));
+ if (buf != NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "WMS: %p growing from %zd to %zd\n",
+ ms, ms->len, newsize);
+#endif
+ wmemset(buf + ms->len + 1, 0, newsize - ms->len);
+ *ms->bufp = buf;
+ ms->len = newsize;
+ return (1);
+ }
+ return (0);
+ }
+ return (1);
+}
+
+static void
+wmemstream_update(struct wmemstream *ms)
+{
+
+ assert(ms->len >= 0 && ms->offset >= 0);
+ *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset;
+}
+
+/*
+ * Based on a starting multibyte state and an input buffer, determine
+ * how many wchar_t's would be output. This doesn't use mbsnrtowcs()
+ * so that it can handle embedded null characters.
+ */
+static size_t
+wbuflen(const mbstate_t *state, const char *buf, int len)
+{
+ mbstate_t lenstate;
+ size_t charlen, count;
+
+ count = 0;
+ lenstate = *state;
+ while (len > 0) {
+ charlen = mbrlen(buf, len, &lenstate);
+ if (charlen == (size_t)-1)
+ return (-1);
+ if (charlen == (size_t)-2)
+ break;
+ if (charlen == 0)
+ /* XXX: Not sure how else to handle this. */
+ charlen = 1;
+ len -= charlen;
+ buf += charlen;
+ count++;
+ }
+ return (count);
+}
+
+static int
+wmemstream_write(void *cookie, const char *buf, int len)
+{
+ struct wmemstream *ms;
+ ssize_t consumed, wlen;
+ size_t charlen;
+
+ ms = cookie;
+ wlen = wbuflen(&ms->mbstate, buf, len);
+ if (wlen < 0) {
+ errno = EILSEQ;
+ return (-1);
+ }
+ if (!wmemstream_grow(ms, ms->offset + wlen))
+ return (-1);
+
+ /*
+ * This copies characters one at a time rather than using
+ * mbsnrtowcs() so it can properly handle embedded null
+ * characters.
+ */
+ consumed = 0;
+ while (len > 0 && ms->offset < ms->len) {
+ charlen = mbrtowc(*ms->bufp + ms->offset, buf, len,
+ &ms->mbstate);
+ if (charlen == (size_t)-1) {
+ if (consumed == 0) {
+ errno = EILSEQ;
+ return (-1);
+ }
+ /* Treat it as a successful short write. */
+ break;
+ }
+ if (charlen == 0)
+ /* XXX: Not sure how else to handle this. */
+ charlen = 1;
+ if (charlen == (size_t)-2) {
+ consumed += len;
+ len = 0;
+ } else {
+ consumed += charlen;
+ buf += charlen;
+ len -= charlen;
+ ms->offset++;
+ }
+ }
+ wmemstream_update(ms);
+#ifdef DEBUG
+ fprintf(stderr, "WMS: write(%p, %d) = %zd\n", ms, len, consumed);
+#endif
+ return (consumed);
+}
+
+static fpos_t
+wmemstream_seek(void *cookie, fpos_t pos, int whence)
+{
+ struct wmemstream *ms;
+ fpos_t old;
+
+ ms = cookie;
+ old = ms->offset;
+ switch (whence) {
+ case SEEK_SET:
+ /* _fseeko() checks for negative offsets. */
+ assert(pos >= 0);
+ ms->offset = pos;
+ break;
+ case SEEK_CUR:
+ /* This is only called by _ftello(). */
+ assert(pos == 0);
+ break;
+ case SEEK_END:
+ if (pos < 0) {
+ if (pos + ms->len < 0) {
+#ifdef DEBUG
+ fprintf(stderr,
+ "WMS: bad SEEK_END: pos %jd, len %zd\n",
+ (intmax_t)pos, ms->len);
+#endif
+ errno = EINVAL;
+ return (-1);
+ }
+ } else {
+ if (FPOS_MAX - ms->len < pos) {
+#ifdef DEBUG
+ fprintf(stderr,
+ "WMS: bad SEEK_END: pos %jd, len %zd\n",
+ (intmax_t)pos, ms->len);
+#endif
+ errno = EOVERFLOW;
+ return (-1);
+ }
+ }
+ ms->offset = ms->len + pos;
+ break;
+ }
+ /* Reset the multibyte state if a seek changes the position. */
+ if (ms->offset != old)
+ memset(&ms->mbstate, 0, sizeof(ms->mbstate));
+ wmemstream_update(ms);
+#ifdef DEBUG
+ fprintf(stderr, "WMS: seek(%p, %jd, %d) %jd -> %jd\n", ms,
+ (intmax_t)pos, whence, (intmax_t)old, (intmax_t)ms->offset);
+#endif
+ return (ms->offset);
+}
+
+static int
+wmemstream_close(void *cookie)
+{
+
+ free(cookie);
+ return (0);
+}
+
+FILE *
+open_wmemstream(wchar_t **bufp, size_t *sizep)
+{
+ struct wmemstream *ms;
+ int save_errno;
+ FILE *fp;
+
+ if (bufp == NULL || sizep == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ *bufp = calloc(1, sizeof(wchar_t));
+ if (*bufp == NULL)
+ return (NULL);
+ ms = malloc(sizeof(*ms));
+ if (ms == NULL) {
+ save_errno = errno;
+ free(*bufp);
+ *bufp = NULL;
+ errno = save_errno;
+ return (NULL);
+ }
+ ms->bufp = bufp;
+ ms->sizep = sizep;
+ ms->len = 0;
+ ms->offset = 0;
+ memset(&ms->mbstate, 0, sizeof(mbstate_t));
+ wmemstream_update(ms);
+ fp = funopen(ms, NULL, wmemstream_write, wmemstream_seek,
+ wmemstream_close);
+ if (fp == NULL) {
+ save_errno = errno;
+ free(ms);
+ free(*bufp);
+ *bufp = NULL;
+ errno = save_errno;
+ return (NULL);
+ }
+ fwide(fp, 1);
+ return (fp);
+}
diff --git a/sys/boot/common/load_elf.c b/sys/boot/common/load_elf.c
index d712b3d..3a4152e 100644
--- a/sys/boot/common/load_elf.c
+++ b/sys/boot/common/load_elf.c
@@ -304,7 +304,7 @@ __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off)
* only adjust the entry point if it's a virtual address to begin with.
*/
off = -0xc0000000u;
- if ((ehdr->e_entry & 0xc0000000u) == 0xc000000u)
+ if ((ehdr->e_entry & 0xc0000000u) == 0xc0000000u)
ehdr->e_entry += off;
#ifdef ELF_VERBOSE
printf("ehdr->e_entry 0x%08x, va<->pa off %llx\n", ehdr->e_entry, off);
diff --git a/sys/dev/mfi/mfi.c b/sys/dev/mfi/mfi.c
index e799b9d..e2401f6 100644
--- a/sys/dev/mfi/mfi.c
+++ b/sys/dev/mfi/mfi.c
@@ -157,6 +157,11 @@ SYSCTL_INT(_hw_mfi, OID_AUTO, polled_cmd_timeout, CTLFLAG_RWTUN,
&mfi_polled_cmd_timeout, 0,
"Polled command timeout - used for firmware flash etc (in seconds)");
+static int mfi_cmd_timeout = MFI_CMD_TIMEOUT;
+TUNABLE_INT("hw.mfi.cmd_timeout", &mfi_cmd_timeout);
+SYSCTL_INT(_hw_mfi, OID_AUTO, cmd_timeout, CTLFLAG_RWTUN, &mfi_cmd_timeout,
+ 0, "Command timeout (in seconds)");
+
/* Management interface */
static d_open_t mfi_open;
static d_close_t mfi_close;
@@ -782,7 +787,7 @@ mfi_attach(struct mfi_softc *sc)
/* Start the timeout watchdog */
callout_init(&sc->mfi_watchdog_callout, CALLOUT_MPSAFE);
- callout_reset(&sc->mfi_watchdog_callout, MFI_CMD_TIMEOUT * hz,
+ callout_reset(&sc->mfi_watchdog_callout, mfi_cmd_timeout * hz,
mfi_timeout, sc);
if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
@@ -3714,7 +3719,7 @@ mfi_dump_all(void)
break;
device_printf(sc->mfi_dev, "Dumping\n\n");
timedout = 0;
- deadline = time_uptime - MFI_CMD_TIMEOUT;
+ deadline = time_uptime - mfi_cmd_timeout;
mtx_lock(&sc->mfi_io_lock);
TAILQ_FOREACH(cm, &sc->mfi_busy, cm_link) {
if (cm->cm_timestamp <= deadline) {
@@ -3745,10 +3750,11 @@ mfi_timeout(void *data)
time_t deadline;
int timedout = 0;
- deadline = time_uptime - MFI_CMD_TIMEOUT;
+ deadline = time_uptime - mfi_cmd_timeout;
if (sc->adpreset == 0) {
if (!mfi_tbolt_reset(sc)) {
- callout_reset(&sc->mfi_watchdog_callout, MFI_CMD_TIMEOUT * hz, mfi_timeout, sc);
+ callout_reset(&sc->mfi_watchdog_callout,
+ mfi_cmd_timeout * hz, mfi_timeout, sc);
return;
}
}
@@ -3785,7 +3791,7 @@ mfi_timeout(void *data)
mtx_unlock(&sc->mfi_io_lock);
- callout_reset(&sc->mfi_watchdog_callout, MFI_CMD_TIMEOUT * hz,
+ callout_reset(&sc->mfi_watchdog_callout, mfi_cmd_timeout * hz,
mfi_timeout, sc);
if (0)
diff --git a/sys/dev/watchdog/watchdog.c b/sys/dev/watchdog/watchdog.c
index e7edf31..71d8ecd 100644
--- a/sys/dev/watchdog/watchdog.c
+++ b/sys/dev/watchdog/watchdog.c
@@ -1,5 +1,8 @@
/*-
* Copyright (c) 2004 Poul-Henning Kamp
+ * Copyright (c) 2013 iXsystems.com,
+ * author: Alfred Perlstein <alfred@freebsd.org>
+ *
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,21 +32,40 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#include <sys/types.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
+#include <sys/syslog.h>
#include <sys/watchdog.h>
#include <sys/bus.h>
#include <machine/bus.h>
+#include <sys/syscallsubr.h> /* kern_clock_gettime() */
+
+static int wd_set_pretimeout(int newtimeout, int disableiftoolong);
+static void wd_timeout_cb(void *arg);
+
+static struct callout wd_pretimeo_handle;
+static int wd_pretimeout;
+static int wd_pretimeout_act = WD_SOFT_LOG;
+
+static struct callout wd_softtimeo_handle;
+static int wd_softtimer; /* true = use softtimer instead of hardware
+ watchdog */
+static int wd_softtimeout_act = WD_SOFT_LOG; /* action for the software timeout */
+
static struct cdev *wd_dev;
-static volatile u_int wd_last_u;
+static volatile u_int wd_last_u; /* last timeout value set by kern_do_pat */
-static int
-kern_do_pat(u_int utim)
+static int wd_lastpat_valid = 0;
+static time_t wd_lastpat = 0; /* when the watchdog was last patted */
+
+int
+wdog_kern_pat(u_int utim)
{
int error;
@@ -51,11 +73,20 @@ kern_do_pat(u_int utim)
return (EINVAL);
if ((utim & WD_LASTVAL) != 0) {
+ /*
+ * if WD_LASTVAL is set, fill in the bits for timeout
+ * from the saved value in wd_last_u.
+ */
MPASS((wd_last_u & ~WD_INTERVAL) == 0);
utim &= ~WD_LASTVAL;
utim |= wd_last_u;
- } else
+ } else {
+ /*
+ * Otherwise save the new interval.
+ * This can be zero (to disable the watchdog)
+ */
wd_last_u = (utim & WD_INTERVAL);
+ }
if ((utim & WD_INTERVAL) == WD_TO_NEVER) {
utim = 0;
@@ -65,18 +96,49 @@ kern_do_pat(u_int utim)
/* Assume no watchdog available; watchdog flags success */
error = EOPNOTSUPP;
}
- EVENTHANDLER_INVOKE(watchdog_list, utim, &error);
+ if (wd_softtimer) {
+ if (utim == 0) {
+ callout_stop(&wd_softtimeo_handle);
+ } else {
+ (void) callout_reset(&wd_softtimeo_handle,
+ hz*utim, wd_timeout_cb, "soft");
+ }
+ error = 0;
+ } else {
+ EVENTHANDLER_INVOKE(watchdog_list, utim, &error);
+ }
+ wd_set_pretimeout(wd_pretimeout, true);
+ /*
+ * If we were able to arm/strobe the watchdog, then
+ * update the last time it was strobed for WDIOC_GETTIMELEFT
+ */
+ if (!error) {
+ struct timespec ts;
+
+ error = kern_clock_gettime(curthread /* XXX */,
+ CLOCK_MONOTONIC_FAST, &ts);
+ if (!error) {
+ wd_lastpat = ts.tv_sec;
+ wd_lastpat_valid = 1;
+ }
+ }
return (error);
}
static int
-wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
- int flags __unused, struct thread *td)
+wd_valid_act(int act)
+{
+
+ if ((act & ~(WD_SOFT_MASK)) != 0)
+ return false;
+ return true;
+}
+
+static int
+wd_ioctl_patpat(caddr_t data)
{
u_int u;
- if (cmd != WDIOCPATPAT)
- return (ENOIOCTL);
u = *(u_int *)data;
if (u & ~(WD_ACTIVE | WD_PASSIVE | WD_LASTVAL | WD_INTERVAL))
return (EINVAL);
@@ -89,24 +151,162 @@ wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
return (ENOSYS); /* XXX Not implemented yet */
u &= ~(WD_ACTIVE | WD_PASSIVE);
- return (kern_do_pat(u));
+ return (wdog_kern_pat(u));
}
-u_int
-wdog_kern_last_timeout(void)
+static int
+wd_get_time_left(struct thread *td, time_t *remainp)
{
+ struct timespec ts;
+ int error;
- return (wd_last_u);
+ error = kern_clock_gettime(td, CLOCK_MONOTONIC_FAST, &ts);
+ if (error)
+ return (error);
+ if (!wd_lastpat_valid)
+ return (ENOENT);
+ *remainp = ts.tv_sec - wd_lastpat;
+ return (0);
}
-int
-wdog_kern_pat(u_int utim)
+static void
+wd_timeout_cb(void *arg)
{
+ const char *type = arg;
- if (utim & ~(WD_LASTVAL | WD_INTERVAL))
- return (EINVAL);
+#ifdef DDB
+ if ((wd_pretimeout_act & WD_SOFT_DDB)) {
+ char kdb_why[80];
+ snprintf(kdb_why, sizeof(buf), "watchdog %s timeout", type);
+ kdb_backtrace();
+ kdb_enter(KDB_WHY_WATCHDOG, kdb_why);
+ }
+#endif
+ if ((wd_pretimeout_act & WD_SOFT_LOG))
+ log(LOG_EMERG, "watchdog %s-timeout, WD_SOFT_LOG", type);
+ if ((wd_pretimeout_act & WD_SOFT_PRINTF))
+ printf("watchdog %s-timeout, WD_SOFT_PRINTF\n", type);
+ if ((wd_pretimeout_act & WD_SOFT_PANIC))
+ panic("watchdog %s-timeout, WD_SOFT_PANIC set", type);
+}
+
+/*
+ * Called to manage timeouts.
+ * newtimeout needs to be in the range of 0 to actual watchdog timeout.
+ * if 0, we disable the pre-timeout.
+ * otherwise we set the pre-timeout provided it's not greater than the
+ * current actual watchdog timeout.
+ */
+static int
+wd_set_pretimeout(int newtimeout, int disableiftoolong)
+{
+ u_int utime;
+
+ utime = wdog_kern_last_timeout();
+ /* do not permit a pre-timeout >= than the timeout. */
+ if (newtimeout >= utime) {
+ /*
+ * If 'disableiftoolong' then just fall through
+ * so as to disable the pre-watchdog
+ */
+ if (disableiftoolong)
+ newtimeout = 0;
+ else
+ return EINVAL;
+ }
+
+ /* disable the pre-timeout */
+ if (newtimeout == 0) {
+ wd_pretimeout = 0;
+ callout_stop(&wd_pretimeo_handle);
+ return 0;
+ }
+
+ /* We determined the value is sane, so reset the callout */
+ (void) callout_reset(&wd_pretimeo_handle, hz*(utime - newtimeout),
+ wd_timeout_cb, "pre-timeout");
+ wd_pretimeout = newtimeout;
+ return 0;
+}
- return (kern_do_pat(utim));
+static int
+wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
+ int flags __unused, struct thread *td)
+{
+ u_int u;
+ time_t timeleft;
+ int error;
+
+ error = 0;
+
+ switch (cmd) {
+ case WDIOC_SETSOFT:
+ u = *(int *)data;
+ /* do nothing? */
+ if (u == wd_softtimer)
+ break;
+ /* If there is a pending timeout disallow this ioctl */
+ if (wd_last_u != 0) {
+ error = EINVAL;
+ break;
+ }
+ wd_softtimer = u;
+ break;
+ case WDIOC_SETSOFTTIMEOUTACT:
+ u = *(int *)data;
+ if (wd_valid_act(u)) {
+ wd_softtimeout_act = u;
+ } else {
+ error = EINVAL;
+ }
+ break;
+ case WDIOC_SETPRETIMEOUTACT:
+ u = *(int *)data;
+ if (wd_valid_act(u)) {
+ wd_pretimeout_act = u;
+ } else {
+ error = EINVAL;
+ }
+ break;
+ case WDIOC_GETPRETIMEOUT:
+ *(int *)data = (int)wd_pretimeout;
+ break;
+ case WDIOC_SETPRETIMEOUT:
+ error = wd_set_pretimeout(*(int *)data, false);
+ break;
+ case WDIOC_GETTIMELEFT:
+ error = wd_get_time_left(td, &timeleft);
+ if (error)
+ break;
+ *(int *)data = (int)timeleft;
+ break;
+ case WDIOC_SETTIMEOUT:
+ u = *(u_int *)data;
+ error = wdog_kern_pat(u);
+ break;
+ case WDIOC_GETTIMEOUT:
+ u = wdog_kern_last_timeout();
+ *(u_int *)data = u;
+ break;
+ case WDIOCPATPAT:
+ error = wd_ioctl_patpat(data);
+ break;
+ default:
+ error = ENOIOCTL;
+ break;
+ }
+ return (error);
+}
+
+/*
+ * Return the last timeout set, this is NOT the seconds from NOW until timeout,
+ * rather it is the amount of seconds passed to WDIOCPATPAT/WDIOC_SETTIMEOUT.
+ */
+u_int
+wdog_kern_last_timeout(void)
+{
+
+ return (wd_last_u);
}
static struct cdevsw wd_cdevsw = {
@@ -120,10 +320,16 @@ watchdog_modevent(module_t mod __unused, int type, void *data __unused)
{
switch(type) {
case MOD_LOAD:
+ callout_init(&wd_pretimeo_handle, true);
+ callout_init(&wd_softtimeo_handle, true);
wd_dev = make_dev(&wd_cdevsw, 0,
UID_ROOT, GID_WHEEL, 0600, _PATH_WATCHDOG);
return 0;
case MOD_UNLOAD:
+ callout_stop(&wd_pretimeo_handle);
+ callout_stop(&wd_softtimeo_handle);
+ callout_drain(&wd_pretimeo_handle);
+ callout_drain(&wd_softtimeo_handle);
destroy_dev(wd_dev);
return 0;
case MOD_SHUTDOWN:
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index fe2b945..7bd8c7a9 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -2678,6 +2678,7 @@ set_error:
if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) &&
((state == SCTP_COMM_LOST) || (state == SCTP_CANT_STR_ASSOC))) {
+ SOCK_LOCK(stcb->sctp_socket);
if (from_peer) {
if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_COOKIE_WAIT) {
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ECONNREFUSED);
@@ -2709,7 +2710,7 @@ set_error:
if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) &&
((state == SCTP_COMM_LOST) || (state == SCTP_CANT_STR_ASSOC))) {
- socantrcvmore(stcb->sctp_socket);
+ socantrcvmore_locked(stcb->sctp_socket);
}
sorwakeup(stcb->sctp_socket);
sowwakeup(stcb->sctp_socket);
diff --git a/sys/sys/watchdog.h b/sys/sys/watchdog.h
index ba58a7c..92c47de 100644
--- a/sys/sys/watchdog.h
+++ b/sys/sys/watchdog.h
@@ -1,5 +1,8 @@
/*-
* Copyright (c) 2003 Poul-Henning Kamp
+ * Copyright (c) 2013 iXsystems.com,
+ * author: Alfred Perlstein <alfred@freebsd.org>
+ *
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,7 +35,18 @@
#define _PATH_WATCHDOG "fido"
-#define WDIOCPATPAT _IOW('W', 42, u_int)
+#define WDIOCPATPAT _IOW('W', 42, u_int) /* pat the watchdog */
+#define WDIOC_SETTIMEOUT _IOW('W', 43, int) /* set/reset the timer */
+#define WDIOC_GETTIMEOUT _IOR('W', 44, int) /* get total timeout */
+#define WDIOC_GETTIMELEFT _IOR('W', 45, int) /* get time left */
+#define WDIOC_GETPRETIMEOUT _IOR('W', 46, int) /* get the pre-timeout */
+#define WDIOC_SETPRETIMEOUT _IOW('W', 47, int) /* set the pre-timeout */
+/* set the action when a pre-timeout occurs see: WD_SOFT_* */
+#define WDIOC_SETPRETIMEOUTACT _IOW('W', 48, int)
+
+/* use software watchdog instead of hardware */
+#define WDIOC_SETSOFT _IOW('W', 49, int)
+#define WDIOC_SETSOFTTIMEOUTACT _IOW('W', 50, int)
#define WD_ACTIVE 0x8000000
/*
@@ -76,6 +90,15 @@
#define WD_TO_8SEC 33
#define WD_TO_16SEC 34
#define WD_TO_32SEC 35
+#define WD_TO_64SEC 36
+#define WD_TO_128SEC 37
+
+/* action on pre-timeout trigger */
+#define WD_SOFT_PANIC 0x01 /* panic */
+#define WD_SOFT_DDB 0x02 /* enter debugger */
+#define WD_SOFT_LOG 0x04 /* log(9) */
+#define WD_SOFT_PRINTF 0x08 /* printf(9) */
+#define WD_SOFT_MASK 0x0f /* all of the above */
#ifdef _KERNEL
diff --git a/tools/regression/lib/libc/stdio/Makefile b/tools/regression/lib/libc/stdio/Makefile
index d62ac84..f496f73 100644
--- a/tools/regression/lib/libc/stdio/Makefile
+++ b/tools/regression/lib/libc/stdio/Makefile
@@ -1,6 +1,8 @@
# $FreeBSD$
-TESTS= test-getdelim test-perror test-print-positional test-printbasic test-printfloat test-scanfloat
+TESTS= test-fmemopen test-getdelim test-open_memstream test-open_wmemstream \
+ test-perror test-print-positional test-printbasic test-printfloat \
+ test-scanfloat
CFLAGS+= -lm
.PHONY: tests
diff --git a/tools/regression/lib/libc/stdio/test-open_memstream.c b/tools/regression/lib/libc/stdio/test-open_memstream.c
new file mode 100644
index 0000000..1a168c6
--- /dev/null
+++ b/tools/regression/lib/libc/stdio/test-open_memstream.c
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (c) 2013 Advanced Computing Technologies LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+static char *buf;
+static size_t len;
+
+static void
+assert_stream(const char *contents)
+{
+ if (strlen(contents) != len)
+ printf("bad length %zd for \"%s\"\n", len, contents);
+ else if (strncmp(buf, contents, strlen(contents)) != 0)
+ printf("bad buffer \"%s\" for \"%s\"\n", buf, contents);
+}
+
+static void
+open_group_test(void)
+{
+ FILE *fp;
+ off_t eob;
+
+ fp = open_memstream(&buf, &len);
+ if (fp == NULL)
+ err(1, "failed to open stream");
+
+ fprintf(fp, "hello my world");
+ fflush(fp);
+ assert_stream("hello my world");
+ eob = ftello(fp);
+ rewind(fp);
+ fprintf(fp, "good-bye");
+ fseeko(fp, eob, SEEK_SET);
+ fclose(fp);
+ assert_stream("good-bye world");
+ free(buf);
+}
+
+static void
+simple_tests(void)
+{
+ static const char zerobuf[] =
+ { 'f', 'o', 'o', 0, 0, 0, 0, 'b', 'a', 'r', 0 };
+ char c;
+ FILE *fp;
+
+ fp = open_memstream(&buf, NULL);
+ if (fp != NULL)
+ errx(1, "did not fail to open stream");
+ else if (errno != EINVAL)
+ err(1, "incorrect error for bad length pointer");
+ fp = open_memstream(NULL, &len);
+ if (fp != NULL)
+ errx(1, "did not fail to open stream");
+ else if (errno != EINVAL)
+ err(1, "incorrect error for bad buffer pointer");
+ fp = open_memstream(&buf, &len);
+ if (fp == NULL)
+ err(1, "failed to open stream");
+ fflush(fp);
+ assert_stream("");
+ if (fwide(fp, 0) >= 0)
+ printf("stream is not byte-oriented\n");
+
+ fprintf(fp, "fo");
+ fflush(fp);
+ assert_stream("fo");
+ fputc('o', fp);
+ fflush(fp);
+ assert_stream("foo");
+ rewind(fp);
+ fflush(fp);
+ assert_stream("");
+ fseek(fp, 0, SEEK_END);
+ fflush(fp);
+ assert_stream("foo");
+
+ /*
+ * Test seeking out past the current end. Should zero-fill the
+ * intermediate area.
+ */
+ fseek(fp, 4, SEEK_END);
+ fprintf(fp, "bar");
+ fflush(fp);
+
+ /*
+ * Can't use assert_stream() here since this should contain
+ * embedded null characters.
+ */
+ if (len != 10)
+ printf("bad length %zd for zero-fill test\n", len);
+ else if (memcmp(buf, zerobuf, sizeof(zerobuf)) != 0)
+ printf("bad buffer for zero-fill test\n");
+
+ fseek(fp, 3, SEEK_SET);
+ fprintf(fp, " in ");
+ fflush(fp);
+ assert_stream("foo in ");
+ fseek(fp, 0, SEEK_END);
+ fflush(fp);
+ assert_stream("foo in bar");
+
+ rewind(fp);
+ if (fread(&c, sizeof(c), 1, fp) != 0)
+ printf("fread did not fail\n");
+ else if (!ferror(fp))
+ printf("error indicator not set after fread\n");
+ else
+ clearerr(fp);
+
+ fseek(fp, 4, SEEK_SET);
+ fprintf(fp, "bar baz");
+ fclose(fp);
+ assert_stream("foo bar baz");
+ free(buf);
+}
+
+static void
+seek_tests(void)
+{
+ FILE *fp;
+
+ fp = open_memstream(&buf, &len);
+ if (fp == NULL)
+ err(1, "failed to open stream");
+#define SEEK_FAIL(offset, whence, error) do { \
+ errno = 0; \
+ if (fseeko(fp, (offset), (whence)) == 0) \
+ printf("fseeko(%s, %s) did not fail, set pos to %jd\n", \
+ __STRING(offset), __STRING(whence), \
+ (intmax_t)ftello(fp)); \
+ else if (errno != (error)) \
+ printf("fseeko(%s, %s) failed with %d rather than %s\n",\
+ __STRING(offset), __STRING(whence), errno, \
+ __STRING(error)); \
+} while (0)
+
+#define SEEK_OK(offset, whence, result) do { \
+ if (fseeko(fp, (offset), (whence)) != 0) \
+ printf("fseeko(%s, %s) failed: %s\n", \
+ __STRING(offset), __STRING(whence), strerror(errno)); \
+ else if (ftello(fp) != (result)) \
+ printf("fseeko(%s, %s) seeked to %jd rather than %s\n", \
+ __STRING(offset), __STRING(whence), \
+ (intmax_t)ftello(fp), __STRING(result)); \
+} while (0)
+
+ SEEK_FAIL(-1, SEEK_SET, EINVAL);
+ SEEK_FAIL(-1, SEEK_CUR, EINVAL);
+ SEEK_FAIL(-1, SEEK_END, EINVAL);
+ fprintf(fp, "foo");
+ SEEK_OK(-1, SEEK_CUR, 2);
+ SEEK_OK(0, SEEK_SET, 0);
+ SEEK_OK(-1, SEEK_END, 2);
+ SEEK_OK(OFF_MAX - 1, SEEK_SET, OFF_MAX - 1);
+ SEEK_FAIL(2, SEEK_CUR, EOVERFLOW);
+ fclose(fp);
+}
+
+int
+main(int ac, char **av)
+{
+
+ open_group_test();
+ simple_tests();
+ seek_tests();
+ return (0);
+}
diff --git a/tools/regression/lib/libc/stdio/test-open_memstream.t b/tools/regression/lib/libc/stdio/test-open_memstream.t
new file mode 100644
index 0000000..8bdfd03
--- /dev/null
+++ b/tools/regression/lib/libc/stdio/test-open_memstream.t
@@ -0,0 +1,10 @@
+#!/bin/sh
+# $FreeBSD$
+
+cd `dirname $0`
+
+executable=`basename $0 .t`
+
+make $executable 2>&1 > /dev/null
+
+exec ./$executable
diff --git a/tools/regression/lib/libc/stdio/test-open_wmemstream.c b/tools/regression/lib/libc/stdio/test-open_wmemstream.c
new file mode 100644
index 0000000..4cd0ab9
--- /dev/null
+++ b/tools/regression/lib/libc/stdio/test-open_wmemstream.c
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (c) 2013 Advanced Computing Technologies LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+static wchar_t *buf;
+static size_t len;
+
+static void
+assert_stream(const wchar_t *contents)
+{
+ if (wcslen(contents) != len)
+ printf("bad length %zd for \"%ls\"\n", len, contents);
+ else if (wcsncmp(buf, contents, wcslen(contents)) != 0)
+ printf("bad buffer \"%ls\" for \"%ls\"\n", buf, contents);
+}
+
+static void
+open_group_test(void)
+{
+ FILE *fp;
+ off_t eob;
+
+ fp = open_wmemstream(&buf, &len);
+ if (fp == NULL)
+ err(1, "failed to open stream");
+
+ fwprintf(fp, L"hello my world");
+ fflush(fp);
+ assert_stream(L"hello my world");
+ eob = ftello(fp);
+ rewind(fp);
+ fwprintf(fp, L"good-bye");
+ fseeko(fp, eob, SEEK_SET);
+ fclose(fp);
+ assert_stream(L"good-bye world");
+ free(buf);
+}
+
+static void
+simple_tests(void)
+{
+ static const wchar_t zerobuf[] =
+ { L'f', L'o', L'o', 0, 0, 0, 0, L'b', L'a', L'r', 0 };
+ wchar_t c;
+ FILE *fp;
+
+ fp = open_wmemstream(&buf, NULL);
+ if (fp != NULL)
+ errx(1, "did not fail to open stream");
+ else if (errno != EINVAL)
+ err(1, "incorrect error for bad length pointer");
+ fp = open_wmemstream(NULL, &len);
+ if (fp != NULL)
+ errx(1, "did not fail to open stream");
+ else if (errno != EINVAL)
+ err(1, "incorrect error for bad buffer pointer");
+ fp = open_wmemstream(&buf, &len);
+ if (fp == NULL)
+ err(1, "failed to open stream");
+ fflush(fp);
+ assert_stream(L"");
+ if (fwide(fp, 0) <= 0)
+ printf("stream is not wide-oriented\n");
+
+ fwprintf(fp, L"fo");
+ fflush(fp);
+ assert_stream(L"fo");
+ fputwc(L'o', fp);
+ fflush(fp);
+ assert_stream(L"foo");
+ rewind(fp);
+ fflush(fp);
+ assert_stream(L"");
+ fseek(fp, 0, SEEK_END);
+ fflush(fp);
+ assert_stream(L"foo");
+
+ /*
+ * Test seeking out past the current end. Should zero-fill the
+ * intermediate area.
+ */
+ fseek(fp, 4, SEEK_END);
+ fwprintf(fp, L"bar");
+ fflush(fp);
+
+ /*
+ * Can't use assert_stream() here since this should contain
+ * embedded null characters.
+ */
+ if (len != 10)
+ printf("bad length %zd for zero-fill test\n", len);
+ else if (memcmp(buf, zerobuf, sizeof(zerobuf)) != 0)
+ printf("bad buffer for zero-fill test\n");
+
+ fseek(fp, 3, SEEK_SET);
+ fwprintf(fp, L" in ");
+ fflush(fp);
+ assert_stream(L"foo in ");
+ fseek(fp, 0, SEEK_END);
+ fflush(fp);
+ assert_stream(L"foo in bar");
+
+ rewind(fp);
+ if (fread(&c, sizeof(c), 1, fp) != 0)
+ printf("fread did not fail\n");
+ else if (!ferror(fp))
+ printf("error indicator not set after fread\n");
+ else
+ clearerr(fp);
+
+ fseek(fp, 4, SEEK_SET);
+ fwprintf(fp, L"bar baz");
+ fclose(fp);
+ assert_stream(L"foo bar baz");
+ free(buf);
+}
+
+static void
+seek_tests(void)
+{
+ FILE *fp;
+
+ fp = open_wmemstream(&buf, &len);
+ if (fp == NULL)
+ err(1, "failed to open stream");
+#define SEEK_FAIL(offset, whence, error) do { \
+ errno = 0; \
+ if (fseeko(fp, (offset), (whence)) == 0) \
+ printf("fseeko(%s, %s) did not fail, set pos to %jd\n", \
+ __STRING(offset), __STRING(whence), \
+ (intmax_t)ftello(fp)); \
+ else if (errno != (error)) \
+ printf("fseeko(%s, %s) failed with %d rather than %s\n",\
+ __STRING(offset), __STRING(whence), errno, \
+ __STRING(error)); \
+} while (0)
+
+#define SEEK_OK(offset, whence, result) do { \
+ if (fseeko(fp, (offset), (whence)) != 0) \
+ printf("fseeko(%s, %s) failed: %s\n", \
+ __STRING(offset), __STRING(whence), strerror(errno)); \
+ else if (ftello(fp) != (result)) \
+ printf("fseeko(%s, %s) seeked to %jd rather than %s\n", \
+ __STRING(offset), __STRING(whence), \
+ (intmax_t)ftello(fp), __STRING(result)); \
+} while (0)
+
+ SEEK_FAIL(-1, SEEK_SET, EINVAL);
+ SEEK_FAIL(-1, SEEK_CUR, EINVAL);
+ SEEK_FAIL(-1, SEEK_END, EINVAL);
+ fwprintf(fp, L"foo");
+ SEEK_OK(-1, SEEK_CUR, 2);
+ SEEK_OK(0, SEEK_SET, 0);
+ SEEK_OK(-1, SEEK_END, 2);
+ SEEK_OK(OFF_MAX - 1, SEEK_SET, OFF_MAX - 1);
+ SEEK_FAIL(2, SEEK_CUR, EOVERFLOW);
+ fclose(fp);
+}
+
+int
+main(int ac, char **av)
+{
+
+ open_group_test();
+ simple_tests();
+ seek_tests();
+ return (0);
+}
diff --git a/tools/regression/lib/libc/stdio/test-open_wmemstream.t b/tools/regression/lib/libc/stdio/test-open_wmemstream.t
new file mode 100644
index 0000000..8bdfd03
--- /dev/null
+++ b/tools/regression/lib/libc/stdio/test-open_wmemstream.t
@@ -0,0 +1,10 @@
+#!/bin/sh
+# $FreeBSD$
+
+cd `dirname $0`
+
+executable=`basename $0 .t`
+
+make $executable 2>&1 > /dev/null
+
+exec ./$executable
diff --git a/tools/test/iconv/tablegen/cmp.sh b/tools/test/iconv/tablegen/cmp.sh
index 50664c4..349c08f 100755
--- a/tools/test/iconv/tablegen/cmp.sh
+++ b/tools/test/iconv/tablegen/cmp.sh
@@ -1,4 +1,4 @@
#!/bin/sh
# $FreeBSD$
-diff -I\$FreeBSD$1 $2 | grep '^-' >/dev/null && printf "\tDIFFER: $1 $2\n" && exit 0 || exit 0
+diff -I\$\FreeBSD $1 $2 | grep '^-' >/dev/null && printf "\tDIFFER: $1 $2\n" && exit 0 || exit 0
diff --git a/usr.sbin/watchdogd/watchdogd.8 b/usr.sbin/watchdogd/watchdogd.8
index b00fef1..3fb61f6 100644
--- a/usr.sbin/watchdogd/watchdogd.8
+++ b/usr.sbin/watchdogd/watchdogd.8
@@ -1,3 +1,5 @@
+.\" Copyright (c) 2013 iXsystems.com,
+.\" author: Alfred Perlstein <alfred@freebsd.org>
.\" Copyright (c) 2004 Poul-Henning Kamp <phk@FreeBSD.org>
.\" Copyright (c) 2003 Sean M. Kelly <smkelly@FreeBSD.org>
.\" All rights reserved.
@@ -25,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd September 2, 2006
+.Dd September 2, 2013
.Dt WATCHDOGD 8
.Os
.Sh NAME
@@ -33,11 +35,17 @@
.Nd watchdog daemon
.Sh SYNOPSIS
.Nm
-.Op Fl d
+.Op Fl dnw
+.Op Fl -debug
+.Op Fl -softtimeout
+.Op Fl -softtimeout-action Ar action
+.Op Fl -pretimeout Ar timeout
+.Op Fl -pretimeout-action Ar action
.Op Fl e Ar cmd
.Op Fl I Ar file
.Op Fl s Ar sleep
.Op Fl t Ar timeout
+.Op Fl T Ar script_timeout
.Sh DESCRIPTION
The
.Nm
@@ -62,6 +70,13 @@ is not specified, the daemon will perform a trivial file system
check instead.
.Pp
The
+.Fl n
+argument 'dry-run' will cause watchdog not to arm the system watchdog and
+instead only run the watchdog function and report on failures.
+This is useful for developing new watchdogd scripts as the system will not
+reboot if there are problems with the script.
+.Pp
+The
.Fl s Ar sleep
argument can be used to control the sleep period between each execution
of the check and defaults to one second.
@@ -78,6 +93,16 @@ If this occurs,
will no longer execute and thus the kernel's watchdog routines will take
action after a configurable timeout.
.Pp
+The
+.Fl T Ar script_timeout
+specifies the threshold (in seconds) at which the watchdogd will complain
+that its script has run for too long.
+If unset
+.Ar script_timeout
+defaults to the value specified by the
+.Fl s Ar sleep
+option.
+.Pp
Upon receiving the
.Dv SIGTERM
or
@@ -90,17 +115,89 @@ will terminate.
The
.Nm
utility recognizes the following runtime options:
-.Bl -tag -width ".Fl I Ar file"
+.Bl -tag -width ".Fl -softtimeout-action Ar action "
.It Fl I Ar file
Write the process ID of the
.Nm
utility in the specified file.
-.It Fl d
+.It Fl d Fl -debug
Do not fork.
When this option is specified,
.Nm
will not fork into the background at startup.
+.Pp
+.It Fl w
+Complain when the watchdog script takes too long.
+This flag will cause watchdogd to complain when the amount of time to
+execute the watchdog script exceeds the threshold of 'sleep' option.
+.Pp
+.It Fl -pretimeout Ar timeout
+Set a "pretimeout" watchdog.
+At "timeout" seconds before the watchdog will fire attempt an action.
+The action is set by the --pretimeout-action flag.
+The default is just to log a message (WD_SOFT_LOG) via
+.Xr log 9 .
+.Pp
+.It Fl -pretimeout-action Ar action
+Set the timeout action for the pretimeout.
+See the section
+.Sx Timeout Actions .
+.Pp
+.It Fl -softtimeout
+Instead of arming the various hardware watchdogs, only use a basic software
+watchdog.
+The default action is just to
+.Xr log 9
+a message (WD_SOFT_LOG).
+.Pp
+.It Fl -softtimeout-action Ar action
+Set the timeout action for the softtimeout.
+See the section
+.Sx Timeout Actions .
+.Pp
.El
+.Sh Timeout Actions
+The following timeout actions are available via the
+.Fl -pretimeout-action
+and
+.Fl -softtimeout-action
+flags:
+.Bl -tag -width ".Ar printf "
+.It Ar panic
+Call
+.Xr panic 9
+when the timeout is reached.
+.Pp
+.It Ar ddb
+Enter the kernel debugger via
+.Xr kdb_enter 9
+when the timeout is reached.
+.Pp
+.It Ar log
+Log a message using
+.Xr log 9
+when the timeout is reached.
+.Pp
+.It Ar printf
+call the kernel
+.Xr printf 9
+to display a message to the console and
+.Xr dmesg 8
+buffer.
+.Pp
+.El
+Actions can be combined in a comma separated list as so:
+.Ar log,printf
+which would both
+.Xr printf 9
+and
+.Xr log 9
+which will send messages both to
+.Xr dmesg 8
+and the kernel
+.Xr log 4
+device for
+.Xr syslog 8 .
.Sh FILES
.Bl -tag -width ".Pa /var/run/watchdogd.pid" -compact
.It Pa /var/run/watchdogd.pid
@@ -125,3 +222,6 @@ and
.Pp
Some contributions made by
.An Jeff Roberson Aq jeff@FreeBSD.org .
+.Pp
+The pretimeout and softtimeout action system was added by
+.An Alfred Perlstein Aq alfred@freebsd.org .
diff --git a/usr.sbin/watchdogd/watchdogd.c b/usr.sbin/watchdogd/watchdogd.c
index 50bdd91..bb92387 100644
--- a/usr.sbin/watchdogd/watchdogd.c
+++ b/usr.sbin/watchdogd/watchdogd.c
@@ -1,5 +1,8 @@
/*-
* Copyright (c) 2003-2004 Sean M. Kelly <smkelly@FreeBSD.org>
+ * Copyright (c) 2013 iXsystems.com,
+ * author: Alfred Perlstein <alfred@freebsd.org>
+ *
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -50,8 +53,11 @@ __FBSDID("$FreeBSD$");
#include <string.h>
#include <strings.h>
#include <sysexits.h>
+#include <syslog.h>
#include <unistd.h>
+#include <getopt.h>
+
static void parseargs(int, char *[]);
static void sighandler(int);
static void watchdog_loop(void);
@@ -63,13 +69,39 @@ static void usage(void);
static int debugging = 0;
static int end_program = 0;
static const char *pidfile = _PATH_VARRUN "watchdogd.pid";
-static u_int timeout = WD_TO_16SEC;
+static u_int timeout = WD_TO_128SEC;
+static u_int pretimeout = 0;
static u_int passive = 0;
static int is_daemon = 0;
+static int is_dry_run = 0; /* do not arm the watchdog, only
+ report on timing of the watch
+ program */
+static int do_timedog = 0;
+static int do_syslog = 0;
static int fd = -1;
static int nap = 1;
+static int carp_thresh_seconds = -1;
static char *test_cmd = NULL;
+static const char *getopt_shortopts;
+
+static int pretimeout_set;
+static int pretimeout_act;
+static int pretimeout_act_set;
+
+static int softtimeout_set;
+static int softtimeout_act;
+static int softtimeout_act_set;
+
+static struct option longopts[] = {
+ { "debug", no_argument, &debugging, 1 },
+ { "pretimeout", required_argument, &pretimeout_set, 1 },
+ { "pretimeout-action", required_argument, &pretimeout_act_set, 1 },
+ { "softtimeout", no_argument, &softtimeout_set, 1 },
+ { "softtimeout-action", required_argument, &softtimeout_act_set, 1 },
+ { NULL, 0, NULL, 0}
+};
+
/*
* Ask malloc() to map minimum-sized chunks of virtual address space at a time,
* so that mlockall() won't needlessly wire megabytes of unused memory into the
@@ -93,12 +125,18 @@ main(int argc, char *argv[])
parseargs(argc, argv);
+ if (do_syslog) {
+ openlog("watchdogd", LOG_CONS|LOG_NDELAY|LOG_PERROR,
+ LOG_DAEMON);
+
+ }
+
rtp.type = RTP_PRIO_REALTIME;
rtp.prio = 0;
if (rtprio(RTP_SET, 0, &rtp) == -1)
err(EX_OSERR, "rtprio");
- if (watchdog_init() == -1)
+ if (!is_dry_run && watchdog_init() == -1)
errx(EX_SOFTWARE, "unable to initialize watchdog");
if (is_daemon) {
@@ -108,6 +146,7 @@ main(int argc, char *argv[])
pfh = pidfile_open(pidfile, 0600, &otherpid);
if (pfh == NULL) {
if (errno == EEXIST) {
+ watchdog_onoff(0);
errx(EX_SOFTWARE, "%s already running, pid: %d",
getprogname(), otherpid);
}
@@ -164,6 +203,9 @@ static int
watchdog_init(void)
{
+ if (is_dry_run)
+ return 0;
+
fd = open("/dev/" _PATH_WATCHDOG, O_RDWR);
if (fd >= 0)
return (0);
@@ -172,26 +214,98 @@ watchdog_init(void)
}
/*
+ * If we are doing timing, then get the time.
+ */
+static int
+watchdog_getuptime(struct timespec *tp)
+{
+ int error;
+
+ if (!do_timedog)
+ return 0;
+
+ error = clock_gettime(CLOCK_UPTIME_FAST, tp);
+ if (error)
+ warn("clock_gettime");
+ return (error);
+}
+
+static long
+watchdog_check_dogfunction_time(struct timespec *tp_start,
+ struct timespec *tp_end)
+{
+ struct timeval tv_start, tv_end, tv;
+ const char *cmd_prefix, *cmd;
+ int sec;
+
+ if (!do_timedog)
+ return (0);
+
+ TIMESPEC_TO_TIMEVAL(&tv_start, tp_start);
+ TIMESPEC_TO_TIMEVAL(&tv_end, tp_end);
+ timersub(&tv_end, &tv_start, &tv);
+ sec = tv.tv_sec;
+ if (sec < carp_thresh_seconds)
+ return (sec);
+
+ if (test_cmd) {
+ cmd_prefix = "Watchdog program";
+ cmd = test_cmd;
+ } else {
+ cmd_prefix = "Watchdog operation";
+ cmd = "stat(\"/etc\", &sb)";
+ }
+ if (do_syslog)
+ syslog(LOG_CRIT, "%s: '%s' took too long: "
+ "%d.%06ld seconds >= %d seconds threshhold",
+ cmd_prefix, cmd, sec, (long)tv.tv_usec,
+ carp_thresh_seconds);
+ warnx("%s: '%s' took too long: "
+ "%d.%06ld seconds >= %d seconds threshhold",
+ cmd_prefix, cmd, sec, (long)tv.tv_usec, carp_thresh_seconds);
+ return (sec);
+}
+
+
+/*
* Main program loop which is iterated every second.
*/
static void
watchdog_loop(void)
{
+ struct timespec ts_start, ts_end;
struct stat sb;
- int failed;
+ long waited;
+ int error, failed;
while (end_program != 2) {
failed = 0;
+ error = watchdog_getuptime(&ts_start);
+ if (error) {
+ end_program = 1;
+ goto try_end;
+ }
+
if (test_cmd != NULL)
failed = system(test_cmd);
else
failed = stat("/etc", &sb);
+ error = watchdog_getuptime(&ts_end);
+ if (error) {
+ end_program = 1;
+ goto try_end;
+ }
+
+ waited = watchdog_check_dogfunction_time(&ts_start, &ts_end);
+
if (failed == 0)
watchdog_patpat(timeout|WD_ACTIVE);
- sleep(nap);
+ if (nap - waited > 0)
+ sleep(nap - waited);
+try_end:
if (end_program != 0) {
if (watchdog_onoff(0) == 0) {
end_program = 2;
@@ -211,6 +325,9 @@ static int
watchdog_patpat(u_int t)
{
+ if (is_dry_run)
+ return 0;
+
return ioctl(fd, WDIOCPATPAT, &t);
}
@@ -221,11 +338,62 @@ watchdog_patpat(u_int t)
static int
watchdog_onoff(int onoff)
{
-
- if (onoff)
+ int error;
+
+ /* fake successful watchdog op if a dry run */
+ if (is_dry_run)
+ return 0;
+
+ if (onoff) {
+ /*
+ * Call the WDIOC_SETSOFT regardless of softtimeout_set
+ * because we'll need to turn it off if someone had turned
+ * it on.
+ */
+ error = ioctl(fd, WDIOC_SETSOFT, &softtimeout_set);
+ if (error) {
+ warn("setting WDIOC_SETSOFT %d", softtimeout_set);
+ return (error);
+ }
+ error = watchdog_patpat((timeout|WD_ACTIVE));
+ if (error) {
+ warn("watchdog_patpat failed");
+ goto failsafe;
+ }
+ if (softtimeout_act_set) {
+ error = ioctl(fd, WDIOC_SETSOFTTIMEOUTACT,
+ &softtimeout_act);
+ if (error) {
+ warn("setting WDIOC_SETSOFTTIMEOUTACT %d",
+ softtimeout_act);
+ goto failsafe;
+ }
+ }
+ if (pretimeout_set) {
+ error = ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout);
+ if (error) {
+ warn("setting WDIOC_SETPRETIMEOUT %d",
+ pretimeout);
+ goto failsafe;
+ }
+ }
+ if (pretimeout_act_set) {
+ error = ioctl(fd, WDIOC_SETPRETIMEOUTACT,
+ &pretimeout_act);
+ if (error) {
+ warn("setting WDIOC_SETPRETIMEOUTACT %d",
+ pretimeout_act);
+ goto failsafe;
+ }
+ }
+ /* pat one more time for good measure */
return watchdog_patpat((timeout|WD_ACTIVE));
- else
+ } else {
return watchdog_patpat(0);
+ }
+failsafe:
+ watchdog_patpat(0);
+ return (error);
}
/*
@@ -235,27 +403,132 @@ static void
usage(void)
{
if (is_daemon)
- fprintf(stderr, "usage: watchdogd [-d] [-e cmd] [-I file] [-s sleep] [-t timeout]\n");
+ fprintf(stderr, "usage:\n"
+" watchdogd [-dnw] [-e cmd] [-I file] [-s sleep] [-t timeout]\n"
+" [-T script_timeout]\n"
+" [--debug]\n"
+" [--pretimeout seconds] [-pretimeout-action action]\n"
+" [--softtimeout] [-softtimeout-action action]\n"
+);
else
fprintf(stderr, "usage: watchdog [-d] [-t timeout]\n");
exit(EX_USAGE);
}
+static long
+fetchtimeout(int opt, const char *longopt, const char *myoptarg)
+{
+ const char *errstr;
+ char *p;
+ long rv;
+
+ errstr = NULL;
+ p = NULL;
+ errno = 0;
+ rv = strtol(myoptarg, &p, 0);
+ if ((p != NULL && *p != '\0') || errno != 0)
+ errstr = "is not a number";
+ if (rv <= 0)
+ errstr = "must be greater than zero";
+ if (errstr) {
+ if (longopt)
+ errx(EX_USAGE, "--%s argument %s", longopt, errstr);
+ else
+ errx(EX_USAGE, "-%c argument %s", opt, errstr);
+ }
+ return (rv);
+}
+
+struct act_tbl {
+ const char *at_act;
+ int at_value;
+};
+
+struct act_tbl act_tbl[] = {
+ { "panic", WD_SOFT_PANIC },
+ { "ddb", WD_SOFT_DDB },
+ { "log", WD_SOFT_LOG },
+ { "printf", WD_SOFT_PRINTF },
+ { NULL, 0 }
+};
+
+static void
+timeout_act_error(const char *lopt, const char *badact)
+{
+ char *opts, *oldopts;
+ int i;
+
+ opts = NULL;
+ for (i = 0; act_tbl[i].at_act != NULL; i++) {
+ oldopts = opts;
+ if (asprintf(&opts, "%s%s%s",
+ oldopts == NULL ? "" : oldopts,
+ oldopts == NULL ? "" : ", ",
+ act_tbl[i].at_act) == -1)
+ err(EX_OSERR, "malloc");
+ free(oldopts);
+ }
+ warnx("bad --%s argument '%s' must be one of (%s).",
+ lopt, badact, opts);
+ usage();
+}
+
+/*
+ * Take a comma separated list of actions and or the flags
+ * together for the ioctl.
+ */
+static int
+timeout_act_str2int(const char *lopt, const char *acts)
+{
+ int i;
+ char *dupacts, *tofree;
+ char *o;
+ int rv = 0;
+
+ tofree = dupacts = strdup(acts);
+ if (!tofree)
+ err(EX_OSERR, "malloc");
+ while ((o = strsep(&dupacts, ",")) != NULL) {
+ for (i = 0; act_tbl[i].at_act != NULL; i++) {
+ if (!strcmp(o, act_tbl[i].at_act)) {
+ rv |= act_tbl[i].at_value;
+ break;
+ }
+ }
+ if (act_tbl[i].at_act == NULL)
+ timeout_act_error(lopt, o);
+ }
+ free(tofree);
+ return rv;
+}
+
/*
* Handle the few command line arguments supported.
*/
static void
parseargs(int argc, char *argv[])
{
+ int longindex;
int c;
char *p;
+ const char *lopt;
double a;
+ /*
+ * if we end with a 'd' aka 'watchdogd' then we are the daemon program,
+ * otherwise run as a command line utility.
+ */
c = strlen(argv[0]);
if (argv[0][c - 1] == 'd')
is_daemon = 1;
- while ((c = getopt(argc, argv,
- is_daemon ? "I:de:s:t:?" : "dt:?")) != -1) {
+
+ if (is_daemon)
+ getopt_shortopts = "I:de:ns:t:ST:w?";
+ else
+ getopt_shortopts = "dt:?";
+
+ while ((c = getopt_long(argc, argv, getopt_shortopts, longopts,
+ &longindex)) != -1) {
switch (c) {
case 'I':
pidfile = optarg;
@@ -266,17 +539,19 @@ parseargs(int argc, char *argv[])
case 'e':
test_cmd = strdup(optarg);
break;
+ case 'n':
+ is_dry_run = 1;
+ break;
#ifdef notyet
case 'p':
passive = 1;
break;
#endif
case 's':
- p = NULL;
- errno = 0;
- nap = strtol(optarg, &p, 0);
- if ((p != NULL && *p != '\0') || errno != 0)
- errx(EX_USAGE, "-s argument is not a number");
+ nap = fetchtimeout(c, NULL, optarg);
+ break;
+ case 'S':
+ do_syslog = 1;
break;
case 't':
p = NULL;
@@ -286,6 +561,7 @@ parseargs(int argc, char *argv[])
errx(EX_USAGE, "-t argument is not a number");
if (a < 0)
errx(EX_USAGE, "-t argument must be positive");
+
if (a == 0)
timeout = WD_TO_NEVER;
else
@@ -294,12 +570,39 @@ parseargs(int argc, char *argv[])
printf("Timeout is 2^%d nanoseconds\n",
timeout);
break;
+ case 'T':
+ carp_thresh_seconds = fetchtimeout(c, "NULL", optarg);
+ break;
+ case 'w':
+ do_timedog = 1;
+ break;
+ case 0:
+ lopt = longopts[longindex].name;
+ if (!strcmp(lopt, "pretimeout")) {
+ pretimeout = fetchtimeout(0, lopt, optarg);
+ } else if (!strcmp(lopt, "pretimeout-action")) {
+ pretimeout_act = timeout_act_str2int(lopt,
+ optarg);
+ } else if (!strcmp(lopt, "softtimeout-action")) {
+ softtimeout_act = timeout_act_str2int(lopt,
+ optarg);
+ } else {
+ /* warnx("bad option at index %d: %s", optind,
+ argv[optind]);
+ usage();
+ */
+ }
+ break;
case '?':
default:
usage();
/* NOTREACHED */
}
}
+
+ if (carp_thresh_seconds == -1)
+ carp_thresh_seconds = nap;
+
if (argc != optind)
errx(EX_USAGE, "extra arguments.");
if (is_daemon && timeout < WD_TO_1SEC)
OpenPOWER on IntegriCloud