veyron/services/mgmt/suidhelper: added remove functionality

When actually operating with chown, application workspaces and logs will
not be owned by the application's associated system name. Consequently,
the node manager will not be able to delete application instances. Extend
the helper with remove functionality.

Change-Id: Iab7094d5ed947909bc11900d545d906e4098efc4
diff --git a/services/mgmt/suidhelper/impl/args.go b/services/mgmt/suidhelper/impl/args.go
index 2c9da35..2cc26ae 100644
--- a/services/mgmt/suidhelper/impl/args.go
+++ b/services/mgmt/suidhelper/impl/args.go
@@ -22,6 +22,7 @@
 	argv      []string
 	envv      []string
 	dryrun    bool
+	remove    bool
 }
 
 type ArgsSavedForTest struct {
@@ -37,6 +38,7 @@
 var (
 	flagUsername, flagWorkspace, flagStdoutLog, flagStderrLog, flagRun *string
 	flagMinimumUid                                                     *int64
+	flagRemove                                                         *bool
 )
 
 func init() {
@@ -53,11 +55,18 @@
 	flagStderrLog = sflag.StderrLog
 	flagRun = sflag.Run
 	flagMinimumUid = sflag.MinimumUid
+	flagRemove = sflag.Remove
 }
 
 // ParseArguments populates the WorkParameter object from the provided args
 // and env strings.
 func (wp *WorkParameters) ProcessArguments(fs *flag.FlagSet, env []string) error {
+	if *flagRemove {
+		wp.remove = true
+		wp.argv = fs.Args()
+		return nil
+	}
+
 	username := *flagUsername
 	if username == "" {
 		return fmt.Errorf("--username missing")
diff --git a/services/mgmt/suidhelper/impl/args_test.go b/services/mgmt/suidhelper/impl/args_test.go
index b3a86b1..a9f0cde 100644
--- a/services/mgmt/suidhelper/impl/args_test.go
+++ b/services/mgmt/suidhelper/impl/args_test.go
@@ -35,6 +35,8 @@
 				argv0:     "",
 				argv:      []string{""},
 				envv:      []string{"A=B"},
+				dryrun:    false,
+				remove:    false,
 			},
 		},
 
@@ -52,6 +54,32 @@
 				argv0:     "/bin/veyron",
 				argv:      []string{"/bin/veyron", "one", "two"},
 				envv:      []string{"A=B"},
+				dryrun:    false,
+				remove:    false,
+			},
+		},
+		{
+			[]string{"setuidhelper", "--username", testUserName},
+			[]string{"A=B"},
+			fmt.Errorf("suidhelper does not permit uids less than 501"),
+			WorkParameters{},
+		},
+
+		{
+			[]string{"setuidhelper", "--rm", "hello", "vanadium"},
+			[]string{"A=B"},
+			nil,
+			WorkParameters{
+				uid:       0,
+				gid:       0,
+				workspace: "",
+				stderrLog: "",
+				stdoutLog: "",
+				argv0:     "",
+				argv:      []string{"hello", "vanadium"},
+				envv:      nil,
+				dryrun:    false,
+				remove:    true,
 			},
 		},
 		{
diff --git a/services/mgmt/suidhelper/impl/flag/flag.go b/services/mgmt/suidhelper/impl/flag/flag.go
index c21e1ca..674227e 100644
--- a/services/mgmt/suidhelper/impl/flag/flag.go
+++ b/services/mgmt/suidhelper/impl/flag/flag.go
@@ -15,6 +15,7 @@
 var (
 	Username, Workspace, StdoutLog, StderrLog, Run *string
 	MinimumUid                                     *int64
+	Remove                                         *bool
 )
 
 func init() {
@@ -28,6 +29,7 @@
 	StderrLog = fs.String("stderrlog", "", "Path to the stdin log file.")
 	Run = fs.String("run", "", "Path to the application to exec.")
 	MinimumUid = fs.Int64("minuid", uidThreshold, "UIDs cannot be less than this number.")
+	Remove = fs.Bool("rm", false, "Remove the file trees given as command-line arguments.")
 }
 
 const uidThreshold = 501
diff --git a/services/mgmt/suidhelper/impl/run.go b/services/mgmt/suidhelper/impl/run.go
index bc7b6dd..99876ed 100644
--- a/services/mgmt/suidhelper/impl/run.go
+++ b/services/mgmt/suidhelper/impl/run.go
@@ -10,11 +10,13 @@
 		return err
 	}
 
-	// 1. For each chown directory, chown.
+	if work.remove {
+		return work.Remove()
+	}
+
 	if err := work.Chown(); err != nil {
 		return err
 	}
 
-	// 2. Run the command if it exists.
 	return work.Exec()
 }
diff --git a/services/mgmt/suidhelper/impl/system.go b/services/mgmt/suidhelper/impl/system.go
index 3059e30..9df0f9b 100644
--- a/services/mgmt/suidhelper/impl/system.go
+++ b/services/mgmt/suidhelper/impl/system.go
@@ -43,3 +43,12 @@
 	}
 	return syscall.Exec(hw.argv0, hw.argv, hw.envv)
 }
+
+func (hw *WorkParameters) Remove() error {
+	for _, p := range hw.argv {
+		if err := os.RemoveAll(p); err != nil {
+			return fmt.Errorf("os.RemoveAll(%s) failed: %v", p, err)
+		}
+	}
+	return nil
+}