README file for 'Salmon'.

Copyright 2012-2020 by The Software Samurai.
On the web:  http://www.SoftwareSam.us/
Software released under GNU GPL3, and documentation released under FDL1.3
This document describes version 0.0.03 of Salmon.

=======================================================================

Unpack this archive using the following command:
              tar -xjvf salmon-x.x.xx.tar.bz2

  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
-- =========================================
--              Introduction               =
-- =========================================

This package contains source code and basic documentation for the 'Salmon' 
application.

You will need to build the source and target application binaries for your 
target system (see below).

The 'Salmon' application is a GNU/Linux console (command-line) utility which 
demonstrates some common examples of spawing an external process from within 
an application.

Because this is a demonstration program only, the logic is straightforward but 
not comprehensive. A production-worthy application will give much more attention 
to possible error conditions. Also, the documentation (this document) is quite 
simple, but we hope you will find it useful.

Note to novice programmers:
This application is written in C++, but it discusses various C-language 
primitive functions. When we use the word "function," we mean a C-language 
routine. When we use the word "method," we mean a C++ routine. This difference 
in terminology may seem to be a small one, but wars have been fought over it. 
(We are geeks, after all.) Since Software Sam is a teacher, here's the scoop: 
A C-language "function" _does something_ (moves the program flow from point 
A to point B), while a C++ "method" _accomplishes something_ (solves a problem). 
This is tied to the concept of object-oriented programming as contrasted with 
the so-called "structured" programming that was common in the days when 
dinosaurs roamed the earth. (And don't even mention the spaghetti code written 
by the Neanderthals in the UNIX world!)

Also, we apologize for the application name, but we couldn't resist. 
This project was written strictly for fun. "Salmon" swim upstream to spawn fish 
eggs, not process IDs, but we hope you get the idea.

A. This application is intended as a reference for students and others who want 
   to understand the use of the "exec" group of primitive functions and other 
   functions related to spawing a child process from within a parent application.

   The Salmon package includes source code for two executable binary files, 
   'Salmon' and 'childApp'. Salmon contains the interesting code which invokes 
   childApp with various arguments. childApp simply reports the arguments it has 
   received from the controlling application, then termnates.

B. The "exec" function group invokes an external program.
   1) If the exec call is successful, then the process ID used to invoke the 
      external program will terminate when the called application or script exits.

   2) If the calling PID is the only process ID running in the calling 
      application, then the calling application terminates immediately when the 
      'exec' function is called.

   3) If multiple process IDs are active when the external application is called, 
      then only the process which executes the 'exec' function is detached from 
      the calling application and will eventually terminate while the remaining 
      processes will continue to execute within the calling application. See the 
      description of the "fork" function, below for more information.

   4) An external application could be a web browser, email client, image viewer 
      etc. It may or may not write to the terminal window from which it was 
      launched. If it does, it will overwrite the display data of whatever 
      application is already running in that window, so be aware.

C. Technical Note: Do not confuse the creation of mutiple, independent processes 
   with the concept of multithreading. Processes and threads have significant 
   differences in terms of resource allocation and scope of access. To perform 
   multiple tasks within a single application, we strongly recommend 
   multithreading rather than creating multiple processes. 

   Multithreading is an operation in which multiple execution threads are 
   created, and each is launched into its own private method to perform a 
   specific task. The creation, use of and termination of programatic threads is 
   not covered in this example application. Please see our "Sew" (as in "sew, 
   a needle pulling thread") application for a demonstration of multithreading.

D. The "fork" function creates a split in a program's flow of execution.

   1) The "fork" group of functions is used to create a child process within the 
      calling application. For the "fork" function, the parent and child 
      processes initially have identical copies of the application environment 
      (the "process image"). See the "fork" function documentation (info fork) 
      for exactly what is included in this process image.

      Note that for this application, we want the forked process to immediately 
      execute an external process. For this reason, we use the "vfork" function 
      rather than "fork" because "vfork" does not create an unnecessary copy of 
      the process image which will be immediately discarded anyway. Instead, the 
      "vfork" function allows the parent and child to _share_ the single process 
      image for the short time they are running concurrently in the parent 
      application.

   2) The parent process continues to execute the calling application.
      a) This process may immediately continue, OR
      b) the process may sleep using the "waitpid" function until the child 
      process terminates.

