Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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?

share|improve this question
    
What's wrong with "immediatly", by just running the program? Or, wrap it in a shell script that checks for a particular exit code and the program just has to call exit(code). –  Keith Jun 9 '11 at 16:13
    
See also batch. But why wouldn't you just run update code in the same process or in a child one? –  alex Jun 9 '11 at 16:42
    
The answers to yesterday's Lightweight utility/program to run a command after a random delay are easily modified to obtain a fixed delay with granularities of seconds or finer. –  dmckee Jun 9 '11 at 17:35
    
@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 Trauntvein Jun 10 '11 at 20:18
1  
Ah, that might explain it. Stopping the daemon will send SIGHUP to it's child processes, hence your update script will terminate prematurely. Use nohup command to protect your script. –  alex Jun 11 '11 at 6:22
add comment

4 Answers 4

up vote 3 down vote accepted

It would be easier to simply launch the program directly and have it run the update immediately (or it could sleep for some small delay if you really needed it to).

If this is in a script itself (eg, bash), simply call the update script directly (likely redirecting the output):

update-my-thingy < /dev/null > /dev/null 2>&1

If it's in a program, you can always use the system() call to do the same thing:

system("update-my-thingy < /dev/null > /dev/null 2>&1");
share|improve this answer
    
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 Trauntvein Jun 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 Trauntvein Jun 13 '11 at 15:17
add comment

This depends on what you program is doing. For daemons it is done the following way:

  • determine the age of the executable
  • if the age is less then the program start, then the executable was overwritten with a new version
  • in such case wait until there is no work to be done (or don't)
  • exec() on self to restart the program with a new binary
share|improve this answer
add comment

Fork a script that waits for a few seconds.

{ sleep 5; update-my-program; } &

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.

share|improve this answer
add comment

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:

% suicide() { _me="${1}" ; ( 
>> trap 'wait "${_me}" && set -- &&\
>>> eval `newgrp && exec { YOUR | CMD | HERE }`' INT &&\
>> kill -s INT "-${_me}"
>> ) & }
...
% suicide "${$}"

Ok, so here's the workflow:

  • 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.

In fact, we were so thorough that we could safely do this:

...exec { wget -O - "${URL}" >"${0}" ; exec "${0}" }
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.