This repository has been archived by the owner on Jan 8, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This uses ps to find the pid of an app, since app bundles use an executable like Test.app/Contents/MacOS/Test. It will SIGTERM this process (and SIGKILL after a second), and then call open. It doesn't fail to call open if the kill failed. Bundled is a Test.app that is used in the test.
- Loading branch information
Showing
14 changed files
with
413 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
// Copyright 2015 Keybase, Inc. All rights reserved. Use of | ||
// this source code is governed by the included BSD license. | ||
|
||
package process | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"strconv" | ||
"strings" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/keybase/go-logging" | ||
"github.com/keybase/go-updater/command" | ||
) | ||
|
||
// RestartAppDarwin restarts an app. We will still call open if the kill fails. | ||
func RestartAppDarwin(appPath string, log logging.Logger) error { | ||
if appPath == "" { | ||
return fmt.Errorf("No app path to restart") | ||
} | ||
procName := filepath.Join(appPath, "Contents/MacOS/") | ||
TerminateAll(procName, log) | ||
return OpenAppDarwin(appPath, log) | ||
} | ||
|
||
// findPS finds PIDs for processes with prefix using ps. | ||
// The command `ps ax -o pid,comm` returns process list in 2 columns, pid and executable name. | ||
// For example: | ||
// | ||
// 67846 /Applications/Keybase.app/Contents/SharedSupport/bin/keybase | ||
// 67847 /Applications/Keybase.app/Contents/SharedSupport/bin/keybase | ||
// 53852 /Applications/Keybase.app/Contents/SharedSupport/bin/kbfs | ||
// 3915 /Applications/Keybase.app/Contents/SharedSupport/bin/updater | ||
// 67833 /Applications/Keybase.app/Contents/MacOS/Keybase | ||
// | ||
func findPS(prefix string, log logging.Logger) ([]int, error) { | ||
log.Debugf("Finding process with prefix: %q", prefix) | ||
result, err := command.Exec("ps", []string{"ax", "-o", "pid,comm"}, time.Minute, log) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return parsePS(&result.Stdout, prefix, log) | ||
} | ||
|
||
func parsePS(reader io.Reader, prefix string, log logging.Logger) ([]int, error) { | ||
if reader == nil { | ||
return nil, fmt.Errorf("Nothing to parse") | ||
} | ||
if prefix == "" { | ||
return nil, fmt.Errorf("No prefix") | ||
} | ||
pids := []int{} | ||
scanner := bufio.NewScanner(reader) | ||
for scanner.Scan() { | ||
line := strings.TrimSpace(scanner.Text()) | ||
fields := strings.Fields(line) | ||
if len(fields) >= 2 && strings.HasPrefix(fields[1], prefix) { | ||
pid, err := strconv.Atoi(fields[0]) | ||
if err != nil { | ||
log.Warningf("Invalid pid for %s", fields) | ||
} else if pid > 0 { | ||
pids = append(pids, pid) | ||
} | ||
} | ||
} | ||
return pids, nil | ||
} | ||
|
||
// TerminateAll stops processes with executable names that start with prefix | ||
func TerminateAll(prefix string, log logging.Logger) { | ||
pids, err := findPS(prefix, log) | ||
if err != nil { | ||
log.Warningf("Error finding process: %s", err) | ||
} | ||
if pids == nil { | ||
log.Warningf("No processes found with prefix %q", prefix) | ||
return | ||
} | ||
for _, pid := range pids { | ||
if err := TerminatePid(pid, log); err != nil { | ||
log.Warningf("Error terminating %d: %s", pid, err) | ||
} | ||
} | ||
} | ||
|
||
// TerminatePid calls SIGTERM, then waits a second and then calls SIGKILL. | ||
// We don't mind if we call SIGKILL on an already terminated process. | ||
// If SIGKILL failed then we've got bigger problems. | ||
func TerminatePid(pid int, log logging.Logger) error { | ||
log.Debugf("Searching OS for %d", pid) | ||
process, err := os.FindProcess(pid) | ||
if err != nil { | ||
return fmt.Errorf("Error finding OS process: %s", err) | ||
} | ||
if process == nil { | ||
return fmt.Errorf("No process found with pid %d", pid) | ||
} | ||
|
||
log.Debugf("Terminating: %#v", process) | ||
err = process.Signal(syscall.SIGTERM) | ||
if err != nil { | ||
log.Warningf("Error sending terminate: %s", err) | ||
} | ||
time.Sleep(time.Second) | ||
_ = process.Kill() | ||
return nil | ||
} | ||
|
||
// OpenAppDarwin starts an app | ||
func OpenAppDarwin(appPath string, log logging.Logger) error { | ||
tryOpen := func() error { | ||
result, err := command.Exec("/usr/bin/open", []string{appPath}, time.Minute, log) | ||
if err != nil { | ||
return fmt.Errorf("Open error: %s; %s", err, result.CombinedOutput()) | ||
} | ||
return nil | ||
} | ||
// We need to try 10 times because Gatekeeper has some issues, for example, | ||
// http://www.openradar.me/23614087 | ||
for i := 0; i < 10; i++ { | ||
err := tryOpen() | ||
if err == nil { | ||
break | ||
} | ||
log.Errorf("Open error (trying again in a second): %s", err) | ||
time.Sleep(1 * time.Second) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// Copyright 2015 Keybase, Inc. All rights reserved. Use of | ||
// this source code is governed by the included BSD license. | ||
|
||
// +build darwin | ||
|
||
package process | ||
|
||
import ( | ||
"bytes" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/keybase/go-logging" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
var log = logging.Logger{Module: "test"} | ||
|
||
func TestRestartDarwin(t *testing.T) { | ||
appPath := filepath.Join(os.Getenv("GOPATH"), "src/github.com/keybase/go-updater/test/Test.app") | ||
defer TerminateAll(appPath, log) | ||
|
||
err := OpenAppDarwin(appPath, log) | ||
require.NoError(t, err) | ||
|
||
err = RestartAppDarwin(appPath, log) | ||
require.NoError(t, err) | ||
} | ||
|
||
func TestFindPS(t *testing.T) { | ||
pids, err := findPS("ps", log) | ||
assert.NoError(t, err) | ||
require.Equal(t, 1, len(pids)) | ||
assert.True(t, pids[0] != 0) | ||
} | ||
|
||
func TestParsePS(t *testing.T) { | ||
var ps = ` | ||
67846 /Applications/Keybase.app/Contents/SharedSupport/bin/keybase | ||
67847 /Applications/Keybase.app/Contents/SharedSupport/bin/keybase | ||
852 /Applications/Keybase.app/Contents/SharedSupport/bin/kbfs | ||
5 /Applications/Keybase.app/Contents/SharedSupport/bin/updater | ||
43 /Applications/Keybase.app/Contents/MacOS/Keybase | ||
67845 /Applications/Keybase.app/Contents/Frameworks/Keybase Helper.app/Contents/MacOS/Keybase Helper | ||
636 login ?? | ||
1777 /usr/sbin/distnoted` | ||
pids, err := parsePS(bytes.NewBufferString(ps), "/Applications/Keybase.app/Contents/MacOS", log) | ||
assert.NoError(t, err) | ||
assert.Equal(t, []int{43}, pids) | ||
|
||
pids, err = parsePS(bytes.NewBufferString(ps), "/Applications/Keybase.app/Contents/SharedSupport/bin/keybase", log) | ||
assert.NoError(t, err) | ||
assert.Equal(t, []int{67846, 67847}, pids) | ||
|
||
pids, err = parsePS(bytes.NewBufferString(ps), "login", log) | ||
assert.NoError(t, err) | ||
assert.Equal(t, []int{636}, pids) | ||
} | ||
|
||
func TestParsePSNil(t *testing.T) { | ||
pids, err := parsePS(nil, "", log) | ||
require.EqualError(t, err, "Nothing to parse") | ||
assert.Nil(t, pids) | ||
} | ||
|
||
func TestParsePSNoPrefix(t *testing.T) { | ||
pids, err := parsePS(bytes.NewBuffer([]byte{}), "", log) | ||
require.EqualError(t, err, "No prefix") | ||
assert.Nil(t, pids) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>BuildMachineOSBuild</key> | ||
<string>15E65</string> | ||
<key>CFBundleDevelopmentRegion</key> | ||
<string>en</string> | ||
<key>CFBundleExecutable</key> | ||
<string>Test</string> | ||
<key>CFBundleIdentifier</key> | ||
<string>keybase.Test</string> | ||
<key>CFBundleInfoDictionaryVersion</key> | ||
<string>6.0</string> | ||
<key>CFBundleName</key> | ||
<string>Test</string> | ||
<key>CFBundlePackageType</key> | ||
<string>APPL</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>1.0</string> | ||
<key>CFBundleSignature</key> | ||
<string>????</string> | ||
<key>CFBundleSupportedPlatforms</key> | ||
<array> | ||
<string>MacOSX</string> | ||
</array> | ||
<key>CFBundleVersion</key> | ||
<string>1</string> | ||
<key>DTCompiler</key> | ||
<string>com.apple.compilers.llvm.clang.1_0</string> | ||
<key>DTPlatformBuild</key> | ||
<string>7D175</string> | ||
<key>DTPlatformVersion</key> | ||
<string>GM</string> | ||
<key>DTSDKBuild</key> | ||
<string>15E60</string> | ||
<key>DTSDKName</key> | ||
<string>macosx10.11</string> | ||
<key>DTXcode</key> | ||
<string>0730</string> | ||
<key>DTXcodeBuild</key> | ||
<string>7D175</string> | ||
<key>LSMinimumSystemVersion</key> | ||
<string>10.11</string> | ||
<key>NSHumanReadableCopyright</key> | ||
<string>Copyright © 2016 Keybase. All rights reserved.</string> | ||
<key>NSMainNibFile</key> | ||
<string>MainMenu</string> | ||
<key>NSPrincipalClass</key> | ||
<string>NSApplication</string> | ||
</dict> | ||
</plist> |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
APPL???? |
Binary file not shown.
Oops, something went wrong.