about summary refs log tree commit diff
path: root/vendor/github.com/radareorg/r2pipe-go/r2pipe.go
diff options
context:
space:
mode:
authorEmile <git@emile.space>2024-08-16 19:50:26 +0200
committerEmile <git@emile.space>2024-08-16 19:50:26 +0200
commit1a57267a17c2fc17fb6e104846fabc3e363c326c (patch)
tree1e574e3a80622086dc3c81ff9cba65ef7049b1a9 /vendor/github.com/radareorg/r2pipe-go/r2pipe.go
initial commit
Diffstat (limited to 'vendor/github.com/radareorg/r2pipe-go/r2pipe.go')
-rw-r--r--vendor/github.com/radareorg/r2pipe-go/r2pipe.go243
1 files changed, 243 insertions, 0 deletions
diff --git a/vendor/github.com/radareorg/r2pipe-go/r2pipe.go b/vendor/github.com/radareorg/r2pipe-go/r2pipe.go
new file mode 100644
index 0000000..d87e2fe
--- /dev/null
+++ b/vendor/github.com/radareorg/r2pipe-go/r2pipe.go
@@ -0,0 +1,243 @@
+// radare - LGPL - Copyright 2015 - nibble
+
+/*
+Package r2pipe allows to call r2 commands from Go. A simple hello world would
+look like the following snippet:
+
+	package main
+
+	import (
+		"fmt"
+
+		"github.com/radare/r2pipe-go"
+	)
+
+	func main() {
+		r2p, err := r2pipe.NewPipe("malloc://256")
+		if err != nil {
+			panic(err)
+		}
+		defer r2p.Close()
+
+		_, err = r2p.Cmd("w Hello World")
+		if err != nil {
+			panic(err)
+		}
+		buf, err := r2p.Cmd("ps")
+		if err != nil {
+			panic(err)
+		}
+		fmt.Println(buf)
+	}
+*/
+package r2pipe
+
+import (
+	"bufio"
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"os"
+	"os/exec"
+	"strconv"
+	"strings"
+	"unsafe"
+)
+
+// A Pipe represents a communication interface with r2 that will be used to
+// execute commands and obtain their results.
+type Pipe struct {
+	File   string
+	r2cmd  *exec.Cmd
+	stdin  io.WriteCloser
+	stdout io.ReadCloser
+	stderr io.ReadCloser
+	Core   unsafe.Pointer
+	cmd    CmdDelegate
+	close  CloseDelegate
+}
+
+type (
+	CmdDelegate   func(*Pipe, string) (string, error)
+	CloseDelegate func(*Pipe) error
+	EventDelegate func(*Pipe, string, interface{}, string) bool
+)
+
+// NewPipe returns a new r2 pipe and initializes an r2 core that will try to
+// load the provided file or URI. If file is an empty string, the env vars
+// R2PIPE_{IN,OUT} will be used as file descriptors for input and output, this
+// is the case when r2pipe is called within r2.
+func NewPipe(file string) (*Pipe, error) {
+	if file == "" {
+		return newPipeFd()
+	}
+
+	return newPipeCmd(file)
+}
+
+func newPipeFd() (*Pipe, error) {
+	r2pipeIn := os.Getenv("R2PIPE_IN")
+	r2pipeOut := os.Getenv("R2PIPE_OUT")
+
+	if r2pipeIn == "" || r2pipeOut == "" {
+		return nil, fmt.Errorf("missing R2PIPE_{IN,OUT} vars")
+	}
+
+	r2pipeInFd, err := strconv.Atoi(r2pipeIn)
+	if err != nil {
+		return nil, fmt.Errorf("failed to convert IN into file descriptor")
+	}
+
+	r2pipeOutFd, err := strconv.Atoi(r2pipeOut)
+	if err != nil {
+		return nil, fmt.Errorf("failed to convert OUT into file descriptor")
+	}
+
+	stdout := os.NewFile(uintptr(r2pipeInFd), "R2PIPE_IN")
+	stdin := os.NewFile(uintptr(r2pipeOutFd), "R2PIPE_OUT")
+
+	r2p := &Pipe{
+		File:   "",
+		r2cmd:  nil,
+		stdin:  stdin,
+		stdout: stdout,
+		Core:   nil,
+	}
+
+	return r2p, nil
+}
+
+func newPipeCmd(file string) (*Pipe, error) {
+
+	r2p := &Pipe{File: file, r2cmd: exec.Command("radare2", "-q0", file)}
+	var err error
+	r2p.stdin, err = r2p.r2cmd.StdinPipe()
+	if err == nil {
+		r2p.stdout, err = r2p.r2cmd.StdoutPipe()
+		if err == nil {
+			r2p.stderr, err = r2p.r2cmd.StdoutPipe()
+		}
+		if err = r2p.r2cmd.Start(); err == nil {
+			//Read the initial data
+			_, err = bufio.NewReader(r2p.stdout).ReadString('\x00')
+		}
+	}
+	return r2p, err
+}
+
+// Write implements the standard Write interface: it writes data to the r2
+// pipe, blocking until r2 have consumed all the data.
+func (r2p *Pipe) Write(p []byte) (n int, err error) {
+	return r2p.stdin.Write(p)
+}
+
+// Read implements the standard Read interface: it reads data from the r2
+// pipe's stdin, blocking until the previously issued commands have finished.
+func (r2p *Pipe) Read(p []byte) (n int, err error) {
+	return r2p.stdout.Read(p)
+}
+
+func (r2p *Pipe) ReadErr(p []byte) (n int, err error) {
+	return r2p.stderr.Read(p)
+}
+
+func (r2p *Pipe) On(evname string, p interface{}, cb EventDelegate) error {
+	path, err := r2p.Cmd("===stderr")
+	if err != nil {
+		return err
+	}
+	f, err := os.OpenFile(path, os.O_RDONLY, 0600)
+
+	if err != nil {
+		return err
+	}
+	go func() {
+		var buf bytes.Buffer
+		for {
+			io.Copy(&buf, f)
+			if buf.Len() > 0 {
+				if !cb(r2p, evname, p, buf.String()) {
+					break
+				}
+			}
+		}
+		f.Close()
+	}()
+	return nil
+}
+
+// Cmd is a helper that allows to run r2 commands and receive their output.
+func (r2p *Pipe) Cmd(cmd string) (string, error) {
+	if r2p.Core != nil {
+		if r2p.cmd != nil {
+			return r2p.cmd(r2p, cmd)
+		}
+
+		return "", nil
+	}
+
+	if _, err := fmt.Fprintln(r2p, cmd); err != nil {
+		return "", err
+	}
+
+	buf, err := bufio.NewReader(r2p).ReadString('\x00')
+	if err != nil {
+		return "", err
+	}
+	return strings.TrimRight(buf, "\n\x00"), err
+}
+
+//like cmd but formats the command
+func (r2p *Pipe) Cmdf(f string, args ...interface{}) (string, error) {
+	return r2p.Cmd(fmt.Sprintf(f, args...))
+}
+
+// Cmdj acts like Cmd but interprets the output of the command as json. It
+// returns the parsed json keys and values.
+func (r2p *Pipe) Cmdj(cmd string) (out interface{}, err error) {
+	rstr, err := r2p.Cmd(cmd)
+	if err == nil {
+		err = json.Unmarshal([]byte(rstr), out)
+	}
+	return out, err
+}
+
+//like cmdj but formats the command
+func (r2p *Pipe) Cmdjf(f string, args ...interface{}) (interface{}, error) {
+	return r2p.Cmdj(fmt.Sprintf(f, args...))
+}
+
+// Close shuts down r2, closing the created pipe.
+func (r2p *Pipe) Close() error {
+	if r2p.close != nil {
+		return r2p.close(r2p)
+	}
+
+	if r2p.File == "" {
+		return nil
+	}
+
+	if _, err := r2p.Cmd("q"); err != nil {
+		return err
+	}
+
+	return r2p.r2cmd.Wait()
+}
+
+// Forcing shutdown of r2, closing the created pipe.
+func (r2p *Pipe) ForceClose() error {
+	if r2p.close != nil {
+		return r2p.close(r2p)
+	}
+
+	if r2p.File == "" {
+		return nil
+	}
+
+	if _, err := r2p.Cmd("q!"); err != nil {
+		return err
+	}
+
+	return r2p.r2cmd.Wait()
+}