home *** CD-ROM | disk | FTP | other *** search
- From: robp@amiga.UUCP
- Followup-To: Questions about running background tasks.
- Organization: Commodore-Amiga Inc., 983 University Ave #D, Los Gatos CA 95030
- Keywords: RUNBACKGROUND background-tasks dump-initial-CLI
- Summary: You CAN kill the initial CLI, and run functions as BACKGROUND tasks.
-
- SYNOPSIS:
-
- This tutorial note deals with the AmigaDOS Execute function, showing in
- detail how its various FileHandle combinations affect the way the function
- executes. It also provides the source for a RUNBACKGROUND command that
- you can add to your workbench to start one or more processes from the
- s/startup-sequence and still allow the initial CLI to end with the
- command line "endcli > NIL:".
-
-
- SPECIAL NOTES ABOUT EXECUTE REDIRECTION
-
- The Execute function can accept file handles as part of the command,
- where the file handle is used to redirect the stdin and/or stdout.
- There are special rules that Execute uses for this redirection. The
- following specifies the various forms that the Execute function call
- can take and their effects.
-
-
- Execute("something",0,0)
-
- means use "*" (that is, the current CLI window) for output.
- There is NO input expected for stdin. This form crashes if
- used under Workbench because there is NO WINDOW to which the
- output can be directed! The "something" string can include
- the redirection commands (< and >). If you specify no
- redirection commands, the string will literally be interpreted
- as though you asked for the function:
-
- Execute("something > * ",0,0);
-
- which says "use the current window for output". Since workbench
- itself has no current window of its own (its process simply has
- no window assigned), the function cannot terminate correctly.
-
- When you perform the Execute function with this calling sequence,
- it calls the RUN command, which, in turn, uses CreateProc()
- to load the code for the something you wish to run. This
- code runs as a child of the CLI and the CLI must hang around
- until the child finishes. Likewise it also means that the
- Execute command will not finish until the child exits.
-
- If you call Execute as:
-
- Execute("RUN something",0,0);
-
- you are asking the system to spawn a new CLI process that can
- handle the child process on its own, thereby seeming to free
- the originating CLI from handling the process, and control
- returns to the originating CLI immediately after RUN begins
- to operate. HOWEVER, even though you've called RUN, the
- originating CLI still may not be able to "endcli > NIL:" itself,
- because "something" inherits the stdout (*) from the calling process.
-
- This effectively prevents the originating CLI from going away until
- the user-count of its stdout output handle has gone to zero, since
- that process has become a user of the stdout (*). If EITHER ONE of
- the file- handles is a 0, the user-count for * is incremented,
- regardless of whether the program uses stdin or stdout at all.
- This happens since Execute cannot tell what will be in the
- execute-string.
-
- Thus, the originating CLI must hang around if either of the handles
- is zero, just to be sure the input and output paths are available
- if needed. See Execute("something", nilfh, nilfh) for a way
- around this.
-
-
- Execute("something",0,fout)
-
- means use fout (a valid file handle) for output. There is NO input
- expected from stdin, although the current CLI window (*) is still
- marked as having one more user.
-
- The FileHandle fout is not closed for you once Execute has finished.
- You must close fout yourself.
-
- This form of the function works either under Workbench or CLI,
- as long as fout is valid. The "something" can ALWAYS be a command that
- includes both < or > redirection. The output redirection will be
- utilized for error messages from the CLI if the comand-string
- encounters an error.
-
-
- Execute("something",fin,fout)
-
- Means do "something", then continue to read commands from "fin".
- The "something" can just as easily be specified as "" (the null
- string), which means do nothing, then continue to read commands from
- the file whose handle is "fin". Again, you'll have to close fin
- yourself.
-
- Control returns when the CLI finishes "something". Anything
- specified in fin will be done simultaneously with your own
- task that now is free to run. (The CLI has done "something",
- and is now free to go on to do something else, while the execute
- process continues to read fin.)
-
- When fin is an interactive input stream (See IsInteractive()),
- the fin handle becomes the handle that you would get
- if you opened "*". This means that you have asked the
- system to create a new interactive CLI process, with
- prompts and so on. Since a CLI process is now a user of
- "*", the window cannot be made to go away until you issue an
- explicit ENDCLI command.
-
- It also means that the caller of this form of Execute will NOT
- regain control until the CLI completes its task. Because fin
- it is an interactive process, it always has some input pending;
- thus it cannot be finished with its job until the input actually
- ends the CLI. So the caller will go to sleep waiting for the
- interactive CLI to end.
-
-
- Execute("something", fin, (fout = 0))
-
- FileHandle fout should normally be a file handle specifying some output
- such as a con: window or a file. There is, however, one very special
- case, which is when fout is 0. This means, as described earlier,
- use "*" as the output.
-
- Note that in this special case, fin MUST be a console handler
- stream (CON:, RAW:, or "*"). Additionally, if you want to
- recognize an end-of-file condition on the input, you'll have
- to use only CON: windows. CON: windows translate CTRL-\ into
- an end-of-file; RAW: windows, on the other hand, pass on the
- numeric value of this key combination without treating it as EOF.
-
-
- Execute("something_with_no_stdin_or_stdout", nilfh, nilfh)
-
- Here, "nilfh" is a file handle resulting from a call to Open
- with a filename of NIL:, as:
-
- struct FileHandle *nilfh;
-
- nilfh = Open("NIL:",MODE_NEWFILE);
-
- Several developers and users have expressed an interest
- in being able to start background processes using the Workbench
- startup script (s/startup-sequence). An example would be to start
- things such as the CLOCK or something else, then end up on the
- Workbench without the originating CLI having to hang around for
- the user to close down.
-
- If you want the originating CLI to go away, you must somehow
- prevent any process started by that CLI from opening "*"
- for either input or output as mentioned above.
-
- Within the "something..." string, you must spawn an independent
- CLI process. Additionally the independent process must have its own
- I/O restricted so that it does not in any way depend on or
- attempt to communicate with the originating CLI. Here, then,
- is PART of the something-string:
-
- RUN >NIL: <NIL: <rest_of_string>
-
- This starts an independent process.
-
- The rest_of_string can contain the command and its parameters.
- But be aware that if your command uses the standard startup sequence,
- it still needs to open its stdin and stdout file handles. You'll
- need to assure that a proper file handle is actually provided for
- stdin and stdout, so the rest of the string should look like this:
-
- rest_of_string = "MyCommand >NIL: <NIL: Parm1 Parm2 Parm3 etc";
-
-
- Thus, the complete Execute command string appropriate to letting
- the originating CLI close down and leave the CLOCK open on the
- Workbench is to run a program such is listed below. Note, however,
- that the file handle to NIL:, dynamically allocated by this program,
- must hang around "forever" until the next system reset. It has to be
- dynamically allocated, since you want this program and its calling
- CLI to die and leave the CLOCK and Workbench active.
-
-
- /* runclock.c */
-
- /* Author: Rob Peck. 5/9/86 */
-
- #include "exec/types.h"
- #include "exec/memory.h"
- #include "libraries/dosextens.h"
-
- extern struct FileHandle *Open();
-
- main()
- {
- LONG success;
- struct FileHandle *nilfh;
- nilfh = (struct FileHandle *)
- AllocMem(sizeof(struct FileHandle),MEMF_CLEAR | MEMF_PUBLIC);
-
- success = Execute("RUN >NIL: <NIL: CLOCK >NIL: <NIL:",nilfh,nilfh);
- if(success)
- {
- printf("Started CLOCK as a separate process\n");
- }
- else
- {
- FreeMem(nilfh, sizeof(struct FileHandle));
- }
- /* Notice that if the program succeeds, nobody ever frees this mem */
- }
-
-
- To try this program, your startup-sequence should contain
- at least the following lines:
-
- LOADWB
- RUNCLOCK
- ENDCLI > NIL:
-
- Notice that this program does NOT close the "nilfh", that is,
- the file handle obtained by opening NIL:. This will tie up
- a small block of memory somewhere in the system until the next
- boot, unless someone spawns a process that keeps track of when
- the CLOCK tool exits, then finally frees this file handle. But
- this piece of memory is probably not too high a price to pay
- to obtain the functionality people have requested.
-
-
- Here is a general purpose implementation of the above technique,
- allowing you to run a named process "in the background". It
- includes a delay parameter to minimize disk thrashing among
- sequentially loaded processes. It is a bit more complicated
- in that we've installed the appropriate error handling.
-
- DISCLAIMER:
-
- This program is provided as a service to the programmer
- community to demonstrate one or more features of the Amiga
- personal computer. These code samples may be freely used
- for commercial or noncommercial purposes.
-
- Commodore Electronics, Ltd ("Commodore") makes no
- warranties, either expressed or implied, with respect
- to the program described herein, its quality, performance,
- merchantability, or fitness for any particular purpose.
- This program is provided "as is" and the entire risk
- as to its quality and performance is with the user.
-
- (Other standard disclaimers apply)
-
- /*
-
- --------------
- runbackground.c
- ---------------
-
- SUMMARY: A Workbench Disk can be used to autostart an application
- through the use of the startup script and close the startup CLI.
-
-
- Users have commented that it is not possible to start a process going
- from the startup script and then cause the initial CLI to go away.
- Here is the solution to that problem, named appropriately:
-
- RUNBACKGROUND
-
- which starts and runs a background task. This does indeed allow you to
- create a startup script that will set up your workbench running any
- programs you might wish, removing the initial CLI in the process.
-
- Your s/startup-sequence can contain lines such as the following:
-
- RUNBACKGROUND -3 clock
- RUNBACKGROUND utilities/calculator
- RUNBACKGROUND -5 utilities/notepad
-
- where RUNBACKGROUND is the command and the second parameter is the filename
- which may be preceded by a flag-variable that specifies an optional delay
- time. The delay can be from 0 to 9, for the number of seconds that
- the startup script should sleep while allowing the background task to
- load and start. I've put that in to minimize thrashing of the disk as it
- tries to load several projects at once.
-
-
- LIMITATIONS:
-
- The program that you run cannot require any input from an interactive
- CLI that starts it. Additionally, you cannot specify any file
- redirection in the command line since this program provides the
- redirection for you already. If you need to use redirection for
- your command, you can modify the source code where shown, thus
- allowing the redirection to become one of the parameters passed
- through to your program.
-
- RUNBACKGROUND does pass your command line parameters to the program
- you wish to start, but limits the total length of your command
- string to 227 (255 minus the 28 characters for "RUN >NIL: <NIL: "
- preceding your own file pathname and ">NIL: < NIL: " following it.)
-
-
- LINKING INFORMATION:
-
- (Amiga/Lattice C) use -v option for pass 2 (lc2 -v filename.q)
- to disable stack checking code installation.
- (stack checking code sometimes is incorrect).
-
- FROM lib:Astartup.obj runbackground.o
- TO runbackground
- LIBRARY lib:amiga.lib, lib:lc.lib
-
- **************************** NOTE: ********************************
- If you use Lstartup.obj, it won't let the startup CLI go away. This is
- because the source code for Lstartup.asm either opens its own window
- or uses an existing CLI window (Open("*",....)), so that it has some
- guaranteed place to put the output. Astartup.obj does not do this.
- *********************************************************************
-
- Hope this helps.
-
-
- robp.
- */
-
- /* runbackground.c */
- /* Author: Rob Peck. 5/9/86 */
-
-
- #include "exec/types.h"
- #include "exec/memory.h"
- #include "libraries/dosextens.h"
-
- extern struct FileHandle *Open();
- extern struct FileLock *Lock();
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- LONG success, delaywillbe;
- UBYTE commandstring[255];
- char *test, *filename;
- LONG fromparm;
- struct FileInfoBlock *fib;
- struct FileHandle *nilfh; /* NOTE: will hang around until next reset */
- struct FileLock *lock;
-
- fib = NULL; /* No file info block so far. */
- delaywillbe = 1;
-
- if(argc < 2 )
- {
- usage:
- printf("Usage: RUNBACKGROUND [ -<loaddelay>] <name> [<parm(s)>]\n");
- printf(" where optional loaddelay is 0-9,\n");
- printf(" specified in seconds for the CLI\n");
- printf(" to sleep, waiting for task to load\n");
- printf(" (minimizes inter-task disk-thrashing)\n");
- if(fib) FreeMem(fib, sizeof(struct FileInfoBlock));
- exit(0);
- }
-
- /* See if there is a delay parameter present */
-
- test = argv[1];
-
- if(*test++ == '-')
- {
- filename = argv[2]; /* argv[1] is delay so argv[2] is file */
- fromparm = 3; /* Copy parms from 3 to end */
-
- if(*test >= '0' && *test <= '9')
- {
- delaywillbe = 1 + (50 * (*test - '0'));
- }
- if (argc < 3) goto usage; /* Only a delay, and no filename!! */
-
- argc--; /* one less parm to copy */
- }
- else
- {
- filename = argv[1];
- fromparm = 2; /* Copy parms from 2 to end */
- }
-
- /* Now see if the file exists! If not, it can crash the background
- * CLI and take the system along with it.
- */
- lock = Lock(filename,ACCESS_READ);
- if(!lock)
- {
- test = filename;
- if(*test == '?') goto usage;
- else
- {
- printf("%ls: Command not found\n",filename);
- goto usage;
- }
- }
- else
- {
- /* If file exists, it better be a file and not a directory */
-
- /* Unfortunately, it is difficult to tell if it is an executable
- * file. If not executable, we'll still get blown out of the
- * water, but that is up to the user to do it right!
- */
-
- fib = (struct FileInfoBlock *)
- AllocMem(sizeof(struct FileInfoBlock),MEMF_CLEAR);
- if(!fib)
- {
- UnLock(lock);
- printf("Ran out of memory!\n");
- exit(0);
- }
- else
- {
- success = Examine(lock,fib);
- if(fib->fib_DirEntryType > 0) /* its a directory!! */
- {
- printf("%ls is a directory, not a file!\n",filename);
- goto usage;
- }
- }
- FreeMem(fib, sizeof(struct FileInfoBlock));
- UnLock(lock);
- }
-
- nilfh = Open("NIL:",MODE_NEWFILE); /* will always succeed */
-
- strcpy( &commandstring[0], "RUN >NIL: <NIL: " );
- strcat( &commandstring[0], filename);
-
- /* REMOVE THIS NEXT LINE IF YOU WANT TO INCLUDE REDIRECTION IN
- * THE COMMAND LINE FOR RUNBACKGROUND. (The line was installed
- * to assure that something like "RUNBACKGROUND date ?" would
- * not crash the system. "Date ?" is expecting to have an interactive
- * CLI, and unless it is specifically told to direct its output to nil:
- * it causes a crash. If the next line is removed, and you are careful
- * about putting only NON-interactive commands in the command line,
- * everything should be ok. Notice that if you do not specify a
- * non-interactive file handle (named_disk_file or NIL:) for your
- * program, it may still prevent the originating CLI from going away
- * until your program ends. Also note that specifying two instances
- * of the same redirection (">somewhere" or "<somewhere") for a
- * background task crashes.
- */
-
- strcat( &commandstring[0], " >NIL: <NIL: "); /* stop interactive crash */
-
- argc--;
-
- while(--argc > 0) /* Rebuild parameter string for passing it on */
- {
- strcat( &commandstring[0], " "); /* add a blank */
- strcat( &commandstring[0], argv[fromparm++]);
- }
-
- success = Execute( &commandstring[0] , nilfh, nilfh);
-
- /* The full command passed to Execute now looks like this:
- *
- * "RUN >NIL: <NIL: FILENAME >NIL: <NIL: PARAMETER(s)"
- *
- */
-
- if(success)
- {
- printf("Started %ls as a background task\n",filename);
-
- /* Execute, in this case, returns IMMEDIATELY. The process
- * that is loading the code that is to be run as a background
- * process is working to get everything in and started.
- */
- }
- /* Now, to minimize thrashing between tasks, lets put this task to
- * sleep so that the each task actually gets a chance to load.
- */
- Delay(delaywillbe);
- }
-
- strcpy( to, from )
- register char *to, *from;
- {
- do {
- *to++ = *from;
- } while( *from++ );
- }
- strcat( to, from )
- register char *to, *from;
- {
- while( *to ) to++;
- strcpy( to, from );
- }