diff --git a/Documentation/components/drivers/special/syslog.rst b/Documentation/components/drivers/special/syslog.rst index d53639bddfb..23a558d1a4e 100644 --- a/Documentation/components/drivers/special/syslog.rst +++ b/Documentation/components/drivers/special/syslog.rst @@ -539,6 +539,73 @@ Other miscellaneous settings - ``CONFIG_RAMLOG_NPOLLWAITERS``: The maximum number of threads that may be waiting on the poll method. +RAMLOG Rate Limiting +-------------------- + +The RAMLOG SYSLOG channel supports rate limiting to prevent log flooding. +You can set or get the rate limit using the following IOCTLs: + +- ``SYSLOGIOC_SETRATELIMIT``: Set the rate limit. +- ``SYSLOGIOC_GETRATELIMIT``: Get the current rate limit. + +The argument is a pointer to: + +.. code-block:: c + + struct syslog_ratelimit_s + { + unsigned int interval; /* The interval in seconds */ + unsigned int burst; /* The max allowed log entries during interval */ + }; + +**Example (C code):** + +.. code-block:: c + + struct syslog_ratelimit_s limit = { .interval = 1, .burst = 100 }; + ioctl(fd, SYSLOGIOC_SETRATELIMIT, (unsigned long)&limit); + +**NSH Tool Example: setlograte** + +You can implement a simple NSH command to control the RAMLOG rate limit at runtime, similar to setlogmask: + +.. code-block:: c + + int cmd_setlograte(int argc, char **argv) + { + int fd; + struct syslog_ratelimit_s limit; + + if (argc != 3) + { + printf("Usage: setlograte \n"); + return -1; + } + + limit.interval = atoi(argv[1]); + limit.burst = atoi(argv[2]); + + fd = open("/dev/ramlog", O_RDWR); + if (fd < 0) + { + printf("Failed to open /dev/ramlog\n"); + return -1; + } + + if (ioctl(fd, SYSLOGIOC_SETRATELIMIT, (unsigned long)&limit) < 0) + { + printf("Failed to set rate limit\n"); + close(fd); + return -1; + } + + printf("Set RAMLOG rate limit: interval=%u sec, burst=%u\n", limit.interval, limit.burst); + close(fd); + return 0; + } + +This command allows you to set the maximum number of log entries (burst) allowed in a given interval (seconds) for the RAMLOG device at runtime. + SYSLOG Protocol (RFC 5424) ========================== diff --git a/drivers/syslog/ramlog.c b/drivers/syslog/ramlog.c index d8cf0cbd1a8..86820cace37 100644 --- a/drivers/syslog/ramlog.c +++ b/drivers/syslog/ramlog.c @@ -65,6 +65,14 @@ * Private Types ****************************************************************************/ +struct ramlog_ratelimit_s +{ + unsigned int interval; /* The interval in seconds */ + unsigned int burst; /* The max allowed note number during interval */ + unsigned int printed; /* The number of printed note during interval */ + unsigned long begin; /* The timestamp in seconds */ +}; + struct ramlog_header_s { uint32_t rl_magic; /* The rl_magic number for ramlog buffer init */ @@ -98,8 +106,9 @@ struct ramlog_dev_s FAR struct ramlog_header_s *rl_header; - uint32_t rl_bufsize; /* Size of the Circular RAM buffer */ - struct list_node rl_list; /* The head of ramlog_user_s list */ + uint32_t rl_bufsize; /* Size of the circular buffer */ + struct list_node rl_list; /* The list of ramlog_user_s */ + struct ramlog_ratelimit_s rl_ratelimit; /* The ratelimit for ramlog */ }; /**************************************************************************** @@ -173,6 +182,65 @@ static struct ramlog_dev_s g_sysdev = * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: ramlog_ratelimit + * + * Description: + * Check whether the log is limited. + * + * Input Parameters: + * dev - The pointer of ramlog device. + * + * Returned Value: + * True is returned if the log is limited. + * + ****************************************************************************/ + +static bool ramlog_ratelimit(FAR struct ramlog_dev_s *dev) +{ + bool ret; + clock_t ticks; + uint32_t seconds; + FAR struct ramlog_ratelimit_s *limit; + + limit = &dev->rl_ratelimit; + + if (limit->interval == 0) + { + return false; + } + + ticks = clock_systime_ticks(); + seconds = ticks * CONFIG_USEC_PER_TICK / 1000000; + + if (limit->begin == 0) + { + limit->begin = seconds; + } + + /* Reset statistical information */ + + if ((seconds - limit->begin) >= limit->interval) + { + limit->begin = seconds; + limit->printed = 0; + } + + /* Check if the note is limited */ + + if (limit->burst && limit->burst > limit->printed) + { + limit->printed++; + ret = false; + } + else + { + ret = true; + } + + return ret; +} + /**************************************************************************** * Name: ramlog_bufferused ****************************************************************************/ @@ -301,6 +369,12 @@ static ssize_t ramlog_addbuf(FAR struct ramlog_dev_s *priv, flags = enter_critical_section(); + if (ramlog_ratelimit(priv)) + { + leave_critical_section(flags); + return len; + } + #ifdef CONFIG_RAMLOG_SYSLOG if (header->rl_magic != RAMLOG_MAGIC_NUMBER && priv == &g_sysdev) { @@ -521,6 +595,39 @@ static int ramlog_file_ioctl(FAR struct file *filep, int cmd, case BIOC_FLUSH: ramlog_bufferflush(priv); break; + + case SYSLOGIOC_SETRATELIMIT: + if (arg == 0) + { + ret = -EINVAL; + } + else + { + FAR struct syslog_ratelimit_s *limit = + (FAR struct syslog_ratelimit_s *)arg; + + priv->rl_ratelimit.interval = limit->interval; + priv->rl_ratelimit.burst = limit->burst; + ret = 0; + } + break; + + case SYSLOGIOC_GETRATELIMIT: + if (arg == 0) + { + ret = -EINVAL; + } + else + { + FAR struct syslog_ratelimit_s *limit = + (FAR struct syslog_ratelimit_s *)arg; + + limit->interval = priv->rl_ratelimit.interval; + limit->burst = priv->rl_ratelimit.burst; + ret = 0; + } + break; + default: ret = -ENOTTY; break; diff --git a/include/nuttx/syslog/syslog.h b/include/nuttx/syslog/syslog.h index addf2ead100..5f096ad721a 100644 --- a/include/nuttx/syslog/syslog.h +++ b/include/nuttx/syslog/syslog.h @@ -88,6 +88,11 @@ #define SYSLOGIOC_SETFILTER _SYSLOGIOC(0x0002) +/* Set/Get syslog ratelimit */ + +#define SYSLOGIOC_SETRATELIMIT _SYSLOGIOC(0x0003) +#define SYSLOGIOC_GETRATELIMIT _SYSLOGIOC(0x0004) + #define SYSLOG_CHANNEL_NAME_LEN 32 #define SYSLOG_CHANNEL_DISABLE 0x01 @@ -127,6 +132,12 @@ struct syslog_channel_ops_s syslog_close_t sc_close; /* Channel close callback */ }; +struct syslog_ratelimit_s +{ + unsigned int interval; /* The interval in seconds */ + unsigned int burst; /* The max allowed note number during interval */ +}; + struct syslog_channel_info_s { char sc_name[SYSLOG_CHANNEL_NAME_LEN];