diff --git a/drivers/syslog/Kconfig b/drivers/syslog/Kconfig index eb5b455af6d..de0d8a137f1 100644 --- a/drivers/syslog/Kconfig +++ b/drivers/syslog/Kconfig @@ -11,6 +11,12 @@ config ARCH_SYSLOG bool default n +# Selected if the SYSLOG device supports multi-byte write operations + +config SYSLOG_WRITE + bool + default n + config RAMLOG bool "RAM log device support" default n @@ -76,6 +82,30 @@ config DRIVER_NOTE to read data from the in-memory, scheduler instrumentation "note" buffer. +config SYSLOG_BUFFER + bool "Use buffered output" + default n + depends on SYSLOG_WRITE + ---help--- + Enables an buffering logic that will be used to serialize debug + output from concurrent tasks. This enables allocation of one buffer + per thread, each of size CONFIG_SYSLOG_BUFSIZE. + + The use of SYSLOG buffering is optional. If not enabled, however, + then the output from multiple tasks that attempt to generate SYSLOG + output may be interleaved and difficult to read. + +config SYSLOG_BUFSIZE + int "Output buffer size" + default 32 + range 1 65535 + depends on SYSLOG_BUFFER + ---help--- + If CONFIG_SYSLOG_BUFFER is enabled, then CONFIG_SYSLOG_BUFSIZE + provides the size of the per-thread output buffer. Setting + CONFIG_SYSLOG_BUFSIZE to a value less than one effectly disables + output SYSLOG buffering. + config SYSLOG_INTBUFFER bool "Use interrupt buffer" default n @@ -108,6 +138,7 @@ choice config SYSLOG_CHAR bool "Log to a character device" + select SYSLOG_WRITE ---help--- Enable the generic character device for the SYSLOG. The full path to the SYSLOG device is provided by SYSLOG_DEVPATH. A valid character device (or @@ -126,6 +157,7 @@ config SYSLOG_CONSOLE bool "Log to /dev/console" depends on DEV_CONSOLE select SYSLOG_SERIAL_CONSOLE if SERIAL_CONSOLE + select SYSLOG_WRITE ---help--- Use the system console as a SYSLOG output device. @@ -140,6 +172,7 @@ endchoice config SYSLOG_FILE bool "Sylog file output" default n + select SYSLOG_WRITE ---help--- Build in support to use a file to collect SYSOG output. File SYSLOG channels differ from other SYSLOG channels in that they cannot be diff --git a/drivers/syslog/Make.defs b/drivers/syslog/Make.defs index 2cad218e5ae..f3634498390 100644 --- a/drivers/syslog/Make.defs +++ b/drivers/syslog/Make.defs @@ -38,7 +38,7 @@ # Include SYSLOG Infrastructure CSRCS += vsyslog.c syslog_stream.c syslog_emergstream.c syslog_channel.c -CSRCS += syslog_putc.c syslog_force.c syslog_flush.c +CSRCS += syslog_putc.c syslog_write.c syslog_force.c syslog_flush.c ifeq ($(CONFIG_SYSLOG_INTBUFFER),y) CSRCS += syslog_intbuffer.c diff --git a/drivers/syslog/README.txt b/drivers/syslog/README.txt index 2764f0e2adf..c0adaaa8da1 100644 --- a/drivers/syslog/README.txt +++ b/drivers/syslog/README.txt @@ -129,6 +129,9 @@ SYSLOG Channels { /* I/O redirection methods */ + #ifdef CONFIG_SYSLOG_WRITE + syslog_write_t sc_write; /* Write multiple bytes */ + #endif syslog_putc_t sc_putc; /* Normal buffered output */ syslog_putc_t sc_force; /* Low-level output for interrupt handlers */ syslog_flush_t sc_flush; /* Flush buffered output (on crash) */ diff --git a/drivers/syslog/ramlog.c b/drivers/syslog/ramlog.c index d322710433c..f6a09c8a4f1 100644 --- a/drivers/syslog/ramlog.c +++ b/drivers/syslog/ramlog.c @@ -127,6 +127,9 @@ static int ramlog_poll(FAR struct file *filep, FAR struct pollfd *fds, #ifdef CONFIG_RAMLOG_SYSLOG static const struct syslog_channel_s g_ramlog_syslog_channel = { +#ifdef CONFIG_SYSLOG_WRITE + syslog_default_write, +#endif ramlog_putc, ramlog_putc, ramlog_flush diff --git a/drivers/syslog/syslog.h b/drivers/syslog/syslog.h index 67aedbb9f14..8a8872bd56d 100644 --- a/drivers/syslog/syslog.h +++ b/drivers/syslog/syslog.h @@ -240,19 +240,57 @@ int syslog_flush_intbuffer(FAR const struct syslog_channel_s *channel, * Name: syslog_putc * * Description: - * This is the low-level system logging interface. + * This is the low-level, single character, system logging interface. * * Input Parameters: * ch - The character to add to the SYSLOG (must be positive). * * Returned Value: - * On success, the character is echoed back to the caller. A negated - * errno value is returned on any failure. + * On success, the character is echoed back to the caller. Minus one + * is returned on any failure with the errno set correctly. * ****************************************************************************/ int syslog_putc(int ch); +/**************************************************************************** + * Name: syslog_write + * + * Description: + * This is the low-level, multiple character, system logging interface. + * + * Input Parameters: + * buffer - The buffer containing the data to be output + * buflen - The number of bytes in the buffer + * + * Returned Value: + * On success, the number of characters written is returned. A negated + * errno value is returned on any failure. + * + ****************************************************************************/ + +ssize_t syslog_write(FAR const char *buffer, size_t buflen); + +/**************************************************************************** + * Name: syslog_default_write + * + * Description: + * This provides a default write method for syslog devices that do not + * support multiple byte writes This functions simply loops, outputting + * one cahracter at a time. + * + * Input Parameters: + * buffer - The buffer containing the data to be output + * buflen - The number of bytes in the buffer + * + * Returned Value: + * On success, the number of characters written is returned. A negated + * errno value is returned on any failure. + * + ****************************************************************************/ + +ssize_t syslog_default_write(FAR const char *buffer, size_t buflen); + /**************************************************************************** * Name: syslog_force * @@ -272,6 +310,25 @@ int syslog_putc(int ch); int syslog_force(int ch); +/**************************************************************************** + * Name: syslog_dev_write + * + * Description: + * This is the low-level, multile byte, system logging interface provided + * for the character driver interface. + * + * Input Parameters: + * buffer - The buffer containing the data to be output + * buflen - The number of bytes in the buffer + * + * Returned Value: + * On success, the character is echoed back to the caller. Minus one + * is returned on any failure with the errno set correctly. + * + ****************************************************************************/ + +ssize_t syslog_dev_write(FAR const char *buffer, size_t buflen); + /**************************************************************************** * Name: syslog_dev_putc * diff --git a/drivers/syslog/syslog_channel.c b/drivers/syslog/syslog_channel.c index a36cb6e70f4..107bdae0c3e 100644 --- a/drivers/syslog/syslog_channel.c +++ b/drivers/syslog/syslog_channel.c @@ -79,6 +79,9 @@ static int syslog_default_flush(void); #if defined(CONFIG_RAMLOG_SYSLOG) const struct syslog_channel_s g_default_channel = { +#ifdef CONFIG_SYSLOG_WRITE + syslog_default_write, +#endif ramlog_putc, ramlog_putc, syslog_default_flush @@ -86,6 +89,9 @@ const struct syslog_channel_s g_default_channel = #elif defined(HAVE_LOWPUTC) const struct syslog_channel_s g_default_channel = { +#ifdef CONFIG_SYSLOG_WRITE + syslog_default_write, +#endif up_putc, up_putc, syslog_default_flush @@ -93,6 +99,9 @@ const struct syslog_channel_s g_default_channel = #else const struct syslog_channel_s g_default_channel = { +#ifdef CONFIG_SYSLOG_WRITE + syslog_default_write, +#endif syslog_default_putc, syslog_default_putc, syslog_default_flush diff --git a/drivers/syslog/syslog_chardev.c b/drivers/syslog/syslog_chardev.c index b852db05a21..0db139ab82b 100644 --- a/drivers/syslog/syslog_chardev.c +++ b/drivers/syslog/syslog_chardev.c @@ -56,8 +56,8 @@ * Private Function Prototypes ****************************************************************************/ -static ssize_t syslog_write(FAR struct file *filep, FAR const char *buffer, - size_t buflen); +static ssize_t syslog_chardev_write(FAR struct file *filep, + FAR const char *buffer, size_t buflen); /**************************************************************************** * Private Data @@ -68,7 +68,7 @@ static const struct file_operations syslog_fops = NULL, /* open */ NULL, /* close */ NULL, /* read */ - syslog_write, /* write */ + syslog_chardev_write, /* write */ NULL, /* seek */ NULL /* ioctl */ #ifndef CONFIG_DISABLE_POLL @@ -84,23 +84,13 @@ static const struct file_operations syslog_fops = ****************************************************************************/ /**************************************************************************** - * Name: syslog_write + * Name: syslog_chardev_write ****************************************************************************/ -static ssize_t syslog_write(FAR struct file *filep, FAR const char *buffer, - size_t len) +static ssize_t syslog_chardev_write(FAR struct file *filep, + FAR const char *buffer, size_t len) { - size_t nwritten; - int ret; - - for (nwritten = 0; nwritten < len; nwritten++) - { - int ch = *buffer++; - ret = syslog_putc(ch); - UNUSED(ret); - } - - return len; + return syslog_write(buffer, len); } /**************************************************************************** diff --git a/drivers/syslog/syslog_console.c b/drivers/syslog/syslog_console.c index 04a21d2ec91..b1a9cf2cb6e 100644 --- a/drivers/syslog/syslog_console.c +++ b/drivers/syslog/syslog_console.c @@ -106,14 +106,7 @@ static ssize_t syslog_console_read(FAR struct file *filep, FAR char *buffer, static ssize_t syslog_console_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { - ssize_t ret = buflen; - - for (; buflen; buflen--) - { - syslog_putc(*buffer++); - } - - return ret; + return syslog_write(buffer, len); } /**************************************************************************** diff --git a/drivers/syslog/syslog_consolechannel.c b/drivers/syslog/syslog_consolechannel.c index 8ed6374ff65..1c338b02573 100644 --- a/drivers/syslog/syslog_consolechannel.c +++ b/drivers/syslog/syslog_consolechannel.c @@ -83,6 +83,9 @@ static int syslog_console_force(int ch); static const struct syslog_channel_s g_syslog_console_channel = { +#ifdef CONFIG_SYSLOG_WRITE + syslog_dev_write, +#endif syslog_dev_putc, #ifdef HAVE_LOWPUTC up_putc, diff --git a/drivers/syslog/syslog_devchannel.c b/drivers/syslog/syslog_devchannel.c index e0251efc8e2..74805d59bff 100644 --- a/drivers/syslog/syslog_devchannel.c +++ b/drivers/syslog/syslog_devchannel.c @@ -74,6 +74,9 @@ static int syslog_devchan_force(int ch); static const struct syslog_channel_s g_syslog_dev_channel = { +#ifdef CONFIG_SYSLOG_WRITE + syslog_dev_write, +#endif #ifdef CONFIG_SYSLOG_CHAR_CRLF syslog_devchan_putc, #else diff --git a/drivers/syslog/syslog_device.c b/drivers/syslog/syslog_device.c index 862a3d2b5dc..1978ec211cd 100644 --- a/drivers/syslog/syslog_device.c +++ b/drivers/syslog/syslog_device.c @@ -184,6 +184,114 @@ static inline void syslog_dev_givesem(void) sem_post(&g_syslog_dev.sl_sem); } +/**************************************************************************** + * Name: syslog_dev_outputready + * + * Description: + * Ignore any output: + * + * (1) Before the SYSLOG device has been initialized. This could happen + * from debug output that occurs early in the boot sequence before + * syslog_dev_initialize() is called (SYSLOG_UNINITIALIZED). + * (2) While the device is being initialized. The case could happen if + * debug output is generated while syslog_dev_initialize() executes + * (SYSLOG_INITIALIZING). + * (3) While we are generating SYSLOG output. The case could happen if + * debug output is generated while syslog_dev_putc() executes + * (This case is actually handled inside of syslog_semtake()). + * (4) Any debug output generated from interrupt handlers. A disadvantage + * of using the generic character device for the SYSLOG is that it + * cannot handle debug output generated from interrupt level handlers. + * (5) Any debug output generated from the IDLE loop. The character + * driver interface is blocking and the IDLE thread is not permitted + * to block. + * (6) If an irrecoverable failure occurred during initialization. In + * this case, we won't ever bother to try again (ever). + * + * NOTE: That the third case is different. It applies only to the thread + * that currently holds the sl_sem sempaphore. Other threads should wait. + * that is why that case is handled in syslog_semtake(). + * + * Input Parameters: + * ch - The character to add to the SYSLOG (must be positive). + * + * Returned Value: + * On success, the character is echoed back to the caller. A negated + * errno value is returned on any failure. + * + ****************************************************************************/ + +static int syslog_dev_outputready(void) +{ + int ret; + + /* Cases (4) and (5) */ + + if (up_interrupt_context() || getpid() == 0) + { + return -ENOSYS; + } + + /* We can save checks in the usual case: That after the SYSLOG device + * has been successfully opened. + */ + + if (g_syslog_dev.sl_state != SYSLOG_OPENED) + { + /* Case (1) and (2) */ + + if (g_syslog_dev.sl_state == SYSLOG_UNINITIALIZED || + g_syslog_dev.sl_state == SYSLOG_INITIALIZING) + { + return -EAGAIN; /* Can't access the SYSLOG now... maybe next time? */ + } + + /* Case (6) */ + + if (g_syslog_dev.sl_state == SYSLOG_FAILURE) + { + return -ENXIO; /* There is no SYSLOG device */ + } + + /* syslog_dev_initialize() is called as soon as enough of the operating + * system is in place to support the open operation... but it is + * possible that the SYSLOG device is not yet registered at that time. + * In this case, we know that the system is sufficiently initialized + * to support an attempt to re-open the SYSLOG device. + * + * NOTE that the scheduler is locked. That is because we do not have + * fully initialized semaphore capability until the SYSLOG device is + * successfully initialized + */ + + sched_lock(); + if (g_syslog_dev.sl_state == SYSLOG_REOPEN) + { + /* Try again to initialize the device. We may do this repeatedly + * because the log device might be something that was not ready + * the first time that syslog_dev_initializee() was called (such as a + * USB serial device that has not yet been connected or a file in + * an NFS mounted file system that has not yet been mounted). + */ + + DEBUGASSERT(g_syslog_dev.sl_devpath != NULL); + ret = syslog_dev_initialize(g_syslog_dev.sl_devpath, + (int)g_syslog_dev.sl_oflags, + (int)g_syslog_dev.sl_mode); + if (ret < 0) + { + sched_unlock(); + return ret; + } + } + + sched_unlock(); + DEBUGASSERT(g_syslog_dev.sl_state == SYSLOG_OPENED); + } + + return OK; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -354,19 +462,167 @@ int syslog_dev_uninitialize(void) } #endif /* CONFIG_SYSLOG_FILE */ +/**************************************************************************** + * Name: syslog_dev_write + * + * Description: + * This is the low-level, multile byte, system logging interface provided + * for the character driver interface. + * + * Input Parameters: + * buffer - The buffer containing the data to be output + * buflen - The number of bytes in the buffer + * + * Returned Value: + * On success, the character is echoed back to the caller. Minus one + * is returned on any failure with the errno set correctly. + * + ****************************************************************************/ + +ssize_t syslog_dev_write(FAR const char *buffer, size_t buflen) +{ + FAR const char *endptr; + ssize_t nwritten; + size_t writelen; + int errcode; + int ret; + + /* Check if the system is ready to do output operations */ + + ret = syslog_dev_outputready(); + if (ret < 0) + { + errcode = -ret; + goto errout_with_errcode; + } + + /* The syslog device is ready for writing */ + + ret = syslog_dev_takesem(); + if (ret < 0) + { + /* We probably already hold the semaphore and were probably + * re-entered by the logic kicked off by file_write(). + * We might also have been interrupted by a signal. Either + * way, we are outta here. + */ + + errcode = -ret; + goto errout_with_errcode; + } + + /* Loop until we have output all characters */ + + for (endptr = buffer; *endptr != '\n'; endptr++) + { + switch (*endptr) + { + case '\r': + { + /* Write everything up to this point, ignore the carriage + * return. + * + * - buffer points to next byte to output. + * - endptr points to the carriage return. + */ + + writelen = (size_t)((uintptr_t)endptr - (uintptr_t)buffer); + if (writelen > 0) + { + nwritten = file_write(&g_syslog_dev.sl_file, buffer, writelen); + if (nwritten < 0) + { + errcode = -nwritten; + goto errout_with_sem; + } + } + + /* Adjust pointers */ + + writelen++; /* Skip the carriage return */ + buffer += writelen; /* Points past the carriage return */ + } + break; + + case '\n': + { + /* Write everything up to this point, then add a carriage + * return and linefeed; + * + * - buffer points to next byte to output. + * - endptr points to the new line. + */ + + writelen = (size_t)((uintptr_t)endptr - (uintptr_t)buffer); + if (writelen > 0) + { + nwritten = file_write(&g_syslog_dev.sl_file, buffer, writelen); + if (nwritten < 0) + { + errcode = -nwritten; + goto errout_with_sem; + } + } + + nwritten = file_write(&g_syslog_dev.sl_file, g_syscrlf, 2); + if (nwritten < 0) + { + errcode = -nwritten; + goto errout_with_sem; + } + + /* Adjust pointers */ + + writelen++; /* Skip the line feed */ + buffer += writelen; /* Points past the line feed */ + } + break; + + default: + break; + } + } + + /* Write any data at the end of the buffer. + * + * - buffer points to next byte to output. + * - endptr points to the NULL terminator. + */ + + writelen = (size_t)((uintptr_t)endptr - (uintptr_t)buffer); + if (writelen > 0) + { + nwritten = file_write(&g_syslog_dev.sl_file, buffer, writelen); + if (nwritten < 0) + { + errcode = -nwritten; + goto errout_with_sem; + } + } + + syslog_dev_givesem(); + return buflen; + +errout_with_sem: + syslog_dev_givesem(); +errout_with_errcode: + set_errno(errcode); + return -1; +} + /**************************************************************************** * Name: syslog_dev_putc * * Description: - * This is the low-level system logging interface provided for the - * character driver interface. + * This is the low-level, single character, system logging interface + * provided for the character driver interface. * * Input Parameters: * ch - The character to add to the SYSLOG (must be positive). * * Returned Value: - * On success, the character is echoed back to the caller. A negated - * errno value is returned on any failure. + * On success, the character is echoed back to the caller. Minus one + * is returned on any failure with the errno set correctly. * ****************************************************************************/ @@ -377,99 +633,15 @@ int syslog_dev_putc(int ch) int errcode; int ret; - /* Ignore any output: - * - * (1) Before the SYSLOG device has been initialized. This could happen - * from debug output that occurs early in the boot sequence before - * syslog_dev_initialize() is called (SYSLOG_UNINITIALIZED). - * (2) While the device is being initialized. The case could happen if - * debug output is generated while syslog_dev_initialize() executes - * (SYSLOG_INITIALIZING). - * (3) While we are generating SYSLOG output. The case could happen if - * debug output is generated while syslog_dev_putc() executes - * (This case is actually handled inside of syslog_semtake()). - * (4) Any debug output generated from interrupt handlers. A disadvantage - * of using the generic character device for the SYSLOG is that it - * cannot handle debug output generated from interrupt level handlers. - * (5) Any debug output generated from the IDLE loop. The character - * driver interface is blocking and the IDLE thread is not permitted - * to block. - * (6) If an irrecoverable failure occurred during initialization. In - * this case, we won't ever bother to try again (ever). - * - * NOTE: That the third case is different. It applies only to the thread - * that currently holds the sl_sem sempaphore. Other threads should wait. - * that is why that case is handled in syslog_semtake(). - */ + /* Check if the system is ready to do output operations */ - /* Cases (4) and (5) */ - - if (up_interrupt_context() || getpid() == 0) + ret = syslog_dev_outputready(); + if (ret < 0) { - errcode = ENOSYS; + errcode = -ret; goto errout_with_errcode; } - /* We can save checks in the usual case: That after the SYSLOG device - * has been successfully opened. - */ - - if (g_syslog_dev.sl_state != SYSLOG_OPENED) - { - /* Case (1) and (2) */ - - if (g_syslog_dev.sl_state == SYSLOG_UNINITIALIZED || - g_syslog_dev.sl_state == SYSLOG_INITIALIZING) - { - errcode = EAGAIN; /* Can't access the SYSLOG now... maybe next time? */ - goto errout_with_errcode; - } - - /* Case (6) */ - - if (g_syslog_dev.sl_state == SYSLOG_FAILURE) - { - errcode = ENXIO; /* There is no SYSLOG device */ - goto errout_with_errcode; - } - - /* syslog_dev_initialize() is called as soon as enough of the operating - * system is in place to support the open operation... but it is - * possible that the SYSLOG device is not yet registered at that time. - * In this case, we know that the system is sufficiently initialized - * to support an attempt to re-open the SYSLOG device. - * - * NOTE that the scheduler is locked. That is because we do not have - * fully initialized semaphore capability until the SYSLOG device is - * successfully initialized - */ - - sched_lock(); - if (g_syslog_dev.sl_state == SYSLOG_REOPEN) - { - /* Try again to initialize the device. We may do this repeatedly - * because the log device might be something that was not ready - * the first time that syslog_dev_initializee() was called (such as a - * USB serial device that has not yet been connected or a file in - * an NFS mounted file system that has not yet been mounted). - */ - - DEBUGASSERT(g_syslog_dev.sl_devpath != NULL); - ret = syslog_dev_initialize(g_syslog_dev.sl_devpath, - (int)g_syslog_dev.sl_oflags, - (int)g_syslog_dev.sl_mode); - if (ret < 0) - { - sched_unlock(); - errcode = -ret; - goto errout_with_errcode; - } - } - - sched_unlock(); - DEBUGASSERT(g_syslog_dev.sl_state == SYSLOG_OPENED); - } - /* Ignore carriage returns */ if (ch == '\r') diff --git a/drivers/syslog/syslog_emergstream.c b/drivers/syslog/syslog_emergstream.c index 2ea230b63cc..8347c15a8e5 100644 --- a/drivers/syslog/syslog_emergstream.c +++ b/drivers/syslog/syslog_emergstream.c @@ -99,7 +99,7 @@ static void emergstream_putc(FAR struct lib_outstream_s *this, int ch) * * Input parameters: * stream - User allocated, uninitialized instance of struct - * lib_lowoutstream_s to be initialized. + * lib_outstream_s to be initialized. * * Returned Value: * None (User allocated instance initialized). diff --git a/drivers/syslog/syslog_filechannel.c b/drivers/syslog/syslog_filechannel.c index efd5e541b37..1fcef4aa4a1 100644 --- a/drivers/syslog/syslog_filechannel.c +++ b/drivers/syslog/syslog_filechannel.c @@ -72,6 +72,9 @@ static int syslog_file_force(int ch); static const struct syslog_channel_s g_syslog_file_channel = { +#ifdef CONFIG_SYSLOG_WRITE + syslog_dev_write, +#endif syslog_dev_putc, syslog_file_force, syslog_dev_flush, diff --git a/drivers/syslog/syslog_stream.c b/drivers/syslog/syslog_stream.c index 8305089b8bd..3426b316911 100644 --- a/drivers/syslog/syslog_stream.c +++ b/drivers/syslog/syslog_stream.c @@ -1,7 +1,7 @@ /**************************************************************************** * drivers/syslog/syslog_stream.c * - * Copyright (C) 2012, 2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2012, 2016-2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -59,6 +59,24 @@ static void syslogstream_putc(FAR struct lib_outstream_s *this, int ch) { +#ifdef CONFIG_SYSLOG_BUFFER + FAR struct lib_syslogstream_s *stream = (FAR struct lib_syslogstream_s *)this; + + /* Add the incoming character to the buffer */ + + stream->buf[stream->nbuf] = ch; + stream->nbuf++; + this->nput++; + + /* Is the buffer full? Did we encounter a new line? */ + + if (stream->nbuf >= CONFIG_SYSLOG_BUFSIZE || ch == '\n') + { + /* Yes.. then flush the buffer */ + + (void)this->flush(this); + } +#else int ret; /* Try writing until the write was successful or until an irrecoverable @@ -83,9 +101,46 @@ static void syslogstream_putc(FAR struct lib_outstream_s *this, int ch) * ignored in this context. */ } - while (errno == -EINTR); + while (get_errno() == -EINTR); +#endif } +/**************************************************************************** + * Name: syslogstream_flush + ****************************************************************************/ + +#ifdef CONFIG_SYSLOG_BUFFER +static int syslogstream_flush(FAR struct lib_outstream_s *this) +{ + FAR struct lib_syslogstream_s *stream = (FAR struct lib_syslogstream_s *)this; + int ret = OK; + + /* Is there anything buffered? */ + + if (stream->nbuf > 0) + { + /* Yes write the buffered data */ + + do + { + int status = syslog_write(stream->buf, stream->nbuf); + if (status < 0) + { + ret = -get_errno(); + } + else + { + stream->nbuf = 0; + ret = OK; + } + } + while (ret == -EINTR); + } + + return ret; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -99,16 +154,23 @@ static void syslogstream_putc(FAR struct lib_outstream_s *this, int ch) * * Input parameters: * stream - User allocated, uninitialized instance of struct - * lib_lowoutstream_s to be initialized. + * lib_syslogstream_s to be initialized. * * Returned Value: * None (User allocated instance initialized). * ****************************************************************************/ -void syslogstream(FAR struct lib_outstream_s *stream) +void syslogstream(FAR struct lib_syslogstream_s *stream) { - stream->put = syslogstream_putc; - stream->flush = lib_noflush; - stream->nput = 0; +#ifdef CONFIG_SYSLOG_BUFFER + stream->public.put = syslogstream_putc; + stream->public.flush = syslogstream_flush; + stream->public.nput = 0; + stream->nbuf = 0; +#else + stream->public.put = syslogstream_putc; + stream->public.flush = lib_noflush; + stream->public.nput = 0; +#endif } diff --git a/drivers/syslog/syslog_write.c b/drivers/syslog/syslog_write.c new file mode 100644 index 00000000000..a3100702ca4 --- /dev/null +++ b/drivers/syslog/syslog_write.c @@ -0,0 +1,107 @@ +/**************************************************************************** + * drivers/syslog/syslog_write.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 NuttX 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 OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "syslog.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: syslog_default_write + * + * Description: + * This provides a default write method for syslog devices that do not + * support multiple byte writes This functions simply loops, outputting + * one cahracter at a time. + * + * Input Parameters: + * buffer - The buffer containing the data to be output + * buflen - The number of bytes in the buffer + * + * Returned Value: + * On success, the number of characters written is returned. A negated + * errno value is returned on any failure. + * + ****************************************************************************/ + +ssize_t syslog_default_write(FAR const char *buffer, size_t buflen) +{ + size_t nwritten; + int ret; + + for (nwritten = 0; nwritten < buflen; nwritten++) + { + int ch = *buffer++; + ret = syslog_putc(ch); + UNUSED(ret); + } + + return buflen; +} + +/**************************************************************************** + * Name: syslog_write + * + * Description: + * This is the low-level, multiple character, system logging interface. + * + * Input Parameters: + * buffer - The buffer containing the data to be output + * buflen - The number of bytes in the buffer + * + * Returned Value: + * On success, the number of characters written is returned. A negated + * errno value is returned on any failure. + * + ****************************************************************************/ + +ssize_t syslog_write(FAR const char *buffer, size_t buflen) +{ +#ifdef CONFIG_SYSLOG_WRITE + return g_syslog_channel->sc_write(buffer, buflen); +#else + return syslog_default_write(buffer, buflen); +#endif +} diff --git a/drivers/syslog/vsyslog.c b/drivers/syslog/vsyslog.c index ba0d3726f84..5d45d83092e 100644 --- a/drivers/syslog/vsyslog.c +++ b/drivers/syslog/vsyslog.c @@ -67,7 +67,7 @@ int _vsyslog(int priority, FAR const IPTR char *fmt, FAR va_list *ap) { - struct lib_outstream_s stream; + struct lib_syslogstream_s stream; #ifdef CONFIG_SYSLOG_TIMESTAMP struct timespec ts; int ret = -1; @@ -114,7 +114,7 @@ int _vsyslog(int priority, FAR const IPTR char *fmt, FAR va_list *ap) { /* Use the normal SYSLOG stream */ - syslogstream((FAR struct lib_outstream_s *)&stream); + syslogstream((FAR struct lib_syslogstream_s *)&stream); } #if defined(CONFIG_SYSLOG_TIMESTAMP) diff --git a/include/nuttx/streams.h b/include/nuttx/streams.h index 9d48c6d3931..c8eae2d6251 100644 --- a/include/nuttx/streams.h +++ b/include/nuttx/streams.h @@ -189,6 +189,18 @@ struct lib_rawsostream_s int fd; }; +/* This is a special stream that does buffered character I/O. NOTE that is + * CONFIG_SYSLOG_BUFFER is not defined, it is the same as struct lib_outstream_s */ + +struct lib_syslogstream_s +{ + struct lib_outstream_s public; +#ifdef CONFIG_SYSLOG_BUFFER + unsigned int nbuf; + char buf[CONFIG_SYSLOG_BUFSIZE]; +#endif +}; + /**************************************************************************** * Public Data ****************************************************************************/ @@ -360,7 +372,7 @@ void lib_nulloutstream(FAR struct lib_outstream_s *nulloutstream); * ****************************************************************************/ -void syslogstream(FAR struct lib_outstream_s *stream); +void syslogstream(FAR struct lib_syslogstream_s *stream); /**************************************************************************** * Name: emergstream diff --git a/include/nuttx/syslog/syslog.h b/include/nuttx/syslog/syslog.h index 01de19736d2..bce437d7a76 100644 --- a/include/nuttx/syslog/syslog.h +++ b/include/nuttx/syslog/syslog.h @@ -43,6 +43,8 @@ #include #include + +#include #include /**************************************************************************** @@ -104,6 +106,7 @@ enum syslog_init_e /* This structure provides the interface to a SYSLOG device */ +typedef CODE ssize_t (*syslog_write_t)(FAR const char *buf, size_t buflen); typedef CODE int (*syslog_putc_t)(int ch); typedef CODE int (*syslog_flush_t)(void); @@ -111,8 +114,11 @@ struct syslog_channel_s { /* I/O redirection methods */ - syslog_putc_t sc_putc; /* Normal buffered output */ - syslog_putc_t sc_force; /* Low-level output for interrupt handlers */ +#ifdef CONFIG_SYSLOG_WRITE + syslog_write_t sc_write; /* Write multiple bytes */ +#endif + syslog_putc_t sc_putc; /* Normal buffered output */ + syslog_putc_t sc_force; /* Low-level output for interrupt handlers */ syslog_flush_t sc_flush; /* Flush buffered output (on crash) */ /* Implementation specific logic may follow */