E. The child process _also_ continues to execute within the calling application; 
   _however_, its actions must be strictly limited to avoid destruction of the 
   calling application's data and environment.

   1) When an application spawns a child process using "vfork", that process 
      needs something to do. That "something" must not interfere with the 
      operation of the parent process.

      a) It may not modify the environment (with certain exceptions)
      b) It may not change variable values (with certain exceptions)
      c) It may not gather input nor produce (asynchronous) output which 
         conflicts with the parent process.
      d) Perhaps most importantly, the child may not allocate or release 
         stack space. In practical terms, this means that the child process 
         must NEVER return from the method in which it was spawned.

   2) Resource conflicts for the input/output streams, 'stdin', 'stdout' and 
      'stderr' may occur between the parent and child processes. To avoid 
      resource conflicts with console applications or scripts launched by 
      the child process:

      a) The child process will avoid capture of input and will produce no 
         output to the standard streams, 'stdout' and 'stderr'. 
         Note that this is not always practical, and must be handled carefully. 
         Either the called executable was written by you, and is therefore 
         guaranteed to generate no I/O to the shared streams, or it must have 
         a "silent mode", similar to our 'cTrash' utility.

      b) The parent process sleeps until the child process terminates.
         See the '-f' option for an example.

      c) Input (stdin) and output (stdout and stderr) for the child process 
         may be redirected, which allows the parent process to retain control 
         of stdin, stdout and stderr. See the '-s' option for an example.

         -- Typically, any startup messages of an external (non-console) 
            application can be discarded by redirecting 'stdout'; and 'stderr' 
            to temporary files.
            This redirection can be accomplished by using the "dup2" function. 
            See the '-s' option for an example.

         -- The "dup2" redirection method may also be used to capture any output 
            produced by the child process which may be needed later.

         -- For simple external calls when the output is not needed, the output 
            can be redirected to the "bit bucket". The bit bucket consists of a 
            device, '/dev/null' which simply discards whatever is sent to it. 
            Note that this is a shared resource.

            Command-line Example:
               echo $PWD 1>/dev/null 2>/dev/null
            Example using 'system' function call:
               system ( "echo $PWD 1>/dev/null 2>/dev/null" ) ;

      d) The child process could be launched in a new terminal window. 
         Note that this is an advanced operation which may not be transparently 
         supported by all terminal emulators and/or shell programs. See the '-t' 
         option for further discussion and an example. 

  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

