Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a040abe
shellspy progress
redscaresu Mar 21, 2022
7e0c26b
fix bug
redscaresu Apr 5, 2022
e81d183
comment out the tests for now
redscaresu Apr 5, 2022
a818f16
remove functional operators
redscaresu Apr 8, 2022
cd9c71d
im confused
redscaresu Apr 9, 2022
0cf16e5
fix the test for getting output from a command
redscaresu Apr 13, 2022
42d7422
remove unnecessary tests
redscaresu Apr 13, 2022
0dd5cfb
run remotely
redscaresu Apr 14, 2022
fb5f6ab
tweak
redscaresu Apr 19, 2022
b279224
fix the tests
redscaresu Apr 19, 2022
caedbde
working tests
redscaresu Apr 19, 2022
cc6521d
add sleep in
redscaresu Apr 20, 2022
aa8f791
fix broken testcase
redscaresu Apr 21, 2022
14b731e
fix broken test
redscaresu Apr 22, 2022
be3931d
remove debug print statements
redscaresu Apr 22, 2022
27a1c2b
create or append file
redscaresu Apr 22, 2022
b755eb5
update gitignore
redscaresu Apr 22, 2022
1216439
lol, no need to refactor when you can delete the whole function
redscaresu Apr 22, 2022
983b14b
code style
redscaresu Apr 22, 2022
64ccbb2
io.pipe looks interesting, time to delete the git hook. its annoying
redscaresu Apr 27, 2022
b837d7a
almost....
redscaresu May 3, 2022
3bd1cbc
write the input to the file
redscaresu May 10, 2022
5b60b19
remove unused struct value
redscaresu May 10, 2022
c33c0ad
show where the file is outputted
redscaresu May 12, 2022
b4683af
print out errors as well
redscaresu May 15, 2022
bc8b666
exit program if it detects exit string
redscaresu May 15, 2022
6eb0bd6
trying to send the terminate message to users
redscaresu May 20, 2022
cc73903
exit chan
redscaresu May 21, 2022
deb4346
its a start
redscaresu Jun 27, 2022
2b6a55c
commit test
redscaresu Jun 28, 2022
e1bf0ba
do something useful with the error
redscaresu Jun 28, 2022
f7816e6
incerase coverage
redscaresu Jun 28, 2022
f3edcb6
adding foo to things
redscaresu Jun 29, 2022
6c5c6e8
write the output to the file
redscaresu Jul 13, 2022
75dc508
send message to client that server is closed
redscaresu Jul 18, 2022
a531e9c
add command prompt
redscaresu Jul 19, 2022
442c90e
updates
redscaresu Jul 27, 2022
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
3 changes: 0 additions & 3 deletions .githooks/pre-commit

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.shellspy-*.txt
shellspy-*.txt
shellspy.txt
dist/
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ OR
```
> go run cmd/main.go --mode local
shellspy is running locally
```
```

TODO
- per session readmefile
- authentication
3 changes: 1 addition & 2 deletions cmd/shellspy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@ import (

func main() {

cliArgs := os.Args
shellspy.RunCLI(cliArgs, os.Stdout)
shellspy.RunCLI(os.Args[1:], os.Stdout)
}
250 changes: 84 additions & 166 deletions shellspy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package shellspy

import (
"bufio"
"bytes"
"flag"
"fmt"
"io"
Expand All @@ -11,225 +10,144 @@ import (
"os/exec"
"os/signal"
"strings"
"time"
"syscall"
)

type session struct {
Input io.Reader
Output io.Writer
TranscriptOutput io.Writer
File *os.File
Port string
type Session struct {
Input io.Reader
Output io.Writer
Terminal io.Writer
Transcript io.Writer
}

type Option func(*session)

func WithOutput(output io.Writer) Option {
return func(s *session) {
s.Output = output
}
type Server struct {
Port int
C net.Conn
}

func WithTranscriptOutput(TranscriptOutput io.Writer) Option {
return func(s *session) {
s.TranscriptOutput = TranscriptOutput
func RunCLI(cliArgs []string, output io.Writer) {

if len(cliArgs) == 0 {
RunLocally(output)
}
}

func NewSession(opts ...Option) (*session, error) {
if len(cliArgs) == 2 {
fs := flag.NewFlagSet("cmd", flag.ExitOnError)
portFlag := fs.Int("port", 2000, "-port 3000")

session := &session{}
fs.Parse(cliArgs)

for _, o := range opts {
o(session)
}

file, err := CreateTranscriptFile()
if err != nil {
return session, err
if portFlag != nil {
RunRemotely(*portFlag)
}
}
session.File = file

return session, nil
}

func RunCLI(cliArgs []string, w io.Writer) {
func NewSession(output io.Writer) (*Session, error) {

s, err := NewSession(
WithOutput(w),
)
s := &Session{}

file, err := CreateTranscriptFile()
if err != nil {
fmt.Printf("%v", err)
os.Exit(1)
}

fmt.Println(len(cliArgs))
fmt.Println(cliArgs)
if len(cliArgs) == 1 {
RunLocally(s, w)
}

fs := flag.NewFlagSet("cmd", flag.ContinueOnError)
fs.Parse(os.Args[1:])

switch os.Args[1] {
case "port":
args := fs.Args()
s.Port = args[1]
RunRemotely(s, w)
return nil, err
}

s.Transcript = file
s.Input = os.Stdin
s.Terminal = output
s.Output = io.MultiWriter(s.Terminal, s.Transcript)
return s, nil
}

func RunLocally(s *session, w io.Writer) {
func CreateTranscriptFile() (*os.File, error) {

buf := &bytes.Buffer{}
buf.WriteString("shellspy is running locally\n")
fmt.Fprint(w, buf)
s.Output = w
input := io.Reader(os.Stdin)
Input(input, s)
file, err := os.OpenFile("shellspy.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return nil, err
}
return file, nil
}

func RunRemotely(s *session, w io.Writer) error {
func RunRemotely(port int) error {

port := s.Port
buf := &bytes.Buffer{}
buf.WriteString("shellspy is running remotely " + port + "\n")
fmt.Fprint(w, buf)
s.TranscriptOutput = buf

address := "localhost:" + port
fmt.Printf("shellspy is running remotely on port %v and the output file is shellspy.txt\n", port)
address := fmt.Sprintf("localhost:%d", port)

listener, err := net.Listen("tcp", address)
if err != nil {
return err
}
killSignal := make(chan os.Signal, 1)
signal.Notify(killSignal, os.Interrupt)

for {

killSignal := make(chan os.Signal, 1)
signal.Notify(killSignal, syscall.SIGINT, syscall.SIGTERM)

go func() {
<-killSignal
os.Exit(0)
}()

conn, err := listener.Accept()
if err != nil {
return err
}
go handleConn(conn, s)

<-killSignal
fmt.Println("\nconnection terminated by server!")
listener.Close()
go handleConn(conn)

}
}

func handleConn(c net.Conn, s *session) {

fmt.Fprintf(c, "hello, welcome to shellspy"+"\n")
input := io.Reader(c)
exitStatus := Input(input, s)
if exitStatus == "0" {
func handleConn(c net.Conn) {
s, err := NewSession(c)
if err != nil {
fmt.Fprint(c, err)
c.Close()
fmt.Printf("connection is closed due to: %v", err)
}
c.Close()
}

func Input(input io.Reader, s *session) string {

scanner := bufio.NewScanner(input)
for scanner.Scan() {
s.Input = strings.NewReader(scanner.Text())
exitStatus := s.Run()
if exitStatus == "0" {
return "0"
}
}
return ""
fmt.Printf("new connection established %s and the file is shellspy.txt ", c.RemoteAddr())
s.Input = c
s.Start()
}

func (s *session) Run() string {

writer := &bytes.Buffer{}
twriter := &bytes.Buffer{}
iReader := &bytes.Buffer{}
fmt.Fprint(iReader, s.Input)
file := s.File
input := iReader.String()
input = strings.TrimPrefix(input, "&{")
input = strings.TrimSuffix(input, " 0 -1}")
stdOut, exitStatus := RunServer(input, file)
if exitStatus == "0" {
return "0"
func RunLocally(output io.Writer) {
s, err := NewSession(output)
if err != nil {
fmt.Println("cannot create transcript file")
os.Exit(1)
}
s.Output = writer
s.TranscriptOutput = twriter
fmt.Fprint(writer, stdOut)
fmt.Fprint(twriter, stdOut)
return ""
s.Start()
}

func RunServer(line string, file *os.File) (string, string) {
func (s *Session) Start() {

cmd := CommandFromString(line)
fmt.Fprintln(s.Output, "welcome to shellspy")
fmt.Fprintf(s.Output, "$ ")
scanner := bufio.NewScanner(s.Input)

if strings.HasPrefix(line, "exit") {
return "", "0"
for scanner.Scan() {
cmd := CommandFromString(scanner.Text())
input := scanner.Text() + "\n"
cmd.Stdout = s.Output
cmd.Stderr = s.Output
fmt.Fprint(s.Transcript, input)
if scanner.Text() == "exit" {
os.Exit(0)
}
err := cmd.Run()
if err != nil {
fmt.Fprint(s.Output, err)
}
fmt.Fprintf(s.Output, "$ ")
}

stdOut, stdErr := RunFromCmd(cmd)
WriteTranscript(stdOut, stdErr, cmd, file)
return stdOut, ""
}

func CommandFromString(line string) *exec.Cmd {

trim := strings.TrimSuffix(line, "\n")
name := strings.Fields(trim)
args := name[1:]
join := strings.Join(args, " ")
cmd := exec.Command(name[0], join)
cmd := exec.Command(name[0], args...)
return cmd
}

func RunFromCmd(cmd *exec.Cmd) (string, string) {
var outb bytes.Buffer
var errb bytes.Buffer
cmd.Stdout = &outb
cmd.Stderr = &errb

cmd.Run()

stdOut := outb.String()
stdErr := errb.String()

return stdOut, stdErr
}

func CreateTranscriptFile() (*os.File, error) {
now := time.Now()
filename := ".shellspy-" + now.Format("2006-01-02-15:04:05") + ".txt"
file, err := os.OpenFile(filename,
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return nil, err
}
return file, nil
}

func WriteTranscript(stdOut, stdErr string, cmd *exec.Cmd, file *os.File) os.File {

if _, err := file.WriteString(cmd.String()); err != nil {
err = fmt.Errorf("unable to write cmd to disk due to error; %w", err)
file.WriteString(err.Error())
}

file.WriteString("\n")

if stdErr != "" {
file.WriteString(stdErr)
}

if _, err := file.WriteString(stdOut); err != nil {
err = fmt.Errorf("unable to write stdOut to disk due to error; %w", err)
file.WriteString(err.Error())
}

return *file
}
Loading