home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 007.lha / runback.doc < prev    next >
Text File  |  1986-11-09  |  17KB  |  496 lines

  1. From: robp@amiga.UUCP
  2. Followup-To: Questions about running background tasks.
  3. Organization: Commodore-Amiga Inc., 983 University Ave #D, Los Gatos CA 95030
  4. Keywords: RUNBACKGROUND background-tasks dump-initial-CLI
  5. Summary: You CAN kill the initial CLI, and run functions as BACKGROUND tasks.
  6.  
  7. SYNOPSIS:
  8.  
  9. This tutorial note deals with the AmigaDOS Execute function, showing in 
  10. detail how its various FileHandle combinations affect the way the function
  11. executes.  It also provides the source for a RUNBACKGROUND command that
  12. you can add to your workbench to start one or more processes from the
  13. s/startup-sequence and still allow the initial CLI to end with the
  14. command line "endcli > NIL:".
  15.  
  16.  
  17. SPECIAL NOTES ABOUT EXECUTE REDIRECTION
  18.  
  19. The Execute function can accept file handles as part of the command, 
  20. where the file handle is used to redirect the stdin and/or stdout.  
  21. There are special rules that Execute uses for this redirection.  The
  22. following specifies the various forms that the Execute function call
  23. can take and their effects.
  24.  
  25.  
  26. Execute("something",0,0) 
  27.  
  28.     means use "*" (that is, the current CLI window) for output.  
  29.     There is NO input expected for stdin.  This form crashes if 
  30.     used under Workbench because there is NO WINDOW to which the 
  31.     output can be directed!  The "something" string can include
  32.     the redirection commands (< and >).  If you specify no
  33.     redirection commands, the string will literally be interpreted
  34.     as though you asked for the function:
  35.  
  36.         Execute("something > * ",0,0);
  37.  
  38.     which says "use the current window for output".  Since workbench
  39.     itself has no current window of its own (its process simply has
  40.     no window assigned), the function cannot terminate correctly.
  41.  
  42.     When you perform the Execute function with this calling sequence,
  43.     it calls the RUN command, which, in turn, uses CreateProc()
  44.     to load the code for the something you wish to run.   This
  45.     code runs as a child of the CLI and the CLI must hang around 
  46.     until the child finishes.  Likewise it also means that the 
  47.     Execute command will not finish until the child exits.
  48.  
  49.     If you call Execute as:
  50.  
  51.         Execute("RUN something",0,0);
  52.  
  53.     you are asking the system to spawn a new CLI process that can
  54.     handle the child process on its own, thereby seeming to free
  55.     the originating CLI from handling the process, and control
  56.     returns to the originating CLI immediately after RUN begins
  57.     to operate.  HOWEVER, even though you've called RUN, the 
  58.     originating CLI still may not be able to "endcli > NIL:" itself,
  59.     because  "something" inherits the stdout (*) from the calling process.
  60.     
  61.     This effectively prevents the originating CLI from going away until 
  62.     the user-count of its stdout output handle has gone to zero,  since 
  63.     that process has become a user of the stdout (*).  If EITHER ONE of 
  64.     the file- handles is a 0, the user-count for * is incremented, 
  65.     regardless of whether the program uses stdin or stdout at all.  
  66.     This happens since Execute cannot tell what will be in the 
  67.     execute-string.
  68.  
  69.     Thus, the originating CLI must hang around if either of the handles
  70.     is zero, just to be sure the input and output paths are available
  71.     if needed.  See Execute("something", nilfh, nilfh) for a way
  72.     around this.
  73.  
  74.  
  75. Execute("something",0,fout)
  76.  
  77.     means use fout (a valid file handle) for output.  There is NO input 
  78.     expected from stdin, although the current CLI window (*) is still
  79.     marked as having one more user.
  80.  
  81.     The FileHandle fout is not closed for you once Execute has finished.  
  82.     You must close fout yourself.
  83.  
  84.     This form of the function works either under Workbench or CLI,
  85.     as long as fout is valid.  The "something" can ALWAYS be a command that
  86.     includes both < or > redirection.  The output redirection will be
  87.     utilized for error messages from the CLI if the comand-string 
  88.     encounters an error.   
  89.  
  90.         
  91. Execute("something",fin,fout) 
  92.  
  93.     Means do "something", then continue to read commands from "fin".  
  94.     The "something" can just as easily be specified as "" (the null 
  95.     string), which means do nothing, then continue to read commands from 
  96.     the file whose handle is "fin".  Again, you'll have to close fin 
  97.     yourself.
  98.  
  99.     Control returns when the CLI finishes "something".   Anything 
  100.     specified in fin will be done simultaneously with your own
  101.     task that now is free to run.  (The CLI has done "something",
  102.     and is now free to go on to do something else, while the execute
  103.     process continues to read fin.)
  104.  
  105.     When fin is an interactive input stream (See IsInteractive()),
  106.     the fin handle becomes the handle that you would get
  107.     if you opened "*".   This means that you have asked the
  108.     system to create a new interactive CLI process, with
  109.     prompts and so on.   Since a CLI process is now a user of
  110.     "*", the window cannot be made to go away until you issue an
  111.     explicit ENDCLI command.  
  112.  
  113.     It also means that the caller of this form of Execute will NOT
  114.     regain control until the CLI completes its task.  Because fin 
  115.     it is an interactive process, it always has some input pending;
  116.     thus it cannot be finished with its job until the input actually
  117.     ends the CLI.  So the caller will go to sleep waiting for the
  118.     interactive CLI to end. 
  119.  
  120.  
  121. Execute("something", fin, (fout = 0))
  122.     
  123.     FileHandle fout should normally be a file handle specifying some output 
  124.     such as a con: window or a file.  There is, however, one very special 
  125.     case, which is when fout is 0.  This means, as described earlier,
  126.     use "*" as the output.  
  127.  
  128.     Note that in this special case,  fin MUST be a console handler 
  129.     stream (CON:, RAW:, or "*").  Additionally, if you want to 
  130.     recognize an end-of-file condition on the input, you'll have 
  131.     to use only CON: windows.  CON: windows translate CTRL-\ into 
  132.     an end-of-file;  RAW: windows, on the other hand, pass on the 
  133.     numeric value of this key combination without treating it as EOF.
  134.  
  135.  
  136. Execute("something_with_no_stdin_or_stdout", nilfh, nilfh)
  137.  
  138.     Here, "nilfh" is a file handle resulting from a call to Open
  139.     with a filename of NIL:, as:
  140.  
  141.         struct FileHandle *nilfh;
  142.  
  143.         nilfh = Open("NIL:",MODE_NEWFILE);
  144.  
  145.     Several developers and users have expressed an interest
  146.     in being able to start background processes using the Workbench
  147.     startup script (s/startup-sequence).  An example would be to start
  148.     things such as the CLOCK or something else, then end up on the 
  149.     Workbench without the originating CLI having to hang around for 
  150.     the user to close down. 
  151.  
  152.     If you want the originating CLI to go away, you must somehow
  153.     prevent any process started by that CLI from opening "*"
  154.     for either input or output as mentioned above.   
  155.  
  156.     Within the "something..." string, you must spawn an independent
  157.     CLI process.  Additionally the independent process must have its own
  158.     I/O restricted so that it does not in any way depend on or
  159.     attempt to communicate with the originating CLI.  Here, then,
  160.     is PART of the something-string:
  161.  
  162.         RUN >NIL: <NIL: <rest_of_string>
  163.  
  164.     This starts an independent process.
  165.  
  166.     The rest_of_string can contain the command and its parameters.
  167.     But be aware that if your command uses the standard startup sequence,
  168.     it still needs to open its stdin and stdout file handles.  You'll
  169.     need to assure that a proper file handle is actually provided for
  170.     stdin and stdout, so the rest of the string should look like this:
  171.  
  172.     rest_of_string = "MyCommand >NIL: <NIL: Parm1 Parm2 Parm3 etc";
  173.  
  174.     
  175.     Thus, the complete Execute command string appropriate to letting
  176.     the originating CLI close down and leave the CLOCK open on the 
  177.     Workbench is to run a program such is listed below.  Note, however, 
  178.     that the file handle to NIL:, dynamically allocated by this program, 
  179.     must hang around "forever" until the next system reset.  It has to be 
  180.     dynamically allocated, since you want this program and its calling 
  181.     CLI to die and leave the CLOCK and Workbench active. 
  182.  
  183.  
  184. /* runclock.c */
  185.  
  186. /* Author:  Rob Peck.  5/9/86 */
  187.  
  188. #include "exec/types.h"
  189. #include "exec/memory.h"
  190. #include "libraries/dosextens.h"
  191.  
  192. extern struct FileHandle *Open();
  193.  
  194. main()
  195. {
  196.     LONG success;
  197.     struct FileHandle *nilfh;
  198.     nilfh = (struct FileHandle *)
  199.         AllocMem(sizeof(struct FileHandle),MEMF_CLEAR | MEMF_PUBLIC);
  200.  
  201.     success = Execute("RUN >NIL: <NIL: CLOCK >NIL: <NIL:",nilfh,nilfh);
  202.     if(success)
  203.     {
  204.         printf("Started CLOCK as a separate process\n");
  205.     }
  206.     else
  207.     {
  208.         FreeMem(nilfh, sizeof(struct FileHandle));
  209.     }
  210.     /* Notice that if the program succeeds, nobody ever frees this mem */
  211. }
  212.  
  213.  
  214.     To try this program, your startup-sequence should contain
  215.     at least the following lines:
  216.  
  217.         LOADWB
  218.         RUNCLOCK
  219.         ENDCLI > NIL:
  220.  
  221.     Notice that this program does NOT close the "nilfh", that is,
  222.     the file handle obtained by opening NIL:.   This will tie up
  223.     a small block of memory somewhere in the system until the next
  224.     boot, unless someone spawns a process that keeps track of when
  225.     the CLOCK tool exits, then finally frees this file handle.  But
  226.     this piece of memory is probably not too high a price to pay
  227.     to obtain the functionality people have requested.     
  228.        
  229.  
  230.         Here is a general purpose implementation of the above technique,
  231.     allowing you to run a named process "in the background".  It
  232.     includes a delay parameter to minimize disk thrashing among
  233.     sequentially loaded processes.  It is a bit more complicated
  234.     in that we've installed the appropriate error handling. 
  235.  
  236.     DISCLAIMER:
  237.  
  238.     This program is provided as a service to the programmer
  239.         community to demonstrate one or more features of the Amiga
  240.         personal computer.  These code samples may be freely used
  241.         for commercial or noncommercial purposes.
  242.  
  243.         Commodore Electronics, Ltd ("Commodore") makes no
  244.         warranties, either expressed or implied, with respect
  245.         to the program described herein, its quality, performance,
  246.         merchantability, or fitness for any particular purpose.
  247.         This program is provided "as is" and the entire risk
  248.         as to its quality and performance is with the user.
  249.  
  250.     (Other standard disclaimers apply)
  251.  
  252. /* 
  253.  
  254. --------------
  255. runbackground.c  
  256. ---------------
  257.  
  258. SUMMARY:  A Workbench Disk can be used to autostart an application
  259.       through the use of the startup script and close the startup CLI.
  260.  
  261.  
  262. Users have commented that it is not possible to start a process going 
  263. from the startup script and then cause the initial CLI to go away.   
  264. Here is the solution to that problem, named appropriately:
  265.  
  266.     RUNBACKGROUND
  267.  
  268. which starts and runs a background task.  This does indeed allow you to
  269. create a startup script that will set up your workbench running any
  270. programs you might wish, removing the initial CLI in the process.
  271.  
  272. Your s/startup-sequence can contain lines such as the following:
  273.  
  274.     RUNBACKGROUND -3 clock
  275.     RUNBACKGROUND utilities/calculator
  276.     RUNBACKGROUND -5 utilities/notepad
  277.  
  278. where RUNBACKGROUND is the command and the second parameter is the filename
  279. which may be preceded by a flag-variable that specifies an optional delay 
  280. time.  The delay can be from 0 to 9, for the number of seconds that 
  281. the startup script should sleep while allowing the background task to 
  282. load and start.  I've put that in to minimize thrashing of the disk as it
  283. tries to load several projects at once.
  284.  
  285.  
  286. LIMITATIONS:
  287.  
  288.     The program that you run cannot require any input from an interactive
  289.     CLI that starts it.    Additionally, you cannot specify any file 
  290.     redirection in the command line since this program provides the
  291.     redirection for you already.  If you need to use redirection for
  292.     your command, you can modify the source code where shown, thus
  293.     allowing the redirection to become one of the parameters passed
  294.     through to your program.
  295.  
  296.     RUNBACKGROUND does pass your command line parameters to the program
  297.     you wish to start, but limits the total length of your command
  298.     string to 227 (255 minus the 28 characters for "RUN >NIL: <NIL: " 
  299.     preceding your own file pathname and ">NIL: < NIL: " following it.)
  300.  
  301.  
  302. LINKING INFORMATION:
  303.  
  304.     (Amiga/Lattice C)    use -v option for pass 2   (lc2 -v filename.q)
  305.             to disable stack checking code installation.
  306.             (stack checking code sometimes is incorrect).
  307.  
  308.     FROM lib:Astartup.obj runbackground.o
  309.     TO runbackground
  310.     LIBRARY lib:amiga.lib, lib:lc.lib
  311.  
  312.     ****************************  NOTE:  ********************************
  313.     If you use Lstartup.obj, it won't let the startup CLI go away. This is
  314.     because the source code for Lstartup.asm either opens its own window 
  315.     or uses an existing CLI window (Open("*",....)), so that it has some 
  316.     guaranteed place to put the output.   Astartup.obj does not do this.
  317.     *********************************************************************
  318.  
  319. Hope this helps.
  320.  
  321.  
  322. robp.
  323. */
  324.  
  325. /* runbackground.c */
  326. /* Author:  Rob Peck.  5/9/86 */
  327.  
  328.  
  329. #include "exec/types.h"
  330. #include "exec/memory.h"
  331. #include "libraries/dosextens.h"
  332.  
  333. extern struct FileHandle *Open();
  334. extern struct FileLock *Lock();
  335.  
  336. main(argc, argv)
  337. int argc;
  338. char *argv[];
  339. {    
  340.     LONG success, delaywillbe;
  341.     UBYTE commandstring[255];
  342.     char *test, *filename;
  343.     LONG fromparm;
  344.     struct FileInfoBlock *fib;
  345.     struct FileHandle *nilfh;    /* NOTE: will hang around until next reset */
  346.     struct FileLock *lock;
  347.     
  348.     fib = NULL;            /* No file info block so far. */
  349.     delaywillbe = 1;
  350.  
  351.     if(argc < 2 )
  352.     {
  353. usage:
  354.     printf("Usage: RUNBACKGROUND [ -<loaddelay>] <name> [<parm(s)>]\n");
  355.     printf("          where optional loaddelay is 0-9,\n");
  356.     printf("          specified in seconds for the CLI\n");
  357.     printf("          to sleep, waiting for task to load\n");
  358.     printf("          (minimizes inter-task disk-thrashing)\n");
  359.     if(fib) FreeMem(fib, sizeof(struct FileInfoBlock));
  360.     exit(0);
  361.     }
  362.  
  363.     /* See if there is a delay parameter present */
  364.  
  365.     test = argv[1];
  366.  
  367.     if(*test++ == '-')
  368.     {
  369.     filename = argv[2];    /* argv[1] is delay so argv[2] is file  */
  370.     fromparm = 3;        /* Copy parms from 3 to end          */
  371.  
  372.     if(*test >= '0' && *test <= '9')
  373.     {
  374.         delaywillbe = 1 + (50 * (*test - '0')); 
  375.     }
  376.     if (argc < 3) goto usage; /* Only a delay, and no filename!! */
  377.  
  378.     argc--;        /* one less parm to copy */
  379.     }
  380.     else
  381.     {
  382.     filename = argv[1];
  383.     fromparm = 2;        /* Copy parms from 2 to end         */
  384.     }
  385.  
  386.     /* Now see if the file exists!  If not, it can crash the background
  387.      * CLI and take the system along with it.
  388.      */
  389.     lock = Lock(filename,ACCESS_READ); 
  390.     if(!lock)
  391.     {
  392.     test = filename;
  393.     if(*test == '?') goto usage;
  394.     else
  395.     {
  396.         printf("%ls: Command not found\n",filename);
  397.         goto usage;
  398.     }
  399.     }
  400.     else
  401.     {
  402.     /* If file exists, it better be a file and not a directory */
  403.  
  404.     /* Unfortunately, it is difficult to tell if it is an executable
  405.      * file.  If not executable, we'll still get blown out of the
  406.      * water, but that is up to the user to do it right!
  407.      */
  408.  
  409.     fib =  (struct FileInfoBlock *)
  410.             AllocMem(sizeof(struct FileInfoBlock),MEMF_CLEAR);
  411.     if(!fib)
  412.     {
  413.         UnLock(lock);
  414.         printf("Ran out of memory!\n");
  415.         exit(0);
  416.     }
  417.     else
  418.     {
  419.         success = Examine(lock,fib);
  420.         if(fib->fib_DirEntryType > 0)     /* its a directory!! */
  421.         {
  422.         printf("%ls is a directory, not a file!\n",filename);
  423.         goto usage;
  424.         }
  425.     }
  426.        FreeMem(fib, sizeof(struct FileInfoBlock));
  427.     UnLock(lock);
  428.     }
  429.  
  430.     nilfh = Open("NIL:",MODE_NEWFILE); /* will always succeed */
  431.  
  432.     strcpy( &commandstring[0], "RUN >NIL: <NIL: " );
  433.     strcat( &commandstring[0], filename);  
  434.  
  435.     /* REMOVE THIS NEXT LINE IF YOU WANT TO INCLUDE REDIRECTION IN
  436.      * THE COMMAND LINE FOR RUNBACKGROUND.   (The line was installed
  437.      * to assure that something like "RUNBACKGROUND date ?" would
  438.      * not crash the system.  "Date ?" is expecting to have an interactive
  439.      * CLI, and unless it is specifically told to direct its output to nil:
  440.      * it causes a crash.  If the next line is removed, and you are careful
  441.      * about putting only NON-interactive commands in the command line,
  442.      * everything should be ok.  Notice that if you do not specify a 
  443.      * non-interactive file handle (named_disk_file or NIL:) for your
  444.      * program, it may still prevent the originating CLI from going away
  445.      * until your program ends.  Also note that specifying two instances 
  446.      * of the same redirection (">somewhere" or "<somewhere") for a 
  447.      * background task crashes.
  448.      */
  449.  
  450.     strcat( &commandstring[0], " >NIL: <NIL: ");  /* stop interactive crash */ 
  451.  
  452.     argc--;
  453.  
  454.     while(--argc > 0)    /* Rebuild parameter string for passing it on */
  455.     {
  456.     strcat( &commandstring[0], " ");    /* add a blank */
  457.     strcat( &commandstring[0], argv[fromparm++]);  
  458.     }
  459.  
  460.     success = Execute( &commandstring[0] , nilfh, nilfh);
  461.  
  462.     /* The full command passed to Execute now looks like this:
  463.      *
  464.      *    "RUN >NIL: <NIL: FILENAME >NIL: <NIL: PARAMETER(s)"
  465.      *
  466.      */
  467.  
  468.     if(success)
  469.     {
  470.     printf("Started %ls as a background task\n",filename);
  471.  
  472.     /* Execute, in this case, returns IMMEDIATELY.  The process
  473.      * that is loading the code that is to be run as a background
  474.      * process is working to get everything in and started.  
  475.      */
  476.     }
  477.     /* Now, to minimize thrashing between tasks, lets put this task to 
  478.      * sleep so that the each task actually gets a chance to load.
  479.      */
  480.     Delay(delaywillbe);
  481. }
  482.  
  483. strcpy( to, from )
  484. register char *to, *from;
  485. {
  486.    do {
  487.        *to++ = *from;
  488.    } while( *from++ );
  489. }
  490. strcat( to, from )
  491. register char *to, *from;
  492. {
  493.     while( *to ) to++;
  494.     strcpy( to, from );
  495. }
  496.