func do() bool { if err := system.IsSafe(); err != nil { fmt.Fprintf(os.Stderr, "System checks failed: %s\n", err) return false } editor := os.Getenv("EDITOR") if len(editor) == 0 { fmt.Fprintf(os.Stderr, "$EDITOR is not set\n") return false } stateFile := &disk.StateFile{ Path: *stateFileName, Rand: crypto_rand.Reader, Log: func(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, format, args...) }, } stateLock, err := stateFile.Lock(false /* don't create */) if err != nil { fmt.Fprintf(os.Stderr, "Cannot open state file: %s\n", err) return false } if stateLock == nil { fmt.Fprintf(os.Stderr, "Cannot obtain lock on state file\n") return false } defer stateLock.Close() var state *disk.State var passphrase string for { state, err = stateFile.Read(passphrase) if err == nil { break } if err != disk.BadPasswordError { fmt.Fprintf(os.Stderr, "Failed to decrypt state file: %s\n", err) return false } fmt.Fprintf(os.Stderr, "Passphrase: ") passphraseBytes, err := terminal.ReadPassword(0) fmt.Fprintf(os.Stderr, "\n") if err != nil { fmt.Fprintf(os.Stderr, "Failed to read password\n") return false } passphrase = string(passphraseBytes) } tempDir, err := system.SafeTempDir() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get safe temp directory: %s\n", err) return false } tempFile, err := ioutil.TempFile(tempDir, "pond-editstate-") if err != nil { fmt.Fprintf(os.Stderr, "Failed to create temp file: %s\n", err) return false } tempFileName := tempFile.Name() defer func() { os.Remove(tempFileName) }() signals := make(chan os.Signal, 8) signal.Notify(signals, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) go func() { <-signals println("Caught signal: removing", tempFileName) os.Remove(tempFileName) os.Exit(1) }() entities := serialise(tempFile, state) var newStateSerialized []byte for { cmd := exec.Command(editor, tempFileName) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Fprintf(os.Stderr, "Failed to run editor: %s\n", err) return false } tempFile.Close() tempFile, err := os.Open(tempFileName) if err != nil { fmt.Fprintf(os.Stderr, "Failed to open temp file: %s\n", err) return false } newState := new(disk.State) err = parse(newState, tempFile, entities) if err == nil { newStateSerialized, err = proto.Marshal(newState) } if err == nil { break } fmt.Fprintf(os.Stderr, "Error parsing: %s\n", err) fmt.Fprintf(os.Stderr, "Hit enter to edit again, or Ctrl-C to abort\n") var buf [100]byte os.Stdin.Read(buf[:]) } states := make(chan disk.NewState) done := make(chan struct{}) go stateFile.StartWriter(states, done) states <- disk.NewState{newStateSerialized, false} close(states) <-done return true }
func do() bool { if err := system.IsSafe(); err != nil { fmt.Fprintf(os.Stderr, "System checks failed: %s\n", err) return false } editor := os.Getenv("EDITOR") if len(editor) == 0 { fmt.Fprintf(os.Stderr, "$EDITOR is not set\n") return false } stateFile, err := os.Open(*stateFileName) if err != nil { fmt.Fprintf(os.Stderr, "Failed to open state file: %s\n", err) return false } defer stateFile.Close() stateLock, ok := disk.LockStateFile(stateFile) if !ok { fmt.Fprintf(os.Stderr, "Cannot obtain lock on state file\n") return false } defer stateLock.Close() encrypted, err := ioutil.ReadAll(stateFile) if err != nil { fmt.Fprintf(os.Stderr, "Failed to read state file: %s\n", err) return false } salt, ok := disk.GetSCryptSaltFromState(encrypted) if !ok { fmt.Fprintf(os.Stderr, "State file is too short to be valid\n") return false } var state *disk.State var key [32]byte for { state, err = disk.LoadState(encrypted, &key) if err == nil { break } if err != disk.BadPasswordError { fmt.Fprintf(os.Stderr, "Failed to decrypt state file: %s\n", err) return false } fmt.Fprintf(os.Stderr, "Passphrase: ") password, err := terminal.ReadPassword(0) fmt.Fprintf(os.Stderr, "\n") if err != nil { fmt.Fprintf(os.Stderr, "Failed to read password\n") return false } keySlice, err := disk.DeriveKey(string(password), &salt) copy(key[:], keySlice) } tempDir, err := system.SafeTempDir() if err != nil { fmt.Fprintf(os.Stderr, "Failed to get safe temp directory: %s\n", err) return false } tempFile, err := ioutil.TempFile(tempDir, "pond-editstate-") if err != nil { fmt.Fprintf(os.Stderr, "Failed to create temp file: %s\n", err) return false } tempFileName := tempFile.Name() defer func() { os.Remove(tempFileName) }() signals := make(chan os.Signal, 8) signal.Notify(signals, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) go func() { <-signals println("Caught signal: removing", tempFileName) os.Remove(tempFileName) os.Exit(1) }() entities := serialise(tempFile, state) var newStateSerialized []byte for { cmd := exec.Command(editor, tempFileName) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Fprintf(os.Stderr, "Failed to run editor: %s\n", err) return false } tempFile.Close() tempFile, err := os.Open(tempFileName) if err != nil { fmt.Fprintf(os.Stderr, "Failed to open temp file: %s\n", err) return false } newState := new(disk.State) err = parse(newState, tempFile, entities) if err == nil { newStateSerialized, err = proto.Marshal(newState) } if err == nil { break } fmt.Fprintf(os.Stderr, "Error parsing: %s\n", err) fmt.Fprintf(os.Stderr, "Hit enter to edit again, or Ctrl-C to abort\n") var buf [100]byte os.Stdin.Read(buf[:]) } states := make(chan []byte) done := make(chan bool) go disk.StateWriter(*stateFileName, &key, &salt, states, done) states <- newStateSerialized close(states) <-done return true }