From 6e583d11f5bf1c60155eb76c08ccb399aa7608ac Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Tue, 1 Nov 2016 13:45:55 +0900 Subject: [PATCH 01/10] send CTRL-C to mackerel-agent --- wix/wrapper/wrapper_windows.go | 43 +++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/wix/wrapper/wrapper_windows.go b/wix/wrapper/wrapper_windows.go index 26c2a95bf..a3c1db914 100644 --- a/wix/wrapper/wrapper_windows.go +++ b/wix/wrapper/wrapper_windows.go @@ -4,10 +4,12 @@ import ( "bufio" "io" "log" + "os" "os/exec" "path/filepath" "regexp" "syscall" + "time" "unsafe" "golang.org/x/sys/windows/svc" @@ -21,6 +23,13 @@ const startEid = 2 const stopEid = 3 const loggerEid = 4 +var ( + kernel32 = syscall.NewLazyDLL("kernel32") + procAllocConsole = kernel32.NewProc("AllocConsole") + procGenerateConsoleCtrlEvent = kernel32.NewProc("GenerateConsoleCtrlEvent") + procGetModuleFileName = kernel32.NewProc("GetModuleFileNameW") +) + func main() { elog, err := eventlog.Open(name) if err != nil { @@ -42,7 +51,15 @@ type handler struct { } func (h *handler) start() error { - cmd := exec.Command(filepath.Join(filepath.Dir(execdir()), "mackerel-agent.exe")) + procAllocConsole.Call() + dir := execdir() + cmd := exec.Command(filepath.Join(dir, "mackerel-agent.exe")) + cmd.SysProcAttr = &syscall.SysProcAttr{ + CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, + } + cmd.Dir = dir + cmd.Stdin = os.Stdin + h.cmd = cmd r, w := io.Pipe() cmd.Stderr = w @@ -78,8 +95,26 @@ func (h *handler) start() error { return cmd.Start() } +func interrupt(p *os.Process) error { + r1, _, err := procGenerateConsoleCtrlEvent.Call(syscall.CTRL_BREAK_EVENT, uintptr(p.Pid)) + if r1 == 0 { + return err + } + return nil +} + func (h *handler) stop() error { if h.cmd != nil && h.cmd.Process != nil { + err := interrupt(h.cmd.Process) + if err == nil { + end := time.Now().Add(10 * time.Second) + for time.Now().Before(end) { + if h.cmd.ProcessState != nil && h.cmd.ProcessState.Exited() { + return nil + } + time.Sleep(1 * time.Second) + } + } return h.cmd.Process.Kill() } return nil @@ -133,14 +168,10 @@ L: } func execdir() string { - var ( - kernel32 = syscall.NewLazyDLL("kernel32") - procGetModuleFileName = kernel32.NewProc("GetModuleFileNameW") - ) var wpath [syscall.MAX_PATH]uint16 r1, _, err := procGetModuleFileName.Call(0, uintptr(unsafe.Pointer(&wpath[0])), uintptr(len(wpath))) if r1 == 0 { log.Fatal(err) } - return syscall.UTF16ToString(wpath[:]) + return filepath.Dir(syscall.UTF16ToString(wpath[:])) } From 32b2cec8918bb208ab94131e5dbe5cc70b120945 Mon Sep 17 00:00:00 2001 From: daiksy Date: Wed, 2 Nov 2016 17:45:05 +0900 Subject: [PATCH 02/10] add circle.yml --- circle.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 circle.yml diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000..f5ae96e33 --- /dev/null +++ b/circle.yml @@ -0,0 +1,11 @@ +machine: + timezone: + Asia/Tokyo + +test: + override: + - make lint + - make cover + - test `gofmt -l . | wc -l` = 0 + post: + - goveralls -coverprofile=.profile.cov From 7e814ee3ce7f1c8c636e189d6efb5cfdca67b0b1 Mon Sep 17 00:00:00 2001 From: daiksy Date: Wed, 2 Nov 2016 18:27:41 +0900 Subject: [PATCH 03/10] skip some tests on CircleCI --- spec/linux/interface_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/linux/interface_test.go b/spec/linux/interface_test.go index c102a256f..ee3ce446d 100644 --- a/spec/linux/interface_test.go +++ b/spec/linux/interface_test.go @@ -25,6 +25,9 @@ func TestInterfaceGenerate(t *testing.T) { if os.Getenv("TRAVIS") != "" { t.Skip("Skip in Travis for now") } + if os.Getenv("CIRCLECI") != "" { + t.Skip("Skip in CircleCI for now") + } if len(value) == 0 { t.Error("should have at least 1 interface") @@ -60,6 +63,9 @@ func TestGenerateByIpCommand(t *testing.T) { if os.Getenv("TRAVIS") != "" { t.Skip("Skip in Travis for now") } + if os.Getenv("CIRCLECI") != "" { + t.Skip("Skip in CircleCI for now") + } name := "eth0" if _, ok := interfaces[name]; !ok { From e008a73835a2993c12da16146116a1474ec9343c Mon Sep 17 00:00:00 2001 From: daiksy Date: Wed, 2 Nov 2016 18:45:15 +0900 Subject: [PATCH 04/10] remove goveralls --- circle.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/circle.yml b/circle.yml index f5ae96e33..cdc6d4622 100644 --- a/circle.yml +++ b/circle.yml @@ -7,5 +7,3 @@ test: - make lint - make cover - test `gofmt -l . | wc -l` = 0 - post: - - goveralls -coverprofile=.profile.cov From c37f7c7443d6bfe2400d432cfa9b436574b335a9 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 24 Nov 2016 16:48:05 +0900 Subject: [PATCH 05/10] add service installer/uninstaller --- wix/wrapper/install.go | 92 ++++++++++++++++++++++++++++++++++ wix/wrapper/wrapper_windows.go | 15 ++++++ 2 files changed, 107 insertions(+) create mode 100644 wix/wrapper/install.go diff --git a/wix/wrapper/install.go b/wix/wrapper/install.go new file mode 100644 index 000000000..39cb00d2a --- /dev/null +++ b/wix/wrapper/install.go @@ -0,0 +1,92 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package main + +import ( + "fmt" + "os" + "path/filepath" + + "golang.org/x/sys/windows/svc/eventlog" + "golang.org/x/sys/windows/svc/mgr" +) + +func exePath() (string, error) { + prog := os.Args[0] + p, err := filepath.Abs(prog) + if err != nil { + return "", err + } + fi, err := os.Stat(p) + if err == nil { + if !fi.Mode().IsDir() { + return p, nil + } + err = fmt.Errorf("%s is directory", p) + } + if filepath.Ext(p) == "" { + p += ".exe" + fi, err := os.Stat(p) + if err == nil { + if !fi.Mode().IsDir() { + return p, nil + } + err = fmt.Errorf("%s is directory", p) + } + } + return "", err +} + +func installService(name, desc string) error { + exepath, err := exePath() + if err != nil { + return err + } + m, err := mgr.Connect() + if err != nil { + return err + } + defer m.Disconnect() + s, err := m.OpenService(name) + if err == nil { + s.Close() + return fmt.Errorf("service %s already exists", name) + } + s, err = m.CreateService(name, exepath, mgr.Config{DisplayName: desc}, "is", "auto-started") + if err != nil { + return err + } + defer s.Close() + err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info) + if err != nil { + s.Delete() + return fmt.Errorf("SetupEventLogSource() failed: %s", err) + } + return nil +} + +func removeService(name string) error { + m, err := mgr.Connect() + if err != nil { + return err + } + defer m.Disconnect() + s, err := m.OpenService(name) + if err != nil { + return fmt.Errorf("service %s is not installed", name) + } + defer s.Close() + err = s.Delete() + if err != nil { + return err + } + err = eventlog.Remove(name) + if err != nil { + return fmt.Errorf("RemoveEventLogSource() failed: %s", err) + } + return nil +} diff --git a/wix/wrapper/wrapper_windows.go b/wix/wrapper/wrapper_windows.go index 26c2a95bf..492235de6 100644 --- a/wix/wrapper/wrapper_windows.go +++ b/wix/wrapper/wrapper_windows.go @@ -4,6 +4,7 @@ import ( "bufio" "io" "log" + "os" "os/exec" "path/filepath" "regexp" @@ -22,6 +23,20 @@ const stopEid = 3 const loggerEid = 4 func main() { + if len(os.Args) == 2 { + var err error + switch os.Args[1] { + case "install": + err = installService("mackerel-agent", "mackarel agent") + case "remove": + err = removeService("mackerel-agent") + } + if err != nil { + log.Fatal(err) + } + return + } + elog, err := eventlog.Open(name) if err != nil { log.Fatal(err.Error()) From 826f84618be752d45319f1a81c919a81179c19c8 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 24 Nov 2016 17:07:44 +0900 Subject: [PATCH 06/10] fix typo --- wix/wrapper/wrapper_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wix/wrapper/wrapper_windows.go b/wix/wrapper/wrapper_windows.go index 492235de6..767f979a0 100644 --- a/wix/wrapper/wrapper_windows.go +++ b/wix/wrapper/wrapper_windows.go @@ -27,7 +27,7 @@ func main() { var err error switch os.Args[1] { case "install": - err = installService("mackerel-agent", "mackarel agent") + err = installService("mackerel-agent", "mackerel agent") case "remove": err = removeService("mackerel-agent") } From e7a644c74ae9226b661bc99c9649a1daaf65affc Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 2 Dec 2016 11:35:24 +0900 Subject: [PATCH 07/10] do not set os.Stdin not required --- wix/wrapper/wrapper_windows.go | 1 - 1 file changed, 1 deletion(-) diff --git a/wix/wrapper/wrapper_windows.go b/wix/wrapper/wrapper_windows.go index a3c1db914..71b0c518b 100644 --- a/wix/wrapper/wrapper_windows.go +++ b/wix/wrapper/wrapper_windows.go @@ -58,7 +58,6 @@ func (h *handler) start() error { CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, } cmd.Dir = dir - cmd.Stdin = os.Stdin h.cmd = cmd r, w := io.Pipe() From 434b1f59d49d71258b20103ed65dfe3af1f5630e Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Tue, 6 Dec 2016 01:14:28 +0900 Subject: [PATCH 08/10] write comment simply. --- wix/wrapper/install.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/wix/wrapper/install.go b/wix/wrapper/install.go index 39cb00d2a..df04dddd8 100644 --- a/wix/wrapper/install.go +++ b/wix/wrapper/install.go @@ -1,6 +1,4 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +// This is based on https://github.com/golang/sys/tree/master/windows/svc // +build windows From 8a02460c8782e425a347428e07af9191b95873a4 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 7 Dec 2016 00:56:00 +0900 Subject: [PATCH 09/10] put current directory first on PATH environment Fixes #15 --- init_windows.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 init_windows.go diff --git a/init_windows.go b/init_windows.go new file mode 100644 index 000000000..2c1c2f9c6 --- /dev/null +++ b/init_windows.go @@ -0,0 +1,31 @@ +// +build windows + +package main + +import ( + "os" + "path/filepath" + "syscall" + "unsafe" +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32") + procGetModuleFileName = kernel32.NewProc("GetModuleFileNameW") +) + +func getModuleFileName() (string, error) { + var p [syscall.MAX_PATH]uint16 + result, _, err := procGetModuleFileName.Call(0, uintptr(unsafe.Pointer(&p[0])), uintptr(len(p))) + if result == 0 { + return os.Args[0], err + } + return syscall.UTF16ToString(p[:]), nil +} + +func init() { + if p, err := getModuleFileName(); err == nil { + os.Setenv("PATH", filepath.Dir(p)+ + string(filepath.ListSeparator)+os.Getenv("PATH")) + } +} From 78f68eedd3b7623d73aaafdf78287a543663016b Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 7 Dec 2016 18:01:57 +0900 Subject: [PATCH 10/10] fix build.bat wix/wrapper/install.go is not compiled. --- wix/build.bat | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wix/build.bat b/wix/build.bat index 3245fcc3f..c0fba8b5d 100755 --- a/wix/build.bat +++ b/wix/build.bat @@ -25,7 +25,8 @@ go get github.com/mackerelio/mackerel-agent/wix/wrapper go get github.com/mackerelio/mackerel-agent/wix/replace go get github.com/mackerelio/mackerel-agent/wix/generate_wxs -go build -o ..\build\wrapper.exe wrapper\wrapper_windows.go +go build -o ..\build\wrapper.exe wrapper\wrapper_windows.go wrapper\install.go + go build -o ..\build\replace.exe replace\replace_windows.go go build -o ..\build\generate_wxs.exe generate_wxs\generate_wxs.go