-- =========================================
-- User Interface and Command Line Options =
-- =========================================

   Invocation:
   -----------

      ./salmon -[v | l | p | f | s | t | D] [ARG1[ ARG2[ ARG3[ ARG4]]]]


   Optional Arguments ARG1 through ARG4:
   -------------------------------------

   These optional comand-line arguments are recognized by the '-v', '-l' 
   and '-f' options, and specified arguments will be passed to the child 
   application (childApp) which will display them to verify that they were 
   passed successfully.

   ./salmon -l 'catsup is red' 'mustard is yellow' 'pickles are green' 'onions stink!'

       will yield:

   Salmon Swim Upstream To Spawn!
    Invocation: "./salmon -l 'catsup is red' 'mustard is yellow' 'pickles are green' 'onions stink!'"
    Parent PID: 6201
    calling execl( childApp ... )

    childApp: PID: 6201
    ====================
    argv[0] childApp
    argv[1] -l
    argv[2] catsup is red
    argv[3] mustard is yellow
    argv[4] pickles are green
    argv[5] onions stink!


   Required Argument, one of the following:
   ----------------------------------------

   -v    execv() function
         Pass an argv[] array to childApp (calling application terminates).

         The 'v' in "execv" indicates the argv[] vector array, and as a software 
         designer, you may be aware that this is an array of char pointers 
            char const* argv[]
         with the last pointer in the array being the NULL pointer.

         Example:
         --------

         const char* fileName = "./childApp" ;
         char const* argV[] = 
         {
            "This is argv[0]",   // this should be the bare filename of 
                                 // the target (no path component)
            "This is argv[1]",   // additional arguments . . .
            "This is argv[2]",
            "This is argv[3]",
            "This is argv[4]",
            NULL                 // null pointer
         } ;

         execv ( fileName, argV ) ;


   -l    execl() function
         Pass a list of individual arguments to childApp (calling application 
         terminates).

         The 'l' in "execl" indicates a list (sequence) of individual arguments, 
         with the last item in the list being the NULL pointer.

         Example:
         --------

         const char* fileName = "./childApp" ;

         execl ( fileName,             // path/filename of target application  
                 &filename[2],         // should be bare filename of the target
                 "This is argv[1]",    // additional arguments . . .
                 "This is argv[2]",
                 "This is argv[3]",
                 "This is argv[4]",
                 NULL                  // null pointer
               ) ;


   -p    execlp() function
         Pass a list of individual arguments which invokes an application on the 
         system PATH. (calling application terminates)

         The 'l' in "execlp" indicates a list (sequence) of individual arguments, 
         and the 'p' indicates that the directories specified by the $PATH 
         environment variable will be searched for the application to be invoked.

         Note that if the forward-slash character ( '/' ) is found in the 
         application name, then the string will be interpreted as an absolute or 
         relative path, and the $PATH variable _will not_ be searched. 

         For this example, the external application called is the 'grep' utility.
         'grep' is called with arguments describing what to search for and which 
         files to search.

                     grep -n 'COMPILE' Makefile MakeChild

         Example:
         --------

         execlp ( "grep", "grep", "-n", "COMPILE", "Makefile", "MakeChild", NULL ) ;

         Note that each argument to execlp() is a separate string.


   -f    execl() function executed by "forked" process.
         The primary process calls the "fork" (actually "vfork") function to 
         create a child process, then the child process invokes an external 
         program (childApp) which produces some output, then exits.

         After calling "vfork", the parent process waits silently for the child 
         process to terminate, at which time it reclaims control of the I/O 
         streams.

         Example:
         --------

         pid_t fpid = vfork () ;    // create the child process

         if ( fpid == ZERO)         // the child process (if created) executes here
         {
            execl ( "./childApp",   // target application path/filename 
                    "childApp",     // argv[0] bare target app filename
                    "-f",           // argv[1] additional arguments . . .
                    "some arg",     // argv[2] 
                    "some arg",     // argv[3]
                    "some arg",     // argv[4]
                    "some arg",     // argv[5]
                    NULL
                  ) ;
          
            // In case the exec call fails: child process MUST NOT return,
            // so we force it to exit immediately.
            _exit ( 0 ) ;
         }
         else                       // the parent process continues execution here
         {
            if ( fpid > ZERO )      // child successfully launched
            {
               int childStatus = ZERO ;   // receives child's exit status
               int statusOptions = ZERO ; // status bitmask (none defined at this time)
         
               // sleep until the child process terminates
               waitpid ( fpid, &childStatus, statusOptions ) ;
            }
            else
            {
               // creation of child process failed
            }
         } 


   -s    execvp() function executed by "forked" process.
         The primary process calls the "fork" (actually "vfork") function to 
         create a child process, then the child process invokes an external 
         program (childApp) which produces some output, then exits.

         After calling "vfork", the parent process continues execution immediately.

         For this example, the output streams ('stdout' and 'stderr") of the 
         child process are redirected to temporary files 'stdout.txt' and 
         'stderr.txt", respectively. This allows the parent process to retain 
         control of the output streams, even though both processes are 
         simultaneously executing programs in the same terminal window.

         After both programs have terminated, the output which was redirected to 
         the temporary files may be viewed using the command:  cat *.txt

         Example:
         --------

         const char* childApp = "./childApp" ;// name of app that child invokes
         const char* childArgs[] =            // child's argv[] array
         {
            "childApp",          // argv[0] bare target filename
            "-s",                // argv[1] option switch from invocation of parent
            "Hello stdout!\n",   // argv[2] written to stdout (which is redirected)
            "Hello stderr!\n",   // argv[3] written to stderr (which is redirected)
            NULL                 // terminate the list
         } ;
         const char* soFName = "./sout.txt" ; // stdout target file
         const char* seFName = "./serr.txt" ; // stderr target file
         int soDesc = ZERO,                   // file descriptor for stdout target
             seDesc = ZERO ;                  // file descriptor for stderr target

         pid_t fpid = vfork () ; // create the child process

         if ( fpid == ZERO)      // the child process (if created) executes here
         {
            // Create the temporary files.
            soDesc = open ( soFName, O_WRONLY | O_CREAT | O_TRUNC, 
                            S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) ;
            seDesc = open ( seFName, O_WRONLY | O_CREAT | O_TRUNC, 
                            S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) ;

            // Redirect 'stdout' and 'stderr' streams to the files.
            dup2 ( soDesc, STDOUT_FILENO) ;
            dup2 ( seDesc, STDERR_FILENO) ;

            // Close the temporary files.
            close ( soDesc ) ;
            close ( seDesc ) ;

            // If execvp() call is successful, the child process will not 
            // return to the parent application, else the return value 
            // will be written to stderr (which has been redirected to serr.txt).   
            wcerr << execvp ( childApp, (char* const* )childArgs ) ;
            wcerr << " Error! Child process call to execvp() failed." << endl ;

            // In case the exec call fails: child process MUST NOT return,
            // so we force it to exit immediately.
            _exit ( 0 ) ;
         }
         else                    // the parent process continues execution here
         {
            if ( fpid > ZERO )   // child successfully launched
            {
               wcout << L"Fork of child process was successful!" << endl ;
            }
            else
            {
               wcout << L"Creation of child process failed!" << endl ;
            }
         }


   -t    execl() function executed by "forked" process.
         The primary process calls the "fork" (actually "vfork") function to 
         create a child process, then the child process opens a new terminal 
         window and invokes an external program (grep) within the new window.
 
         After calling "vfork", the parent process continues execution immediately.

         In the Salmon source code, we perform some magic to:
         a) determine the name of the terminal emulator program which is 
            currently running,
         b) determine the name of the shell program running in the terminal window,
         c) calculate the size and position for the new terminal window. 

         However, the following example has been simplified for clarity. In the 
         example, we assume the GNOME terminal emulator, the "bash" shell program 
         and convenient constants for the size and position for the window.

         Example:
         --------

         execlp ( gnome-terminal,             // name of terminal program
                  "gnome-terminal",           // argv[0] (bare program name)
                  "--geometry=65x9+400+300",  // argv[1] (size and position for new window)
                  "--hide-menubar",           // argv[2] (disable the  window's menu bar)
                  "--window",                 // argv[3] (specify new window rather than default (tab))
                  "-q",                       // argv[4] (no output to parent window)
                  "-e",                       // argv[5] (the execlp 'execute' command)
                  // argv[6] (a complex command to be executed in the new terminal window)
                  "bash -c \"grep -n 'COMPILE' Makefile MakeChild ; exec bash\"",
                  NULL                        // argv[7] (signals end of pointer array)
                ) ;

         The main command to the 'execlp' function, labelled as 'argv[6]' in the 
         above example needs some further clarification.

         If all we wanted was to invoke the 'grep' command in the new terminal 
         window and immediately close the window, then argv[6] would be the same 
         as in the '-p' option described above:
 
                  grep -n 'COMPILE' Makefile MakeChild

         However, what we want to do here is to open the new window, invoke the 
         'grep' command and then leave the window open so the user can view the 
         results.

         bash
               This is the name of the shell program. It generates a fresh copy 
               of the shell in which to run the 'grep' command.

         -c
               This is the bash "command" command, indicating that the next 
               token is to be executed by the shell program.

         \"
               This is an _escaped_ double-quotation character. This character 
               will be interpreted by the shell program as a quotation mark 
               beginning the command to be executed.

         grep -n 'COMPILE' Makefile MakeChild
               This is the 'grep' command sequence. 

         ;
               The semicolon character is used to separate a list of commands to 
               be executed sequentially by the shell. In our example, there are 
               two commands, the 'grep' invocation described in the previous line 
               item, and the shell command, 'exec' described in the next item.

         exec bash
               This command _replaces_ the currently running copy of 'bash' with 
               a new copy. This may seem odd, but without the 'exec' two copies 
               of the shell would be left running in the window.
               The _effect_ of this command is to keep the new window open and 
               to return to the command prompt after all the specified commands 
               have finished.

         \"
               This is another _escaped_ double-quotation character, which the
               shell program interprets as the end of the command to be executed.  

  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

