docs: def _exec_process(cmd_list, base_context, instance=0, log=None):
      """
      Process execution tool.
  
      The forks and execs a process with args formatted according to a context.
      This is implemented as a module function to make it available to
      event_targets, legion and tasks.
  
      The args are:
  
      cmd_list    - The path and arg vector
      context     - Task's context
      instance    - An integer instance number used with multi-process tasks
      log     - Logging object (default is nothing logged).
  
      The context is used to format command args.  In addition, these values will
      be used to change the process execution environment:
  
      procname    - Changes the process name of the executed command (but not the path executed).
      user        - Does a setuid for the process
      group       - Does a setgid for the process
      cwd     - Does a chdir before executing
  
      The passed context is extended to include these specific runtime values which
      are only available for cmd_list substitution.
  
      context_prefix+'pid'        -  The process ID of the child process
      context_prefix+'instance'   -  The instance number (0 if not provided)
      context_prefix+'uid'        -  The numeric uid (based on 'user' if set, getuid() otherwise)
      context_prefix+'gid'        -  The numeric gid (based on 'group' if set, getgid() otherwise)
  """
      if not log:                                                                                 # pragma: no cover
          log = logging.getLogger(__name__)
          log.addHandler(logging.NullHandler())
  
      #  Get a copy of the context so changes here will not affect the
      #  task's base context.
      #
      context = base_context.copy()
  
      #  Make sure we have a normalized clone of the cmd_list
      #
      cmd_list = list(cmd_list)
      name = context.get(context_prefix+'name', cmd_list[0])
      log.debug("Starting %s instance %d", name, instance)
  
      procname = _fmt_context(context.get(context_prefix+'procname'), context)
      user = _fmt_context(context.get(context_prefix+'user'), context)
      group = _fmt_context(context.get(context_prefix+'group'), context)
      cwd = _fmt_context(context.get(context_prefix+'cwd'), context)
  
      #  Do the user setup early so we can throw an Exception on failure.
      #  Identity errors are considered fatal as we do not want to run
      #  a process at a higher priv if it was explicitly set to something
      #  else.
      #
      proc_uid = os.geteuid()
      proc_gid = os.getegid()
      do_setuid = (proc_uid != os.getuid())
      do_setgid = (proc_gid != os.getgid())
      if user is not None:
          pw = None
          try:
              uid = int(user)
              try: pw = pwd.getpwuid(uid)
              except: pass                                                                        # pragma: no cover
          except: pass
          if pw is None:
              try:
                  pw = pwd.getpwnam(user)
              except Exception as e:
                  raise TaskError(name, "Bad user %r -- %s" % (user, e))
          if proc_uid != pw.pw_uid:
              proc_uid = pw.pw_uid
              do_setuid = True
          if proc_gid != pw.pw_gid:
              proc_gid = pw.pw_gid
              do_setgid = True
      if group is not None:
          gr = None
          try:
              gid = int(group)
              try: gr = grp.getgrgid(gid)
              except: pass
          except: pass
          if gr is None:
              try:
                  gr = grp.getgrnam(group)
              except Exception as e:
                  raise TaskError(name, "Bad group %r -- %s" % (group, e))
          if proc_uid is not None and proc_gid != gr.gr_gid:
              log.info("gid for user %r (%d) overridden by group %r (%d)", user, proc_gid, group, gr.gr_gid)
              proc_gid = gr.gr_gid
              do_setgid = True
  
      if cwd is not None and not os.path.isdir(cwd):
          raise TaskError(name, "Directory for cwd setting '%s' does not exist" % (cwd,))
  
      #  Add in per-process context
      #
      context[context_prefix+'instance'] = instance
      context[context_prefix+'started'] = time.time()
      context[context_prefix+'uid'] = proc_uid
      context[context_prefix+'gid'] = proc_gid
  
      pid = os.fork()
  
      #  Parent just returns pid
      if pid > 0:
          return pid
  
      #  This section is processing the child.  Exceptions from this point must
      #  never escape to outside handlers or we might create zombie init tasks.
      #
      try:
          # Add the pid to the context now that we have it.
          #
          context[context_prefix+'pid'] = os.getpid()
  
          #  Set up the requested process environment
          #
          if do_setgid:
              try:
                  os.setgid(proc_gid)
                  log.debug("Setgid to %d succeeded in child '%s', instance %d", proc_gid, name, instance)
              except Exception as e:
                  log.error("Setgid to %d failed in child %r, instance %d -- %s",
                          proc_gid, name, instance, e, exc_info=log.isEnabledFor(logging.DEBUG))
                  os._exit(81)
          if do_setuid:
              try:
                  os.setuid(proc_uid)
                  log.debug("Setuid to %d succeeded in child '%s', instance %d", proc_uid, name, instance)
              except Exception as e:
                  log.error("Setuid to %d failed in child %r, instance %d -- %s",
                          proc_uid, name, instance, e, exc_info=log.isEnabledFor(logging.DEBUG))
                  os._exit(82)
          if cwd is not None:
              try:
                  os.chdir(cwd)
                  log.debug("Chdir to '%s' succeeded in child '%s', instance %d", cwd, name, instance)
              except Exception as e:
                  log.error("Chdir to '%s' failed in child %r, instance %d -- %s",
                          cwd, name, instance, e, exc_info=log.isEnabledFor(logging.DEBUG))
                  os._exit(83)
  
          #  Build formatted command
          #
          prog = _fmt_context(cmd_list[0], context)
          cmd = []
          if procname:
              cmd_list.pop(0)
              cmd.append(_fmt_context(context['procname'], context))
          for a in cmd_list:
              cmd.append(_fmt_context(a, context))
  
          log.info("child, Execing: %s <%s>", prog, utils.format_cmd(cmd))
      except Exception as e:
          #  Log any exceptions here while we still can.  After the closeall,
          #  bets are off.
          #
          log.error("Child processing failed for task %r, instance %d -- %s",
                  name, instance, e, exc_info=log.isEnabledFor(logging.DEBUG))
          os._exit(84)
      try:
          retain_fds = [0,1,2]
          for log_fd in utils.log_filenos(log):
              if log_fd not in retain_fds:
                  retain_fds.append(log_fd)
          utils.closeall(exclude=retain_fds)
          fd = None
          try: os.close(0)
          except: pass
          try:
              fd = os.open(std_process_dest, os.O_RDONLY)
          except Exception as e:
              log.error("child read open of %s failed -- %s", std_process_dest, e)
          if fd != 0:
              log.error("child failed to redirect stdin to %s", std_process_dest)
  
          try: os.close(1)
          except: pass
          try:
              fd = os.open('/dev/null', os.O_WRONLY)
          except Exception as e:
              log.error("child write open of %s failed -- %s", std_process_dest, e)
          if fd != 1:
              log.error("child failed to redirect stdout to %s", std_process_dest)
  
          #  Build a fresh environment based on context, with None values excluded and
          #  all other values as strings, formatted where appropriate:
          #
          env = {}
          for tag, val in context.items():
              if val is None:
                  continue
              val = _fmt_context(str(val), context)
              if val is not None:
                  env[tag] = val
      except Exception as e:
          #  At this point we can still send logs to stderr, so log these
          #  too, just in case.
          #
          log.error("Child processing failed for task %r, instance %d -- %s",
              name, instance, e, exc_info=log.isEnabledFor(logging.DEBUG))
          os._exit(85)
      try:
          try: os.close(2)
          except: pass
          try: os.dup(1)
          except: pass
  
          os.execvpe(prog, cmd, env)
      except:
          pass
      #  There is no way to report an exception here, so hopefully the exit code will
      #  be evidence enough.  When child output logging is supported, this can be reworked.
      #
      os._exit(86)