10

I'm trying to run ADB on a linux server with multiple users where I am not root (to play with my android emulator). The adb daemon writes its logs to the file /tmp/adb.log which unfortunately seems to be hard-coded into ADB and this situation is not going to change.

So, adb is failing to run, giving the obvious error: cannot open '/tmp/adb.log': Permission denied. This file is created by another user and /tmp has sticky bit on. If I start adb with adb nodaemon server making it write to stdout, no errors occur (I also set up its port to a unique value to avoid conflicts).

My question is: is there some way to make ADB write to another file than /tmp/adb.log? More generally, is there a way to create a sort of a process-specific symlink? I want to redirect all file accesses to /tmp/adb.log to, saying, a file ~/tmp/adb.log.

Again, I am not root on the server, so chroot, mount -o rbind and chmod are not valid options. If possible, I'd like not to modify ADB sources, but surely if there are no other solutions, I'll do that.

P.S. For the specific ADB case I can resort to running adb nodaemon server with nohup and output redirection, but the general question is still relevant.

gluk47
  • 238
  • 2
    yes. you can put your process in a private mount namespace, and mount some other file over /tmp/adb.log, or even mount its own private /tmp altogether. do man unshare and man namespaces and man nsenter. – mikeserv Oct 21 '15 at 13:59
  • 1
    @mikeserv great, that seems to be exactly what I need, thank you! If you reformat your comment as an answer, I'll be able to set it as accepted. – gluk47 Oct 21 '15 at 14:07
  • Or there's LD_PRELOAD tricks, though that would be more complicated. – thrig Oct 21 '15 at 14:47
  • @thrig yeah, I though about LD_PRELOAD, but frankly, it would be easier to hardcode /home/$USER/tmp/adb.log and rebuild adb :) – gluk47 Oct 21 '15 at 14:49

2 Answers2

15

LD_PRELOAD isn't too difficult, and you don't need to be root. Interpose your own C routine which is called instead of the real open() in the C library. Your routine checks if the file to open is "/tmp/adb.log" and calls the real open with a different filename. Here's your shim_open.c:

/*
 * capture calls to a routine and replace with your code
 * gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.c
 * LD_PRELOAD=/.../shim_open.so cat /tmp/adb.log
 */
#define _FCNTL_H 1 /* hack for open() prototype */
#define _GNU_SOURCE /* needed to get RTLD_NEXT defined in dlfcn.h */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#define OLDNAME "/tmp/adb.log"
#define NEWNAME "/tmp/myadb.log"

int open(const char *pathname, int flags, mode_t mode){
    static int (*real_open)(const char *pathname, int flags, mode_t mode) = NULL;

    if (!real_open) {
        real_open = dlsym(RTLD_NEXT, "open");
        char *error = dlerror();
        if (error != NULL) {
            fprintf(stderr, "%s\n", error);
            exit(1);
        }
    }
    if (strcmp(pathname,OLDNAME)==0) pathname = NEWNAME;
    fprintf(stderr, "opening: %s\n", pathname);
    return real_open(pathname, flags, mode);
}

Compile it with gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.c and test it by putting something in /tmp/myadb.log and running LD_PRELOAD=/.../shim_open.so cat /tmp/adb.log. Then try the LD_PRELOAD on adb.

meuh
  • 51,383
  • Well, indeed your solution is the only one that I managed to make work being a non-root user. I did not cope with unshare (Operation not permitted). I hope that open is enough to handle, but finally, adding unlink to this handler is not difficult. – gluk47 Oct 21 '15 at 15:33
  • Aww. What a shame that I cannot check two answers. I promised mikeserv to check his solution as an answer, and it's indeed a viable one. – gluk47 Oct 21 '15 at 15:48
  • 2
    never mind. I learned about unshare as well, so we all gain! – meuh Oct 21 '15 at 16:01
  • After some time, thank you again for the LD_PRELOAD sample. Since I tried your code, I'm using LD_PRELOAD in various situations where I'd not even think of it. My life has changed to the better :) – gluk47 Apr 01 '16 at 10:24
  • 2
    @gluk47 That's what is so wonderful about Gnu/Linux: you need never stop exploring! There's so much good stuff to discover and share. – meuh Apr 01 '16 at 12:05
  • Hm. it seems better not to hardcode a path like /lib64/libc.so.6, but rather to use smth like real_open = dlsym(RTLD_NEXT, "open");. It's more flexible and less error detection is required (no need to call and check dlopen). – gluk47 Apr 04 '16 at 11:08
  • @gluk47 You are right. My example is rather old. I tried your version and edited the answer. Thanks. – meuh Apr 04 '16 at 12:34
  • Note that the -ldl should really go at the end of the gcc line if linking and compilation is done together. You can check if you've got it right using ldd. Otherwise if libdl.so.2 is not pulled in somewhere you get the error: undefined symbol: dlsym – carveone Nov 07 '23 at 17:46
7

Here is a very simple example of using util-linux's unshare to put a process in a private mount namespace and give it a different view of the same filesystem its parent currently has:

{   cd /tmp                      #usually a safe place for this stuff
    echo hey   >file             #some
    echo there >file2            #evidence
    sudo unshare -m sh -c '      #unshare requires root by default
         mount -B file2 file     #bind mount there over hey
         cat file                #show it
         kill -TSTP "$$"         #suspend root shell and switch back to parent
         umount file             #unbind there
         cat file'               #show it
    cat file                     #root shell just suspended
    fg                           #bring it back
    cat file2                    #round it off
}

there                            #root shell
hey                              #root shell suspended
hey                              #root shell restored
there                            #rounded

You can give a process a private view of its filesystem with the unshare utility on up-to-date linux systems, though the mount namespace facility itself has been fairly mature for the entire 3.x kernel series. You can enter pre-existing namespaces of all kinds with nsenter utility from the same package, and you can find out more with man.

mikeserv
  • 58,310
  • Just one quiestion: is it me or is it a perfect solution but for the root user only? – gluk47 Oct 21 '15 at 15:35
  • @gluk47 - it doesn't have to be. you can unshare all kinds of namespaces - to include the user namespace. and so your user can run a namespace in which it has root access and anything it does within that a root user might screw up does not affect the parent namespace. in other words, a mount namespace can be embedded within a user namespace. you really need to read those man pages. it gets deep. this is precisely how docker and sytemd-nspawn work. – mikeserv Oct 21 '15 at 15:38
  • I've read those man pages and examples from the Internet) It just seems I need to read them more, just thank you for pointing out to this technology, I somehow was not aware of it at all. – gluk47 Oct 21 '15 at 15:47
  • @gluk47 - don't accept answers for loyalty's sake. while the sentiment is appreciated, that kind of stuff defeats the purpose of this place. accept the answer you use. if that one is not this one, please do not accept this answer. by the way, just because a process is launched as root, doesn't mean it has to remain a root process. there is the runuser utility that can be used with unshare, and if you're open to writing compiled programs anyway, there's no reason you couldn't use the unshare() syscall to do the same, or even just system() with suid binary. – mikeserv Oct 21 '15 at 16:39
  • I'd sure not accept the answer if it was not useful. I find both answers relevant and helpful, so the sentiment is the only distinguishing reason to check one of these answers :) – gluk47 Oct 22 '15 at 09:38
  • I am afraid many server Linux setups do not allow unshare to be performed by non root user for "security reasons" out of the box. – Ben Usman Jun 24 '18 at 08:03