Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix clean shutdown with portmaster-control #9

Merged
merged 1 commit into from
Jul 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
63 changes: 39 additions & 24 deletions pmctl/get.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,61 @@
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
}
if err != updates.ErrNotFound {
return nil, err
}

fmt.Printf("%s downloading %s...\n", logPrefix, 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.CheckForUpdates()
if err != nil {
return nil, err
}
} else {
// download
if opts.AllowDownload {
fmt.Printf("%s downloading %s...\n", logPrefix, opts.Identifier)

// download indexes
err = updates.UpdateIndexes()
if err != nil {
return nil, err
}

// download file
file, err := updates.GetPlatformFile(opts.Identifier)
if err != nil {
return nil, err
}
return file, nil
}

// get file
return updates.GetPlatformFile(identifier)
// 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
}
}
return nil, errors.New("please try again later or check the Portmaster logs")
}
23 changes: 21 additions & 2 deletions pmctl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import (
"flag"
"fmt"
"os"
"os/user"
"path/filepath"
"runtime"
"strings"

"github.com/safing/portbase/info"
"github.com/safing/portbase/log"
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
}
Expand Down
104 changes: 56 additions & 48 deletions pmctl/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand All @@ -51,94 +65,71 @@ 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
UnknownFlags: true,
},
}

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 {
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:]
if len(os.Args) < 4 {
return cmd.Help()
}
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...)

// 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
Expand All @@ -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 {
Expand All @@ -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
}

Expand Down
10 changes: 8 additions & 2 deletions pmctl/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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 {
Expand All @@ -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
}
Expand Down Expand Up @@ -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
Expand Down
Loading