diff --git a/internal/app/app.go b/internal/app/app.go
index abc34800..8916c95c 100644
--- a/internal/app/app.go
+++ b/internal/app/app.go
@@ -166,6 +166,8 @@ func run(c *cli.Context) error { //nolint:funlen
exe = os.Args[0]
}
+ migrationErr := migrateOldVersions()
+
// Run with profiling if requested.
return withProfiler(c, func() error {
// Restart the app if requested.
@@ -176,6 +178,9 @@ func run(c *cli.Context) error { //nolint:funlen
return WithLocations(func(locations *locations.Locations) error {
// Initialize logging.
return withLogging(c, crashHandler, locations, func() error {
+ if migrationErr != nil {
+ logrus.WithError(migrationErr).Error("Migration failed")
+ }
// Ensure we are the only instance running.
return withSingleInstance(locations, version, func() error {
// Unlock the encrypted vault.
diff --git a/internal/app/migration.go b/internal/app/migration.go
new file mode 100644
index 00000000..097f3907
--- /dev/null
+++ b/internal/app/migration.go
@@ -0,0 +1,106 @@
+// Copyright (c) 2022 Proton AG
+//
+// This file is part of Proton Mail Bridge.
+//
+// Proton Mail Bridge is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Proton Mail Bridge is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Proton Mail Bridge. If not, see .
+
+package app
+
+import (
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/allan-simon/go-singleinstance"
+ "github.com/hashicorp/go-multierror"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+func migrateOldVersions() (allErrors error) {
+ cacheDir, cacheError := os.UserCacheDir()
+ if cacheError != nil {
+ allErrors = multierror.Append(allErrors, errors.Wrap(cacheError, "cannot get os cache"))
+ return // not need to continue for now (with more migrations might be still ok to continue)
+ }
+
+ if err := killV2AppAndRemoveV2LockFiles(filepath.Join(cacheDir, "protonmail", "bridge", "bridge.lock")); err != nil {
+ allErrors = multierror.Append(allErrors, errors.Wrap(err, "cannot migrate lockfiles"))
+ }
+
+ return
+}
+
+func killV2AppAndRemoveV2LockFiles(lockFilePathV2 string) error {
+ l := logrus.WithField("path", lockFilePathV2)
+
+ if _, err := os.Stat(lockFilePathV2); os.IsNotExist(err) {
+ l.Debug("no v2 lockfile")
+ return nil
+ }
+
+ lock, err := singleinstance.CreateLockFile(lockFilePathV2)
+
+ if err == nil {
+ l.Debug("no other v2 instance is running")
+
+ if errClose := lock.Close(); errClose != nil {
+ l.WithError(errClose).Error("Cannot close lock file")
+ }
+
+ return os.Remove(lockFilePathV2)
+ }
+
+ // The other instance is an older version, so we should kill it.
+ pid, err := getPID(lockFilePathV2)
+ if err != nil {
+ return errors.Wrap(err, "cannot get v2 pid")
+ }
+
+ if err := killPID(pid); err != nil {
+ return errors.Wrapf(err, "cannot kill v2 app (PID %d)", pid)
+ }
+
+ // Need to wait some time to release file lock
+ time.Sleep(time.Second)
+
+ return nil
+}
+
+func getPID(lockFilePath string) (int, error) {
+ file, err := os.Open(filepath.Clean(lockFilePath))
+ if err != nil {
+ return 0, err
+ }
+ defer func() { _ = file.Close() }()
+
+ rawPID := make([]byte, 10) // PID is probably up to 7 digits long, 10 should be enough
+ n, err := file.Read(rawPID)
+ if err != nil {
+ return 0, err
+ }
+
+ return strconv.Atoi(strings.TrimSpace(string(rawPID[:n])))
+}
+
+func killPID(pid int) error {
+ p, err := os.FindProcess(pid)
+ if err != nil {
+ return err
+ }
+
+ return p.Kill()
+}
diff --git a/internal/app/singleinstance_unix.go b/internal/app/singleinstance.go
similarity index 71%
rename from internal/app/singleinstance_unix.go
rename to internal/app/singleinstance.go
index f7c98130..0009a88f 100644
--- a/internal/app/singleinstance_unix.go
+++ b/internal/app/singleinstance.go
@@ -15,24 +15,17 @@
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-//go:build !windows
-// +build !windows
-
package app
import (
"fmt"
"os"
- "path/filepath"
- "strconv"
- "strings"
"time"
"github.com/Masterminds/semver/v3"
"github.com/ProtonMail/proton-bridge/v2/internal/focus"
"github.com/allan-simon/go-singleinstance"
"github.com/sirupsen/logrus"
- "golang.org/x/sys/unix"
)
// checkSingleInstance checks if another instance of the application is already running.
@@ -66,7 +59,7 @@ func checkSingleInstance(lockFilePath string, curVersion *semver.Version) (*os.F
return nil, err
}
- if err := unix.Kill(pid, unix.SIGTERM); err != nil {
+ if err := killPID(pid); err != nil {
return nil, err
}
@@ -75,39 +68,3 @@ func checkSingleInstance(lockFilePath string, curVersion *semver.Version) (*os.F
return singleinstance.CreateLockFile(lockFilePath)
}
-
-func getPID(lockFilePath string) (int, error) {
- file, err := os.Open(filepath.Clean(lockFilePath))
- if err != nil {
- return 0, err
- }
- defer func() { _ = file.Close() }()
-
- rawPID := make([]byte, 10) // PID is probably up to 7 digits long, 10 should be enough
- n, err := file.Read(rawPID)
- if err != nil {
- return 0, err
- }
-
- return strconv.Atoi(strings.TrimSpace(string(rawPID[:n])))
-}
-
-/*
-func runningVersionIsOlder() error {
- currentVer, err := semver.StrictNewVersion(constants.Version)
- if err != nil {
- return err
- }
-
- runningVer, err := semver.StrictNewVersion(settingsObj.Get(settings.LastVersionKey))
- if err != nil {
- return err
- }
-
- if !runningVer.LessThan(currentVer) {
- return errors.New("running version is not older")
- }
-
- return nil
-}
-*/
diff --git a/internal/app/singleinstance_windows.go b/internal/app/singleinstance_windows.go
deleted file mode 100644
index 6efaf0c0..00000000
--- a/internal/app/singleinstance_windows.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2022 Proton AG
-//
-// This file is part of Proton Mail Bridge.
-//
-// Proton Mail Bridge is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Proton Mail Bridge is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Proton Mail Bridge. If not, see .
-
-//go:build windows
-// +build windows
-
-package app
-
-import (
- "os"
-
- "github.com/Masterminds/semver/v3"
- "github.com/allan-simon/go-singleinstance"
-)
-
-func checkSingleInstance(lockFilePath string, _ *semver.Version) (*os.File, error) {
- return singleinstance.CreateLockFile(lockFilePath)
-}