Skip to content

Release 2.1.2 #143

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions commands/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package commands
import (
"fmt"

"github.com/fatih/color"
"github.com/phase2/rig/util"
"github.com/urfave/cli"
)
Expand Down Expand Up @@ -58,15 +57,15 @@ func (cmd *Stop) StopOutrigger() error {
}
cmd.out.Info("Stopped machine '%s'", cmd.machine.Name)

cmd.out.Spin("Cleaning up local networking (may require your admin password)")
cmd.out.Spin("Cleaning up local networking...")
if util.IsWindows() {
util.Command("runas", "/noprofile", "/user:Administrator", "route", "DELETE", "172.17.0.0").Run()
util.Command("runas", "/noprofile", "/user:Administrator", "route", "DELETE", "172.17.42.1").Run()
} else {
util.EscalatePrivilege()
util.Command("sudo", "route", "-n", "delete", "-net", "172.17.0.0").Run()
util.Command("sudo", "route", "-n", "delete", "-net", "172.17.42.1").Run()
}
color.Unset()
cmd.out.Info("Networking cleanup completed")

return cmd.Success(fmt.Sprintf("Machine '%s' stopped", cmd.machine.Name))
Expand Down
39 changes: 31 additions & 8 deletions util/logger.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package util

import (
"fmt"
"io/ioutil"
"log"
"os"

"fmt"
"github.com/fatih/color"
spun "github.com/slok/gospinner"
)
Expand All @@ -24,10 +24,11 @@ type logChannels struct {

// RigLogger is the global logger object
type RigLogger struct {
Channel logChannels
Progress *RigSpinner
IsVerbose bool
Spinning bool
Channel logChannels
Progress *RigSpinner
IsVerbose bool
Spinning bool
Privileged bool
}

// RigSpinner object wrapper to facilitate our spinner service
Expand All @@ -51,9 +52,10 @@ func LoggerInit(verbose bool) {
Error: log.New(os.Stderr, color.RedString("[ERROR] "), 0),
Verbose: log.New(verboseWriter, "[VERBOSE] ", 0),
},
IsVerbose: verbose,
Progress: &RigSpinner{s},
Spinning: false,
IsVerbose: verbose,
Progress: &RigSpinner{s},
Spinning: false,
Privileged: false,
}
}

Expand Down Expand Up @@ -125,3 +127,24 @@ func (log *RigLogger) Verbose(format string, a ...interface{}) {
func (log *RigLogger) Note(format string, a ...interface{}) {
log.Channel.Info.Println(fmt.Sprintf(format, a...))
}

// PrivilegeEscallationPrompt interrupts a running spinner to ensure clear
// prompting to the user for sudo password entry. It is up to the caller to know
// that privilege is needed. This prompt is only displayed on the first privilege
// escallation of a given rig process.
func (log *RigLogger) PrivilegeEscallationPrompt() {
defer func() { log.Privileged = true }()

if log.Privileged {
return
}

// This newline ensures the last status before escallation is preserved
// on-screen. It creates extraneous space in verbose mode.
if !log.IsVerbose {
fmt.Println()
}
message := "Administrative privileges needed..."
log.Spin(message)
log.Warning(message)
}
39 changes: 36 additions & 3 deletions util/shell_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ func Convert(cmd *exec.Cmd) Executor {
return Executor{cmd}
}

// EscalatePrivilege attempts to gain administrative privilege
// @todo identify administrative escallation on Windows.
// E.g., "runas", "/noprofile", "/user:Administrator
func EscalatePrivilege() error {
return Command("sudo", "-v").Run()
}

// PassthruCommand is similar to ForceStreamCommand in that it will issue all output
// regardless of verbose mode. Further, this version of the command captures the
// exit status of any executed command. This function is intended to simulate
Expand Down Expand Up @@ -92,36 +99,53 @@ func (x Executor) Execute(forceOutput bool) error {
// CombinedOutput runs a command via exec.CombinedOutput() without modification or output of the underlying command.
func (x Executor) CombinedOutput() ([]byte, error) {
x.Log("Executing")
if out := Logger(); out != nil && x.IsPrivileged() {
out.PrivilegeEscallationPrompt()
defer out.Spin("Resuming operation...")
}
return x.cmd.CombinedOutput()
}

// Run runs a command via exec.Run() without modification or output of the underlying command.
func (x Executor) Run() error {
x.Log("Executing")
if out := Logger(); out != nil && x.IsPrivileged() {
out.PrivilegeEscallationPrompt()
defer out.Spin("Resuming operation...")
}
return x.cmd.Run()
}

// Output runs a command via exec.Output() without modification or output of the underlying command.
func (x Executor) Output() ([]byte, error) {
x.Log("Executing")
if out := Logger(); out != nil && x.IsPrivileged() {
out.PrivilegeEscallationPrompt()
defer out.Spin("Resuming operation...")
}
return x.cmd.Output()
}

// Start runs a command via exec.Start() without modification or output of the underlying command.
func (x Executor) Start() error {
x.Log("Executing")
if out := Logger(); out != nil && x.IsPrivileged() {
out.PrivilegeEscallationPrompt()
defer out.Spin("Resuming operation...")
}
return x.cmd.Start()
}

// Log verbosely logs the command.
func (x Executor) Log(tag string) {
color.Set(color.FgMagenta)
Logger().Verbose("%s: %s", tag, x.ToString())
Logger().Verbose("%s: %s", tag, x)
color.Unset()
}

// ToString converts a Command to a human-readable string with key context details.
func (x Executor) ToString() string {
// String converts a Command to a human-readable string with key context details.
// It is automatically applied in contexts such as fmt functions.
func (x Executor) String() string {
context := ""
if x.cmd.Dir != "" {
context = fmt.Sprintf("(WD: %s", x.cmd.Dir)
Expand All @@ -137,3 +161,12 @@ func (x Executor) ToString() string {

return fmt.Sprintf("%s %s %s", x.cmd.Path, strings.Join(x.cmd.Args[1:], " "), context)
}

// IsPrivileged evaluates the command to determine if administrative privilege
// is required.
// @todo identify administrative escallation on Windows.
// E.g., "runas", "/noprofile", "/user:Administrator
func (x Executor) IsPrivileged() bool {
_, privileged := IndexOfSubstring(x.cmd.Args, "sudo")
return privileged
}
30 changes: 30 additions & 0 deletions util/slices.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package util

import (
"strings"
)

// IndexOfString is a general utility function that can find the index of a value
// present in a string slice. The second value is true if the item is found.
func IndexOfString(slice []string, search string) (int, bool) {
for index, elem := range slice {
if elem == search {
return index, true
}
}

return 0, false
}

// IndexOfSubstring is a variation on IndexOfString which checks to see if a
// given slice value matches our search string, or if that search string is
// a substring of the element. The second value is true if the item is found.
func IndexOfSubstring(slice []string, search string) (int, bool) {
for index, elem := range slice {
if strings.Contains(elem, search) {
return index, true
}
}

return 0, false
}