├── .gitignore ├── Makefile └── enviable.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBS = -ldl 2 | CFLAGS = 3 | 4 | CFLAGS += -DDEBUG 5 | 6 | libenviable.so: enviable.c 7 | $(CC) $(CFLAGS) -fPIC -shared -o $@ $< $(LIBS) 8 | 9 | clean: 10 | rm -f libenviable.so 11 | 12 | .PHONY: clean 13 | -------------------------------------------------------------------------------- /enviable.c: -------------------------------------------------------------------------------- 1 | /* enviable - An LD_PRELOAD to set environment variables at runtime. 2 | * Usage: LD_PRELOAD=./libenviable.so bash 3 | * 4 | * 5 | * Copyright (c) 2013 Geoffrey Thomas 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | */ 29 | 30 | #define _GNU_SOURCE 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #ifdef DEBUG 40 | #include 41 | #define maybe_perror(s) perror(s) 42 | #else 43 | #define maybe_perror(s) 44 | #endif 45 | 46 | static int (*real___libc_current_sigrtmin)(void) = NULL; 47 | 48 | static char *watchfile = "/tmp/vars"; 49 | 50 | /* The following definitions are stolen from bash-4.2 */ 51 | __attribute__((weak)) 52 | extern int do_assignment_no_expand(char *); 53 | __attribute__((weak)) 54 | extern void set_var_attribute(char *, int, int); 55 | #define att_exported 1 56 | 57 | int 58 | __libc_current_sigrtmin(void) 59 | { 60 | return real___libc_current_sigrtmin() + 1; 61 | } 62 | 63 | static void 64 | enviable_setenv(char *line) 65 | { 66 | char *eq = strchr(line, '='); 67 | if (do_assignment_no_expand && set_var_attribute) { 68 | /* We're running inside bash. Since bash maintains its 69 | * own idea of the environment (shell variables marked 70 | * exported), we need to go through that for child 71 | * processes to see changes. */ 72 | do_assignment_no_expand(line); 73 | *eq = '\0'; 74 | set_var_attribute(line, att_exported, 0); 75 | } else { 76 | /* Unknown host process -- fall back to libc. */ 77 | *eq = '\0'; 78 | setenv(line, eq + 1, 1); 79 | } 80 | } 81 | 82 | static void 83 | enviable_callback(int signum, siginfo_t *si, void *context) 84 | { 85 | struct inotify_event event; 86 | if (read(si->si_fd, &event, sizeof(event)) != sizeof(event)) 87 | return; 88 | 89 | FILE *fd = fopen(watchfile, "r"); 90 | if (!fd) { 91 | return; 92 | } 93 | char *line = NULL; 94 | size_t n = 0; 95 | ssize_t len; 96 | while ((len = getline(&line, &n, fd)) > 0) { 97 | if (line[len - 1] == '\n') { 98 | line[len - 1] = '\0'; 99 | } else { 100 | /* Conservatively reject partial lines. */ 101 | continue; 102 | } 103 | enviable_setenv(line); 104 | } 105 | free(line); 106 | fclose(fd); 107 | } 108 | 109 | void __attribute__((constructor)) 110 | enviable_init(void) 111 | { 112 | real___libc_current_sigrtmin = dlsym(RTLD_NEXT, "__libc_current_sigrtmin"); 113 | if (!real___libc_current_sigrtmin) { 114 | /* Give up; we can't safely claim a signal. */ 115 | maybe_perror("enviable: incompatible libc"); 116 | return; 117 | } 118 | 119 | int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); 120 | if (fd == -1) { 121 | maybe_perror("enviable: inotify_init"); 122 | return; 123 | } 124 | 125 | int watch = inotify_add_watch(fd, watchfile, IN_CLOSE_WRITE); 126 | if (watch == -1) { 127 | maybe_perror("enviable: inotify_add_watch"); 128 | goto err; 129 | } 130 | 131 | int sigrtmin = real___libc_current_sigrtmin(); 132 | 133 | struct sigaction act = { 134 | .sa_sigaction = enviable_callback, 135 | .sa_mask = 0, 136 | .sa_flags = SA_SIGINFO, 137 | }, oldact; 138 | 139 | if (sigaction(sigrtmin, &act, &oldact) == -1) { 140 | maybe_perror("enviable: sigaction"); 141 | goto err; 142 | } 143 | 144 | if ((oldact.sa_flags & SA_SIGINFO) || 145 | (oldact.sa_handler != SIG_DFL)) { 146 | /* Oops. Someone else already claimed a handler! */ 147 | goto err_sig; 148 | } 149 | 150 | int flags = fcntl(fd, F_GETFL); 151 | if (flags == -1) { 152 | maybe_perror("enviable: fcntl F_GETFL"); 153 | goto err_sig; 154 | } 155 | if (fcntl(fd, F_SETFL, flags | O_ASYNC) == -1) { 156 | maybe_perror("enviable: fcntl F_SETFL O_ASYNC"); 157 | goto err_sig; 158 | } 159 | if (fcntl(fd, F_SETOWN, getpid()) == -1) { 160 | maybe_perror("enviable: fcntl F_SETOWN"); 161 | goto err_sig; 162 | } 163 | if (fcntl(fd, F_SETSIG, sigrtmin) == -1) { 164 | maybe_perror("enviable: fcntl F_SETSIG"); 165 | goto err_sig; 166 | } 167 | 168 | return; 169 | err_sig: 170 | sigaction(sigrtmin, &oldact, NULL); 171 | err: 172 | close(fd); 173 | } 174 | --------------------------------------------------------------------------------