Salmon - Release Notes


Copyright 2019 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.04 of Salmon.


Introduction

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

  1. 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 terminates.

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

    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.

  3. 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 program. 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.
      1. This process may immediately continue, OR
      2. the process may sleep using the “waitpid” function until the child process terminates.
    3. 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.
        • It may not modify the environment (with certain exceptions)
        • It may not change variable values (with certain exceptions)
        • It may not gather input nor produce (asynchronous) output which conflicts with the parent process.
        • 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.
    4. 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:
      1. 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.
      2. The parent process sleeps until the child process terminates,
        See the ‘-f’ option for an example.
      3. 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.
        • Any output produced by the child process which may be needed later may also be redirected to temporary files for later retrieval.
        • 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" ) ;
        • 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 command-line 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], // this should be the 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.

      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 ; } }

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


    • –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:
      • determine the name of the terminal emulator program which is currently running,
      • determine the name of the shell program running in the terminal window,
      • 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 ) ;

      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 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/

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

    • Release v:0.0.04

      • Minor changes required to address warnings generated by the move to GNU G++ v:9.0.1
        (no functionality change)
      • 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.

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

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

      • 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 applications from source:

      • 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’.
      • 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. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

        If you are not using GCC, then verify that your compiler fully supports the C++11 standard.
      • 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
      • 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
      • Navigate to the source directory:
        cd Salmon
      • Build both the main application and the target application:
        gmake salmon gmake -f MakeChild

        When both applications build without errors and without warnings, congratulations!
      • 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]$


      These notes are adapted from the “README” file included with the application archive.
      Thank you, and we hope you enjoy using the exec functions!  — Software Sam