code: func (c *cmd) killChild(childErr chan error) error {
  	// Get the child process
  	child := c.child
  
  	// If there is no child process (failed to start), we can quit early
  	if child == nil {
  		if c.verbose {
  			c.UI.Info("No child process to kill")
  		}
  		return nil
  	}
  
  	// Attempt termination first
  	if c.verbose {
  		c.UI.Info(fmt.Sprintf("Terminating child pid %d", child.Pid))
  	}
  	if err := signalPid(child.Pid, syscall.SIGTERM); err != nil {
  		return fmt.Errorf("Failed to terminate %d: %v", child.Pid, err)
  	}
  
  	// Wait for termination, or until a timeout
  	select {
  	case <-childErr:
  		if c.verbose {
  			c.UI.Info("Child terminated")
  		}
  		return nil
  	case <-time.After(lockKillGracePeriod):
  		if c.verbose {
  			c.UI.Info(fmt.Sprintf("Child did not exit after grace period of %v",
  				lockKillGracePeriod))
  		}
  	}
  
  	// Send a final SIGKILL
  	if c.verbose {
  		c.UI.Info(fmt.Sprintf("Killing child pid %d", child.Pid))
  	}
  	if err := signalPid(child.Pid, syscall.SIGKILL); err != nil {
  		return fmt.Errorf("Failed to kill %d: %v", child.Pid, err)
  	}
  	return nil
  }
docs: // killChild is used to forcefully kill the child, first using SIGTERM
  // to allow for a graceful cleanup and then using SIGKILL for a hard
  // termination.
  // On Windows, the child is always hard terminated with a SIGKILL, even
  // on the first attempt.