forked from hackergrrl/fallback-ipfs-shell
/
getshell.go
129 lines (107 loc) · 2.63 KB
/
getshell.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package shell
import (
"context"
"fmt"
"io"
"os"
"os/signal"
"path/filepath"
"strings"
api "github.com/ipfs/go-ipfs-api"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
embedded "github.com/whyrusleeping/ipfs-embedded-shell"
)
func NewShell() (Shell, error) {
myShell, err := NewApiShell()
if err == nil {
// fmt.Println("got an api shell!")
return myShell, nil
}
myShell, err = NewEmbeddedShell()
if err == nil {
// fmt.Println("got an embedded shell!")
return myShell, nil
}
return nil, err
}
func NewApiShell() (Shell, error) {
addr, err := apiAddr()
if err != nil {
return nil, err
}
apiShell := api.NewShell(addr)
_, _, err = apiShell.Version()
if err != nil {
return nil, err
}
return apiShell, nil
}
func NewEmbeddedShell() (Shell, error) {
ctx, cancel := context.WithCancel(context.Background())
// Cancel the ipfs node context if the process gets interrupted or killed.
// TODO(noffle): is this needed?
go func() {
interrupts := make(chan os.Signal, 1)
signal.Notify(interrupts, os.Interrupt, os.Kill)
<-interrupts
cancel()
}()
shell, err := tryLocal(ctx)
if err == nil {
return shell, nil
}
node, err := embedded.NewTmpDirNode(ctx)
if err != nil {
return nil, err
}
return embedded.NewShell(node), nil
}
func tryLocal(ctx context.Context) (Shell, error) {
repoPath, err := getRepoPath()
if err != nil {
return nil, err
}
node, err := embedded.NewDefaultNodeWithFSRepo(ctx, repoPath)
if err != nil {
return nil, fmt.Errorf("couldn't get embedded shell: %s", err)
}
return embedded.NewShell(node), nil
}
func getRepoPath() (string, error) {
repoPath, err := fsrepo.BestKnownPath()
if err != nil {
return "", err
}
return repoPath, nil
}
// APIAddr returns the registered API addr, according to the api file
// in the fsrepo. This is a concurrent operation, meaning that any
// process may read this file. modifying this file, therefore, should
// use "mv" to replace the whole file and avoid interleaved read/writes.
func apiAddr() (string, error) {
repoPath, err := getRepoPath()
if err != nil {
return "", err
}
repoPath = filepath.Clean(repoPath)
apiFilePath := filepath.Join(repoPath, "api")
// if there is no file, assume there is no api addr.
f, err := os.Open(apiFilePath)
if err != nil {
if os.IsNotExist(err) {
return "", err
}
return "", err
}
defer f.Close()
// read up to 2048 bytes. io.ReadAll is a vulnerability, as
// someone could hose the process by putting a massive file there.
buf := make([]byte, 2048)
n, err := f.Read(buf)
if err != nil && err != io.EOF {
return "", err
}
s := string(buf[:n])
s = strings.TrimSpace(s)
return s, nil
}