commit 4f4f77d01c96a8c75bcaa2661b8df1e84522e234 Author: Marcell Mars Date: Sun Jul 30 12:37:23 2023 +0000 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..8b34c75 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# sneakernet + +a downloadable archive containing sections of the Memory of the World, an extensive archive of human knowledge which defies copyright in favour of the importance of an open and democratic access to the wealth of human knowledge. + +## usage + +1) plug the usb stick into the cable +2) transfer starts & light start to blink +3) wait until the light stops blinking +3a) press any key until 3) + +_the first file getting copied is packed with all the info you'll need to keep the transfer rolling once you're back home_ + +![](photo1610985806.jpeg) diff --git a/copystation.service b/copystation.service new file mode 100644 index 0000000..1685f88 --- /dev/null +++ b/copystation.service @@ -0,0 +1,20 @@ +[Unit] +Description=copystation + +[Service] +Type=simple +Restart=always +RestartSec=5s +WorkingDirectory=/home/pi/ +ExecStart=/home/pi/copystation +Restart=always +PermissionsStartOnly=true +StandardOutput=syslog +StandardError=syslog +SyslogIdentifier=sleepservice + +[Install] +WantedBy=multi-user.target + +# this goes to: /etc/systemd/system/copystation.service +# and chmod +x /home/pi/copystation (binary) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e7fcfc2 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module main + +go 1.15 + +replace github.com/marcellmars/lsblk => /home/m/devel/mygodev/lsblk +//replace github.com/marcellmars/lsblk => /home/pi/devel/lsblk + +require ( + github.com/gvalkov/golang-evdev v0.0.0-20191114124502-287e62b94bcb + github.com/marcellmars/lsblk v0.0.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..30f2020 --- /dev/null +++ b/go.sum @@ -0,0 +1,19 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gvalkov/golang-evdev v0.0.0-20191114124502-287e62b94bcb h1:WHSAxLz3P5t4DKukfJ5wu7+aMyVkuTNSbCiAjVS92sM= +github.com/gvalkov/golang-evdev v0.0.0-20191114124502-287e62b94bcb/go.mod h1:SAzVFKCRezozJTGavF3GX8MBUruETCqzivVLYiywouA= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/thoas/go-funk v0.7.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= +github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/wesovilabs/koazee v0.0.5/go.mod h1:pYhJpCWJQGXU5aVVD+LxutvCKLDSK8I7g5htWvaZlvw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go new file mode 100644 index 0000000..bb0441a --- /dev/null +++ b/main.go @@ -0,0 +1,401 @@ +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) + } +} diff --git a/photo1610985806.jpeg b/photo1610985806.jpeg new file mode 100644 index 0000000..5b3a8dc Binary files /dev/null and b/photo1610985806.jpeg differ