Esempio n. 1
0
// FromStream reads the contents of the supplied io.Reader newBinary
// and uses them to update the current program's executable file.
//
// FromStream performs the following actions to ensure a cross-platform safe
// update:
//
// - Creates a new file, /path/to/.program-name.new with mode 0755 and copies
// the contents of newBinary into the file
//
// - Renames the current program's executable file from /path/to/program-name
// to /path/to/.program-name.old
//
// - Renames /path/to/.program-name.new to /path/to/program-name
//
// - If the rename is successful, it erases /path/to/.program.old. If this operation
// fails, no error is reported.
//
// - If the rename is unsuccessful, it attempts to rename /path/to/.program-name.old
// back to /path/to/program-name. If this operation fails, the error is not reported
// in order to not mask the error that caused the rename recovery attempt.
func FromStream(newBinary io.Reader) (err error, errRecover error) {
	// get the path to the executable
	thisExecPath, err := osext.Executable()
	if err != nil {
		return
	}

	// get the directory the executable exists in
	execDir := filepath.Dir(thisExecPath)
	execName := filepath.Base(thisExecPath)

	// Copy the contents of of newbinary to a the new executable file
	newExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.new", execName))
	fp, err := os.OpenFile(newExecPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
	if err != nil {
		return
	}
	defer fp.Close()
	_, err = io.Copy(fp, newBinary)

	// if we don't call fp.Close(), windows won't let us move the new executable
	// because the file will still be "in use"
	fp.Close()

	// this is where we'll move the executable to so that we can swap in the updated replacement
	oldExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.old", execName))

	// delete any existing old exec file - this is necessary on Windows for two reasons:
	// 1. after a successful update, windows can't remove the .old file because the process is still running
	// 2. windows rename operations fail if the destination file already exists
	_ = os.Remove(oldExecPath)

	// move the existing executable to a new file in the same directory
	err = os.Rename(thisExecPath, oldExecPath)
	if err != nil {
		return
	}

	// move the new exectuable in to become the new program
	err = os.Rename(newExecPath, thisExecPath)

	if err != nil {
		// copy unsuccessful
		errRecover = os.Rename(oldExecPath, thisExecPath)
	} else {
		// copy successful, remove the old binary
		_ = os.Remove(oldExecPath)
	}

	return
}
Esempio n. 2
0
// SanityCheck() attempts to determine whether an in-place executable update could
// succeed by performing preliminary checks (to establish valid permissions, etc).
// This helps avoid downloading updates when we know the update can't be successfully
// applied later.
func SanityCheck() (err error) {
	// get the path to the executable
	thisExecPath, err := osext.Executable()
	if err != nil {
		return
	}

	// get the directory the executable exists in
	execDir := filepath.Dir(thisExecPath)
	execName := filepath.Base(thisExecPath)

	// attempt to open a file in the executable's directory
	newExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.new", execName))
	fp, err := os.OpenFile(newExecPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
	if err != nil {
		return
	}
	fp.Close()

	_ = os.Remove(newExecPath)
	return
}
Esempio n. 3
0
// Update the currently running binary to a specific version
func (d *Dist) UpdateTo(to string) (err error) {
	if d.Version == to {
		return errors.New("nothing to update")
	}
	binary, _ := osext.Executable()
	reader, err := os.Open(binary)
	if err != nil {
		return err
	}
	defer reader.Close()
	url := fmt.Sprintf("%s/projects/%s/diff/%s/%s/%s-%s", d.Host, d.Project, d.Version, to, runtime.GOOS, runtime.GOARCH)
	patch, err := d.httpGet(url)
	if err != nil {
		return err
	}
	writer := new(bytes.Buffer)
	err = binarydist.Patch(reader, writer, bytes.NewReader(patch))
	if err != nil {
		return err
	}
	reader.Close()
	err, _ = update.FromStream(writer)
	return
}