* Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/
+/*
+ * Done in syslogd_and_logger.c:
#include "libbb.h"
+#define SYSLOG_NAMES
+#define SYSLOG_NAMES_CONST
+#include <syslog.h>
+*/
+
#include <paths.h>
#include <sys/un.h>
-
-/* SYSLOG_NAMES defined to pull prioritynames[] and facilitynames[]
- * from syslog.h. Grrrr - glibc puts those in _rwdata_! :( */
-#define SYSLOG_NAMES
-#define SYSLOG_NAMES_CONST /* uclibc is saner :) */
-#include <sys/syslog.h>
#include <sys/uio.h>
#if ENABLE_FEATURE_REMOTE_LOG
struct shbuf_ds *shbuf;
#endif
time_t last_log_time;
- /* localhost's name */
- char localHostName[64];
+ /* localhost's name. We print only first 64 chars */
+ char *hostname;
/* We recv into recvbuf... */
- char recvbuf[MAX_READ];
+ char recvbuf[MAX_READ * (1 + ENABLE_FEATURE_SYSLOGD_DUP)];
/* ...then copy to parsebuf, escaping control chars */
/* (can grow x2 max) */
char parsebuf[MAX_READ*2];
#define G (*ptr_to_globals)
#define INIT_G() do { \
- PTR_TO_GLOBALS = memcpy(xzalloc(sizeof(G)), &init_data, sizeof(init_data)); \
+ SET_PTR_TO_GLOBALS(memcpy(xzalloc(sizeof(G)), &init_data, sizeof(init_data))); \
} while (0)
USE_FEATURE_REMOTE_LOG( OPTBIT_remote ,) // -R
USE_FEATURE_REMOTE_LOG( OPTBIT_locallog ,) // -L
USE_FEATURE_IPC_SYSLOG( OPTBIT_circularlog,) // -C
+ USE_FEATURE_SYSLOGD_DUP( OPTBIT_dup ,) // -D
OPT_mark = 1 << OPTBIT_mark ,
OPT_nofork = 1 << OPTBIT_nofork ,
OPT_remotelog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_remote )) + 0,
OPT_locallog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_locallog )) + 0,
OPT_circularlog = USE_FEATURE_IPC_SYSLOG( (1 << OPTBIT_circularlog)) + 0,
+ OPT_dup = USE_FEATURE_SYSLOGD_DUP( (1 << OPTBIT_dup )) + 0,
};
#define OPTION_STR "m:nO:l:S" \
USE_FEATURE_ROTATE_LOGFILE("s:" ) \
USE_FEATURE_ROTATE_LOGFILE("b:" ) \
USE_FEATURE_REMOTE_LOG( "R:" ) \
USE_FEATURE_REMOTE_LOG( "L" ) \
- USE_FEATURE_IPC_SYSLOG( "C::")
+ USE_FEATURE_IPC_SYSLOG( "C::") \
+ USE_FEATURE_SYSLOGD_DUP( "D" )
#define OPTION_DECL *opt_m, *opt_l \
USE_FEATURE_ROTATE_LOGFILE(,*opt_s) \
USE_FEATURE_ROTATE_LOGFILE(,*opt_b) \
#error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
#endif
-/* our shared key */
-#define KEY_ID ((long)0x414e4547) /* "GENA" */
+/* our shared key (syslogd.c and logread.c must be in sync) */
+enum { KEY_ID = 0x414e4547 }; /* "GENA" */
static void ipcsyslog_cleanup(void)
{
static void ipcsyslog_init(void)
{
if (DEBUG)
- printf("shmget(%lx, %d,...)\n", KEY_ID, G.shm_size);
+ printf("shmget(%x, %d,...)\n", (int)KEY_ID, G.shm_size);
G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 0644);
if (G.shmid == -1) {
sprintf(newFile, "%s.%d", G.logFilePath, i);
if (i == 0) break;
sprintf(oldFile, "%s.%d", G.logFilePath, --i);
+ /* ignore errors - file might be missing */
rename(oldFile, newFile);
}
/* newFile == "f.0" now */
}
G.curFileSize +=
#endif
- full_write(G.logFD, msg, len);
+ full_write(G.logFD, msg, len);
fl.l_type = F_UNLCK;
fcntl(G.logFD, F_SETLKW, &fl);
}
c_fac = facilitynames;
while (c_fac->c_name) {
if (c_fac->c_val != (LOG_FAC(pri) << 3)) {
- c_fac++; continue;
+ c_fac++;
+ continue;
}
/* facility is found, look for prio */
c_pri = prioritynames;
while (c_pri->c_name) {
if (c_pri->c_val != LOG_PRI(pri)) {
- c_pri++; continue;
+ c_pri++;
+ continue;
}
snprintf(res20, 20, "%s.%s",
c_fac->c_name, c_pri->c_name);
else {
char res[20];
parse_fac_prio_20(pri, res);
- sprintf(G.printbuf, "%s %s %s %s\n", timestamp, G.localHostName, res, msg);
+ sprintf(G.printbuf, "%s %.64s %s %s\n", timestamp, G.hostname, res, msg);
}
/* Log message locally (to file or shared mem) */
timestamp_and_log(LOG_SYSLOG | LOG_INFO, (char*)msg, 0);
}
+/* tmpbuf[len] is a NUL byte (set by caller), but there can be other,
+ * embedded NULs. Split messages on each of these NULs, parse prio,
+ * escape control chars and log each locally. */
static void split_escape_and_log(char *tmpbuf, int len)
{
char *p = tmpbuf;
puts("syslogd exiting");
if (ENABLE_FEATURE_IPC_SYSLOG)
ipcsyslog_cleanup();
- exit(1);
+ kill_myself_with_sig(sig);
}
#ifdef SYSLOGD_MARK
if (!G.remoteAddr)
return -1;
}
- return socket(G.remoteAddr->sa.sa_family, SOCK_DGRAM, 0);
+ return socket(G.remoteAddr->u.sa.sa_family, SOCK_DGRAM, 0);
}
#endif
-static void do_syslogd(void) ATTRIBUTE_NORETURN;
+static void do_syslogd(void) NORETURN;
static void do_syslogd(void)
{
int sock_fd;
+#if ENABLE_FEATURE_SYSLOGD_DUP
+ int last_sz = -1;
+ char *last_buf;
+ char *recvbuf = G.recvbuf;
+#else
+#define recvbuf (G.recvbuf)
+#endif
/* Set up signal handlers */
- signal(SIGINT, quit_signal);
- signal(SIGTERM, quit_signal);
- signal(SIGQUIT, quit_signal);
+ bb_signals(0
+ + (1 << SIGINT)
+ + (1 << SIGTERM)
+ + (1 << SIGQUIT)
+ , quit_signal);
signal(SIGHUP, SIG_IGN);
/* signal(SIGCHLD, SIG_IGN); - why? */
#ifdef SYSLOGD_MARK
timestamp_and_log_internal("syslogd started: BusyBox v" BB_VER);
for (;;) {
- size_t sz;
+ ssize_t sz;
+
+#if ENABLE_FEATURE_SYSLOGD_DUP
+ last_buf = recvbuf;
+ if (recvbuf == G.recvbuf)
+ recvbuf = G.recvbuf + MAX_READ;
+ else
+ recvbuf = G.recvbuf;
+#endif
read_again:
- sz = safe_read(sock_fd, G.recvbuf, MAX_READ - 1);
- if (sz < 0) {
+ sz = safe_read(sock_fd, recvbuf, MAX_READ - 1);
+ if (sz < 0)
bb_perror_msg_and_die("read from /dev/log");
- }
- /* Drop trailing NULs (typically there is one NUL) */
+ /* Drop trailing '\n' and NULs (typically there is one NUL) */
while (1) {
if (sz == 0)
goto read_again;
/* man 3 syslog says: "A trailing newline is added when needed".
* However, neither glibc nor uclibc do this:
* syslog(prio, "test") sends "test\0" to /dev/log,
- * syslog(prio, "test\n") sends "test\n\0",
+ * syslog(prio, "test\n") sends "test\n\0".
* IOW: newline is passed verbatim!
* I take it to mean that it's syslogd's job
- * to make those look identical in the log files */
- if (G.recvbuf[sz-1] && G.recvbuf[sz-1] != '\n')
+ * to make those look identical in the log files. */
+ if (recvbuf[sz-1] != '\0' && recvbuf[sz-1] != '\n')
break;
sz--;
}
- /* Maybe we need to add '\n' here, not later?
- * It looks like stock syslogd does send '\n' over network,
- * but we do not (see sendto below) */
- G.recvbuf[sz] = '\0'; /* make sure it *is* NUL terminated */
-
- /* TODO: maybe suppress duplicates? */
+#if ENABLE_FEATURE_SYSLOGD_DUP
+ if ((option_mask32 & OPT_dup) && (sz == last_sz))
+ if (memcmp(last_buf, recvbuf, sz) == 0)
+ continue;
+ last_sz = sz;
+#endif
#if ENABLE_FEATURE_REMOTE_LOG
/* We are not modifying log messages in any way before send */
/* Remote site cannot trust _us_ anyway and need to do validation again */
if (-1 == G.remoteFD)
goto no_luck;
}
+ /* Stock syslogd sends it '\n'-terminated
+ * over network, mimic that */
+ recvbuf[sz] = '\n';
/* send message to remote logger, ignore possible error */
- sendto(G.remoteFD, G.recvbuf, sz, MSG_DONTWAIT,
- &G.remoteAddr->sa, G.remoteAddr->len);
+ /* TODO: on some errors, close and set G.remoteFD to -1
+ * so that DNS resolution and connect is retried? */
+ sendto(G.remoteFD, recvbuf, sz+1, MSG_DONTWAIT,
+ &G.remoteAddr->u.sa, G.remoteAddr->len);
no_luck: ;
}
#endif
- if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog))
- split_escape_and_log(G.recvbuf, sz);
+ if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog)) {
+ recvbuf[sz] = '\0'; /* ensure it *is* NUL terminated */
+ split_escape_and_log(recvbuf, sz);
+ }
} /* for (;;) */
+#undef recvbuf
}
int syslogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int syslogd_main(int argc, char **argv)
+int syslogd_main(int argc UNUSED_PARAM, char **argv)
{
char OPTION_DECL;
- char *p;
INIT_G();
#if ENABLE_FEATURE_REMOTE_LOG
option_mask32 |= OPT_locallog;
/* Store away localhost's name before the fork */
- gethostname(G.localHostName, sizeof(G.localHostName));
- p = strchr(G.localHostName, '.');
- if (p) {
- *p = '\0';
- }
+ G.hostname = safe_gethostname();
+ *strchrnul(G.hostname, '.') = '\0';
if (!(option_mask32 & OPT_nofork)) {
bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
do_syslogd();
/* return EXIT_SUCCESS; */
}
+
+/* Clean up. Needed because we are included from syslogd_and_logger.c */
+#undef G
+#undef GLOBALS
+#undef INIT_G
+#undef OPTION_STR
+#undef OPTION_DECL
+#undef OPTION_PARAM