sneakernet/main.go

402 lines
9.2 KiB
Go

package main
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"sort"
"strings"
"syscall"
"time"
evdev "github.com/gvalkov/golang-evdev"
"github.com/marcellmars/lsblk"
)
var (
copyStatus = false
cancelCopy = false
)
func check(e error) {
if e != nil {
fmt.Println(fmt.Errorf("FROM CHECK: %w", e))
panic(e)
}
return
}
func recoverFromError() {
if r := recover(); r != nil {
fmt.Println("MYERROR:", r)
panic(r)
}
}
//_START: CopyToDisk
type Files struct {
Files []PathTotalSize
}
type PathTotalSize struct {
IsDir bool
Path string
FileSize int64
FileSizeHumanReadable string
TotalSize int64
TotalSizeHumanReadable string
}
func byteCountSI(b int64) string {
const unit = 1000
if b < unit {
return fmt.Sprintf("%dB", b)
}
div, exp := int64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.3f%cB",
float64(b)/float64(div), "kMGTPE"[exp])
}
func copyToDisk(p string, waitingLoop chan string, cancelCopy chan bool) {
var totalSize int64
var ps Files
var bufSize int64
fmt.Println("COPY STARTED...")
s := time.Now()
// srcBaseDir := "/dev/shm/src/"
// srcBaseDir := "/home/m/CalibreLibraries/"
srcBaseDir := "/home/pi/"
srcDir := "MemoryOfTheWorld4Orsimanirana/"
// srcDir := "PirateCareCollection/"
destBaseDir := "/mnt/MOTW0/"
// destBaseDir := "/dev/shm/dest/"
//
ps.Files = append(
ps.Files,
PathTotalSize{
IsDir: true,
Path: srcBaseDir + srcDir,
FileSize: 0,
FileSizeHumanReadable: "0Kb",
TotalSize: 0,
TotalSizeHumanReadable: "0Kb",
})
ps.Files = append(
ps.Files,
PathTotalSize{
IsDir: false,
Path: srcBaseDir + srcDir + "_README_.txt",
FileSize: 4,
FileSizeHumanReadable: "4Kb",
TotalSize: 4,
TotalSizeHumanReadable: "4Kb",
})
ps.Files = append(
ps.Files,
PathTotalSize{
IsDir: false,
Path: srcBaseDir + srcDir + "BROWSE_LIBRARY.html",
FileSize: 4,
FileSizeHumanReadable: "4Kb",
TotalSize: 4,
TotalSizeHumanReadable: "4Kb",
})
err := filepath.Walk(filepath.Join(srcBaseDir, srcDir+"static"), func(path string, info os.FileInfo, err error) error {
check(err)
totalSize = totalSize + info.Size()
ps.Files = append(
ps.Files,
PathTotalSize{
IsDir: info.IsDir(),
Path: path,
FileSize: info.Size(),
FileSizeHumanReadable: byteCountSI(info.Size()),
TotalSize: totalSize,
TotalSizeHumanReadable: byteCountSI(totalSize),
})
return nil
})
check(err)
err = filepath.Walk(filepath.Join(srcBaseDir, srcDir), func(path string, info os.FileInfo, err error) error {
check(err)
totalSize = totalSize + info.Size()
if path == srcBaseDir+srcDir+"static" || path == srcBaseDir+srcDir+"_README_.html" || path == srcBaseDir+srcDir+"BROWSE_LIBRARY.html" {
return nil
}
ps.Files = append(
ps.Files,
PathTotalSize{
IsDir: info.IsDir(),
Path: path,
FileSize: info.Size(),
FileSizeHumanReadable: byteCountSI(info.Size()),
TotalSize: totalSize,
TotalSizeHumanReadable: byteCountSI(totalSize),
})
return nil
})
check(err)
copyStatus = true
availableDisk := lsblk.USBMountedPartitionAvailableSpace("/mnt/MOTW0")
for _, file := range ps.Files {
setLedLight(p, true)
// sf := time.Now()
destPath := filepath.Join(destBaseDir, strings.ReplaceAll(file.Path, srcBaseDir, ""))
if file.IsDir {
err := os.MkdirAll(destPath, os.ModePerm)
check(err)
continue
}
if availableDisk > 0 && (file.TotalSize > availableDisk) {
fmt.Println("NOT ENOUGH OF DISK SPACE:", "Last file copied:", file.Path)
waitingLoop <- "finishing"
return
}
// fmt.Printf("_%s_", file.FileSizeHumanReadable)
inFile, err := os.Open(file.Path)
check(err)
outFile, err := os.Create(destPath)
check(err)
reader := bufio.NewReader(inFile)
writer := bufio.NewWriter(outFile)
bufSize = 512 * 2 * 16
buf := make([]byte, 0, bufSize)
for {
n, err := io.ReadFull(reader, buf[:cap(buf)])
buf = buf[:n]
if err != nil {
if err == io.EOF {
break
}
if err != io.ErrUnexpectedEOF {
check(err)
}
}
writer.Write(buf)
writer.Flush()
// outFile.Sync()
}
outFile.Sync()
outFile.Close()
inFile.Close()
select {
case cc := <-cancelCopy:
if cc {
fmt.Println("CANCEL VIA KEYBOARD:", "Last file copied:", file.Path)
setLedLight(p, true)
waitingLoop <- "finishing"
return
}
default:
}
setLedLight(p, false)
}
setLedLight(p, true)
lastFile := ps.Files[len(ps.Files)-1]
fmt.Println(lastFile.Path, lastFile.TotalSizeHumanReadable)
e := time.Now()
fmt.Printf("TIME: %v\n", e.Sub(s))
fmt.Println("BUFFER SIZE:", byteCountSI(bufSize))
copyStatus = false
waitingLoop <- "finishing"
}
//_END: CopyToDisk
//_START: NumKey & LED
func setLedLight(p string, state bool) {
d, err := os.Create(p)
check(err)
defer d.Close()
var led evdev.InputEvent
var bb bytes.Buffer
var time syscall.Timeval
err = syscall.Gettimeofday(&time)
check(err)
led.Time = time
led.Type = evdev.EV_LED
led.Code = evdev.LED_NUML
if state {
led.Value = 1
} else {
led.Value = 0
}
binary.Write(&bb, binary.LittleEndian, led)
d.Write(bb.Bytes())
}
func grebIt(greb chan *evdev.InputEvent, device *evdev.InputDevice) {
for {
i, _ := device.ReadOne()
greb <- i
}
}
//_END: NumKey & LED
//_START: Initial unmounting
func umountUSBDisk() bool {
for range [10]int{} {
if lsblk.USBMountpointed("/mnt/MOTW0") {
fmt.Println("Initial pass unmount MOTW0.")
_, err := exec.Command("umount", "/mnt/MOTW0").Output()
check(err)
} else {
return true
}
time.Sleep(500 * time.Millisecond)
}
return false
}
//_END: Initial unmounting
//_START: Mounting USB disk
func mountUSBDisk(waitingLoop chan string) bool {
largestNotMounted := lsblk.USBNotMountedPartitionOfLargestSize()
if largestNotMounted.Name != "" {
fmt.Println("mounting usb disk...", largestNotMounted.Name, largestNotMounted.Mountpoint, largestNotMounted.Fstype, largestNotMounted.Size.HumanReadable)
// _, err := exec.Command("mount", "-t", largestNotMounted.Fstype, largestNotMounted.Name, "/mnt/MOTW0").Output()
_, err := exec.Command("mount", largestNotMounted.Name, "/mnt/MOTW0").Output()
check(err)
fmt.Println("just mounted:", largestNotMounted.Name, largestNotMounted.Mountpoint, largestNotMounted.Fstype, largestNotMounted.Size.HumanReadable, "available space:", lsblk.USBMountedPartitionAvailableSpace("/mnt/MOTW0"))
fmt.Printf("^")
return true
}
if lsblk.USBMountpointed("/mnt/MOTW0") {
return true
}
return false
}
//_END: Mounting USB disk
func main() {
defer recoverFromError()
//_START: NumKey & LED
devices, _ := evdev.ListInputDevices()
myNumKeyboard := []string{}
for _, d := range devices {
// fmt.Println("name: ", d.Name, "phys: ", d.Phys, "file.name: ", d.File.Name(), "bustype: ", d.Bustype, "vendor: ", d.Vendor, "product: ", d.Product, "version: ", d.Version)
if d.Vendor == 1241 && d.Product == 4611 {
myNumKeyboard = append(myNumKeyboard, d.File.Name())
// fmt.Println("myNumKeyboard:", myNumKeyboard)
// break
}
}
sort.Strings(myNumKeyboard)
if len(myNumKeyboard) > 0 {
fmt.Println("Good! Keyboard attached:", myNumKeyboard)
setLedLight(myNumKeyboard[0], false)
} else {
os.Exit(1)
}
device, _ := evdev.Open(myNumKeyboard[0])
// device.Grab()
initialLoop := true
greb := make(chan *evdev.InputEvent, 1)
go grebIt(greb, device)
//_END: NumKey & LED
//_START waitingLoop
waitingLoop := make(chan string, 1024*100)
waitingLoop <- "initial"
//_END copyLoop
cancelCopy := make(chan bool, 10)
// go copyToDisk(myNumKeyboard[0], waitingLoop, greb)
for {
select {
case ev := <-greb:
if ev.Type == evdev.EV_KEY && ev.Value == 0 {
fmt.Println(ev.String())
if copyStatus {
cancelCopy <- true
}
if initialLoop {
initialLoop = false
}
// } else if ev.Type == evdev.EV_LED {
// fmt.Println("LED:", ev.String())
}
default:
}
select {
case wl := <-waitingLoop:
if wl == "initial" {
fmt.Println("waitingLoop state:", wl)
if !umountUSBDisk() {
fmt.Println("Need a hard reset.")
os.Exit(1)
} else {
waitingLoop <- "mounting"
}
} else if wl == "mounting" {
if mountUSBDisk(waitingLoop) {
waitingLoop <- "copying"
} else {
waitingLoop <- "mounting"
}
} else if wl == "copying" {
fmt.Println("waitingLoop state:", wl)
go copyToDisk(myNumKeyboard[0], waitingLoop, cancelCopy)
} else if wl == "finishing" {
fmt.Println("waitingLoop state:", wl)
if !umountUSBDisk() {
fmt.Println("Need a hard reset.")
os.Exit(1)
} else {
waitingLoop <- "ejecting"
setLedLight(myNumKeyboard[0], false)
}
} else if wl == "ejecting" {
fmt.Println("waitingLoop state:", wl)
for {
if lsblk.NoUSBDisksAttached() {
waitingLoop <- "initial"
break
} else {
time.Sleep(500 * time.Millisecond)
}
}
}
default:
}
time.Sleep(10 * time.Millisecond)
}
}