-- =========================================
-- NOTES on the current release of Salmon  =
-- =========================================

   The files included in this release.

   Salmon/            // Source code directory
      Salmon.cpp      // Main demonstration application
      childApp.cpp    // Secondary application invoked by the child process
      GlobalDef.hpp   // '#include' files and general definitions
      Makefile        // Build the 'salmon' application
      MakeChild       // Build the 'childApp' application
      README          // Package description, release notes (this file)

   1) This application was designed under Fedora Linux and is built using the 
      GNU C++ compiler, version 4.8.3 or higher (C++11 support).

      (Note for users of Microsoft Windows(tm), we strongly recommend that you 
      immediately wipe your system and install Ubuntu 18.04 LTS.)
               http://releases.ubuntu.com/"

   2) The '-std=gnu11' switch is required for a successful build. 
      See the Makefile for details.

   3) Current Release v:0.0.04
      a) Minor changes required to address warnings generated by the move to 
         GNU G++ v:9.0.1  (no functionality change)

      b) Add Wayland-specific code to address difficulties in defining a 
         new terminal window when the X-server is no longer available.
         See the DefineChildEmulator() method for details.

      Release v:0.0.03  Documentation update only.

      Release v:0.0.02  First public release.
      a) Although this demonstration app has been used in various workshops and 
         for classroom activities, this is the first version posted to the 
         SoftwareSam website. If you discover a problem with the code or an 
         ambiguity in the documentation, please send us a note about your 
         experiences.

      b) Not all members of the "exec" function group are exercised in this 
         application.
 
         All variations used here pass a copy of the parent's environment to the 
         child process. Additional "exec" variations exist for passing a custom 
         environment to the child process, similar to the argenv[] array that is 
         passed to all applications on startup.

         Consult the C-library documentation: 'info -f libc.info -n Processes'
         for additional information.

      c) The algorithm used for opening a secondary terminal window via the 
         child process ('t' option) supports only the three(3) most common 
         terminal emulators and a limited number of shell programs.

         The intention here is to demonstrate the concepts clearly while keeping 
         the actual code as simple as possible. All mainstream terminal emulators 
         and shell programs we have studied follow the same general command 
         structure, and adapting these concepts to additional emulators and/or 
         shells should be straightforward.


