On May 9, 2008, at 4:05 AM, Toby Blake wrote: One thing my installer plugin needs to do is take input from the user, validate it and write it out to a file. There's every possibility that this file could be only writable by root on any given system, so my plugin needs to get appropriate authorization for the user to write this file as root.
First, the sermonizing: Please remember that any halfway-smart user can outwit your installer. They can change the installation and volume checks, delete or replace the scripts that run before or after, and even just install "by hand" using common Unix tricks, or a program like Pacifist.
Therefore, you cannot expect with 100% reliability that your scripts will run and write stuff before the program launches. Your program WILL have to deal with missing preferences, bogus registration information, etc, anyway. If your program crashes when the prefs file is missing, that is a program bug. Although it would be nice to fix it in the installer, you cannot guarantee it to be fixed.
However, we all want to do the right thing for the user, and the right thing is to gather user registration info during installation, preferably letting them know if they have bogus info, so they don't have to waste time/disk space running a useless install.
end sermon.
As was said before, Installer Plugins do not run as root, but installer scripts do, if the installer requires root authentication.
Asking for root during installer plugins is gross.
Here are two possibilities: 1) Run your own Cocoa program during preflight. 2) Use an installer panel, write the information to /tmp in a known location, move during postflight. I suggest you pick a name like com.myCompany.pkg.myGreatApp, because if there's an existing file by that name, you can be pretty sure it's either a leftover from a previous installation, or some kind of hackery.
(There's no way to send information to the installer scripts, alas. The installer securely creates a temp folder and shoves its name into the environment variable $INSTALLER_TEMP, but it doesn't create this until the user clicks "Install.")
Problem with #1 is that it's more work, and slightly ugly to have something popping up.
Problem with #2 is that you have to write to a known location, then move it later, so what do you do if there's a file there already? Trying to manipulate a file there already can cause security problems, because anyone can write to /tmp and place a symlink to the kernel. If you feel ambitious, you can mv the existing file out of the way and make a new one.
Here's a way to decide if you are creating the file securely, in perl: my $success = sysopen(FILEHANDLE, $filename, 0x0001 | 0x0200 | 0x0800);
This attempts to open a file using the UNIX open modes, O_WRONLY, O_CREAT, and O_EXCL.
This tells unix, "I want you to open a file for me that I will only write to, and I want you to create it if it does not exist, and I want you to return an error if the file already exists."
You can do the same stuff with the standard UNIX open commands in C or whatever.
If you get a failure, you can test to see if the file already exists. If so, you COULD move it aside and try again. Hopefully, no one will re-create a file of the same name BETWEEN the move and the open. (That's why it's called a "race condition" -- you have to race.)
If you can't succeed in a few tries, however, what can you do? If you are in a preflight or postflight script, you cannot do anything. You must always return success ("0" in the UNIX parlance) from preflight and postflight scripts, and go on without doing whatever it was.
If you are in an installer panel, I suppose you could put up an error, but imagine just how horrid the error message would be! |