PROJECT MOVED -> https://lab.nexedi.com/nexedi/usersyslog
[usersyslog.git] / usersyslog.c
1 /* usersyslog - redirect syslog accesses to a specific logfile */
2 /*
3 * Copyright (C) 2015 Vincent Pelletier <vincent@nexedi.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 /* Needed for RTLD_NEXT */
21 #define _GNU_SOURCE
22
23 #include <string.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <dlfcn.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31
32 #ifdef _PATH_LOG
33 #define ORIGINAL_LOG_SOCKET _PATH_LOG
34 #else
35 /* XXX: not BSD-compatible (/var/run/log) */
36 #define ORIGINAL_LOG_SOCKET "/dev/log"
37 #endif
38
39 static int (*original_connect)(int fd, const struct sockaddr *addr,
40 socklen_t len);
41 static struct sockaddr_un replacement_addr_un;
42
43 /* Call dlsym(RTLD_NEXT, name), abort()'ing with informative message to
44 * stderr if it cannot be found. */
45 static inline void *dlsym_or_abort(const char *name) {
46 char *error;
47 void *symbol;
48 dlerror(); /* Clear any previous error */
49 symbol = dlsym(RTLD_NEXT, name);
50 if (!symbol && (error = dlerror())) {
51 fprintf(stderr, "Error loading '%s': %s\n", name, error);
52 abort();
53 }
54 return symbol;
55 }
56
57 static void __attribute__ ((constructor)) init(void) {
58 const char *log_path;
59 original_connect = dlsym_or_abort("connect");
60 log_path = getenv("LOG_SOCKET");
61 if (!log_path)
62 /* IDEA: If LOG_SOCKET is not set, try $XDG_CONFIG_HOME/log
63 * and maybe warn if the latter does not exist. */
64 log_path = ORIGINAL_LOG_SOCKET;
65 if (strnlen(log_path, sizeof(replacement_addr_un.sun_path) + 1) >=
66 sizeof(replacement_addr_un.sun_path)) {
67 fprintf(stderr, "LOG_SOCKET value too long\n");
68 abort();
69 }
70 replacement_addr_un.sun_family = AF_UNIX;
71 (void)strncpy(replacement_addr_un.sun_path, log_path,
72 sizeof(replacement_addr_un.sun_path));
73 }
74
75 int connect(int fd, const struct sockaddr *addr, socklen_t len) {
76 const struct sockaddr_un *addr_un = (const struct sockaddr_un *) addr;
77 // XXX: len >= sizeof(addr_un) ?
78 if (addr->sa_family == AF_UNIX && len == sizeof(*addr_un) &&
79 !strcmp(addr_un->sun_path, ORIGINAL_LOG_SOCKET))
80 addr = (const struct sockaddr *) &replacement_addr_un;
81 return (*original_connect)(fd, addr, len);
82 }
83
84 int main(int argc, char *argv[]) {
85 char *ld_preload, *p;
86 int ret = -1;
87
88 if (argc <= 1)
89 fprintf(stderr, "usage: usersyslog <command> [<args>]\n");
90 else {
91 /* In order not to depend on /proc, getauxval(AT_EXECFN) from <sys/auxv.h>
92 * should be used. However, this requires glibc >= 2.16. */
93 ld_preload = realpath("/proc/self/exe", NULL);
94 if (!ld_preload)
95 p = "realpath";
96 else {
97 if ((p = getenv("LD_PRELOAD"))) {
98 size_t n = strlen(ld_preload);
99 ld_preload = realloc(ld_preload, n + strlen(p) + 2);
100 ld_preload[n++] = ':';
101 strcpy(ld_preload + n, p);
102 }
103 ret = setenv("LD_PRELOAD", ld_preload, 1);
104 free(ld_preload);
105 if (ret)
106 p = "setenv";
107 else {
108 ret = execvp(argv[1], &argv[1]);
109 p = "execvp";
110 }
111 }
112 perror(p);
113 }
114 return ret;
115 }