-- ======================================
-- Building the 'Salmon' applications:  =
-- ======================================

   Building the applications from source:

   1) Open a terminal window of a convenient size. 
      The application should _build_ in any terminal emulator window; however, 
      application functionality testing has been done only in 'gnome-terminal', 
      'konsole' and 'xterm'.

   2) Verify that your compiler is at least version 4.8.0:
            g++ --version

            g++ (GCC) 4.8.3 20140911 (Red Hat 4.8.3-7)
            Copyright (C) 2013 Free Software Foundation, Inc.

      If you are not using GCC, then verify that your compiler fully supports 
      the C++11 standard.

   3) Download the Salmon archive and copy it to the directory where your 
      software projects live:
            cp --preserve=all salmon-0.0.02.tar.bz2 ~/SoftwareDesign
            cd ~/SoftwareDesign

   4) Unpack the archive.
      The Salmon directory will be created and all source files will be expanded 
      into that directory.
            tar -xjvf salmon-0.0.02.tar.bz2

   5) Navigate to the source directory:
            cd Salmon

   6) Build both the main application and the target application:
            gmake salmon
            gmake -f MakeChild

      When both applications build without errors and without warnings, 
                           CONGRATULATIONS!

   7) Test the application:
            ./salmon -v

      You should see something like the following, indicating that both 
      applications are functioning properly:

      Salmon Swim Upstream To Spawn!
       Invocation: './salmon -v'
       Parent PID: 8592
       calling execv( childApp argv[] )
      
            childApp: PID: 8592
            ====================
            argv[0] childApp
            argv[1] -v
            argv[2] Types of Salmon:
            argv[3] Trout
            argv[4] Char
            argv[5] Grayling
            argv[6] Whitefish
            
            [Salmon]$ 


-- ============================
-- Known bugs and other issues:
-- ============================
   -- Terminal emulator and shell program configuration are system dependent, 
      which may affect output formatting and/or creation of secondary terminal 
      windows.
   -- No outstanding bug reports.

