You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: upgrader.go
    path of file: ./repos/portmaster/updates
    
    the code of the file until where you have to start completion: package updates

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	processInfo "github.com/shirou/gopsutil/process"
	"github.com/tevino/abool"

	"github.com/safing/portbase/dataroot"
	"github.com/safing/portbase/info"
	"github.com/safing/portbase/log"
	"github.com/safing/portbase/notifications"
	"github.com/safing/portbase/rng"
	"github.com/safing/portbase/updater"
	"github.com/safing/portbase/utils/renameio"
	"github.com/safing/portmaster/updates/helper"
)

const (
	upgradedSuffix = "-upgraded"
	exeExt         = ".exe"
)

var (
	upgraderActive = abool.NewBool(false)

	pmCtrlUpdate *updater.File
	pmCoreUpdate *updater.File

	spnHubUpdate *updater.File

	rawVersionRegex = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+b?\*?$`)
)

func initUpgrader() error {
	return module.RegisterEventHook(
		ModuleName,
		ResourceUpdateEvent,
		"run upgrades",
		upgrader,
	)
}

func upgrader(_ context.Context, _ interface{}) error {
	// Lock runs, but discard additional runs.
	if !upgraderActive.SetToIf(false, true) {
		return nil
	}
	defer upgraderActive.SetTo(false)

	// Upgrade portmaster-start.
	err := upgradePortmasterStart()
	if err != nil {
		log.Warningf("updates: failed to upgrade portmaster-start: %s", err)
	}

	// Upgrade based on binary.
	binBaseName := strings.Split(filepath.Base(os.Args[0]), "_")[0]
	switch binBaseName {
	case "portmaster-core":
		// Notify about upgrade.
		if err := upgradeCoreNotify(); err != nil {
			log.Warningf("updates: failed to notify about core upgrade: %s", err)
		}

		// Fix chrome sandbox permissions.
		if err := helper.EnsureChromeSandboxPermissions(registry); err != nil {
			log.Warningf("updates: failed to handle electron upgrade: %s", err)
		}

		// Upgrade system integration.
		upgradeSystemIntegration()

	case "spn-hub":
		/
    
func upgradeBinary(fileToUpgrade string, file *updater.File) error {
	fileExists := false
	_, err := os.Stat(fileToUpgrade)
	if err == nil {
		// file exists and is accessible
		fileExists = true
	}

	if fileExists {
		// get current version
		var currentVersion string
		cmd := exec.Command(fileToUpgrade, "version", "--short")
		out, err := cmd.Output()
		if err == nil {
			// abort if version matches
			currentVersion = strings.Trim(strings.TrimSpace(string(out)), "*")
			if currentVersion == file.Version() {
				log.Debugf("updates: %s is already v%s", fileToUpgrade, file.Version())
				// already up to date!
				return nil
			}
		} else {
			log.Warningf("updates: failed to run %s to get version for upgrade check: %s", fileToUpgrade, err)
			currentVersion = "0.0.0"
		}

		// test currentVersion for sanity
		if !rawVersionRegex.MatchString(currentVersion) {
			log.Debugf("updates: version string returned by %s is invalid: %s", fileToUpgrade, currentVersion)
		}

		// try removing old version
		err = os.Remove(fileToUpgrade)
		if err != nil {
			// ensure tmp dir is here
			err = registry.TmpDir().Ensure()
			if err != nil {
				return fmt.Errorf("could not prepare tmp directory for moving file that needs upgrade: %w", err)
			}

			// maybe we're on windows and it's in use, try moving
			err = os.Rename(fileToUpgrade, filepath.Join(
				registry.TmpDir().Path,
				fmt.Sprintf(
					"%s-%d%s",
					filepath.Base(fileToUpgrade),
					time.Now().UTC().Unix(),
					upgradedSuffix,
				),
			))
			if err != nil {
				return fmt.Errorf("unable to move file that needs upgrade: %w", err)
			}
		}
	}

	// copy upgrade
	err = CopyFile(file.Path(), fileToUpgrade)
	if err != nil {
		// try again
		time.Sleep(1 * time.Second)
		err = CopyFile(file.Path(), fileToUpgrade)
		if err != nil {
			return err
		}
	}

	// check permissions
	if !onWindows {
		info, err := os.Stat(fileToUpgrade)
		if err != nil {
			return fmt.Errorf("failed to get file info on %s: %w", fileToUpgrade, err)
		}
		if info.Mode() != 0o0755 {
			err := os.Chmod(fileToUpgrade, 0o0755) //nolint:gosec // Set execute permissions.
			if err != nil {
				return fmt.Errorf("failed to set permissions on %s: %w", fileToUpgrade, err)
			}
		}
	}

	log.Infof("updates: upgraded %s to v%s", fileToUpgrade, file.Version())
	return nil
}