m/p/cmd: handle exiting processes

RunCommand was seen waiting for the line that would trigger its
supplied predicate function even after the managed process exited,
which is deemed a bad strategy.

Change-Id: I5fa5add1daf3c4f0c69f72f5b5859e88f7bc2679
Reviewed-on: https://review.monogon.dev/c/monogon/+/847
Reviewed-by: Sergiusz Bazanski <serge@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/pkg/cmd/run.go b/metropolis/pkg/cmd/run.go
index 7591d56..f92cb84 100644
--- a/metropolis/pkg/cmd/run.go
+++ b/metropolis/pkg/cmd/run.go
@@ -14,9 +14,11 @@
 )
 
 // RunCommand starts a new process and waits until either its completion, or
-// until the supplied predicate function returns true. The function is called
+// until the supplied predicate function pf returns true. The function is called
 // for each line produced by the new process.
 //
+// The returned boolean value equals the last value returned by pf.
+//
 // The process will be killed both in the event the context is cancelled, and
 // when expectedOutput is found.
 func RunCommand(ctx context.Context, path string, args []string, pf func(string) bool) (bool, error) {
@@ -50,6 +52,13 @@
 		return false, fmt.Errorf("couldn't start the process: %w", err)
 	}
 
+	// Handle the case in which the process finishes before pf takes the chance to
+	// kill it.
+	complC := make(chan error, 1)
+	go func() {
+		complC <- cmd.Wait()
+	}()
+
 	// Try matching against expectedOutput and return the result.
 	for {
 		select {
@@ -61,6 +70,8 @@
 				cmd.Wait()
 				return true, nil
 			}
+		case err := <-complC:
+			return false, err
 		}
 	}
 }