views:

41

answers:

1

I have a bash backup script run as root (cron) that delegates certain tasks to other specific bash scripts owned by different users. (simplified example, principle is, some things have to be done as root, different tasks are delegated to users with the appropriate environment (oracle, amazon, ...)

mkdir -p /tmp/backup$NAME
su - oracle -c "~/.backups/export-test.sh"
tar cf /tmp/backup/$NOW.tar /tmp/backup$NAME
su - amazon upload_to_amazon.sh /tmp/backup/$NOW.tar

This script itself does then some tasks as user oracle:

mkdir -p $TMP_LOCATION
cd ~/.backups
exp $TMP_LOCATION/$NAME-$NOW

When I try to mimic this behaviour in python I came up with the following (started from cron as root)

name = "oracle"

# part run as root
os.makedirs(tmp_backup + name)


os.setegid(pwd.getpwnam(name)[3])
os.seteuid(pwd.getpwnam(name)[2])

# part run as oracle
os.makedirs(tmp_location)
os.chdir(os.path.expanduser("~{user}/.backups".format(user=name)))
subprocess.check_call(["exp",
                       "os.path.join(tmp_location, name+'-'+now)"
                      ])

In bash when using su -, a real new shell is invoked and all environment variables of that user are set. How can I improve this for my python script? Is there a standard recipe I can follow? I'm thinking of environment variables, umask, ...

the environment is Solaris if that might matter.

+1  A: 

all environment variables of that user are set

Usually because a shell runs a .profile file when it starts up.

You have several choices.

  1. Create a proper subprocess with subprocess.Popen to execute the shell .profile -- same as su -.

  2. Carefully locate the environment variable settings and mimic them in Python. The issue is that a .profile can do all kinds of crazy things, making it a potential problem to determine the exact effects of the .profile.

  3. Or you can extract the relevant environment variables to make the accessible to both the shell environment and your Python programs.

First. Read the .profile for each user to be clear on what environment variables it sets (different from things like aliases or other craziness that doesn't apply to your Python script). Some of these environment variables are relevant to the scripts you're running. Some aren't relevant.

Second. Split the "relevant" environment variables into a tidy env_backups.sh script or env_uploads.sh script.

Once you have those environment variable scripts, update your .profile files to replace the environment variables settings with source env_backup.sh or source env_uploads.sh.

Third. Source the relevant env_this and env_that scripts before running the Python program. Now your Python environment shares the variables with your shell environment and you only maintain them in one place.

my_script.sh

source ~oracle/env_backup.sh
source ~amazon/env_uploads.sh
python my_script.py

That seems best to me. (Since that's how we do it.)

S.Lott
Regarding (1), you can execute .profile as a subprocess all day long and it won't modify the environment of the parent -- this isn't really an option. (3) is better, but suffers from the potential problem that oracle's environment variables may get overwritten by amazon's.
bstpierre
(3) does not give seperation of user environments: when running the oracle part, the amazon keys are also in the environment
Joram
@Joram: So? What problem does that cause?
S.Lott
@bstpierre: Since #1 doesn't need to modify the environment of the parent, I don't get the comment.
S.Lott
Are you suggesting that by using `subprocess.Popen`, you'll execute .profile?
bstpierre
@bstpierre: With `shell=True` that's what's supposed to happen.
S.Lott
Non-login shells don't execute .profile, that's only run for new logins. Try putting it in .bashrc (or whatever is appropriate for your shell). See `bash(1)` in the section `INVOCATION`. But the OP's use of `check_call` is a passthrough to `Popen`, so adding `shell=True` to the call to `check_call()` should suffice -- no need to change to using `Popen` directly.
bstpierre
@S.Lott since the tasks are delegated, I also want separation of responsibility (each user has its own working environment for its specific task). I like a lot the separation that happens when using su - with the shell.I want to mimic this concept in python (delegation + separation).I want to call an example function 'backup_oracle' that gets its config and does its task + reports back to the main scriptwithout that the main script needs to know how backup_oracle works, it only needs to know how to call it, pass its config and interpret the results
Joram
@Joram: the separation doesn't actually exist when using `su`. One person can set (or reset) any environment they want. Delegation certainly exists. But left-over environment variables are of no consequence at all; if you stop worrying about that, you might be able to simplify this into something that works. And is simple.
S.Lott