├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile.am ├── PATENTS ├── README.md ├── configure.ac └── src ├── Makefile.am ├── liblogfaf.c └── logfaf.in /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Facebook has adopted a Code of Conduct that we expect project participants to adhere to. 4 | Please read the [full text](https://code.fb.com/codeofconduct/) 5 | so that you can understand what actions will and will not be tolerated. 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to `liblogfaf` 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Pull Requests 6 | We actively welcome your pull requests. 7 | 8 | 1. Fork the repo and create your branch from `master`. 9 | 2. If you've added code that should be tested, add tests. 10 | 3. If you've changed APIs, update the documentation. 11 | 4. Ensure the test suite passes. 12 | 5. Make sure your code lints. 13 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 14 | 15 | ## Contributor License Agreement ("CLA") 16 | In order to accept your pull request, we need you to submit a CLA. You only need 17 | to do this once to work on any of Facebook's open source projects. 18 | 19 | Complete your CLA here: 20 | 21 | ## Issues 22 | We use GitHub issues to track public bugs. Please ensure your description is 23 | clear and has sufficient instructions to be able to reproduce the issue. 24 | 25 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 26 | disclosure of security bugs. In those cases, please go through the process 27 | outlined on that page and do not file a public issue. 28 | 29 | ## License 30 | By contributing to `dhcplb`, you agree that your contributions will be licensed 31 | under the LICENSE file in the root directory of this source tree. 32 | 33 | # I don't want to make a pull request! 34 | We love pull requests, but it's not necessary to write code to contribute. If 35 | for any reason you can't make a pull request (e.g. you just want to suggest us 36 | an improvement), let us know. 37 | [Create an issue](https://help.github.com/articles/creating-an-issue/) 38 | on the `dhcplb` issue tracker and we will review your request. 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Facebook, Inc. and its affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. 2 | 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | SUBDIRS = src 6 | -------------------------------------------------------------------------------- /PATENTS: -------------------------------------------------------------------------------- 1 | Additional Grant of Patent Rights Version 2 2 | 3 | "Software" means the liblogfaf software distributed by Facebook, Inc. 4 | 5 | Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software 6 | ("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable 7 | (subject to the termination provision below) license under any Necessary 8 | Claims, to make, have made, use, sell, offer to sell, import, and otherwise 9 | transfer the Software. For avoidance of doubt, no license is granted under 10 | Facebook’s rights in any patent claims that are infringed by (i) modifications 11 | to the Software made by you or any third party or (ii) the Software in 12 | combination with any software or other technology. 13 | 14 | The license granted hereunder will terminate, automatically and without notice, 15 | if you (or any of your subsidiaries, corporate affiliates or agents) initiate 16 | directly or indirectly, or take a direct financial interest in, any Patent 17 | Assertion: (i) against Facebook or any of its subsidiaries or corporate 18 | affiliates, (ii) against any party if such Patent Assertion arises in whole or 19 | in part from any software, technology, product or service of Facebook or any of 20 | its subsidiaries or corporate affiliates, or (iii) against any party relating 21 | to the Software. Notwithstanding the foregoing, if Facebook or any of its 22 | subsidiaries or corporate affiliates files a lawsuit alleging patent 23 | infringement against you in the first instance, and you respond by filing a 24 | patent infringement counterclaim in that lawsuit against that party that is 25 | unrelated to the Software, the license granted hereunder will not terminate 26 | under section (i) of this paragraph due to such counterclaim. 27 | 28 | A "Necessary Claim" is a claim of a patent owned by Facebook that is 29 | necessarily infringed by the Software standing alone. 30 | 31 | A "Patent Assertion" is any lawsuit or other action alleging direct, indirect, 32 | or contributory infringement or inducement to infringe any patent, including a 33 | cross-claim or counterclaim. 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # liblogfaf 2 | Making syslog() not block 3 | 4 | liblogfaf (faf stands for fire-and-forget) is a dynamic library that is 5 | designed to be LD_PRELOAD-ed while starting a process that uses openlog() & 6 | syslog() functions to send syslog messages. It overrides logging functions to 7 | make log messages sent as UDP datagrams instead of getting written to /dev/log 8 | (which can block). This is useful for processes that call syslog() as part of 9 | their main execution flow and can therefore be easily broken when /dev/log 10 | buffer gets full, for example when the process that is expected to read from it 11 | (usually system syslog daemon like rsyslog or syslog-ng) stops doing that. 12 | 13 | Please note that liblogfaf should *not* be used in an environment where 14 | reliable log message delivery is required. 15 | 16 | ## Requirements 17 | liblogfaf is known to work on Linux, FreeBSD, and OS X. 18 | 19 | ## Building and installing liblogfaf 20 | * Ensure you have libtool, autoconf, and automake installed; 21 | * run `autoreconf -i` to generate autoconf and automake files; 22 | * run `./configure`. You can pass the '--enable-debug' option to build 23 | liblogfaf in debug mode; 24 | * `make` will build everything; 25 | * `make install` will install the library along with the `logfaf` script. 26 | 27 | ## Using liblogfaf 28 | The library comes with a `logfaf` script that can be used to start any binary 29 | with liblogfaf enabled. For example, if you run: 30 | 31 | echo test | logfaf logger 32 | 33 | You should see logger sending syslog UDP messages to 127.0.0.1:514 34 | 35 | ## License 36 | MIT license 37 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. 2 | 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | AC_INIT(liblogfaf, [1.0], [antont@fb.com]) 7 | AC_CONFIG_SRCDIR([src/liblogfaf.c]) 8 | AM_CONFIG_HEADER([config.h]) 9 | AM_INIT_AUTOMAKE([foreign]) 10 | 11 | AC_PROG_CC 12 | AC_PROG_INSTALL 13 | AC_PROG_LN_S 14 | 15 | AC_ENABLE_SHARED 16 | AC_DISABLE_STATIC 17 | AC_PROG_LIBTOOL 18 | 19 | AC_HEADER_STDC 20 | AC_C_CONST 21 | 22 | AC_MSG_CHECKING([build with debug output]) 23 | AC_ARG_ENABLE([debug], 24 | [AS_HELP_STRING([--enable-debug], 25 | [enable debug output [default=no]])], 26 | [debug_enabled="$enableval"], 27 | [debug_enabled=no]) 28 | AC_MSG_RESULT([$debug_enabled]) 29 | 30 | if test x"$debug_enabled" != x"yes"; then 31 | AM_CFLAGS="$AM_CFLAGS -DNDEBUG" 32 | fi 33 | 34 | SYSTEM=`uname -s 2>/dev/null` 35 | case "$SYSTEM" in 36 | "Linux"|"Darwin") 37 | DL_LDLIBS="-ldl" 38 | ;; 39 | "FreeBSD") 40 | DL_LDLIBS="-lc" 41 | ;; 42 | *) 43 | AC_MSG_ERROR(["Unsupported platform: $SYSTEM"]) 44 | ;; 45 | esac 46 | 47 | AC_SUBST([AM_CFLAGS]) 48 | AC_SUBST([DL_LDLIBS]) 49 | 50 | AC_CONFIG_FILES([Makefile src/Makefile]) 51 | AC_OUTPUT 52 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. 2 | 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | AUTOMAKE_OPTIONS = foreign 7 | CFLAGS += -Wall 8 | 9 | lib_LTLIBRARIES = liblogfaf.la 10 | liblogfaf_la_SOURCES = liblogfaf.c 11 | 12 | liblogfaf_la_LIBADD=@DL_LDLIBS@ 13 | 14 | nodist_bin_SCRIPTS = logfaf 15 | dist_noinst_DATA = logfaf.in 16 | DISTCLEANFILES = logfaf 17 | 18 | logfaf:: Makefile $(srcdir)/logfaf.in 19 | rm -f $(srcdir)/logfaf.tmp $(srcdir)/logfaf && \ 20 | $(SED) -e 's|@libdir\@|$(libdir)|g' $(srcdir)/logfaf.in > \ 21 | $(srcdir)/logfaf.tmp && mv $(srcdir)/logfaf.tmp $(srcdir)/logfaf 22 | -------------------------------------------------------------------------------- /src/liblogfaf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #if defined(__APPLE__) 19 | #include 20 | #define HOST_NAME_MAX 255 21 | #else 22 | #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX 23 | #endif 24 | 25 | #define MAX_MESSAGE_LEN 65536 26 | 27 | // From RFC3164 28 | static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", 29 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; 30 | 31 | typedef struct { 32 | char hostname[HOST_NAME_MAX]; 33 | char progname[1024]; 34 | 35 | int syslog_facility; 36 | const char *syslog_tag; 37 | 38 | struct addrinfo *serveraddr; 39 | struct addrinfo *bind_ip; 40 | int sockfd; 41 | 42 | pthread_mutex_t lock; 43 | } SharedData; 44 | 45 | static SharedData shared_data = { "", "" }; 46 | 47 | #ifdef NDEBUG 48 | #define DBG(x) 49 | #else 50 | #define DBG(x) debugprintf x 51 | 52 | static void debugprintf(char *fmt, ...) { 53 | va_list ap; 54 | va_start(ap, fmt); 55 | vfprintf(stderr, fmt, ap); 56 | va_end(ap); 57 | fflush(stderr); 58 | } 59 | #endif 60 | 61 | static void set_defaults(SharedData *sd) { 62 | char *slash_ptr = strrchr(sd->progname, '/'); 63 | // If progname contains a slash, extract basename to use it as syslog tag 64 | sd->syslog_tag = slash_ptr ? slash_ptr + 1 : sd->progname; 65 | sd->syslog_facility = 0; 66 | } 67 | 68 | static void init_progname(SharedData *sd) { 69 | #if defined(__APPLE__) 70 | sscanf(*_NSGetProgname(), "%1023s", sd->progname); 71 | #elif defined(__FreeBSD__) 72 | sscanf(getprogname(), "%1023s", sd->progname); 73 | #else 74 | FILE* cmdline = fopen("/proc/self/cmdline", "rb"); 75 | if (cmdline) { 76 | fscanf(cmdline, "%1023s", sd->progname); 77 | fclose(cmdline); 78 | } 79 | #endif 80 | } 81 | 82 | static void init_hostname(SharedData *sd) { 83 | if (gethostname(sd->hostname, sizeof(sd->hostname)) != 0) { 84 | perror("liblogfaf: gethostname() error"); 85 | exit(1); 86 | } 87 | // We don't really need FQDN, so we truncate the hostname up until 88 | // the first dot. 89 | char *dot_ptr = strchr(sd->hostname, '.'); 90 | if (dot_ptr != NULL) { 91 | *dot_ptr = '\0'; 92 | } 93 | } 94 | 95 | static void init_connection(SharedData *sd) { 96 | char *server_hostname; 97 | server_hostname = getenv("LIBLOGFAF_SERVER"); 98 | if (server_hostname == NULL) 99 | server_hostname = "localhost"; 100 | 101 | char *server_port; 102 | server_port = getenv("LIBLOGFAF_PORT"); 103 | if (server_port == NULL) 104 | server_port = "514"; 105 | 106 | char *bind_ip; 107 | bind_ip = getenv("LIBLOGFAF_BIND_IP"); 108 | 109 | int gai_error; 110 | struct addrinfo hints; 111 | 112 | memset(&hints, 0, sizeof(struct addrinfo)); 113 | hints.ai_family = AF_UNSPEC; 114 | hints.ai_socktype = SOCK_DGRAM; 115 | hints.ai_flags = 0; 116 | hints.ai_protocol = 0; 117 | 118 | gai_error = getaddrinfo(server_hostname, server_port, 119 | &hints, &sd->serveraddr); 120 | if (gai_error != 0) { 121 | fprintf(stderr, "liblogfaf: getaddrinfo() failed: %s\n", 122 | gai_strerror(gai_error)); 123 | exit(1); 124 | } 125 | 126 | if ((sd->sockfd = socket(sd->serveraddr->ai_family, 127 | sd->serveraddr->ai_socktype, 128 | sd->serveraddr->ai_protocol)) < 0) { 129 | perror("liblogfaf: cannot create socket"); 130 | exit(1); 131 | } 132 | 133 | if (bind_ip) { 134 | gai_error = getaddrinfo(bind_ip, 0, 135 | &hints, &sd->bind_ip); 136 | if (gai_error != 0) { 137 | fprintf(stderr, 138 | "liblogfaf: getaddrinfo() failed for " 139 | "LIBLOGFAF_BIND_IP: %s\n", 140 | gai_strerror(gai_error)); 141 | exit(1); 142 | } 143 | if (bind(sd->sockfd, sd->bind_ip->ai_addr, sd->bind_ip->ai_addrlen)) { 144 | perror("liblogfaf: bind() failed"); 145 | exit(1); 146 | } 147 | } 148 | } 149 | 150 | static void logmessage(SharedData *sd, int priority, const char *message) { 151 | DBG(("liblogfaf: logmessage(%d, %s)\n", priority, message)); 152 | time_t ts; 153 | struct tm time_tm; 154 | char msg[MAX_MESSAGE_LEN]; 155 | ts = time(NULL); 156 | localtime_r(&ts, &time_tm); 157 | 158 | snprintf(msg, MAX_MESSAGE_LEN, "<%u>%s %2d %02d:%02d:%02d %s %s: %s", 159 | priority + sd->syslog_facility * 8, 160 | months[time_tm.tm_mon], time_tm.tm_mday, 161 | time_tm.tm_hour, time_tm.tm_min, time_tm.tm_sec, 162 | (char *)&sd->hostname, sd->syslog_tag, message); 163 | 164 | // We want fire-and-forget, so lack of error checking here is intentional 165 | sendto(sd->sockfd, msg, strlen(msg), 0, 166 | sd->serveraddr->ai_addr, sd->serveraddr->ai_addrlen); 167 | } 168 | 169 | __attribute__((constructor)) static void _liblogfaf_init(void) { 170 | DBG(("liblogfaf: init()\n")); 171 | init_progname(&shared_data); 172 | init_hostname(&shared_data); 173 | init_connection(&shared_data); 174 | if (pthread_mutex_init(&shared_data.lock, NULL) != 0) { 175 | fprintf(stderr, "liblogfaf: pthread_mutex_init() failed\n"); 176 | exit(1); 177 | } 178 | set_defaults(&shared_data); 179 | } 180 | 181 | __attribute__((destructor)) static void _liblogfaf_fini(void) { 182 | DBG(("liblogfaf: fini()\n")); 183 | if (pthread_mutex_destroy(&shared_data.lock) != 0) { 184 | fprintf(stderr, "liblogfaf: pthread_mutex_destroy() failed\n"); 185 | exit(1); 186 | } 187 | freeaddrinfo(shared_data.serveraddr); 188 | } 189 | 190 | void openlog(const char *ident, int option, int facility) { 191 | DBG(("liblogfaf: openlog(%s, %d, %d)\n", ident, option, facility)); 192 | if (pthread_mutex_lock(&shared_data.lock) != 0) { 193 | fprintf(stderr, "liblogfaf: pthread_mutex_lock() failed\n"); 194 | exit(1); 195 | } 196 | shared_data.syslog_facility = facility; 197 | if (ident) 198 | shared_data.syslog_tag = ident; 199 | if (pthread_mutex_unlock(&shared_data.lock) != 0) { 200 | fprintf(stderr, "liblogfaf: pthread_mutex_unlock() failed\n"); 201 | exit(1); 202 | } 203 | // making use of the `option` parameter can be added here if you need it 204 | } 205 | 206 | void closelog(void) { 207 | DBG(("liblogfaf: closelog()\n")); 208 | if (pthread_mutex_lock(&shared_data.lock) != 0) { 209 | fprintf(stderr, "liblogfaf: pthread_mutex_lock() failed\n"); 210 | exit(1); 211 | } 212 | set_defaults(&shared_data); 213 | if (pthread_mutex_unlock(&shared_data.lock) != 0) { 214 | fprintf(stderr, "liblogfaf: pthread_mutex_unlock() failed\n"); 215 | exit(1); 216 | } 217 | } 218 | 219 | void __syslog_chk(int priority, int flag, const char *format, ...) { 220 | DBG(("liblogfaf: __syslog_chk(%d, %d, %s)\n", 221 | priority, flag, format)); 222 | va_list ap; 223 | char str[MAX_MESSAGE_LEN]; 224 | va_start(ap, format); 225 | vsnprintf(str, MAX_MESSAGE_LEN, format, ap); 226 | va_end(ap); 227 | logmessage(&shared_data, priority, str); 228 | } 229 | 230 | void syslog(int priority, const char *format, ...) { 231 | DBG(("liblogfaf: syslog(%d, %s)\n", priority, format)); 232 | va_list ap; 233 | char str[MAX_MESSAGE_LEN]; 234 | va_start(ap, format); 235 | vsnprintf(str, MAX_MESSAGE_LEN, format, ap); 236 | va_end(ap); 237 | logmessage(&shared_data, priority, str); 238 | } 239 | 240 | -------------------------------------------------------------------------------- /src/logfaf.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright (c) Facebook, Inc. and its affiliates. 4 | 5 | # This source code is licensed under the MIT license found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | set -e -u 9 | 10 | help() { 11 | >&2 echo "Usage: ${0##*/} [-h] [-S servername] [-P port] [-B ip] " 12 | >&2 echo " -h show help" 13 | >&2 echo " -S servername set syslog server name (default: localhost)" 14 | >&2 echo " -P port set syslog port (default: 514)" 15 | >&2 echo " -B ip bind to ip (default: no binding)" 16 | } 17 | 18 | while getopts :hS:P:B: OPT; do 19 | case ${OPT} in 20 | h) 21 | help 22 | exit 1 23 | ;; 24 | 25 | S) 26 | export LIBLOGFAF_SERVER=${OPTARG} 27 | ;; 28 | 29 | P) 30 | export LIBLOGFAF_PORT=${OPTARG} 31 | ;; 32 | 33 | B) 34 | export LIBLOGFAF_BIND_IP=${OPTARG} 35 | ;; 36 | 37 | \?) 38 | help 39 | exit 1 40 | ;; 41 | esac 42 | done 43 | shift $((OPTIND-1)) 44 | 45 | PLATFORM=$(uname -s) 46 | if [ "$PLATFORM" = "Darwin" ]; then 47 | DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES="@libdir@/liblogfaf.0.dylib" exec "$@" 48 | else 49 | LIB_PATH="@libdir@/liblogfaf.so" 50 | LD_PRELOAD="${LIB_PATH}:${LD_PRELOAD:-}" exec "$@" 51 | fi 52 | --------------------------------------------------------------------------------