mirror of
https://github.com/safing/portmaster
synced 2025-09-02 02:29:12 +00:00
Merge pull request #9 from safing/develop
Fix clean shutdown with portmaster-control
This commit is contained in:
commit
9bdebf207b
6 changed files with 173 additions and 105 deletions
2
main.go
2
main.go
|
@ -31,7 +31,7 @@ func init() {
|
|||
func main() {
|
||||
|
||||
// Set Info
|
||||
info.Set("Portmaster", "0.3.0", "AGPLv3", true)
|
||||
info.Set("Portmaster", "0.3.1", "AGPLv3", true)
|
||||
|
||||
// Start
|
||||
err := modules.Start()
|
||||
|
|
61
pmctl/get.go
61
pmctl/get.go
|
@ -1,17 +1,18 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/utils"
|
||||
"github.com/safing/portmaster/updates"
|
||||
)
|
||||
|
||||
func getFile(identifier string) (*updates.File, error) {
|
||||
func getFile(opts *Options) (*updates.File, error) {
|
||||
// get newest local file
|
||||
updates.LoadLatest()
|
||||
file, err := updates.GetPlatformFile(identifier)
|
||||
|
||||
file, err := updates.GetLocalPlatformFile(opts.Identifier)
|
||||
if err == nil {
|
||||
return file, nil
|
||||
}
|
||||
|
@ -19,28 +20,42 @@ func getFile(identifier string) (*updates.File, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("%s downloading %s...\n", logPrefix, identifier)
|
||||
// download
|
||||
if opts.AllowDownload {
|
||||
fmt.Printf("%s downloading %s...\n", logPrefix, opts.Identifier)
|
||||
|
||||
// if no matching file exists, load index
|
||||
err = updates.LoadIndexes()
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// create dirs
|
||||
err = utils.EnsureDirectory(updateStoragePath, 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// download indexes
|
||||
err = updates.UpdateIndexes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// download indexes
|
||||
err = updates.CheckForUpdates()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// download file
|
||||
file, err := updates.GetPlatformFile(opts.Identifier)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// wait for 30 seconds
|
||||
fmt.Printf("%s waiting for download of %s (by Portmaster Core) to complete...\n", logPrefix, opts.Identifier)
|
||||
|
||||
// try every 0.5 secs
|
||||
for tries := 0; tries < 60; tries++ {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// reload local files
|
||||
updates.LoadLatest()
|
||||
|
||||
// get file
|
||||
file, err := updates.GetLocalPlatformFile(opts.Identifier)
|
||||
if err == nil {
|
||||
return file, nil
|
||||
}
|
||||
if err != updates.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// get file
|
||||
return updates.GetPlatformFile(identifier)
|
||||
return nil, errors.New("please try again later or check the Portmaster logs")
|
||||
}
|
||||
|
|
|
@ -5,7 +5,10 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/safing/portbase/info"
|
||||
"github.com/safing/portbase/log"
|
||||
|
@ -55,7 +58,7 @@ func main() {
|
|||
// }()
|
||||
|
||||
// set meta info
|
||||
info.Set("Portmaster Control", "0.2.0", "AGPLv3", true)
|
||||
info.Set("Portmaster Control", "0.2.1", "AGPLv3", true)
|
||||
|
||||
// check if meta info is ok
|
||||
err := info.CheckVersion()
|
||||
|
@ -86,7 +89,23 @@ func initPmCtl(cmd *cobra.Command, args []string) error {
|
|||
return errors.New("please supply the database directory using the --db flag")
|
||||
}
|
||||
|
||||
err := removeOldBin()
|
||||
// check if we are root/admin for self upgrade
|
||||
userInfo, err := user.Current()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
if userInfo.Username != "root" {
|
||||
return nil
|
||||
}
|
||||
case "windows":
|
||||
if !strings.HasSuffix(userInfo.Username, "SYSTEM") { // is this correct?
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
err = removeOldBin()
|
||||
if err != nil {
|
||||
fmt.Printf("%s warning: failed to remove old upgrade: %s\n", logPrefix, err)
|
||||
}
|
||||
|
|
104
pmctl/run.go
104
pmctl/run.go
|
@ -5,12 +5,20 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Options for starting component
|
||||
type Options struct {
|
||||
Identifier string
|
||||
AllowDownload bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(runCmd)
|
||||
runCmd.AddCommand(runCore)
|
||||
|
@ -27,7 +35,10 @@ var runCore = &cobra.Command{
|
|||
Use: "core",
|
||||
Short: "Run the Portmaster Core",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run("core/portmaster-core", cmd, false)
|
||||
return run(cmd, &Options{
|
||||
Identifier: "core/portmaster-core",
|
||||
AllowDownload: true,
|
||||
})
|
||||
},
|
||||
FParseErrWhitelist: cobra.FParseErrWhitelist{
|
||||
// UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags
|
||||
|
@ -39,7 +50,10 @@ var runApp = &cobra.Command{
|
|||
Use: "app",
|
||||
Short: "Run the Portmaster App",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run("app/portmaster-app", cmd, true)
|
||||
return run(cmd, &Options{
|
||||
Identifier: "app/portmaster-app",
|
||||
AllowDownload: false,
|
||||
})
|
||||
},
|
||||
FParseErrWhitelist: cobra.FParseErrWhitelist{
|
||||
// UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags
|
||||
|
@ -51,7 +65,10 @@ var runNotifier = &cobra.Command{
|
|||
Use: "notifier",
|
||||
Short: "Run the Portmaster Notifier",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run("notifier/portmaster-notifier", cmd, true)
|
||||
return run(cmd, &Options{
|
||||
Identifier: "notifier/portmaster-notifier",
|
||||
AllowDownload: false,
|
||||
})
|
||||
},
|
||||
FParseErrWhitelist: cobra.FParseErrWhitelist{
|
||||
// UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags
|
||||
|
@ -59,68 +76,42 @@ var runNotifier = &cobra.Command{
|
|||
},
|
||||
}
|
||||
|
||||
func run(identifier string, cmd *cobra.Command, filterDatabaseFlag bool) error {
|
||||
func run(cmd *cobra.Command, opts *Options) error {
|
||||
|
||||
// get original arguments
|
||||
if len(os.Args) <= 3 {
|
||||
var args []string
|
||||
if len(os.Args) < 4 {
|
||||
return cmd.Help()
|
||||
}
|
||||
var args []string
|
||||
|
||||
// filter out database flag
|
||||
if filterDatabaseFlag {
|
||||
skip := false
|
||||
for _, arg := range os.Args[3:] {
|
||||
if skip {
|
||||
skip = false
|
||||
continue
|
||||
}
|
||||
|
||||
if arg == "--db" {
|
||||
// flag is seperated, skip two arguments
|
||||
skip = true
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(arg, "--db=") {
|
||||
// flag is one string, skip one argument
|
||||
continue
|
||||
}
|
||||
|
||||
args = append(args, arg)
|
||||
}
|
||||
} else {
|
||||
args = os.Args[3:]
|
||||
}
|
||||
args = os.Args[3:]
|
||||
|
||||
// adapt identifier
|
||||
if windows() {
|
||||
identifier += ".exe"
|
||||
opts.Identifier += ".exe"
|
||||
}
|
||||
|
||||
// run
|
||||
for {
|
||||
file, err := getFile(identifier)
|
||||
file, err := getFile(opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s could not get component: %s", logPrefix, err)
|
||||
return fmt.Errorf("could not get component: %s", err)
|
||||
}
|
||||
|
||||
// check permission
|
||||
if !windows() {
|
||||
info, err := os.Stat(file.Path())
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s failed to get file info on %s: %s", logPrefix, file.Path(), err)
|
||||
return fmt.Errorf("failed to get file info on %s: %s", file.Path(), err)
|
||||
}
|
||||
if info.Mode() != 0755 {
|
||||
err := os.Chmod(file.Path(), 0755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s failed to set exec permissions on %s: %s", logPrefix, file.Path(), err)
|
||||
return fmt.Errorf("failed to set exec permissions on %s: %s", file.Path(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("%s starting %s %s\n", logPrefix, file.Path(), strings.Join(args, " "))
|
||||
// os.Exit(0)
|
||||
|
||||
// create command
|
||||
exc := exec.Command(file.Path(), args...)
|
||||
|
@ -128,17 +119,17 @@ func run(identifier string, cmd *cobra.Command, filterDatabaseFlag bool) error {
|
|||
// consume stdout/stderr
|
||||
stdout, err := exc.StdoutPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s failed to connect stdout: %s", logPrefix, err)
|
||||
return fmt.Errorf("failed to connect stdout: %s", err)
|
||||
}
|
||||
stderr, err := exc.StderrPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s failed to connect stderr: %s", logPrefix, err)
|
||||
return fmt.Errorf("failed to connect stderr: %s", err)
|
||||
}
|
||||
|
||||
// start
|
||||
err = exc.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s failed to start %s: %s", logPrefix, identifier, err)
|
||||
return fmt.Errorf("failed to start %s: %s", opts.Identifier, err)
|
||||
}
|
||||
|
||||
// start output writers
|
||||
|
@ -149,6 +140,24 @@ func run(identifier string, cmd *cobra.Command, filterDatabaseFlag bool) error {
|
|||
io.Copy(os.Stderr, stderr)
|
||||
}()
|
||||
|
||||
// catch interrupt for clean shutdown
|
||||
signalCh := make(chan os.Signal)
|
||||
signal.Notify(
|
||||
signalCh,
|
||||
os.Interrupt,
|
||||
os.Kill,
|
||||
syscall.SIGHUP,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGQUIT,
|
||||
)
|
||||
go func() {
|
||||
for {
|
||||
sig := <-signalCh
|
||||
fmt.Printf("%s got %s signal (ignoring), waiting for %s to exit...\n", logPrefix, sig, opts.Identifier)
|
||||
}
|
||||
}()
|
||||
|
||||
// wait for completion
|
||||
err = exc.Wait()
|
||||
if err != nil {
|
||||
|
@ -157,31 +166,30 @@ func run(identifier string, cmd *cobra.Command, filterDatabaseFlag bool) error {
|
|||
switch exErr.ProcessState.ExitCode() {
|
||||
case 0:
|
||||
// clean exit
|
||||
fmt.Printf("%s clean exit of %s, but with error: %s\n", logPrefix, identifier, err)
|
||||
fmt.Printf("%s clean exit of %s, but with error: %s\n", logPrefix, opts.Identifier, err)
|
||||
os.Exit(1)
|
||||
case 1:
|
||||
// error exit
|
||||
fmt.Printf("%s error during execution of %s: %s\n", logPrefix, identifier, err)
|
||||
fmt.Printf("%s error during execution of %s: %s\n", logPrefix, opts.Identifier, err)
|
||||
os.Exit(1)
|
||||
case 2357427: // Leet Speak for "restart"
|
||||
// restart request
|
||||
fmt.Printf("%s restarting %s\n", logPrefix, identifier)
|
||||
fmt.Printf("%s restarting %s\n", logPrefix, opts.Identifier)
|
||||
continue
|
||||
default:
|
||||
fmt.Printf("%s unexpected error during execution of %s: %s\n", logPrefix, identifier, err)
|
||||
fmt.Printf("%s unexpected error during execution of %s: %s\n", logPrefix, opts.Identifier, err)
|
||||
os.Exit(exErr.ProcessState.ExitCode())
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("%s unexpected error type during execution of %s: %s\n", logPrefix, identifier, err)
|
||||
fmt.Printf("%s unexpected error type during execution of %s: %s\n", logPrefix, opts.Identifier, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// clean exit
|
||||
break
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s completed successfully\n", logPrefix, identifier)
|
||||
fmt.Printf("%s %s completed successfully\n", logPrefix, opts.Identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,10 @@ import (
|
|||
"github.com/safing/portmaster/updates"
|
||||
)
|
||||
|
||||
var (
|
||||
oldBinSuffix = "-old"
|
||||
)
|
||||
|
||||
func checkForUpgrade() (update *updates.File) {
|
||||
info := info.GetInfo()
|
||||
file, err := updates.GetLocalPlatformFile("control/portmaster-control")
|
||||
|
@ -25,6 +29,8 @@ func checkForUpgrade() (update *updates.File) {
|
|||
|
||||
func doSelfUpgrade(file *updates.File) error {
|
||||
|
||||
// FIXME: fix permissions if needed
|
||||
|
||||
// get destination
|
||||
dst, err := os.Executable()
|
||||
if err != nil {
|
||||
|
@ -36,7 +42,7 @@ func doSelfUpgrade(file *updates.File) error {
|
|||
}
|
||||
|
||||
// mv destination
|
||||
err = os.Rename(dst, dst+"_old")
|
||||
err = os.Rename(dst, dst+oldBinSuffix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -105,7 +111,7 @@ func removeOldBin() error {
|
|||
}
|
||||
|
||||
// delete old
|
||||
err = os.Remove(dst + "_old")
|
||||
err = os.Remove(dst + oldBinSuffix)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
|
|
|
@ -11,14 +11,19 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/utils"
|
||||
)
|
||||
|
||||
func updater() {
|
||||
time.Sleep(10 * time.Second)
|
||||
for {
|
||||
err := CheckForUpdates()
|
||||
err := UpdateIndexes()
|
||||
if err != nil {
|
||||
log.Warningf("updates: failed to check for updates: %s", err)
|
||||
log.Warningf("updates: updating index failed: %s", err)
|
||||
}
|
||||
err = DownloadUpdates()
|
||||
if err != nil {
|
||||
log.Warningf("updates: downloading updates failed: %s", err)
|
||||
}
|
||||
time.Sleep(1 * time.Hour)
|
||||
}
|
||||
|
@ -40,10 +45,9 @@ func markPlatformFileForDownload(identifier string) {
|
|||
markFileForDownload(identifier)
|
||||
}
|
||||
|
||||
// CheckForUpdates checks if updates are available and downloads updates of used components.
|
||||
func CheckForUpdates() (err error) {
|
||||
|
||||
// download new index
|
||||
// UpdateIndexes downloads the current update indexes.
|
||||
func UpdateIndexes() (err error) {
|
||||
// download new indexes
|
||||
var data []byte
|
||||
for tries := 0; tries < 3; tries++ {
|
||||
data, err = fetchData("stable.json", tries)
|
||||
|
@ -52,39 +56,72 @@ func CheckForUpdates() (err error) {
|
|||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to download: %s", err)
|
||||
}
|
||||
|
||||
newStableUpdates := make(map[string]string)
|
||||
err = json.Unmarshal(data, &newStableUpdates)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to parse: %s", err)
|
||||
}
|
||||
|
||||
if len(newStableUpdates) == 0 {
|
||||
return errors.New("stable.json is empty")
|
||||
return errors.New("index is empty")
|
||||
}
|
||||
|
||||
// update stable index
|
||||
updatesLock.Lock()
|
||||
stableUpdates = newStableUpdates
|
||||
updatesLock.Unlock()
|
||||
|
||||
// check dir
|
||||
err = utils.EnsureDirectory(updateStoragePath, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save stable index
|
||||
err = ioutil.WriteFile(filepath.Join(updateStoragePath, "stable.json"), data, 0644)
|
||||
if err != nil {
|
||||
log.Warningf("updates: failed to save new version of stable.json: %s", err)
|
||||
}
|
||||
|
||||
// update version status
|
||||
updatesLock.RLock()
|
||||
updateStatus(versionClassStable, stableUpdates)
|
||||
updatesLock.RUnlock()
|
||||
|
||||
// FIXME IN STABLE: correct log line
|
||||
log.Infof("updates: downloaded new update index: stable.json (alpha until we actually reach stable)")
|
||||
log.Infof("updates: updated index stable.json (alpha/beta until we actually reach stable)")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DownloadUpdates checks if updates are available and downloads updates of used components.
|
||||
func DownloadUpdates() (err error) {
|
||||
|
||||
// ensure important components are always updated
|
||||
updatesLock.Lock()
|
||||
if runtime.GOOS == "windows" {
|
||||
markPlatformFileForDownload("core/portmaster-core.exe")
|
||||
markPlatformFileForDownload("control/portmaster-control.exe")
|
||||
markPlatformFileForDownload("app/portmaster-app.exe")
|
||||
markPlatformFileForDownload("notifier/portmaster-notifier.exe")
|
||||
} else {
|
||||
markPlatformFileForDownload("core/portmaster-core")
|
||||
markPlatformFileForDownload("control/portmaster-control")
|
||||
markPlatformFileForDownload("app/portmaster-app")
|
||||
markPlatformFileForDownload("notifier/portmaster-notifier")
|
||||
}
|
||||
updatesLock.Unlock()
|
||||
|
||||
// RLock for the remaining function
|
||||
updatesLock.RLock()
|
||||
defer updatesLock.RUnlock()
|
||||
|
||||
// update existing files
|
||||
log.Tracef("updates: updating existing files")
|
||||
updatesLock.RLock()
|
||||
for identifier, newVersion := range newStableUpdates {
|
||||
for identifier, newVersion := range stableUpdates {
|
||||
oldVersion, ok := localUpdates[identifier]
|
||||
if ok && newVersion != oldVersion {
|
||||
|
||||
|
@ -103,24 +140,7 @@ func CheckForUpdates() (err error) {
|
|||
|
||||
}
|
||||
}
|
||||
updatesLock.RUnlock()
|
||||
log.Tracef("updates: finished updating existing files")
|
||||
|
||||
// update stable index
|
||||
updatesLock.Lock()
|
||||
stableUpdates = newStableUpdates
|
||||
updatesLock.Unlock()
|
||||
|
||||
// save stable index
|
||||
err = ioutil.WriteFile(filepath.Join(updateStoragePath, "stable.json"), data, 0644)
|
||||
if err != nil {
|
||||
log.Warningf("updates: failed to save new version of stable.json: %s", err)
|
||||
}
|
||||
|
||||
// update version status
|
||||
updatesLock.RLock()
|
||||
defer updatesLock.RUnlock()
|
||||
updateStatus(versionClassStable, stableUpdates)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue