Commit 2a5f78f6 authored by Dave Collins's avatar Dave Collins
Browse files

Initial fork from http://code.google.com/p/winsvc/.

- Update all paths to github.com/conformal/winsvc
- Add README.md
- Add .gitignore
parents
# Temp files
*~
# Log files
*.log
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at http://tip.golang.org/AUTHORS.
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at http://tip.golang.org/CONTRIBUTORS.
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
winsvc
======
Package winsvc is simply a fork from http://code.google.com/p/winsvc. As
can be guessed from the name, it only works on Windows.
## Documentation
Full `go doc` style documentation for the project can be viewed online without
installing this package by using the GoDoc site here:
http://godoc.org/github.com/conformal/winsvc
You can also view the documentation locally once the package is installed with
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
http://localhost:6060/pkg/github.com/conformal/winsvc
## Installation
```bash
$ go get github.com/conformal/winsvc
```
## License
See the LICENSE file.
// 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.
package debug
import (
"os"
"strconv"
)
// Log allows different log implementations to be used.
type Log interface {
Close() error
Info(eid uint32, msg string) error
Warning(eid uint32, msg string) error
Error(eid uint32, msg string) error
}
// ConsoleLog provides access to the console.
type ConsoleLog struct {
Name string
}
// New creates new ConsoleLog.
func New(source string) *ConsoleLog {
return &ConsoleLog{Name: source}
}
// Close closes console log l.
func (l *ConsoleLog) Close() error {
return nil
}
func (l *ConsoleLog) report(kind string, eid uint32, msg string) error {
s := l.Name + "." + kind + "(" + strconv.Itoa(int(eid)) + "): " + msg + "\n"
_, err := os.Stdout.Write([]byte(s))
return err
}
// Info writes an information event msg with event id eid to the console l.
func (l *ConsoleLog) Info(eid uint32, msg string) error {
return l.report("info", eid, msg)
}
// Warning writes an warning event msg with event id eid to the console l.
func (l *ConsoleLog) Warning(eid uint32, msg string) error {
return l.report("warn", eid, msg)
}
// Error writes an error event msg with event id eid to the console l.
func (l *ConsoleLog) Error(eid uint32, msg string) error {
return l.report("error", eid, msg)
}
// 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.
// Package debug provides facilities to execute svc.Handler on console.
//
package debug
import (
"github.com/conformal/winsvc/svc"
"os"
"os/signal"
"syscall"
)
// Run executes service named name by calling appropriate handler function.
// The process is running on console, unlike real service. Use Ctrl+C to
// send "Stop" command to your service.
func Run(name string, handler svc.Handler) error {
cmds := make(chan svc.ChangeRequest)
changes := make(chan svc.Status)
sig := make(chan os.Signal)
signal.Notify(sig)
go func() {
status := svc.Status{State: svc.Stopped}
for {
select {
case <-sig:
cmds <- svc.ChangeRequest{svc.Stop, status}
case status = <-changes:
}
}
}()
_, errno := handler.Execute([]string{name}, cmds, changes)
if errno != 0 {
return syscall.Errno(errno)
}
return nil
}
// 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.
package eventlog
import (
"github.com/conformal/winsvc/registry"
"github.com/conformal/winsvc/winapi"
"errors"
"syscall"
)
const (
// Log levels.
Info = winapi.EVENTLOG_INFORMATION_TYPE
Warning = winapi.EVENTLOG_WARNING_TYPE
Error = winapi.EVENTLOG_ERROR_TYPE
)
const addKeyName = `SYSTEM\CurrentControlSet\Services\EventLog\Application`
// Install modifies PC registry to allow logging with event source src.
// It adds all required keys/values to event log key. Install uses msgFile
// as event message file. Use bitwise of log.Error, log.Warning and log.Info
// to specify events supported.
func Install(src, msgFile string, eventsSupported uint32) error {
appkey, err := registry.OpenKey(syscall.HKEY_LOCAL_MACHINE, addKeyName)
if err != nil {
return err
}
defer appkey.Close()
sk, alreadyExist, err := appkey.CreateSubKey(src)
if err != nil {
return err
}
defer sk.Close()
if alreadyExist {
return errors.New(addKeyName + `\` + src + " registry key already exists")
}
err = sk.SetUInt32("CustomSource", 1)
if err != nil {
return err
}
err = sk.SetString("EventMessageFile", msgFile)
if err != nil {
return err
}
err = sk.SetUInt32("TypesSupported", eventsSupported)
if err != nil {
return err
}
return nil
}
// InstallAsEventCreate is the same as Install, but uses
// %SystemRoot%\System32\EventCreate.exe as event message file.
func InstallAsEventCreate(src string, eventsSupported uint32) error {
return Install(src, "%SystemRoot%\\System32\\EventCreate.exe", eventsSupported)
}
// Remove deletes all registry elements installed by correspondent Install.
func Remove(src string) error {
appkey, err := registry.OpenKey(syscall.HKEY_LOCAL_MACHINE, addKeyName)
if err != nil {
return err
}
defer appkey.Close()
return appkey.DeleteSubKey(src)
}
// 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.
// Package eventlog implements access to Windows event log.
//
package eventlog
import (
"github.com/conformal/winsvc/winapi"
"errors"
"syscall"
)
// Log provides access to system log.
type Log struct {
Handle syscall.Handle
}
// Open retrieves a handle to the specified event log.
func Open(source string) (*Log, error) {
return OpenRemote("", source)
}
// OpenRemote does the same as Open, but on different computer host.
func OpenRemote(host, source string) (*Log, error) {
if source == "" {
return nil, errors.New("Specify event log source")
}
var s *uint16
if host != "" {
s = syscall.StringToUTF16Ptr(host)
}
h, err := winapi.RegisterEventSource(s, syscall.StringToUTF16Ptr(source))
if err != nil {
return nil, err
}
return &Log{Handle: h}, nil
}
// Close closes event log l.
func (l *Log) Close() error {
return winapi.DeregisterEventSource(l.Handle)
}
func (l *Log) report(etype uint16, eid uint32, msg string) error {
ss := []*uint16{syscall.StringToUTF16Ptr(msg)}
return winapi.ReportEvent(l.Handle, etype, 0, eid, 0, 1, 0, &ss[0], nil)
}
// Info writes an information event msg with event id eid to the end of event log l.
// eid must be between 1 and 1000 if using EventCreate.exe as event message file.
func (l *Log) Info(eid uint32, msg string) error {
return l.report(winapi.EVENTLOG_INFORMATION_TYPE, eid, msg)
}
// Warning writes an warning event msg with event id eid to the end of event log l.
// eid must be between 1 and 1000 if using EventCreate.exe as event message file.
func (l *Log) Warning(eid uint32, msg string) error {
return l.report(winapi.EVENTLOG_WARNING_TYPE, eid, msg)
}
// Error writes an error event msg with event id eid to the end of event log l.
// eid must be between 1 and 1000 if using EventCreate.exe as event message file.
func (l *Log) Error(eid uint32, msg string) error {
return l.report(winapi.EVENTLOG_ERROR_TYPE, eid, msg)
}
// 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.
package eventlog_test
import (
"github.com/conformal/winsvc/eventlog"
"testing"
)
func TestLog(t *testing.T) {
const name = "mylog"
const supports = eventlog.Error | eventlog.Warning | eventlog.Info
err := eventlog.InstallAsEventCreate(name, supports)
if err != nil {
t.Fatalf("Install failed: %s", err)
}
l, err := eventlog.Open(name)
if err != nil {
t.Fatalf("Open failed: %s", err)
}
defer l.Close()
err = l.Info(1, "info")
if err != nil {
t.Fatalf("Info failed: %s", err)
}
err = l.Warning(2, "warning")
if err != nil {
t.Fatalf("Warning failed: %s", err)
}
err = l.Error(3, "error")
if err != nil {
t.Fatalf("Error failed: %s", err)
}
err = eventlog.Remove(name)
if err != nil {
t.Fatalf("Remove failed: %s", err)
}
}
// 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.
package main
import (
"syscall"
)
// BUG(brainman): MessageBeep Windows api is broken on Windows 7,
// so this example does not beep when runs as service on Windows 7.
var (
beepFunc = syscall.MustLoadDLL("user32.dll").MustFindProc("MessageBeep")
)
func beep() {
beepFunc.Call(0xffffffff)
}
// 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.
package main
import (
"github.com/conformal/winsvc/eventlog"
"github.com/conformal/winsvc/mgr"
"fmt"
"os"
"path/filepath"
)
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})
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
}
// 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.
// Example service program that beeps. It demonstrates how to
// create a service and install / remove it on a computer.
// It also shows how to stop / start / pause / continue any service,
// and how to write to event log. It also shows how to use debug
// facilities available in debug package.
//
package main
import (
"github.com/conformal/winsvc/svc"
"fmt"
"log"
"os"
"strings"
)
func usage(errmsg string) {
fmt.Fprintf(os.Stderr,
"%s\n\n"+
"usage: %s <command>\n"+
" where <command> is one of\n"+
" install, remove, debug, start, stop, pause or continue.\n",
errmsg, os.Args[0])
os.Exit(2)
}
func main() {
const svcName = "myservice"
isIntSess, err := svc.IsAnInteractiveSession()
if err != nil {
log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
}
if !isIntSess {
runService(svcName, false)
return
}
if len(os.Args) < 2 {
usage("no command specified")
}
cmd := strings.ToLower(os.Args[1])
switch cmd {
case "debug":
runService(svcName, true)
return
case "install":
err = installService(svcName, "my service")
case "remove":
err = removeService(svcName)
case "start":
err = startService(svcName)
case "stop":
err = controlService(svcName, svc.Stop, svc.Stopped)
case "pause":
err = controlService(svcName, svc.Pause, svc.Paused)
case "continue":
err = controlService(svcName, svc.Continue, svc.Running)
default:
usage(fmt.Sprintf("invalid command %s", cmd))
}
if err != nil {
log.Fatalf("failed to %s %s: %v", cmd, svcName, err)
}
return
}
// 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.
package main
import (
"github.com/conformal/winsvc/mgr"
"github.com/conformal/winsvc/svc"
"fmt"
"time"
)
func startService(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("could not access service: %v", err)
}
defer s.Close()
err = s.Start([]string{"p1", "p2", "p3"})
if err != nil {
return fmt.Errorf("could not start service: %v", err)
}
return nil
}
func controlService(name string, c svc.Cmd, to svc.State) error {
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
s, err := m.OpenService(name)
if err != nil {
return fmt.Errorf("could not access service: %v", err)
}
defer s.Close()
status, err := s.Control(c)
if err != nil {
return fmt.Errorf("could not send control=%d: %v", c, err)
}
timeout := time.Now().Add(10 * time.Second)
for status.State != to {
if timeout.Before(time.Now()) {
return fmt.Errorf("timeout waiting for service to go to state=%d", to)
}
time.Sleep(300 * time.Millisecond)
status, err = s.Query()
if err != nil {
return fmt.Errorf("could not retrieve service status: %v", err)
}
}
return nil
}
// 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.
package main
import (
"github.com/conformal/winsvc/debug"
"github.com/conformal/winsvc/eventlog"
"github.com/conformal/winsvc/svc"
"fmt"