30

I have a program that stores its settings in ~/.config/myprogram that I use both interactively and with a batch queuing system. When running interactively, I want this program to use my configuration files (and it does). But when running in batch mode, the configuration files aren't necessary because I specify command-line options that overwrite all the relevant settings. Further, accessing the configuration files over the network increases the program's startup time by several seconds; if the files don't exist, the program launches much faster (as each job only takes about a minute, this has a significant impact on batch job throughput). But because I also use the program interactively, I don't want to be moving/deleting my configuration files all the time. Depending on when my batch jobs get scheduled on the cluster (based on other users' usage), I may want to use the program interactively and as part of a batch job at the same time.

(Aside: that network file performance is so slow is probably a bug, but I'm just a user of the cluster, so I can only work around it, not fix it.)

I could build a version of the program that doesn't read the configuration files (or has a command-line option not to) for batch use, but this program's build environment is poorly-engineered and difficult to set up. I'd much prefer to use the binaries installed through my system's package manager.

How can I trick particular instances of this program into pretending my configuration files don't exist (without modifying the program)? I'm hoping for a wrapper of the form pretendfiledoesntexist ~/.config/myprogram -- myprogram --various-options..., but I'm open to other solutions.

  • 2
    You could run it as a user who doesn't have permissions to read the file. – psimon Jun 26 '14 at 20:27
  • 1
    @psimon As "just a user" of the cluster, I can't create a new user to run my batch job as. That's a clever idea though, and if there are no better suggestions, I'll bug the cluster admin to do it for me. – Jeffrey Bosboom Jun 26 '14 at 20:28
  • Or set up a script that first renames the config file, runs the program and then renames the config file again. – psimon Jun 26 '14 at 20:31
  • @psimon I guess I could have been more clear: I might be using the program interactively and in batch mode at the same time, depending on when my batch jobs get scheduled on the cluster. – Jeffrey Bosboom Jun 26 '14 at 20:32
  • Is this program dynamically linked? (Or, alternatively, which program and which distro? Link to package?) – derobert Jun 26 '14 at 20:34
  • @derobert: fceux (a NES emulator -- I'm working on playing Super Mario Bros. automatically); Ubuntu 12.04 LTS. – Jeffrey Bosboom Jun 26 '14 at 20:36
  • @JeffreyBosboom FYI, the build environment should be fairly easy then: apt-get source fceux; apt-get build-deps fceux – derobert Jun 26 '14 at 20:37
  • Well, if this is the only problem, you can set up a script that takes an option and decides if rename the file or not. Eg. script -i just runs the program and script -b first renames the config file. – psimon Jun 26 '14 at 20:39
  • @derobert: I had difficulty building from the upstream fceux source, but I'll check that out. I'd still prefer an answer that doesn't require rebuilding the program for simplicity, though your "dynamic linking" comment suggests some sort of preload hook, so maybe that's not any simpler. – Jeffrey Bosboom Jun 26 '14 at 20:39
  • @psimon: Wouldn't there be a problem if a batch job moves the file, then I try to launch it interactively (and the file has been moved)? – Jeffrey Bosboom Jun 26 '14 at 20:40
  • 1
    Yes, if its dynamically linked, you can use an LD_PRELOAD hook. That's easier (you can implement that in an hour or two, if you know C) than the alternative, which is ptrace. You could also probably use fakechroot to do this (which is LD_PRELOAD, I believe). – derobert Jun 26 '14 at 20:42
  • http://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/ has an example of LD_PRELOAD. For open, even. – derobert Jun 26 '14 at 20:44
  • @psimon: Would you consider adding your create-a-user comment as an answer? It's not the best answer for me, but it may help future readers (if they already have a 'nobody'-like account to use or have rights to create one), and comments are ephemeral. – Jeffrey Bosboom Jun 26 '14 at 21:03
  • @derobert: Would you consider adding your preload hook comment as an answer? It's not the best answer for me, but it may help future readers, and even if they don't want to write a preload hook, at least they might learn about the possibility. – Jeffrey Bosboom Jun 26 '14 at 21:08
  • @JeffreyBosboom It's nowhere near a complete answer... But if I get some time tonight, I'll write it up as an answer. Probably at least with some sample C code. – derobert Jun 26 '14 at 21:10
  • All of the answers strike me as the wrong approach. psimon is right; if this is per-user configuration, running it with two different configurations implies running it as two different users. – Russell Borogove Jun 27 '14 at 22:59

8 Answers8

32

That program probably resolves the path to that file from $HOME/.config/myprogram. So you could tell it your home directory is elsewhere, like:

HOME=/nowhere your-program

Now, maybe your-program needs some other resource in your home directory. If you know which they are, you can prepare a fake home for your-program with links to the resource it needs in there.

mkdir -p ~/myprogram-home/.config
ln -s ~/.Xauthority ~/myprogram-home/
...
HOME=~/myprogram-home myprogram
  • 5
    This answer solves my problem, so I've accepted it even though it isn't a fully general answer to the question in this question's title. A preload hook, as described in another answer, is a more general (but also higher-effort) solution. – Jeffrey Bosboom Jun 26 '14 at 22:08
27

If all else fails, write a wrapper library that you will inject using LD_PRELOAD so that the call to open("/home/you/my-program/config.interactive") is intercepted but any other will pass through. This works for any type of program, even shell scripts, since it will filter system calls.

extern int errno;

int open(const char *pathname, int flags)
{
  char *config_path = get_config_file_path();
  if (!strstr(pathname, config_path))
  {
    return get_real_open(pathname, flags);
  }
  else
  {
    errno = ENOENT;
    return -1;
  }
}

Note: I have not tested this code, and I am not 100% sure that the errno part works.

Look at how fakeroot does it for calls like getuid(2) and stat(2).

Basically, the linker will link that application to your library, which overrides the open symbol. Since you cannot use two different functions named open in your own library, you have to separate it in a second part (e.g. get_real_open) which will in turn link to the original open call.

Original: ./Application

Application -----> libc.so
            open()

Intercepted: LD_PRELOAD=yourlib_wrap.so ./Application

Application -----> yourlib_wrap.so --------------> yourlib_impl.so -----> libc.so
            open()                 get_real_open()                 open()

Edit: Apparently there is an ld flag you can enable (--wrap <symbol>) that allows you to write wrappers without having to resort to double linking:

/* yourlib.c */
#include <stdio.h>

int __real_open(const char *pathname, int flags)

int __wrap_open(const char *pathname, int flags)
{
  char *config_path = get_config_file_path();
  if (!strstr(pathname, config_path))
  {
    /* the undefined reference here will resolve to "open" at linking time */
    return __real_open(pathname, flags);
  }
  else
  {
    errno = ENOENT;
    return -1; 
  }
}
sleblanc
  • 1,157
2

Move your config file out of the way, and write a shell script wrapper for the interactive use-case that copies the file into its normal destination, runs the program, and deletes it on exit.

tink
  • 6,765
  • See my recent edit: I don't control the batch scheduling, so I may be using the program interactively and as part of a batch job at the same time. – Jeffrey Bosboom Jun 26 '14 at 20:36
1

This should be possible with unionfs / aufs. You create a chroot environment for the process. You use the real directory as read-only layer and put an empty one on top of it. Then you mount the unionfs volume to the respective directory in the chroot environment and delete the file there. The process will not see it but all others do.

Hauke Laging
  • 90,279
0

Rename the config file to e.g. config.interactive. Create another empty file called e.g. config.script.

Now, create a soft link called config (or whatever the application expects as a configuration file) to whichever real config you need and run your application.

ln -s config.interactive config

Remember to tidy up your link afterwards.

garethTheRed
  • 33,957
  • See my recent edit: I don't control the batch scheduling, so I may be using the program interactively and as part of a batch job at the same time. This answer is essentially the same as moving the files around, either manually or with a script. – Jeffrey Bosboom Jun 26 '14 at 20:51
  • 1
    Doh! I need to think and type quicker. Is it a large application? Could it be transferred to a chroot and ran from there interactively? It all depends on what the program interacts with, I suppose. It could also be a very tedious task to get everything it requires into a chroot too. (I think I've talked myself out of that option!) – garethTheRed Jun 26 '14 at 20:58
0

If you’ve precisely characterized how your program uses the configuration file, I’ve overlooked it.  Many programs (such as bash and vi) will check for a configuration file immediately upon start; if the file exists, read it and close it.  These programs never access these initialization files again.  If your program is like that, read on.

I know you’ve rejected answers that make the configuration file truly non-existent (by renaming it), but I have a wrinkle that I haven’t seen proposed by anybody else.  Do this when you invoke the program in batch mode:

DELAY_TIME=1
REALPATH="~/.config/myprogram"
HOLDPATH="${REALPATH}.hold"

mv "$REALPATH" "$HOLDPATH"
(sleep "$DELAY_TIME"; mv "$HOLDPATH" "$REALPATH")&
myprogram

This moves the configuration file out of the way, but then moves it back one second later, even if myprogram is still running.  This creates a very brief window of time during which the file is unavailable – what is the probability that you will run the program interactively during this window?  (Even if you do, you can just exit and restart, and the config file will probably be back in place.)

This does create a race condition; if the program takes too long to get around to opening the file, it might get the real file.  If this happens often enough that it’s a problem, just increase the value of DELAY_TIME.

0

I'm hoping for a wrapper of the form pretendfiledoesntexist ~/.config/myprogram -- myprogram --various-options...

Use firejail.

firejail --noprofile --quiet --blacklist=~/.config/myprogram -- myprogram --various-options...

Strictly this does not trick myprogram the file doesn't exist. It will be access denied rather than no such file. Hopefully access denied will make the program in question behave as you wish anyway.

-1

I like Stephane's answer, but this will trick any program into believing any file is empty - (because its dentry temporarily points to a file that actually is empty):

cat <./test.txt
###OUTPUT###
notempty

mount --bind /dev/null ./test.txt
cat <./test.txt
###NO OUTPUT###

umount ./test.txt
cat <./test.txt
###OUTPUT###
notempty

You could also:

mount --bind ./someotherconfig.conf ./unwanted.conf

If you wanted.

mikeserv
  • 58,310
  • This is essentially equivalent to a couple of the earlier answers (except, I believe, this one requires that the user be privileged). The OP rejected those other answers because he doesn’t want to trick any process – he wants to trick the batch invocation of the program, while letting the interactive invocation see the config file normally. – Scott - Слава Україні Jun 30 '14 at 16:34
  • @Scott - i disagree - every other answer before this one recommended some variation on mving the file - which might have other consequences than affecting its dentry such as actually truncating the file or others and etc - whereas this operate s on nothing but. Still, i should unshare that mount i guess... – mikeserv Jun 30 '14 at 16:39