I am working on a program that can "upgrade itself" by install a new package. In order to accomplish this, I am using the at command to specify that a shell script should be run one minute in the future and the shell script, in turn, will install the package. I would really like this to execute sooner than one minute into the future but that appears to be the best that I can do based upon what I have read in the man page for at. Is there a way to do this with tighter resolution?

@alex: I initially tried running the update script as a call through system() but the update didn't take place. I had reasoned that this was occurring because the first part of the update was to stop my daemon which would clobber the shell launched from my daemon. On second thought, this might have been due to my failure to redirect output.
–
Jon TrauntveinJun 10 '11 at 20:18

My initial approach was to invoke the update script as you describe above. I thought that didn't work because the child process was killed along with my daemon (the update script kills the daemon before upgrading). It could be that the failure happened because I failed to redirect output.
–
Jon TrauntveinJun 10 '11 at 20:20

I had to combine this with nohup in order to prevent the child process from hanging up when my daemon was shut down.
–
Jon TrauntveinJun 13 '11 at 15:17

This is tricky. First, just out of precaution, put the invocation in a function at the beginning of the script, and don't call it till the end:

% killmenow() { ... }
...
% killmenow
EOF

This is the best way to be certain that everything from the current script has been fully read and copied into memory. Just saying.

Now for my attempt at an answer...

POSIX specifies the jobs utilities for these kinds of things. Specifically, a combination of wait, trap, and kill would be safest, I think. To get things moving along as cleanly as possible you can use newgrp as well, which is very like exec in inheritance qualities excepting, as could be pivotal in this case, process id.

Ok, so I know it sounds like a lot, but it's not so much to look at after all:

First we load and run the entire script and we're sure of this because we don't suicide until the very last line.

We pass to suicide a copy of our process id then save it to $_me, which we could just as easily get from within suicide as well in this case, but we might use it elsewhere, who knows.

From within a subshell we call trap to scan in its trapped'$cmd' and instruct it to be on the lookout for any rumor of an INT call.

Sure all is ready, our call to kill is our very last; kill issues an INTERRUPT SIGNAL not only to $_me but we - specify that $_my process whole group should receive it. Have no fear - unless your whole system is already insane - $_me can't kill anything $_me shouldnt be able to right now anyway. Also, you dont have to - specify that in kill's PID argument either; I'm just trying to be thorough.

Ok, but we're not done, right? Nope, we put the whole command set in a & backgrounded subshell so trap is still hanging around and can catch our INT call.

Having recieved its INT, trap NOW evaluates its 'quoted argument' - it's important to remember that anything within this string is only now being pulled out.

Just in case $_me has a few other loose ends to tie up elsewhere, trap first calls wait on $_me which will hold the line only for as long as it must.

When ready, we scrub any remaining env $vars with set -- and then we get down to business.

newgrp is designed to be run at an interactive shell - its not necessarily supposed to provide a process such a clean escape as this. In fact if your system's group require passwords - which they shouldnt - this will not work for you and will die now.

I call this method sneaky below, but i pulled it straight from the POSIX GUIDELINES:

A common implementation of newgrp is that the current shell uses exec to overlay itself with newgrp, which in turn overlays itself with a new shell after changing group...

Then, in the next paragraph...

The newgrp command is intended only for use from an interactive terminal. It does not offer a useful interface for the support of applications.

So the sneaky part is eval, which can be pretty good at that kind of thing - like saving and reusing "$@" for instance, but another time, perhaps...

Instead, we indirectly eval a newgrp && exec statement that first loads another process that is only supposed to take its next command from an interactive prompt, then we sneakily exec in the back door the contents of { YOUR | CMD }.

By this time we should be running a new script in a new shell in a new process group in a clean environment after having to wait only as long as was actually necessary and to have pulled the whole thing off pretty thoroughly.

If your goal is to do the upgrade as soon as your program exits, but no sooner, arrange for your program to terminate by calling execve instead of exit. The execve system call replaces the current program by another. In C, there are several variants (execl, execp, etc.) depending on how you want to pass parameters. In shell, the corresponding built-in is exec.