-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpi3g-usbpatcher.go
141 lines (121 loc) · 2.95 KB
/
pi3g-usbpatcher.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
130
131
132
133
134
135
136
137
138
139
140
141
package main
import (
"io/ioutil"
"os"
"os/exec"
"regexp"
"strings"
)
var version = "1.0.2"
const mountPath = "/mnt"
const updaterPath = "/usr/bin/pi3g-usbpatcher"
var filenameRegexp = regexp.MustCompile(`^pi3g-patch-.+?.(tgz|tar.gz)$`)
const mountBin = "/bin/mount"
const umountBin = "/bin/umount"
const tarBin = "/bin/tar"
const haltBin = "/sbin/halt"
// mount mounts the device at devname to mountPath
func mount(devname string) error {
return exec.Command(mountBin, "-r", devname, mountPath).Run()
}
// umount unmounts the device at devname
func umount(devname string) error {
return exec.Command(umountBin, devname).Run()
}
// archiveList lists contents of archive
func archiveList(archive string) (string, error) {
out, err := exec.Command(tarBin, "tf", archive, "--strip-components=1").Output()
return string(out), err
}
// archiveExtract extracts archive to "/" overwriting if necessary
func archiveExtract(archive string) error {
return exec.Command(tarBin, "xzf", archive, "-C", "/",
"--strip-components=1", "--overwrite").Run()
}
// findPatchFile searches mountPath for a patchFile candidate
func findPatchFile() string {
files, _ := ioutil.ReadDir(mountPath)
for _, f := range files {
if filenameRegexp.MatchString(f.Name()) {
return f.Name()
}
}
return ""
}
// halt shuts down the raspi
func halt() error {
return exec.Command(haltBin).Run()
}
func main() {
debug("Device plugged in, running updater version ", version)
devname := os.Getenv("DEVNAME")
if devname == "" {
debug("DEVNAME not set")
os.Exit(1)
}
debug("Device found: ", devname)
err := mount(devname)
if err != nil {
debug("mount: ", err)
os.Exit(1)
}
patchFile := findPatchFile()
if patchFile == "" {
err = umount(devname)
if err != nil {
debug("umount: ", err)
}
debug("No patch on drive")
os.Exit(1)
}
patchFile = mountPath + "/" + patchFile
debug("Patch file found: ", patchFile)
out, err := archiveList(patchFile)
if err != nil {
err = umount(devname)
if err != nil {
debug("umount: ", err)
}
debug("tar list: ", err)
os.Exit(1)
}
debugf("The following files will be updated:\n%s", out)
// unlink this binary if it is going to be replaced
re := regexp.MustCompile(`^[^/]*` + updaterPath + `$`)
for _, line := range strings.Fields(out) {
if re.MatchString(line) {
debug("Warning: Contains update for updater!")
err := os.Remove(updaterPath)
if err != nil {
err = umount(devname)
if err != nil {
debug("umount: ", err)
}
debug("This is really bad!")
debug("remove: ", err)
os.Exit(1)
}
break
}
}
err = archiveExtract(patchFile)
if err != nil {
err = umount(devname)
if err != nil {
debug("umount: ", err)
}
debug("tar extract: ", err)
os.Exit(1)
}
// we couldn't defer this because main usually doesn't return
err = umount(devname)
if err != nil {
debug("umount: ", err)
}
debug("Shutting down")
err = halt()
if err != nil {
debug("halt: ", err)
os.Exit(1)
}
}