From e2ab860c7206aa44425684bd8b11e58c2779a6dc Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Wed, 21 Oct 2020 16:58:57 -0700 Subject: [PATCH] ACN Process Management (#345) ACN Process Management update connectivity use filepath instead of path for join Co-authored-by: Dan Ballard Reviewed-on: https://git.openprivacy.ca/cwtch.im/ui/pulls/345 --- README.md | 2 +- go.mod | 7 +++---- go.sum | 17 +++++++++++++++ go/os/kill_unix.go | 42 +++++++++++++++++++++++++++++++++++++ go/os/kill_windows.go | 48 +++++++++++++++++++++++++++++++++++++++++++ go/ui/settings.go | 2 ++ main.go | 48 +++++++++++++++++++++++++++++-------------- 7 files changed, 146 insertions(+), 20 deletions(-) create mode 100644 go/os/kill_unix.go create mode 100644 go/os/kill_windows.go diff --git a/README.md b/README.md index c7ca56ba..daf3ef24 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Cwtch - UI - + This codebase provides a graphical user interface for Desktop and Android for [Cwtch: Privacy Preserving Infrastructure for Asynchronous, Decentralized and Metadata Resistant Applications](https://git.openprivacy.ca/cwtch.im/cwtch) # Security diff --git a/go.mod b/go.mod index 6e7a43c2..d66e0dda 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,11 @@ module cwtch.im/ui go 1.12 require ( - cwtch.im/cwtch v0.4.1 - git.openprivacy.ca/openprivacy/connectivity v1.2.2 + cwtch.im/cwtch v0.4.2 + git.openprivacy.ca/openprivacy/connectivity v1.3.1 git.openprivacy.ca/openprivacy/log v1.0.1 github.com/gopherjs/gopherjs v0.0.0-20200209183636-89e6cbcd0b6d // indirect github.com/therecipe/qt v0.0.0-20200126204426-5074eb6d8c41 github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200126204426-5074eb6d8c41 // indirect github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200126204426-5074eb6d8c41 // indirect - golang.org/x/crypto v0.0.0-20200420104511-884d27f42877 // indirect -) \ No newline at end of file +) diff --git a/go.sum b/go.sum index 4c139a30..21f9ac85 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ cwtch.im/cwtch v0.4.0 h1:lhGQiYRBqSF0Pif9QttYVL4B1Oy1vc0v3cZejL7c7x4= cwtch.im/cwtch v0.4.0/go.mod h1:EvZQDbvXNu38m785dWF0MMljqJzwWrNTFT40HvoEAhI= cwtch.im/cwtch v0.4.1 h1:wjf/3Vw5fDByEwwnXqWrPtpKsXTLk0oz0PqNGYcR+MQ= cwtch.im/cwtch v0.4.1/go.mod h1:EvZQDbvXNu38m785dWF0MMljqJzwWrNTFT40HvoEAhI= +cwtch.im/cwtch v0.4.2 h1:qIjTOwKkBf1tIsat27gXPriXSZXtKcTJ8Sf7E/2+GXc= +cwtch.im/cwtch v0.4.2/go.mod h1:10gBkMSqAH95Pz4jTx5mpIHE+dkn+4kRC4BFTxWuQK8= cwtch.im/tapir v0.1.15 h1:XSCWOvjmNkzMT2IceFgTBXWGKtYfr3a8o+La1s10OhE= cwtch.im/tapir v0.1.15/go.mod h1:HzezugpEx+nZ3LdyDsl0w6n45IJYnOt8uqldkLWmaqs= cwtch.im/tapir v0.1.17 h1:2jVZUe1a88tMI4aJPvRTO4Id3NN3PsM62cT5lntEChk= @@ -20,6 +22,8 @@ cwtch.im/tapir v0.1.18 h1:Fs/jL9ZRyel/A1D/BYzIPEVQau8y5BJg44yA+GQDbSM= cwtch.im/tapir v0.1.18/go.mod h1:/IrAI6CBHfgzsfgRT8WHVb1P9fCCz7+45hfsdkKn8Zg= cwtch.im/tapir v0.2.0 h1:7MkoR5+uEuPW34/O0GZRidnIjq/01Cfm8nl5IRuqpGc= cwtch.im/tapir v0.2.0/go.mod h1:xzzZ28adyUXNkYL1YodcHsAiTt3IJ8Loc29YVn9mIEQ= +git.openprivacy.ca/openprivacy/bine v0.0.3 h1:PSHUmNqaW7BZUX8n2eTDeNbjsuRe+t5Ae0Og+P+jDM0= +git.openprivacy.ca/openprivacy/bine v0.0.3/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU= git.openprivacy.ca/openprivacy/connectivity v1.1.0/go.mod h1:4P8mirZZslKbo2zBrXXVjgEdqGwHo/6qoFBwFQW6d6E= git.openprivacy.ca/openprivacy/connectivity v1.1.1 h1:hKxBOmxP7Jdu3K1BJ93mRtKNiWUoP6YHt/o2snE2Z0w= git.openprivacy.ca/openprivacy/connectivity v1.1.1/go.mod h1:4P8mirZZslKbo2zBrXXVjgEdqGwHo/6qoFBwFQW6d6E= @@ -35,6 +39,8 @@ git.openprivacy.ca/openprivacy/connectivity v1.2.1 h1:oRL56TR9ZQnKkGkTIQ9wYbJ2Ik git.openprivacy.ca/openprivacy/connectivity v1.2.1/go.mod h1:B7vzuVmChJtSKoh0ezph5vu6DQ0gIk0zHUNG6IgXCcA= git.openprivacy.ca/openprivacy/connectivity v1.2.2 h1:CeuZB469xHMHxygxZD559CkRUAGR7ct4oeSlsAHQmKo= git.openprivacy.ca/openprivacy/connectivity v1.2.2/go.mod h1:B7vzuVmChJtSKoh0ezph5vu6DQ0gIk0zHUNG6IgXCcA= +git.openprivacy.ca/openprivacy/connectivity v1.3.0 h1:e2EeV6CaMNwOb+PzAjF0hGCeOqAPagRaDL4en5ITf7U= +git.openprivacy.ca/openprivacy/connectivity v1.3.0/go.mod h1:s0/QhONuUqJQfYTAgUlu+ya7G3Ov6bKgpT5QkOhVxDI= git.openprivacy.ca/openprivacy/libricochet-go v1.0.11 h1:C7QFFzG0p5XKu0zcOIdLGwEpA9uU0BceBM7CfVK5D40= git.openprivacy.ca/openprivacy/libricochet-go v1.0.11/go.mod h1:yTMps/ZpYS+BNBBvANsNAft28FXrBvFHQauMYNWPrwE= git.openprivacy.ca/openprivacy/libricochet-go v1.0.13 h1:Z86uL9K47onznY1wP1P/wWfWMbbyvk6xnCp94R180os= @@ -104,6 +110,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/struCoder/pidusage v0.1.3/go.mod h1:pWBlW3YuSwRl6h7R5KbvA4N8oOqe9LjaKW5CwT1SPjI= github.com/therecipe/qt v0.0.0-20191101232336-18864661ae4f h1:06ICDSmDOBUC9jwgv44ngvyHzwudJNLa5H+rbCyDFRY= github.com/therecipe/qt v0.0.0-20191101232336-18864661ae4f/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us= @@ -135,6 +142,9 @@ golang.org/x/crypto v0.0.0-20200320181102-891825fb96df h1:lDWgvUvNnaTnNBc/dwOty8 golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200420104511-884d27f42877 h1:IhZPbxNd1UjBCaD5AfpSSbJTRlp+ZSuyuH5uoksNS04= golang.org/x/crypto v0.0.0-20200420104511-884d27f42877/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -149,6 +159,8 @@ golang.org/x/net v0.0.0-20200320181208-1c781a10960a h1:KaxWXSFrOaE2ptiOotI+zFdzH golang.org/x/net v0.0.0-20200320181208-1c781a10960a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -170,7 +182,11 @@ golang.org/x/sys v0.0.0-20200320181252-af34d8274f85/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190420181800-aa740d480789 h1:FF0rjo15h51+N6642mf5S3QuplmKo2aCrJUYkHTx85s= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -188,3 +204,4 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go/os/kill_unix.go b/go/os/kill_unix.go new file mode 100644 index 00000000..acfaaa5c --- /dev/null +++ b/go/os/kill_unix.go @@ -0,0 +1,42 @@ +// +build unix linux android + +package os + +import ( + "git.openprivacy.ca/openprivacy/log" + "os/exec" + "strconv" + "strings" +) + +// CheckProcessAndKill first executes a process search on the system by the last known pid, if the name of the +// process matches the expected name, then it is killed. +// On unix systems the command "ps" is practically universal and should suffice for this... +func CheckProcessAndKill(pid uint64, processName string) { + log.Debugf("killing: %v", pid) + bytes,err := exec.Command("ps", "-p", strconv.Itoa(int(pid)), "-o", "command").Output() + if err == nil { + // check for a binary @ "/" + if strings.HasSuffix(string(bytes),"/"+processName) { + Kill(pid) + return + } + log.Debugf("pid did not relate to expected process, not killing out of caution") + return + } + // no such process + log.Debugf("no such process %v", pid) +} + +// Kill a process based on pid +func Kill(pid uint64) { + log.Debugf("killing: %v", pid) + bytes,err := exec.Command("kill", strconv.Itoa(int(pid))).Output() + if err == nil { + log.Debugf("kill %v successful: %s", pid, bytes) + } else { + // this might now always succeed + log.Debugf("could not kill pid: %v %v", pid, err) + } + return +} \ No newline at end of file diff --git a/go/os/kill_windows.go b/go/os/kill_windows.go new file mode 100644 index 00000000..6b256512 --- /dev/null +++ b/go/os/kill_windows.go @@ -0,0 +1,48 @@ +// +build windows +package os + +import ( + "git.openprivacy.ca/openprivacy/log" + "os/exec" + "strconv" + "strings" +) + +// CheckProcessAndKill first executes a process search on the system by the last known pid, if the name of the +// process matches the expected name, then it is killed. +// On windows this uses tasklist... +func CheckProcessAndKill(pid uint64, processName string) { + log.Debugf("looking up process: %v", pid) + bytes,err := exec.Command("tasklist", "/fi", "pid eq "+strconv.Itoa(int(pid))).Output() + if err == nil { + // Output will be something like this: + // + // Image Name PID Session Name Session# Mem Usage + // ========================= ======== ================ =========== ============ + // process.exe Services 0 8,936 K + lines := strings.Split(strings.TrimSpace(string(bytes)),"\n") + log.Debugf("%v\n", lines) + + // check for ".exe" + if len(lines) >= 3 && strings.HasPrefix(strings.ToLower(strings.TrimSpace(lines[2])),processName+".exe") { + Kill(pid) + return + } + log.Debugf("pid did not relate to expected process, not attempting process kill out of caution") + return + } + log.Debugf("error checking process: %v", err) +} + +// Kill a process based on pid +func Kill(pid uint64) { + log.Debugf("killing: %v", pid) + bytes,err := exec.Command("taskkill", "/F", "/PID", strconv.Itoa(int(pid))).Output() + if err == nil { + log.Debugf("kill %v successful: %s", pid, bytes) + } else { + // this might now always succeed + log.Debugf("could not kill pid: %v %v", pid, err) + } + return +} \ No newline at end of file diff --git a/go/ui/settings.go b/go/ui/settings.go index 18ffdcfd..7cbb3aca 100644 --- a/go/ui/settings.go +++ b/go/ui/settings.go @@ -19,12 +19,14 @@ type GlobalSettings struct { Zoom float32 Locale string Theme string + PreviousPid int64 } var DefaultGlobalSettings = GlobalSettings{ Zoom: 1.0, Locale: "en", Theme: "light", + PreviousPid: -1, } func InitGlobalSettingsFile(directory string, password string) error { diff --git a/main.go b/main.go index e33ac373..52fab245 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( libapp "cwtch.im/cwtch/app" "cwtch.im/cwtch/event/bridge" "cwtch.im/ui/go/handlers" + os2 "cwtch.im/ui/go/os" "cwtch.im/ui/go/the" "cwtch.im/ui/go/ui" "cwtch.im/ui/go/ui/android" @@ -21,7 +22,6 @@ import ( mrand "math/rand" "os" "os/user" - "path" "path/filepath" "runtime" "time" @@ -74,19 +74,19 @@ func main() { if os.Getenv("CWTCH_FOLDER") != "" { the.CwtchDir = os.Getenv("CWTCH_FOLDER") } else if runtime.GOOS == "android" { - the.CwtchDir = path.Join(androidBaseDir, "files") + the.CwtchDir = filepath.Join(androidBaseDir, "files") } else { usr, err := user.Current() if err != nil { log.Errorf("\nerror: could not load current user: %v\n", err) os.Exit(1) } - the.CwtchDir = path.Join(usr.HomeDir, ".cwtch") + the.CwtchDir = filepath.Join(usr.HomeDir, ".cwtch") } if buildVer == "" && os.Getenv("CWTCH_FOLDER") == "" { log.Infoln("Development build: using dev directory for dev profiles") - the.CwtchDir = path.Join(the.CwtchDir, "dev") + the.CwtchDir = filepath.Join(the.CwtchDir, "dev") } the.ACN = nil @@ -135,7 +135,7 @@ func mainUi(flagLocal bool, flagClientUI bool) { app := gui.NewQGuiApplication(len(os.Args), os.Args) // our globals - err := ui.InitGlobalSettingsFile(path.Join(the.CwtchDir, "global"), the.AppPassword) + err := ui.InitGlobalSettingsFile(filepath.Join(the.CwtchDir, "global"), the.AppPassword) if err != nil { log.Errorf("Could not access global ui config: %v\n", err) os.Exit(-1) @@ -152,7 +152,7 @@ func mainUi(flagLocal bool, flagClientUI bool) { if runtime.GOOS == "windows" { dir = "/" + dir } - gcd.SetAssetPath("file://" + path.Join(dir, "assets") + "/") + gcd.SetAssetPath("file://" + filepath.Join(dir, "assets") + "/") } if buildVer != "" { @@ -215,24 +215,24 @@ func mainUi(flagLocal bool, flagClientUI bool) { func loadACN() { torpath := "tor" if runtime.GOOS == "android" { - torpath = path.Join(androidBaseDir, "lib/libtor.so") + torpath = filepath.Join(androidBaseDir, "lib/libtor.so") } else if runtime.GOOS == "windows" { ex, err := os.Executable() if err != nil { ex = "" } exPath := filepath.Dir(ex) - torpath = path.Join(exPath, "tor-0.3.5.7", "Tor", "tor.exe") + torpath = filepath.Join(exPath, "tor-0.3.5.7", "Tor", "tor.exe") } else { dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) - if _, err := os.Stat(path.Join(dir, "tor")); os.IsNotExist(err) { - if _, err := os.Stat(path.Join(dir, "deploy", "linux", "tor")); os.IsNotExist(err) { + if _, err := os.Stat(filepath.Join(dir, "tor")); os.IsNotExist(err) { + if _, err := os.Stat(filepath.Join(dir, "deploy", "linux", "tor")); os.IsNotExist(err) { log.Warnln("Cannot find bundled Tor") } else { - torpath = path.Join(dir, "deploy", "linux", "tor") + torpath = filepath.Join(dir, "deploy", "linux", "tor") } } else { - torpath = path.Join(dir, "tor") + torpath = filepath.Join(dir, "tor") } } @@ -250,7 +250,7 @@ func loadACN() { // generate torrc on the fly // TODO if we have been configured for it, use system tor (like orbot) - we need a way to config this in the UI first - tor.NewTorrc().WithSocksPort(port).WithOnionTrafficOnly().WithControlPort(controlPort).WithHashedPassword(base64.StdEncoding.EncodeToString(key)).Build(path.Join(the.CwtchDir, "tor", "torrc")) + tor.NewTorrc().WithSocksPort(port).WithOnionTrafficOnly().WithControlPort(controlPort).WithHashedPassword(base64.StdEncoding.EncodeToString(key)).Build(filepath.Join(the.CwtchDir, "tor", "torrc")) the.ACN, err = tor.NewTorACNWithAuth(the.CwtchDir, torpath, controlPort, tor.HashedPasswordAuthenticator{base64.StdEncoding.EncodeToString(key)}) if err != nil { @@ -262,8 +262,8 @@ func loadACN() { func loadNetworkingAndFiles(gcd *ui.GrandCentralDispatcher, service bool, clientUI bool) { if service || clientUI || runtime.GOOS == "android" { - clientIn := path.Join(the.CwtchDir, "clientIn") - serviceIn := path.Join(the.CwtchDir, "serviceIn") + clientIn := filepath.Join(the.CwtchDir, "clientIn") + serviceIn := filepath.Join(the.CwtchDir, "serviceIn") if service { loadACN() serviceBridge := bridge.NewPipeBridgeService(serviceIn, clientIn) @@ -275,9 +275,27 @@ func loadNetworkingAndFiles(gcd *ui.GrandCentralDispatcher, service bool, client the.CwtchApp = libapp.NewAppClient(the.CwtchDir, clientBridge) } } else { + + if gcd.GlobalSettings.PreviousPid != -1 { + // Todo: Check if there is an older version of the ACN running... + log.Debugf("checking to see if we shutdown the previous ACN (%v)", gcd.GlobalSettings.PreviousPid) + + os2.CheckProcessAndKill(uint64(gcd.GlobalSettings.PreviousPid), "tor") + } + loadACN() + pid,err := the.ACN.GetPID() + if err == nil { + gcd.GlobalSettings.PreviousPid = int64(pid) + ui.WriteGlobalSettings(gcd.GlobalSettings) + + } else { + log.Errorf("error fetching pid from ACN %v", err) + } + log.Infoln("Creating New App") the.CwtchApp = libapp.NewApp(the.ACN, the.CwtchDir) + } if !service {