This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push: new 5e50e2c1f system/syslogd: Initial implementation 5e50e2c1f is described below commit 5e50e2c1f95b35582346c374fb3ce10f5ba0b260 Author: Matteo Golin <matteo.go...@gmail.com> AuthorDate: Sat Jun 7 15:01:58 2025 -0400 system/syslogd: Initial implementation Initial implementation of syslogd including version information, simple usage help and UDP transmission. The current implementation transmits RFC 5424 formatted syslog entries over UDP for consumption by a syslog collector. Only some options from the manpage are implemented since NuttX doesn't currently have the ability for some of the more complex features (-l, etc.). Signed-off-by: Matteo Golin <matteo.go...@gmail.com> --- .gitignore | 1 + system/syslogd/CMakeLists.txt | 36 ++++ system/syslogd/Kconfig | 53 ++++++ system/syslogd/Make.defs | 25 +++ system/syslogd/Makefile | 36 ++++ system/syslogd/syslogd_main.c | 376 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 527 insertions(+) diff --git a/.gitignore b/.gitignore index 5bf6033a6..d0382885b 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ build compile_commands.json .aider* .clang-format +.cache diff --git a/system/syslogd/CMakeLists.txt b/system/syslogd/CMakeLists.txt new file mode 100644 index 000000000..61d74f67b --- /dev/null +++ b/system/syslogd/CMakeLists.txt @@ -0,0 +1,36 @@ +# ############################################################################## +# apps/system/syslogd/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_SYSTEM_SYSLOGD) + nuttx_add_application( + MODULE + ${CONFIG_SYSTEM_SYSLOGD} + NAME + ${CONFIG_SYSTEM_SYSLOGD_PROGNAME} + STACKSIZE + ${CONFIG_SYSTEM_SYSLOGD_STACKSIZE} + PRIORITY + ${CONFIG_SYSTEM_SYSLOGD_PRIORITY} + SRCS + syslogd_main.c) + +endif() diff --git a/system/syslogd/Kconfig b/system/syslogd/Kconfig new file mode 100644 index 000000000..b346dc145 --- /dev/null +++ b/system/syslogd/Kconfig @@ -0,0 +1,53 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config SYSTEM_SYSLOGD + bool "syslogd utility" + default n + depends on NET_UDP && SYSLOG_RFC5424 + ---help--- + Enable support for the 'syslogd' utility. This utility will read syslog + messages from the syslog device and transmit them over UDP in RFC 5424 + compatible format. Ensure the syslog device being used is capable of being + read from. + +if SYSTEM_SYSLOGD + +config SYSTEM_SYSLOGD_PROGNAME + string "syslogd progname" + default "syslogd" + ---help--- + This is the name of the program that will be used when the syslogd + program is installed. + +config SYSTEM_SYSLOGD_PRIORITY + int "syslogd task priority" + default 100 + +config SYSTEM_SYSLOGD_STACKSIZE + int "syslogd stack size" + default DEFAULT_TASK_STACKSIZE + +config SYSTEM_SYSLOGD_ENTRYSIZE + int "Max entry size" + default 480 + ---help--- + The maximum size (in bytes) of the UDP message buffer for sending syslog + entries. Set this value to the expected maximum length of a syslog entry. RFC + 5424 specifies a minimum maximum of 480. + +config SYSTEM_SYSLOGD_PORT + int "syslogd port" + default 514 + ---help--- + The default port for syslogd to send UDP traffic to. + +config SYSTEM_SYSLOGD_ADDR + string "Log server address" + default "127.0.0.1" + ---help--- + The network address for syslogd to send UDP traffic to. + +endif diff --git a/system/syslogd/Make.defs b/system/syslogd/Make.defs new file mode 100644 index 000000000..ad90cf9da --- /dev/null +++ b/system/syslogd/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/system/syslogd/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for asyslogditional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_SYSLOGD),) +CONFIGURED_APPS += $(APPDIR)/system/syslogd +endif diff --git a/system/syslogd/Makefile b/system/syslogd/Makefile new file mode 100644 index 000000000..dd60b8353 --- /dev/null +++ b/system/syslogd/Makefile @@ -0,0 +1,36 @@ +############################################################################ +# apps/system/syslogd/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# Standalone syslogd command + +PROGNAME = $(CONFIG_SYSTEM_SYSLOGD_PROGNAME) +PRIORITY = $(CONFIG_SYSTEM_SYSLOGD_PRIORITY) +STACKSIZE = $(CONFIG_SYSTEM_SYSLOGD_STACKSIZE) +MODULE = $(CONFIG_SYSTEM_SYSLOGD) + +# Files + +MAINSRC = syslogd_main.c + +include $(APPDIR)/Application.mk diff --git a/system/syslogd/syslogd_main.c b/system/syslogd/syslogd_main.c new file mode 100644 index 000000000..27c131186 --- /dev/null +++ b/system/syslogd/syslogd_main.c @@ -0,0 +1,376 @@ +/**************************************************************************** + * apps/system/syslogd/syslogd_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for asyslogditional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#ifdef CONFIG_LIBC_EXECFUNCS +#include <spawn.h> +#endif + +#include <getopt.h> +#include <syslog.h> + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/socket.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* syslogd program version */ + +#define SYSLOGD_VERSION "0.0.0" + +/* Minimum buffer size check */ + +#if CONFIG_SYSTEM_SYSLOGD_ENTRYSIZE < 480 +#error "SYSTEM_SYSLOGD_ENTRYSIZE must be more than 480 to satisfy RFC 5424" +#endif + +/* Maximum number of arguments that can be passed to syslogd */ + +#define MAX_ARGS 8 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: print_usage + ****************************************************************************/ + +static void print_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s [-vdn]\n", CONFIG_SYSTEM_SYSLOGD_PROGNAME); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char **argv) +{ + int fd; + int sock; + int c; + ssize_t bread; + ssize_t bsent; + ssize_t endpos; + char *end; + size_t bufpos = 0; + struct sockaddr_in server; + char buffer[CONFIG_SYSTEM_SYSLOGD_ENTRYSIZE]; + bool debugmode = false; + bool skiplog = false; +#ifdef CONFIG_LIBC_EXECFUNCS + pid_t pid; + bool background = true; + char *new_argv[MAX_ARGS + 1]; +#endif + + /* Parse command line options */ + + while ((c = getopt(argc, argv, ":vdn")) != -1) + { + switch (c) + { + case 'v': + + /* Print version and exit */ + + printf("%s " SYSLOGD_VERSION " (NuttX)\n", argv[0]); + return EXIT_SUCCESS; + + case 'd': + + /* Enable debug mode and stay in foreground */ + + debugmode = true; + printf("Enabling debug mode.\n"); +#ifdef CONFIG_LIBC_EXECFUNCS + background = false; +#endif + break; + + case 'n': + + /* Stay in foreground */ + +#ifdef CONFIG_LIBC_EXECFUNCS + background = false; +#endif + break; + + case '?': + print_usage(); + exit(EXIT_FAILURE); + break; + } + } + + /* Run this program in the background as a spawned task if the background + * option was selected. + */ + +#ifdef CONFIG_LIBC_EXECFUNCS + if (background) + { + /* Set up the arguments, which is identical to the original except with + * an added `-n` flag to ensure that the new process does not 'respawn' + */ + + if (argc > MAX_ARGS) + { + fprintf(stderr, + "Cannot spawn syslogd daemon: arg count %d exceeds %d", + argc, MAX_ARGS); + return EXIT_FAILURE; + } + + new_argv[0] = argv[0]; /* Same program name */ + new_argv[1] = "-n"; /* Prevent daemon from spawning another child */ + memcpy(&new_argv[2], &argv[1], sizeof(char *) * (argc - 1)); + + /* Spawn the child for backgrounding now */ + + if (posix_spawn(&pid, argv[0], NULL, NULL, new_argv, NULL) != 0) + { + fprintf(stderr, "Failed to fork() to background process: %d\n", + errno); + return EXIT_FAILURE; + } + else + { + /* We succeeded in spawning, exit now. */ + + return EXIT_SUCCESS; + } + } +#endif /* CONFIG_LIBC_EXECFUNCS */ + + /* Set up client connection information */ + + server.sin_family = AF_INET; + server.sin_port = htons(CONFIG_SYSTEM_SYSLOGD_PORT); + server.sin_addr.s_addr = inet_addr(CONFIG_SYSTEM_SYSLOGD_ADDR); + + if (server.sin_addr.s_addr == INADDR_NONE) + { + fprintf(stderr, "Invalid address '%s'\n", CONFIG_SYSTEM_SYSLOGD_ADDR); + return EXIT_FAILURE; + } + + /* Create a UDP socket */ + + if (debugmode) + { + printf("Creating UDP socket %s:%u\n", CONFIG_SYSTEM_SYSLOGD_ADDR, + CONFIG_SYSTEM_SYSLOGD_PORT); + } + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + { + fprintf(stderr, "Couldn't create UDP socket: %d\n", errno); + return EXIT_FAILURE; + } + + /* Open syslog stream */ + + if (debugmode) + { + printf("Opening syslog device '%s' to read entries.\n", + CONFIG_SYSLOG_DEVPATH); + } + + fd = open(CONFIG_SYSLOG_DEVPATH, O_RDWR); + if (fd < 0) + { + fprintf(stderr, "Could not open syslog stream: %d", errno); + close(sock); + return EXIT_FAILURE; + } + + /* Transmit syslog messages forever */ + + if (debugmode) + { + printf("Beginning to continuously transmit syslog entries.\n"); + } + + for (; ; ) + { + /* Read as much data as possible into the remaining space in our buffer + */ + + bread = read(fd, &buffer[bufpos], sizeof(buffer) - bufpos); + if (bread < 0) + { + fprintf(stderr, "Failed to read from syslog: %d", errno); + close(fd); + close(sock); + return EXIT_FAILURE; + } + + if (bread == 0 && bufpos == 0) + { + /* Stream is over, terminate the program. */ + + if (debugmode) + { + printf("Syslog stream depleted, exiting...\n"); + } + + break; /* Successful exit */ + } + + /* Get the position of the '\n' character of the syslog entry, + * signifying its end. + */ + + end = memchr(buffer, '\n', bufpos + bread); + if (end == NULL) + { + endpos = -1; + } + else + { + endpos = end - buffer; + } + + if (endpos < 0) + { + /* If we couldn't find a newline character in the buffer, it means + * that the syslog entry doesn't end in our local buffer. We either + * need to: + * + * 1) If `bread` is 0, acknowledge that there is no more data to be + * read and our buffer will never contain a newline character. We + * can exit successfully in this case. + * + * 2) Read more and try again if there's still room in our buffer + * + * 3) Acknowledge that the syslog entry is too long for our buffer + * size, and skip it since we can't construct a UDP packet if + * that's the case. + */ + + if (bread == 0) + { + break; /* Successful exit */ + } + else if (bufpos + bread < sizeof(buffer)) + { + /* Try to get more bytes in our buffer in case we read while + * more bytes were coming. + */ + + bufpos += bread; + continue; + } + + /* If we are here, there's no room left in our buffer. We need to + * skip this entry. + */ + + fprintf(stderr, "Couldn't find end of log in local buffer, " + "skipping entry until the next newline...\n"); + skiplog = true; + bufpos = 0; /* Wipe all buffer contents */ + continue; + } + + /* Print out entry if we are in debug mode and not skipping this line. + * `endpos` + 1 to print newline too. + */ + + if (debugmode && !skiplog) + { + bsent = write(0, buffer, endpos + 1); + if (bsent < 0) + { + fprintf(stderr, "Couldn't print syslog entry: %d\n", errno); + } + } + + /* Send entry over UDP (without newline) if we're not skipping this + * line + */ + + if (!skiplog) + { + bsent = sendto(sock, buffer, endpos, 0, + (const struct sockaddr *)&server, sizeof(server)); + if (bsent < 0) + { + fprintf(stderr, "Couldn't send syslog over UDP: %d\n", errno); + } + } + + /* Take whatever bytes were leftover from our bulk read, and move them + * to the front of the buffer. Mark the new starting point for the next + * read so we don't overwrite them. + * + * endpos + 1 to overwrite vestigial newline character. + */ + + bufpos = (bufpos + bread) - (endpos + 1); + + if (bufpos > 0) + { + /* Copy from right after the newline character up until the end of + * unread bytes + */ + + memcpy(buffer, &buffer[endpos + 1], bufpos); + } + + /* If we got here while skipping a log, it means the endpos for the log + * being skipped was found. Now we're done skipping the log. + */ + + if (skiplog) + { + skiplog = false; + } + } + + close(fd); + close(sock); + + return EXIT_SUCCESS; +}