Skip to content

Commit

Permalink
Provide more information when packet capture fails
Browse files Browse the repository at this point in the history
This change is motivated by user @deliciouslytyped and the comment left
here:
#119 (comment). Thanks!

If the user mistypes the interface name, for example, termshark's stderr
is misleading because it always refers the user to a link with
information on how to set up tshark for non-root capture.

To make this better, I now capture the stderr for the test process that
is launched to test packet capture when termshark starts. If the process
returns a non-zero code, I print that stderr on termshark's stderr. For
example:

$ termshark -i wlanfoo
(The termshark UI will start when packets are detected on wlanfoo...)
Cannot capture on device wlanfoo: exit status 1 (exit code 1)
Standard error stream from the capture process:

Starting termshark's custom live capture procedure.
First, trying dumpcap command /usr/bin/dumpcap -i wlanfoo -a duration:1
Capturing on 'wlanfoo'
dumpcap: The capture session could not be initiated on interface 'wlanfoo' (No such device exists).
Please check that you have the proper interface or pipe specified.
Retrying with capture command [/home/gcla/bin/tshark-slow -i wlanfoo -a duration:1]
Capturing on 'wlanfoo'
tshark: The capture session could not be initiated on interface 'wlanfoo' (No such device exists).
Please check that you have the proper interface or pipe specified.
0 packets captured

You might need: sudo setcap cap_net_raw,cap_net_admin eip dumpcap
Or try running with sudo or as root.
See https://termshark.io/no-root for more info.
  • Loading branch information
gcla committed Jul 4, 2022
1 parent a9843d9 commit 786e63f
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 10 deletions.
18 changes: 17 additions & 1 deletion cmd/termshark/termshark.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 5,7 @@
package main

import (
"bytes"
"fmt"
"os"
"os/exec"
Expand Down Expand Up @@ -711,6 712,7 @@ func cmain() int {
//======================================================================

ifaceExitCode := 0
stderr := &bytes.Buffer{}
var ifaceErr error

// This is deferred until after the app is Closed - otherwise messages written to stdout/stderr are
Expand All @@ -722,6 724,19 @@ func cmain() int {
fmt.Fprintf(os.Stderr, ": %v", ifaceErr)
}
fmt.Fprintf(os.Stderr, " (exit code %d)\n", ifaceExitCode)
if stderr.Len() != 0 {
// The default capture bin is termshark itself, with a special environment
// variable set that causes it to try dumpcap, then tshark, in that order (for
// efficiency of capture, but falling back to tshark for extcap interfaces).
// But telling the user the capture process is "termshark" is misleading.
cbin := termshark.CaptureBin()
if cbin == "termshark" {
cbin = "the capture process"
}

fmt.Fprintf(os.Stderr, "Standard error stream from %s:\n", cbin)
fmt.Fprintf(os.Stderr, "\n%s\n", stderr.String())
}
if runtime.GOOS == "linux" && os.Geteuid() != 0 {
fmt.Fprintf(os.Stderr, "You might need: sudo setcap cap_net_raw,cap_net_admin eip %s\n", termshark.PrivilegedBin())
fmt.Fprintf(os.Stderr, "Or try running with sudo or as root.\n")
Expand Down Expand Up @@ -922,10 937,11 @@ func cmain() int {
ifaceExitCode = 0
for _, psrc := range psrcs {
if psrc.IsInterface() {
if ifaceExitCode, ifaceErr = termshark.RunForExitCode(
if ifaceExitCode, ifaceErr = termshark.RunForStderr(
termshark.CaptureBin(),
[]string{"-i", psrc.Name(), "-a", "duration:1"},
append(os.Environ(), "TERMSHARK_CAPTURE_MODE=1"),
stderr,
); ifaceExitCode != 0 {
return 1
}
Expand Down
16 changes: 8 additions & 8 deletions system/dumpcapext.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 8,12 @@
package system

import (
"fmt"
"os"
"os/exec"
"regexp"
"strconv"
"syscall"

log "github.com/sirupsen/logrus"
)

//======================================================================
Expand Down Expand Up @@ -45,31 44,32 @@ func DumpcapExt(dumpcapBin string, tsharkBin string, args ...string) error {
if len(fdnum) == 2 {
fd, err := strconv.Atoi(fdnum[1])
if err != nil {
log.Warnf("Unexpected error parsing %s: %v", args[1], err)
fmt.Fprintf(os.Stderr, "Unexpected error parsing %s: %v\n", args[1], err)
} else {
err = Dup2(fd, 0)
if err != nil {
log.Warnf("Problem duplicating fd %d to 0: %v", fd, err)
log.Warnf("Will not try to replace argument %s to tshark", args[1])
fmt.Fprintf(os.Stderr, "Problem duplicating fd %d to 0: %v\n", fd, err)
fmt.Fprintf(os.Stderr, "Will not try to replace argument %s to tshark\n", args[1])
} else {
log.Infof("Replacing argument %s with - for tshark compatibility", args[1])
fmt.Fprintf(os.Stderr, "Replacing argument %s with - for tshark compatibility\n", args[1])
args[1] = "-"
}
}
}
}
}

fmt.Fprintf(os.Stderr, "Starting termshark's custom live capture procedure.\n")
dumpcapCmd := exec.Command(dumpcapBin, args...)
log.Infof("Starting dumpcap command %v", dumpcapCmd)
fmt.Fprintf(os.Stderr, "First, trying dumpcap command %v\n", dumpcapCmd)
dumpcapCmd.Stdin = os.Stdin
dumpcapCmd.Stdout = os.Stdout
dumpcapCmd.Stderr = os.Stderr
if dumpcapCmd.Run() != nil {
var tshark string
tshark, err = exec.LookPath(tsharkBin)
if err == nil {
log.Infof("Retrying with dumpcap command %v", append([]string{tshark}, args...))
fmt.Fprintf(os.Stderr, "Retrying with capture command %v\n", append([]string{tshark}, args...))
err = syscall.Exec(tshark, append([]string{tshark}, args...), os.Environ())
}
}
Expand Down
6 changes: 5 additions & 1 deletion utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,14 299,18 @@ func TSharkPath() (string, *gowid.KeyValueError) {
}

func RunForExitCode(prog string, args []string, env []string) (int, error) {
return RunForStderr(prog, args, env, ioutil.Discard)
}

func RunForStderr(prog string, args []string, env []string, stderr io.Writer) (int, error) {
var err error
exitCode := -1 // default bad
cmd := exec.Command(prog, args...)
if env != nil {
cmd.Env = env
}
cmd.Stdout = ioutil.Discard
cmd.Stderr = ioutil.Discard
cmd.Stderr = stderr
err = cmd.Run()
if err != nil {
if exerr, ok := err.(*exec.ExitError); ok {
Expand Down

0 comments on commit 786e63f

Please sign in to comment.