veyron/{lib/exec,services/mgmt/device}: Get app pid

As a consequence of using go's builtin support for running a process
as a different uid, suidhelper forks prior to exec-ing an app at the
behest of the device manager. Tracking the lifetime of the app process
requires its pid, not the pid of the (short lived) suidhelper
application.

This CL extends the exec facility to permit the app itself to indicate
its pid as part of indicating its readiness.

Change-Id: I433450fd9164b731c1d719f6f225e02e6586e285
diff --git a/lib/exec/child.go b/lib/exec/child.go
index 80ebfd2..7158ef7 100644
--- a/lib/exec/child.go
+++ b/lib/exec/child.go
@@ -5,6 +5,7 @@
 	"errors"
 	"io"
 	"os"
+	"strconv"
 	"sync"
 )
 
@@ -62,7 +63,7 @@
 
 // SetReady writes a 'ready' status to its parent.
 func (c *ChildHandle) SetReady() error {
-	_, err := c.statusPipe.Write([]byte(readyStatus))
+	_, err := c.statusPipe.Write([]byte(readyStatus + strconv.Itoa(os.Getpid())))
 	c.statusPipe.Close()
 	return err
 }
diff --git a/lib/exec/parent.go b/lib/exec/parent.go
index 612020f..e4d40db 100644
--- a/lib/exec/parent.go
+++ b/lib/exec/parent.go
@@ -7,6 +7,7 @@
 	"io"
 	"os"
 	"os/exec"
+	"strconv"
 	"strings"
 	"sync"
 	"syscall"
@@ -34,6 +35,7 @@
 	waitDone    bool
 	waitErr     error
 	waitLock    sync.Mutex
+	callbackPid int
 }
 
 // ParentHandleOpt is an option for NewParentHandle.
@@ -187,7 +189,12 @@
 			// waitForStatus has closed the channel, but we may not
 			// have read the message from it yet.
 		case st := <-c:
-			if st == readyStatus {
+			if strings.HasPrefix(st, readyStatus) {
+				pid, err := strconv.Atoi(st[len(readyStatus):])
+				if err != nil {
+					return err
+				}
+				p.callbackPid = pid
 				return nil
 			}
 			if strings.HasPrefix(st, failedStatus) {
@@ -253,6 +260,12 @@
 	return 0
 }
 
+// ChildPid returns the pid of a child process as reported by its status
+// callback.
+func (p *ParentHandle) ChildPid() int {
+	return p.callbackPid
+}
+
 // Exists returns true if the child process exists and can be signal'ed
 func (p *ParentHandle) Exists() bool {
 	if p.c.Process != nil {
diff --git a/services/mgmt/device/impl/app_service.go b/services/mgmt/device/impl/app_service.go
index 2d40769..371b8d7 100644
--- a/services/mgmt/device/impl/app_service.go
+++ b/services/mgmt/device/impl/app_service.go
@@ -838,11 +838,16 @@
 		vlog.Errorf("WaitForReady(%v) failed: %v", childReadyTimeout, err)
 		return verror2.Make(ErrOperationFailed, nil)
 	}
+	pid := handle.ChildPid()
 	childName, err := listener.waitForValue(childReadyTimeout)
 	if err != nil {
 		return verror2.Make(ErrOperationFailed, nil)
 	}
-	info.AppCycleMgrName, info.Pid = childName, handle.Pid()
+
+	// Because suidhelper uses Go's in-built support for setuid forking,
+	// handle.Pid() is the pid of suidhelper, not the pid of the app
+	// so use the pid returned in the app's ready status.
+	info.AppCycleMgrName, info.Pid = childName, pid
 	if err := saveInstanceInfo(instanceDir, info); err != nil {
 		return err
 	}