Skip to content

Commit

Permalink
Merge pull request #298 from mackerelio/develop
Browse files Browse the repository at this point in the history
several improvements for Windows
  • Loading branch information
daiksy committed Dec 20, 2016
2 parents fe0986c + fe6e893 commit 208ec27
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 7 deletions.
9 changes: 9 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
machine:
timezone:
Asia/Tokyo

test:
override:
- make lint
- make cover
- test `gofmt -l . | wc -l` = 0
31 changes: 31 additions & 0 deletions init_windows.go
Original file line number Diff line number Diff line change
@@ -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"))
}
}
8 changes: 8 additions & 0 deletions spec/linux/interface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ func TestInterfaceGenerate(t *testing.T) {
t.Errorf("should not raise error: %v", err)
}

if os.Getenv("CIRCLECI") != "" {
t.Skip("Skip in CircleCI for now")
}

if len(value) == 0 {
t.Error("should have at least 1 interface")
return
Expand Down Expand Up @@ -53,6 +57,10 @@ func TestGenerateByIpCommand(t *testing.T) {
t.Errorf("should not raise error: %v", err)
}

if os.Getenv("CIRCLECI") != "" {
t.Skip("Skip in CircleCI for now")
}

name := "eth0"
if _, ok := interfaces[name]; !ok {
t.Error("should have interfaces")
Expand Down
3 changes: 2 additions & 1 deletion wix/build.bat
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
90 changes: 90 additions & 0 deletions wix/wrapper/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// This is based on https://github.com/golang/sys/tree/master/windows/svc

// +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
}
56 changes: 50 additions & 6 deletions wix/wrapper/wrapper_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import (
"bufio"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"syscall"
"time"
"unsafe"

"golang.org/x/sys/windows/svc"
Expand All @@ -21,7 +23,28 @@ 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() {
if len(os.Args) == 2 {
var err error
switch os.Args[1] {
case "install":
err = installService("mackerel-agent", "mackerel 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())
Expand All @@ -42,7 +65,14 @@ 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

h.cmd = cmd
r, w := io.Pipe()
cmd.Stderr = w
Expand Down Expand Up @@ -78,8 +108,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
Expand Down Expand Up @@ -133,14 +181,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[:]))
}

0 comments on commit 208ec27

Please sign in to comment.