Find the answer to your Linux question:
Results 1 to 3 of 3
I am making a menu using the until and case commands. As one of the options I want the script to run in the background for a set amount of ...
  1. #1
    Just Joined!
    Join Date
    Feb 2009
    Posts
    2

    [SOLVED] Background program issue

    I am making a menu using the until and case commands. As one of the options I want the script to run in the background for a set amount of time, or until the user brings it back into the foreground.
    What I am trying to achieve is the same results as this command: " (sleep 10 ; echo "ten seconds later") &"

    That command allows me to use the terminal as i normally would, and displays the text 10 seconds later.

    Heres my script so far......

    function pause(){
    read -n 1 -p "$*"
    }

    clear

    until [ "selection" = "0" ] ; do

    echo " "

    echo -e '\t\t\t' "Unix Helper Utility"

    echo -e '\t\t\t' "==================="

    echo " "

    echo "[1] List File names in current directory"

    echo "[2] Show Time and Date"

    echo "[3] Process ID"

    echo "[4] Send this menu to Background"

    echo "[0] Exit"

    echo " "

    echo "Please pick an option listed above: "

    read selection

    echo " "

    case $selection in

    1 ) clear
    echo " Current Directory list:"
    ls
    pause
    clear ;;
    2 ) clear
    date
    pause
    clear ;;
    3 ) clear
    echo "option 3"
    pause
    clear ;;
    4 ) clear
    sleep 300
    clear ;;
    0 ) clear
    exit


    esac
    done

    the way i have it setup now is if you select option 4 is sleeps for 5 minutes, and the displays the menu text again.
    I would like to have it so that i can use the terminal in that 5 minute period. If i use ^Z and then the bg command i can place it in the background, but i would like to know if there is a way i can make the script do that step.

    thanks in advance.

  2. #2
    Just Joined!
    Join Date
    Feb 2009
    Posts
    2
    Anyone????

  3. #3
    Just Joined!
    Join Date
    Feb 2009
    Posts
    45
    Answer:
    Quote Originally Posted by sabobbin
    If i use ^Z and then the bg command i can place it in the background, but i would like to know if there is a way i can make the script do that step.
    It is not safe for a process to install its process group as the foreground process group of a session where a shell performs job control without the consent of this shell (i.e. via «fg»/«bg»). What you want to do seems possible¹, but youʼd need to call «tcsetpgrp()» directly. Thus a shell script alone wouldnʼt suffice to perform what you intend.

    Instead, I advice you to take a different, purely “shell”ish approach. The following answer assumes your shell is “bash” or a different shell that offers the mechanism that is equivalent on the “PROMPT_COMMAND” environment variable of bash:
    Code:
    $> man bash | grep -A 2 PROMPT_COMMAND;
           PROMPT_COMMAND
                  If set, the value is executed as a command prior to issuing each
                  primary prompt.
    and that «~/.bashrc» is your standard shell rc-file.

    This is the kind of code that should be executed in your case «4)» statement:
    Code:
    […]
    4) clear
    	timelimit=30; ## The time limit in seconds
    	NEW_PROMPT_COMMAND="; if [[ \\\$SECONDS -gt $timelimit ]]; then echo \\\"---Timelimit hit, shutting down shell---\\\"; flush_keyboard_buffer; exit; fi";
    	touch ~/.bashrc_timelimit;
    	cp ~/.bashrc ~/.bashrc_timelimit;
    	(cat ~/.bashrc; echo -e "\nPROMPT_COMMAND+=\"$NEW_PROMPT_COMMAND\"") > ~/.bashrc_timelimit;
    	bash --rcfile ~/.bashrc_timelimit;
    […]
    This solution is in my opinion more elegant because it doesnʼt shut down the CLI after 5 minutes, but after the first time the primary prompt is issued after 5 minutes (i.e. after the last executed command exits or CTRL+C is pressed and sent to the shell process). Thus is doesnʼt interrupt any running command and doesnʼt interrupt the user while [s]he is typing a command.
    Some notes to this piece of code:
    • Note that in this example a real file, namely «~/.bashrc_timelimit» is used instead of a named fifo pipe, because it seems that «bash» will not read an rcfile provided with the --rcfile option when it is a named fifo pipe. This I deduce after I tested it on my systems and «bash» acted as just described.
    • In the variable «NEW_PROMPT_COMMAND» everything must, for obvious reasons, be escaped twice. Itʼs an easy-to-forget source of errors.
    • If the user decides to change the environment variable «PROMPT_COMMAND» on his/her shell session, then this wonʼt work anymore, so this solution requires the benevolence of the user. If the user decides to [s]he doesnʼt want the shell to quit after 5 minutes, he can stop it by removing the decisive characters from «PROMPT_COMMAND».
    • The command “flush_keyboard_buffer” should be an application that flushes the whole input keyboard buffer before exiting. This is important as one wouldnʼt really want any left over input from the last bash session to intrude your menu logic. I donʼt know if thereʼs a GNU application for this, but itʼs essentially this:
      Code:
      /* C98 */
      #include <termios.h>
      #include <sys/ioctl.h>
      
      int main(int argc, char **argv, char **env) {
      	/* …… Vars …… */
      	int counter;
      	char tmp;
      	struct termios saveTTY, setTTY;
      	/* …… Code …… */
      	tcgetattr(0, &setTTY);
      	tcgetattr(0, &saveTTY);
      	setTTY.c_lflag &= ~ICANON;
      	tcsetattr(0, TCSANOW, &setTTY);
      	if (ioctl(0, FIONREAD, &counter)) return 2;
      	while (counter--) read(0, &tmp, 1);
      	tcsetattr(0, TCSANOW, &saveTTY);
      	return 0;
      }


    Another solution might be to invoke a shell but to shut it down after a specified amount of time, i.e. like this:
    Code:
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <signal.h>
    
    int main(int argc, char **argv, char **env) {
    	pid_t pid;
    	char *argvv[2];
    	argvv[0] = "/bin/bash";
    	argvv[1] = NULL;
    	pid = fork();
    	if (pid == 0) {
    		execv(argvv[0], argvv); }
    	else {
    		sleep(3); /* time limit */
    		kill(pid, SIGKILL);
    		exit(0); }
    }
    This however has the drawback that you need to decide what kill signal to send. The shell might ignore a SIGTERM (it didnʼt shut down a shell on my system) and will definitively ignore a SIGINT. SIGKILL however will shut down the shell for good without giving it the chance to restore the terminal settings (i.e. you could end up with a non-echoing terminal), so you will need to manipulate terminal settings manually (directly in the C code or per «stty»). This adds another, unneccesary layer of complexity.

    In case you only wanted to know how a shell script process can stop itself (and therewith put itself into the background):
    Code:
    kill -SIGSTOP $$;
    Footnotes:
    1. The following text describes a scenario on one of my computers that illustrates that it is possible if certain conditions are met.

      If «bash» runs in the foreground and «tcsetpgrp()» is issued from a background process (that ignores «SIGTTIN» and is hence known as process A) to set its own process group to the foreground process group, then it seems to be able to stay in the foreground if «tcsetpgrp()» is called after each «read()» from «stdin». If «tcsetpgrp()» is not called after each «read()» from «stdin», «bash» sets its process group as the foreground process group and it hails read-errors at the background application combined with errno == EIO. That apparently happens when 2 processes continuesly fight to establish their process group as the foregroup process group. If the shell is stopped with
      Code:
      kill(getppid(), SIGSTOP);
      before process A issues «tcsetpgrp()», everything works out fine: process A is the foreground process without repeatedly calling «tcsetpgrp()» until it exits. If it however forgets to “unstop” the parent shell process (via SIGCONT), then the sessions freezes and thatʼs obviously not very intended. Stopping the shell beforehand isnʼt even possible in every case, i.e. when you run your shells under «screen», because this shell multiplexer seems to send SIGCONT to any stopped sub shell (I didnʼt really verify this by looking into the «screen» code, but you canʼt stop sub shells in «screen»; which is a good thing).

      A warning is appropriate here though: just because the repeated firing of «tcsetpgrp()» (when you canʼt stop the parent shell) from the baskground process seems to work on my system, itʼs not guaranteed to work anywhere else, because this is the result of a race condition. When «sleep(1)» is inserted directly after the «read()» but before the «tcsetpgrp()», then «bash» is able to grep a few characters and a few EOFs (in my case: every seconds character was sent to the shell). This situation becomes even more perverse if thereʼs a cascade of parent shells (bash → bash → bash → process A).

      So to summarize:
      • parent shell stoppable? → SIGSTOP shell, put your process into foreground, SIGCONT the shell before exiting (There might, however, be more problems here I donʼt currently see).
      • parent shell unstoppable? → Not reliably possible.
      • Thus: always let the process that thinks it is the session leader and job manager handle foreground positioning.


      In case anyone wants to try out how his/her system reacts to this but is just too lazy to write the neccessary code Iʼve attached the file “bg-test.c.txt” (itʼs a C file, really).


    P.S.:
    • Quote Originally Posted by sabobbin
      Anyone????
      Double postings are bad, mʼkay?
    • Code without code tags is bad, mʼkay?
    Attached Files Attached Files

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
...