Commit ad6f6411 authored by Vincent Pelletier's avatar Vincent Pelletier

Initial commit.

parents
/usersyslog
This diff is collapsed.
CFLAGS ?= -O2 -s
CFLAGS += -Wall -fPIE -pie
LDLIBS = -ldl
PREFIX = /usr/local
.PHONY: all install uninstall clean
all: usersyslog
usersyslog: usersyslog.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS)
install: all
install -Dp usersyslog $(DESTDIR)$(PREFIX)/bin/usersyslog
uninstall:
-cd $(DESTDIR)$(PREFIX) && rm -f bin/usersyslog
clean:
-rm -f usersyslog
usersyslog - redirect syslog accesses to a specific unix socket.
Rationale
---------
The need is to customise the socket used to send log lines to syslog, so as
to direct logging of processes to a user-specified (typically, user-controlled)
syslog daemon. Useful for software packages only able to log via syslog
(ex: postfix).
User syslog daemon must be able to bind to non-default socket path.
Limitations
-----------
This library relies on LD_PRELOAD mechanism, so it inherits its limitations
(it will not work on suid binaries, ...).
Requirements
------------
- make
- gcc (source uses __attribute__)
- glibc (source uses RTLD_NEXT)
Building
--------
Just run "make".
Usage
-----
$ LOG_SOCKET=/path/to/custom/log/socket LD_PRELOAD=/path/to/usersyslog <command>
`usersyslog` is also an executable wrapper that appends itself to LD_PRELOAD:
$ LOG_SOCKET=/path/to/custom/log/socket usersyslog <command>
/* usersyslog - redirect syslog accesses to a specific logfile */
/*
* Copyright (C) 2015 Vincent Pelletier <vincent@nexedi.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Needed for RTLD_NEXT */
#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#ifdef _PATH_LOG
#define ORIGINAL_LOG_SOCKET _PATH_LOG
#else
/* XXX: not BSD-compatible (/var/run/log) */
#define ORIGINAL_LOG_SOCKET "/dev/log"
#endif
static int (*original_connect)(int fd, const struct sockaddr *addr,
socklen_t len);
static struct sockaddr_un replacement_addr_un;
/* Call dlsym(RTLD_NEXT, name), abort()'ing with informative message to
* stderr if it cannot be found. */
static inline void *dlsym_or_abort(const char *name) {
char *error;
void *symbol;
dlerror(); /* Clear any previous error */
symbol = dlsym(RTLD_NEXT, name);
if (!symbol && (error = dlerror())) {
fprintf(stderr, "Error loading '%s': %s\n", name, error);
abort();
}
return symbol;
}
static void __attribute__ ((constructor)) init(void) {
const char *log_path;
original_connect = dlsym_or_abort("connect");
log_path = getenv("LOG_SOCKET");
if (!log_path)
/* IDEA: If LOG_SOCKET is not set, try $XDG_CONFIG_HOME/log
* and maybe warn if the latter does not exist. */
log_path = ORIGINAL_LOG_SOCKET;
if (strnlen(log_path, sizeof(replacement_addr_un.sun_path) + 1) >=
sizeof(replacement_addr_un.sun_path)) {
fprintf(stderr, "LOG_SOCKET value too long\n");
abort();
}
replacement_addr_un.sun_family = AF_UNIX;
(void)strncpy(replacement_addr_un.sun_path, log_path,
sizeof(replacement_addr_un.sun_path));
}
int connect(int fd, const struct sockaddr *addr, socklen_t len) {
const struct sockaddr_un *addr_un = (const struct sockaddr_un *) addr;
// XXX: len >= sizeof(addr_un) ?
if (addr->sa_family == AF_UNIX && len == sizeof(*addr_un) &&
!strcmp(addr_un->sun_path, ORIGINAL_LOG_SOCKET))
addr = (const struct sockaddr *) &replacement_addr_un;
return (*original_connect)(fd, addr, len);
}
int main(int argc, char *argv[]) {
char *ld_preload, *p;
int ret = -1;
if (argc <= 1)
fprintf(stderr, "usage: usersyslog <command> [<args>]\n");
else {
/* In order not to depend on /proc, getauxval(AT_EXECFN) from <sys/auxv.h>
* should be used. However, this requires glibc >= 2.16. */
ld_preload = realpath("/proc/self/exe", NULL);
if (!ld_preload)
p = "realpath";
else {
if ((p = getenv("LD_PRELOAD"))) {
size_t n = strlen(ld_preload);
ld_preload = realloc(ld_preload, n + strlen(p) + 2);
ld_preload[n++] = ':';
strcpy(ld_preload + n, p);
}
ret = setenv("LD_PRELOAD", ld_preload, 1);
free(ld_preload);
if (ret)
p = "setenv";
else {
ret = execvp(argv[1], &argv[1]);
p = "execvp";
}
}
perror(p);
}
return ret;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment