aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2020-12-04 22:58:28 +0200
committerSergey Poznyakoff <gray@gnu.org>2020-12-04 23:11:27 +0200
commit1f75cee8eefea039697affb0ab43e3ccb5357861 (patch)
tree57bc463575ac0bcec06ac27ebde2f72d9938b95a
parent5196a3a826938c08dc890db63c06ee6a6bc61953 (diff)
downloadpies-1f75cee8eefea039697affb0ab43e3ccb5357861.tar.gz
pies-1f75cee8eefea039697affb0ab43e3ccb5357861.tar.bz2
Rewrite stdout/stderr redirection
Instead of spawning a subprocess for each redirection, read the output directly from pipe and send it to syslog using an asynchronous syslog implementation. * src/syslog.c: New file. * src/pies_syslog.h: New file. * src/Makefile.am: Add syslog.c and pies_syslog.h * src/comp.c (component_create): Use pies_log_facility. * src/ctl.c (auth_prog) (prog_serialize): Remove TYPE_REDIRECTOR case * src/diag.c (syslog_printer): Use pies_vsyslog. (syslog_open): Use pies_syslog_open. (syslog_close): Use pies_syslog_close. (diag_setup): Use pies_syslog_open. * src/pies.c (log_facility, log_tag): Remove globals. Use pies_log_facility and pies_log_tag instead. New configuration statement syslog.dev * src/pies.h: Include pies_syslog.h (register_socket,register_program_socket): Additional argument: free_data. * src/prog.h (TYPE_REDIRECTOR): Remove. (prog.v.p.redir): Change data type. (prog.v.r): Remove. * src/progman.c (prog_tag) (destroy_prog) (progman_cleanup): Remove TYPE_REDIRECTOR case (prog_stop_redirectors): Deregister fds (redir_tag,register_redir) (update_redir): Remove. (open_redirector): Rewrite. Set up direct reader from the stream, instead of spawning a subprocess. (prog_start): Change open_redirector actual arguments. * src/socket.c (sockinst): New member: free_data. (register_socket): Initialize the free_data member. (register_program_socket): Likewise.
-rw-r--r--src/Makefile.am2
-rw-r--r--src/comp.c2
-rw-r--r--src/ctl.c12
-rw-r--r--src/diag.c14
-rw-r--r--src/pies.c16
-rw-r--r--src/pies.h9
-rw-r--r--src/pies_syslog.h37
-rw-r--r--src/prog.h10
-rw-r--r--src/progman.c258
-rw-r--r--src/socket.c13
-rw-r--r--src/syslog.c464
-rw-r--r--src/sysvinit.c2
12 files changed, 646 insertions, 193 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index af73ada..7f1aa06 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,6 +30,7 @@ pies_SOURCES = \
pies.c\
progman.c\
socket.c\
+ syslog.c\
userprivs.c
if PIES_COND_SYSVINIT
@@ -44,6 +45,7 @@ noinst_HEADERS = \
cmdline.h\
meta1parse.h\
pies.h\
+ pies_syslog.h\
prog.h\
piesctl-cl.h\
telinit.h
diff --git a/src/comp.c b/src/comp.c
index 2027b47..5a2a139 100644
--- a/src/comp.c
+++ b/src/comp.c
@@ -154,7 +154,7 @@ component_create (const char *name)
{
comp = grecs_zalloc (sizeof (*comp));
comp->listidx = cur;
- comp->facility = log_facility;
+ comp->facility = pies_log_facility;
comp->redir[RETR_OUT].type = comp->redir[RETR_ERR].type = redir_null;
comp->tag = grecs_strdup (name);
comp->socket_type = SOCK_STREAM;
diff --git a/src/ctl.c b/src/ctl.c
index db20d39..9e3ae61 100644
--- a/src/ctl.c
+++ b/src/ctl.c
@@ -1274,7 +1274,7 @@ ctl_accept (int socket, void *data)
io = ctlio_create ();
io->addr = addr;
io->addrlen = addrlen;
- register_socket (fd, NULL, ctlwr, NULL, io);
+ register_socket (fd, NULL, ctlwr, NULL, io, NULL);
return 0;
}
@@ -1301,7 +1301,7 @@ ctl_open (void)
return -1;
}
- register_socket (fd, ctl_accept, NULL, NULL, NULL);
+ register_socket (fd, ctl_accept, NULL, NULL, NULL, NULL);
return 0;
}
@@ -1429,9 +1429,6 @@ auth_prog (struct prog *prog, struct ctlio *io)
return CTL_ADMIN_STATE|CTL_USER_STATE;
switch (prog->type)
{
- case TYPE_REDIRECTOR:
- prog = prog->v.r.master;
- /* FALL THROUGH */
case TYPE_COMPONENT:
if (prog->v.p.comp->adm_acl
&& check_acl (prog->v.p.comp->adm_acl,
@@ -1450,7 +1447,6 @@ auth_prog (struct prog *prog, struct ctlio *io)
static char * const pies_type_str[] = {
[TYPE_COMPONENT] = "component",
- [TYPE_REDIRECTOR] = "redirector",
[TYPE_COMMAND] = "command"
};
@@ -1904,10 +1900,6 @@ prog_serialize (struct json_value *ret, struct prog *prog)
json_object_set (ret, "argv", v);
break;
- case TYPE_REDIRECTOR:
- json_object_set_number (ret, "PID", prog->pid);
- break;
-
case TYPE_COMMAND:
json_object_set_string (ret, "command", "%s", prog->v.c.command);
}
diff --git a/src/diag.c b/src/diag.c
index 24000e2..ed5e5e3 100644
--- a/src/diag.c
+++ b/src/diag.c
@@ -26,13 +26,7 @@ typedef struct
static int
syslog_printer (LOGSTREAM *str, int prio, const char *fmt, va_list ap)
{
-#if HAVE_VSYSLOG
- vsyslog (prio, fmt, ap);
-#else
- char buf[128];
- vsnprintf (buf, sizeof buf, fmt, ap);
- syslog (prio, "%s", buf);
-#endif
+ pies_vsyslog (prio, fmt, ap);
return 0;
}
@@ -41,7 +35,7 @@ syslog_open (int logf, LOGSTREAM *str)
{
if (logf & DIAG_REOPEN_LOG)
{
- openlog (log_tag, LOG_PID, log_facility);
+ pies_syslog_open ();
str->open = 1;
}
else
@@ -53,7 +47,7 @@ static void
syslog_close (LOGSTREAM *str)
{
if (str->open)
- closelog ();
+ pies_syslog_close ();
}
static int
@@ -151,7 +145,7 @@ diag_setup (int flags)
if (flags)
diag_output = flags;
if (diag_output & DIAG_TO_SYSLOG)
- openlog (log_tag, LOG_PID, log_facility);
+ pies_syslog_open ();
}
void
diff --git a/src/pies.c b/src/pies.c
index 7d7f370..4c25c5f 100644
--- a/src/pies.c
+++ b/src/pies.c
@@ -23,8 +23,6 @@
int preprocess_only; /* Preprocess config, do nothing more */
int lint_mode; /* Test configuration syntax and exit */
int log_to_stderr_only; /* Use only stderr for logging */
-int log_facility = LOG_USER;
-char *log_tag;
struct pies_privs pies_privs;
int foreground;
int init_process;
@@ -1537,16 +1535,22 @@ struct grecs_keyword control_keywords[] = {
/* syslog */
static struct grecs_keyword syslog_kw[] = {
+ {"dev",
+ N_("name"),
+ N_("Syslog device: either absolute pathname of the socket file, "
+ "or an IPv4 address and optional port, separated with a colon"),
+ grecs_type_string, GRECS_DFLT,
+ &pies_log_dev},
{"facility",
N_("name"),
N_("Set syslog facility. Arg is one of the following: user, daemon, "
"auth, authpriv, mail, cron, local0 through local7 (case-insensitive), "
"or a facility number."),
grecs_type_string, GRECS_DFLT,
- &log_facility, 0, cb_syslog_facility},
+ &pies_log_facility, 0, cb_syslog_facility},
{"tag", N_("string"), N_("Tag syslog messages with this string"),
grecs_type_string, GRECS_DFLT,
- &log_tag},
+ &pies_log_tag},
#if 0
/* This is reserved for future use */
{
@@ -2354,7 +2358,7 @@ main (int argc, char **argv)
/* Set default logging */
if (SYSVINIT_ACTIVE)
{
- log_facility = LOG_DAEMON;
+ pies_log_facility = LOG_DAEMON;
diag_flags = DIAG_TO_STDERR | DIAG_REOPEN_LOG;
}
else
@@ -2383,7 +2387,7 @@ main (int argc, char **argv)
instance++;
}
setenv ("PIES_INSTANCE", instance, 1);
- log_tag = grecs_strdup (instance);
+ pies_log_tag = grecs_strdup (instance);
set_conf_file_names (instance);
diff --git a/src/pies.h b/src/pies.h
index f2aa5ef..99169db 100644
--- a/src/pies.h
+++ b/src/pies.h
@@ -58,6 +58,7 @@
#include "libpies.h"
#include "envop.h"
#include "grecs/json.h"
+#include "pies_syslog.h"
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
@@ -296,8 +297,6 @@ enum pies_action {
};
extern char *instance;
-extern char *log_tag;
-extern int log_facility;
extern unsigned long shutdown_timeout;
extern struct component default_component;
extern pies_acl_t pies_acl;
@@ -473,11 +472,13 @@ void *register_socket (int fd,
socket_handler_t rd,
socket_handler_t wr,
socket_handler_t ex,
- void *data);
+ void *data,
+ void (*free_data)(void*));
void deregister_socket (int fd);
void update_socket (int fd, int evt, socket_handler_t f);
-int register_program_socket (int socktype, int fd, void *data);
+int register_program_socket (int socktype, int fd, void *data,
+ void (*free_data)(void*));
int pass_fd (const char *socket, int fd, unsigned time_out);
int create_socket (struct pies_url *url, int socket_type,
const char *user, mode_t umask);
diff --git a/src/pies_syslog.h b/src/pies_syslog.h
new file mode 100644
index 0000000..bf83714
--- /dev/null
+++ b/src/pies_syslog.h
@@ -0,0 +1,37 @@
+/* This file is part of GNU Pies.
+ Copyright (C) 2007-2020 Sergey Poznyakoff
+
+ GNU Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
+
+#define PIES_LOG_BUF_SIZE 1024
+#define PIES_LOG_DEV "/dev/log"
+#define PIES_LOG_MAX_QUEUE 64
+
+int pies_syslog_open (void);
+void pies_syslog_close (void);
+void pies_syslog (int pri, char const *fmt, ...);
+void pies_vsyslog (int pri, char const *fmt, va_list ap);
+void pies_syslog_flush (void);
+void pies_syslog_message (int prio, char const *text, char const *tag, pid_t pid);
+
+extern char *pies_fallback_file;
+extern char *pies_log_dev;
+extern char *pies_log_tag;
+extern int pies_log_facility;
+extern size_t pies_log_max_queue;
+
+
+
+
+
diff --git a/src/prog.h b/src/prog.h
index 6647ee4..99145cd 100644
--- a/src/prog.h
+++ b/src/prog.h
@@ -17,7 +17,6 @@
enum prog_type
{
TYPE_COMPONENT,
- TYPE_REDIRECTOR,
TYPE_COMMAND
};
@@ -56,7 +55,7 @@ struct prog
size_t argc;
char **argv; /* Actual command line (NULL-terminated) */
int socket;
- struct prog *redir[2]; /* Pointers to redirectors */
+ int redir[2]; /* FDs of redirectors */
time_t timestamp; /* Time of last startup */
size_t failcount; /* Number of failed starts since timestamp */
enum prog_status status; /* Current component status */
@@ -73,13 +72,6 @@ struct prog
struct
{
char *tag;
- struct component *comp;
- struct prog *master;
- } r;
-
- struct
- {
- char *tag;
char *command;
} c;
} v;
diff --git a/src/progman.c b/src/progman.c
index d849368..81accfb 100644
--- a/src/progman.c
+++ b/src/progman.c
@@ -94,9 +94,6 @@ prog_tag (struct prog const *prog)
case TYPE_COMPONENT:
return prog->v.p.comp->tag;
- case TYPE_REDIRECTOR:
- return prog->v.r.tag;
-
case TYPE_COMMAND:
return prog->v.c.tag;
}
@@ -147,6 +144,21 @@ unlink_prog (struct prog *pp)
progtail = pp->prev;
}
+static inline void
+prog_stop_redirectors (struct prog *p)
+{
+ if (p->v.p.redir[RETR_OUT] != -1)
+ {
+ deregister_socket (p->v.p.redir[RETR_OUT]);
+ p->v.p.redir[RETR_OUT] = -1;
+ }
+ if (p->v.p.redir[RETR_ERR] != -1)
+ {
+ deregister_socket (p->v.p.redir[RETR_ERR]);
+ p->v.p.redir[RETR_ERR] = -1;
+ }
+}
+
void
destroy_prog (struct prog **pp)
{
@@ -163,27 +175,7 @@ destroy_prog (struct prog **pp)
if (p->v.p.status == status_listener && p->v.p.socket != -1)
deregister_socket (p->v.p.socket);
/* FIXME: Remove also all dependent progs (esp. tcpmux) */
- if (p->v.p.redir[RETR_OUT])
- p->v.p.redir[RETR_OUT]->v.r.master = NULL;
- if (p->v.p.redir[RETR_ERR])
- p->v.p.redir[RETR_ERR]->v.r.master = NULL;
- break;
-
- case TYPE_REDIRECTOR:
- {
- struct prog *master = p->v.r.master;
- component_ref_decr (p->v.r.comp);
- if (master)
- {
- if (p == master->v.p.redir[0])
- master->v.p.redir[0] = NULL;
- else if (p == master->v.p.redir[1])
- master->v.p.redir[1] = NULL;
- }
- /* else
- logmsg (LOG_NOTICE, _("orphan redirector: %s"), p->tag);*/
- free (p->v.r.tag);
- }
+ prog_stop_redirectors (p);
break;
case TYPE_COMMAND:
@@ -194,50 +186,6 @@ destroy_prog (struct prog **pp)
*pp = NULL;
}
-static char *
-redir_tag (struct prog *master, int type)
-{
- static char *redirstr[2] = { "stdout", "stderr" };
- char *str = NULL;
- size_t len = 0;
- char const *tag = prog_tag (master);
- if (type < ARRAY_SIZE(redirstr))
- grecs_asprintf (&str, &len, "%s/%s", tag, redirstr[type]);
- else
- grecs_asprintf (&str, &len, "%s/%d", tag, type);
- return str;
-}
-
-static struct prog *
-register_redir (int type, struct prog *master)
-{
- char *tag = redir_tag (master, type);
- struct prog *pp = grecs_zalloc (sizeof (*pp));
-
- pp->type = TYPE_REDIRECTOR;
- pp->v.r.tag = tag;
- pp->v.r.master = master;
- pp->v.r.comp = master->v.p.comp;
- component_ref_incr (pp->v.r.comp);
- link_prog (pp, NULL);
- return pp;
-}
-
-void
-update_redir (int type, struct prog *master, pid_t pid)
-{
- struct prog *pp;
- if (master->v.p.redir[type])
- {
- pp = master->v.p.redir[type];
- prog_stop (pp, SIGKILL); /* Just in case */
- }
-
- pp = register_redir (type, master);
- master->v.p.redir[type] = pp;
- pp->pid = pid;
-}
-
/* Given the component COMP find the element of the program list
after which to link in the new prog associated with that COMP.
The progs in the resulting list must be arranged exactly in the
@@ -403,7 +351,89 @@ redirect_to_file (struct prog *master, int stream)
}
return fd;
}
+
+struct read_buffer
+{
+ struct prog *master;
+ int stream;
+ char text[PIES_LOG_BUF_SIZE];
+ size_t len;
+ int overflow;
+};
+
+static int
+redirect_read (int fd, void *data)
+{
+ struct read_buffer *rb = data;
+ int n;
+ int prio = rb->master->v.p.comp->redir[rb->stream].v.prio;
+ char const *tag = prog_tag (rb->master);
+ pid_t pid = rb->master->pid;
+ char *p;
+
+ n = read (fd, rb->text + rb->len, sizeof (rb->text) - rb->len);
+ if (n == -1)
+ return -1;
+ if (n > 0)
+ {
+ rb->len += n;
+
+ while (rb->len)
+ {
+ p = memchr (rb->text, '\n', rb->len);
+ if (p)
+ {
+ *p++ = 0;
+ if (rb->overflow)
+ rb->overflow = 0;
+ else
+ pies_syslog_message (prio, rb->text, tag, pid);
+ n = rb->len - (p - rb->text);
+ if (n > 0)
+ memmove (rb->text, p, n);
+ rb->len = n;
+ }
+ else if (rb->len == sizeof (rb->text))
+ {
+ rb->text[sizeof (rb->text) - 1] = 0;
+ pies_syslog_message (prio, rb->text, tag, pid);
+ rb->len = 0;
+ rb->overflow = 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static void
+read_buffer_free (void *p)
+{
+ free (p);
+}
+
+int
+redirect_to_syslog (struct prog *master, int stream, int *fd)
+{
+ struct read_buffer *rb;
+ int p[2];
+
+ if (pipe (p))
+ {
+ logmsg (LOG_CRIT, "pipe: %s", strerror (errno));
+ return -1;
+ }
+ rb = grecs_zalloc (sizeof (*rb));
+ rb->master = master;
+ rb->stream = stream;
+ rb->len = 0;
+ rb->overflow = 0;
+
+ register_socket (p[0], redirect_read, NULL, NULL, rb, read_buffer_free);
+ *fd = p[0];
+ return p[1];
+}
+
static void
close_fds (fd_set *fdset)
{
@@ -425,72 +455,20 @@ free_redirector (struct redirector *rp)
}
int
-open_redirector (struct prog *master, int stream)
+open_redirector (struct prog *master, int stream, int *fd)
{
- int p[2];
- FILE *fp;
- char *buf = NULL;
- size_t size = 0;
- pid_t pid;
- int prio;
- char *tag;
- fd_set fdset;
-
switch (master->v.p.comp->redir[stream].type)
{
case redir_null:
+ *fd = -1;
return -1;
case redir_file:
+ *fd = -1;
return redirect_to_file (master, stream);
case redir_syslog:
- break;
- }
-
- if (pipe (p))
- {
- logmsg (LOG_CRIT, "pipe: %s", strerror (errno));
- return -1;
- }
-
- switch (pid = fork ())
- {
- case 0:
- /* Redirector process */
- tag = redir_tag (master, stream);
- mf_proctitle_format ("%s redirector", tag);
- free (tag);
-
- FD_ZERO (&fdset);
- FD_SET (p[0], &fdset);
- close_fds (&fdset);
-
- diag_setup (0);
- signal_setup (redir_exit);
-
- close (p[1]);
- fp = fdopen (p[0], "r");
- if (fp == NULL)
- _exit (1);
- openlog (prog_tag (master), LOG_PID, master->v.p.comp->facility);
- prio = master->v.p.comp->redir[stream].v.prio;
- while (getline (&buf, &size, fp) > 0)
- syslog (prio, "%s", buf);
- _exit (0);
-
- case -1:
- logmsg (LOG_CRIT,
- _("cannot run redirector `%s': fork failed: %s"),
- prog_tag (master), strerror (errno));
- return -1;
-
- default:
- debug (1, (_("redirector for %s started, pid=%lu"),
- prog_tag (master), (unsigned long) pid));
- update_redir (stream, master, pid);
- close (p[0]);
- return p[1];
+ return redirect_to_syslog (master, stream, fd);
}
}
@@ -930,7 +908,7 @@ prog_execute (struct prog *prog)
execvp (prog->v.p.comp->program ?
prog->v.p.comp->program : prog->v.p.argv[0],
prog->v.p.argv);
- openlog (log_tag, LOG_PID, prog->v.p.comp->facility);
+ //FIXME: pies_syslog?
syslog (LOG_CRIT, _("cannot start `%s': %s"), prog_tag (prog),
strerror (errno));
_exit (EX_SOFTWARE);
@@ -1044,8 +1022,10 @@ prog_start (struct prog *prog)
if (prog_init (prog))
return; //FIXME
- redir[RETR_OUT] = open_redirector (prog, RETR_OUT);
- redir[RETR_ERR] = open_redirector (prog, RETR_ERR);
+ redir[RETR_OUT] = open_redirector (prog, RETR_OUT,
+ &prog->v.p.redir[RETR_OUT]);
+ redir[RETR_ERR] = open_redirector (prog, RETR_ERR,
+ &prog->v.p.redir[RETR_ERR]);
switch (pid = fork ())
{
@@ -1361,7 +1341,7 @@ prog_create_socket (struct prog *prog, void *data)
comp->privs.user, comp->umask);
if (fd == -1)
destroy_prog (&prog);
- else if (register_program_socket (comp->socket_type, fd, prog))
+ else if (register_program_socket (comp->socket_type, fd, prog, NULL))
{
close (fd);
destroy_prog (&prog);
@@ -1610,15 +1590,6 @@ prog_start_prerequisites (struct prog *prog)
}
void
-prog_stop_redirectors (struct prog *prog)
-{
- if (prog->v.p.redir[RETR_OUT])
- prog_stop (prog->v.p.redir[RETR_OUT], SIGTERM);
- if (prog->v.p.redir[RETR_ERR])
- prog_stop (prog->v.p.redir[RETR_ERR], SIGTERM);
-}
-
-void
prog_stop_dependents (struct prog *prog)
{
struct component *comp;
@@ -2254,17 +2225,6 @@ progman_cleanup (int expect_term)
break;
- case TYPE_REDIRECTOR:
- /* It was a redirector of an already finished process. */
- print_status (prog_tag (prog), pid, status,
- expect_term ||
- (prog->v.r.master &&
- prog->v.r.master->v.p.status == status_stopping));
- debug (1, (_("removing redirector %s, pid=%lu"),
- prog_tag (prog), (unsigned long)pid));
- destroy_prog (&prog);
- break;
-
case TYPE_COMMAND:
print_status (prog_tag (prog), pid, status, expect_term);
destroy_prog (&prog);
@@ -2351,7 +2311,7 @@ prog_activate_listener (struct prog *prog)
comp->privs.user, comp->umask);
if (fd == -1)
return -1;
- else if (register_program_socket (comp->socket_type, fd, prog))
+ else if (register_program_socket (comp->socket_type, fd, prog, NULL))
{
close (fd);
return -1;
diff --git a/src/socket.c b/src/socket.c
index 4338a49..fb9d6cc 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -404,6 +404,7 @@ struct sockinst
int fd;
int dead;
socket_handler_t handler[3];
+ void (*free_data)(void *);
void *data;
};
@@ -437,7 +438,8 @@ register_socket (int fd,
socket_handler_t rd,
socket_handler_t wr,
socket_handler_t ex,
- void *data)
+ void *data,
+ void (*free_data) (void *))
{
struct sockinst *sip = grecs_malloc (sizeof *sip);
sip->fd = fd;
@@ -445,6 +447,7 @@ register_socket (int fd,
sip->handler[PIES_EVT_RD] = rd;
sip->handler[PIES_EVT_WR] = wr;
sip->handler[PIES_EVT_EX] = ex;
+ sip->free_data = free_data;
sip->data = data;
sip->next = NULL;
sip->prev = si_tail;
@@ -515,6 +518,10 @@ deregister_socket (int fd)
struct sockinst *sp = find_socket (fd);
if (!sp)
return;
+ if (sp->free_data)
+ sp->free_data (sp->data);
+ sp->data = NULL;
+ sp->free_data = NULL;
if (si_iterating)
sp->dead = 1;
else
@@ -523,14 +530,14 @@ deregister_socket (int fd)
int
-register_program_socket (int socktype, int fd, void *data)
+register_program_socket (int socktype, int fd, void *data, void (*free_data) (void*))
{
if (socktype == SOCK_STREAM && listen (fd, 8) == -1)
{
logmsg (LOG_ERR, "listen: %s", strerror (errno));
return 1;
}
- register_socket (fd, progman_accept, NULL, NULL, data);
+ register_socket (fd, progman_accept, NULL, NULL, data, free_data);
return 0;
}
diff --git a/src/syslog.c b/src/syslog.c
new file mode 100644
index 0000000..7f748d6
--- /dev/null
+++ b/src/syslog.c
@@ -0,0 +1,464 @@
+/* This file is part of GNU Pies.
+ Copyright (C) 2007-2020 Sergey Poznyakoff
+
+ GNU Pies is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Pies is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include "pies.h"
+#include "pies_syslog.h"
+
+struct log_message
+{
+ char *text; /* Message text (no terminating nul). */
+ size_t len; /* Number of bytes in text */
+ size_t drop_count; /* Count of messages dropped so far (first message only) */
+ struct log_message *next;
+};
+
+struct log_message_in
+{
+ struct log_message msg;
+ char buf[PIES_LOG_BUF_SIZE];
+};
+
+/* Global variables */
+/* Fallback log file is used to log critical messages when syslog
+ daemon is unavailable. If NULL, stderr will be used. */
+char *pies_fallback_file = "/tmp/pies_logger.log";
+/* Name of the syslog device. If starts with a slash, it is assumed
+ to be a UNIX socket name. Otherwise, it is assumed to be a host name
+ or IPv4 address of the syslog daemon, optionally followed by a colon
+ and port number or service name. */
+char *pies_log_dev = PIES_LOG_DEV;
+/* Log tag */
+char *pies_log_tag = "pies";
+/* Log facility */
+int pies_log_facility = LOG_USER;
+/* Maximum capacity of the log message queue */
+size_t pies_log_max_queue = PIES_LOG_MAX_QUEUE;
+
+/* Log socket descriptor. */
+static int log_fd = -1;
+
+/* Socket address */
+static union {
+ struct sockaddr_in s_in;
+ struct sockaddr_un s_un;
+} log_sa;
+
+/* Socket address length. */
+static socklen_t log_salen = 0;
+
+/* Socked address family. */
+static int log_family;
+
+static inline int
+pri_facility (int pri)
+{
+ return pri & ~0x7;
+}
+
+static inline int
+pri_severity (int pri)
+{
+ return pri & 0x7;
+}
+
+/* Fallback logger */
+static void
+fallback_log (char const *fmt, ...)
+{
+ FILE *fp = NULL;
+ va_list ap;
+
+ if (pies_fallback_file)
+ fp = fopen (pies_fallback_file, "a");
+ if (!fp)
+ fp = stderr;
+ fprintf (fp, "pies[%lu]: ", (unsigned long) getpid());
+ va_start (ap, fmt);
+ vfprintf (fp, fmt, ap);
+ va_end (ap);
+ fputc ('\n', fp);
+ if (fp != stderr)
+ fclose (fp);
+}
+
+static int
+reopen_logger (void)
+{
+ int fd;
+ int flags;
+
+ if (log_salen == 0)
+ {
+ if (pies_log_dev[0] == '/')
+ {
+ size_t len = strlen (pies_log_dev);
+ if (len >= sizeof log_sa.s_un.sun_path)
+ {
+ fallback_log ("%s: UNIX socket name too long", pies_log_dev);
+ return -1;
+ }
+ strcpy (log_sa.s_un.sun_path, pies_log_dev);
+ log_sa.s_un.sun_family = AF_UNIX;
+ log_family = PF_UNIX;
+ log_salen = sizeof (log_sa.s_un);
+ }
+ else
+ {
+ struct addrinfo hints;
+ struct addrinfo *res;
+ int rc;
+ char *node;
+ char *service;
+
+ node = strdup (pies_log_dev);
+ if (!node)
+ return -1;
+
+ service = strchr (node, ':');
+ if (service)
+ *service++ = 0;
+ else
+ service = "syslog";
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+
+ rc = getaddrinfo (node, service, &hints, &res);
+ free(node);
+ if (rc)
+ {
+ fallback_log ("%s: invalid socket address", pies_log_dev);
+ return -1;
+ }
+
+ memcpy (&log_sa, res->ai_addr, res->ai_addrlen);
+ log_family = PF_INET;
+ log_salen = res->ai_addrlen;
+ freeaddrinfo (res);
+ }
+ }
+
+ fd = socket (log_family, SOCK_DGRAM, 0);
+
+ if (fd == -1)
+ {
+ fallback_log ("socket: %s", strerror (errno));
+ return -1;
+ }
+
+ if ((flags = fcntl (fd, F_GETFL)) == -1 ||
+ fcntl (fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
+ (flags = fcntl (fd, F_GETFD)) == -1 ||
+ fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ {
+ close (fd);
+ return -1;
+ }
+
+ if (connect(fd, (struct sockaddr*)&log_sa, log_salen))
+ {
+ fallback_log("socket: %s", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ log_fd = fd;
+
+ return 0;
+}
+
+static struct log_message_in *
+log_message_in_create (void)
+{
+ struct log_message_in *msg;
+
+ if ((msg = malloc (sizeof (*msg))) != NULL)
+ {
+ msg->msg.text = msg->buf;
+ msg->msg.len = 0;
+ msg->msg.drop_count = 0;
+ msg->msg.next = NULL;
+ }
+ return msg;
+}
+
+static void
+log_message_in_format (struct log_message_in *msg,
+ int prio, char const *msgtext, char const *tag,
+ pid_t pid)
+{
+ char tbuf[sizeof ("Jan 1 00:00:00")];
+ char hostbuf[HOST_NAME_MAX+1];
+ struct timeval tv;
+ struct tm tm;
+
+ gettimeofday (&tv, NULL);
+ localtime_r (&tv.tv_sec, &tm);
+ strftime (tbuf, sizeof (tbuf), "%b %d %H:%M:%S", &tm);
+
+ /* Supply default facility, unless prio already contains one.
+ Note: this means that we cannot use LOG_KERN, but that doesn't
+ really matter as we're not a kernel, anyway. */
+ if (pri_facility (prio) == 0)
+ prio |= pies_log_facility;
+
+ if (log_family == PF_UNIX)
+ {
+ msg->msg.len = snprintf (msg->buf, sizeof (msg->buf),
+ "<%d>%s %s[%lu]: %s",
+ prio,
+ tbuf,
+ tag,
+ (unsigned long)pid,
+ msgtext);
+ }
+ else
+ {
+ gethostname (hostbuf, sizeof (hostbuf));
+ msg->msg.len = snprintf (msg->buf, sizeof (msg->buf),
+ "<%d>%s %s %s[%lu]: %s",
+ prio,
+ tbuf,
+ hostbuf,
+ tag,
+ (unsigned long)pid,
+ msgtext);
+ }
+}
+
+static struct log_message *
+log_message_create (int prio, char const *msgtext, char const *tag, pid_t pid)
+{
+ struct log_message_in *msg;
+
+ if ((msg = log_message_in_create ()) != NULL)
+ log_message_in_format (msg, prio, msgtext, tag, pid);
+ return &msg->msg;
+}
+
+/* Log message queue */
+static struct log_message *log_queue_head, *log_queue_tail;
+static size_t log_queue_length;
+
+static void
+log_message_putback (struct log_message *msg)
+{
+ msg->next = log_queue_head;
+ log_queue_head = msg;
+ if (!log_queue_tail)
+ log_queue_tail = msg;
+ log_queue_length++;
+}
+
+static struct log_message *
+log_message_dequeue (void)
+{
+ struct log_message *msg = log_queue_head;
+ if (msg)
+ {
+ log_queue_head = msg->next;
+ if (!log_queue_head)
+ log_queue_tail = log_queue_head;
+ msg->next = NULL;
+ log_queue_length--;
+ }
+ return msg;
+}
+
+static void
+log_message_enqueue (struct log_message *inmsg)
+{
+ if (log_queue_length == pies_log_max_queue)
+ {
+ struct log_message *msg;
+ struct log_message_in *tmp;
+ char buf[PIES_LOG_BUF_SIZE];
+
+ /* Dequeue first message */
+ msg = log_message_dequeue ();
+
+ if (msg->drop_count == 0)
+ {
+ /* If it is not a drop message, free it and create a new
+ drop message */
+ free (msg);
+ tmp = log_message_in_create ();
+ tmp->msg.drop_count = 1;
+ }
+ else
+ /* Otherwise, cast it to log_message_in */
+ tmp = (struct log_message_in *)msg;
+
+ /* Dequeue and drop the first message */
+ free (log_message_dequeue ());
+ tmp->msg.drop_count++;
+
+ /* Reformat the message text */
+ snprintf (buf, sizeof (buf), "%zu messages dropped", tmp->msg.drop_count);
+ log_message_in_format (tmp,
+ LOG_DAEMON|LOG_CRIT,
+ buf,
+ pies_log_tag,
+ getpid ());
+
+ log_message_putback (&tmp->msg);
+ }
+
+ if (log_queue_tail)
+ log_queue_tail->next = inmsg;
+ else
+ log_queue_head = inmsg;
+ log_queue_tail = inmsg;
+ log_queue_length++;
+ pies_syslog_flush ();
+}
+