aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristine Dodrill <me@christine.website>2017-04-14 02:12:41 -0700
committerChristine Dodrill <me@christine.website>2017-04-14 02:12:41 -0700
commit729bbe26deac8975f2b31e4c3ddaae938f078fa5 (patch)
treeb06fd01b1a088373c43d5878fe963a024dad6272
parentb48fc20b2d3792cb9030cd3e8d6b0b4c34d1c97b (diff)
downloadx-729bbe26deac8975f2b31e4c3ddaae938f078fa5.tar.xz
x-729bbe26deac8975f2b31e4c3ddaae938f078fa5.zip
add govendor and gops support
-rw-r--r--irc/kcpd/gopreload.go9
-rw-r--r--irc/kcpd/gops.go13
-rw-r--r--irc/kcpd/vendor-log5
-rw-r--r--irc/kcpd/vendor/github.com/Xe/gopreload/doc.go7
-rw-r--r--irc/kcpd/vendor/github.com/Xe/gopreload/preload.go26
-rw-r--r--irc/kcpd/vendor/github.com/google/gops/agent/agent.go237
-rw-r--r--irc/kcpd/vendor/github.com/google/gops/internal/internal.go52
-rw-r--r--irc/kcpd/vendor/github.com/google/gops/signal/signal.go35
-rw-r--r--irc/kcpd/vendor/github.com/kardianos/osext/osext.go33
-rw-r--r--irc/kcpd/vendor/github.com/kardianos/osext/osext_plan9.go20
-rw-r--r--irc/kcpd/vendor/github.com/kardianos/osext/osext_procfs.go36
-rw-r--r--irc/kcpd/vendor/github.com/kardianos/osext/osext_sysctl.go126
-rw-r--r--irc/kcpd/vendor/github.com/kardianos/osext/osext_windows.go34
13 files changed, 633 insertions, 0 deletions
diff --git a/irc/kcpd/gopreload.go b/irc/kcpd/gopreload.go
new file mode 100644
index 0000000..6829ae5
--- /dev/null
+++ b/irc/kcpd/gopreload.go
@@ -0,0 +1,9 @@
+// gopreload.go
+package main
+
+/*
+ This file is separate to make it very easy to both add into an application, but
+ also very easy to remove.
+*/
+
+import _ "github.com/Xe/gopreload"
diff --git a/irc/kcpd/gops.go b/irc/kcpd/gops.go
new file mode 100644
index 0000000..184b656
--- /dev/null
+++ b/irc/kcpd/gops.go
@@ -0,0 +1,13 @@
+package main
+
+import (
+ "log"
+
+ "github.com/google/gops/agent"
+)
+
+func init() {
+ if err := agent.Listen(nil); err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/irc/kcpd/vendor-log b/irc/kcpd/vendor-log
index 7746125..190fa7e 100644
--- a/irc/kcpd/vendor-log
+++ b/irc/kcpd/vendor-log
@@ -19,3 +19,8 @@ ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d golang.org/x/net/internal/iana
ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d golang.org/x/net/internal/netreflect
ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d golang.org/x/net/ipv4
9f9df34309c04878acc86042b16630b0f696e1de (dirty) gopkg.in/yaml.v1
+a00a8beb369cafd88bb7b32f31fc4ff3219c3565 github.com/Xe/gopreload
+62f833fc9f6c4d3223bdb37bd0c2f8951bed8596 github.com/google/gops/agent
+62f833fc9f6c4d3223bdb37bd0c2f8951bed8596 github.com/google/gops/internal
+62f833fc9f6c4d3223bdb37bd0c2f8951bed8596 github.com/google/gops/signal
+c2c54e542fb797ad986b31721e1baedf214ca413 github.com/kardianos/osext
diff --git a/irc/kcpd/vendor/github.com/Xe/gopreload/doc.go b/irc/kcpd/vendor/github.com/Xe/gopreload/doc.go
new file mode 100644
index 0000000..720c5c1
--- /dev/null
+++ b/irc/kcpd/vendor/github.com/Xe/gopreload/doc.go
@@ -0,0 +1,7 @@
+/*
+Package gopreload is a bit of a hack to emulate the behavior of LD_PRELOAD [ld-preload].
+This allows you to have automatically starting instrumentation, etc.
+
+[ld-preload]: http://man7.org/linux/man-pages/man8/ld.so.8.html (see LD_PRELOAD section)
+*/
+package gopreload
diff --git a/irc/kcpd/vendor/github.com/Xe/gopreload/preload.go b/irc/kcpd/vendor/github.com/Xe/gopreload/preload.go
new file mode 100644
index 0000000..1b5a0c9
--- /dev/null
+++ b/irc/kcpd/vendor/github.com/Xe/gopreload/preload.go
@@ -0,0 +1,26 @@
+//+build linux,go1.8
+
+package gopreload
+
+import (
+ "log"
+ "os"
+ "plugin"
+ "strings"
+)
+
+func init() {
+ gpv := os.Getenv("GO_PRELOAD")
+ if gpv == "" {
+ return
+ }
+
+ for _, elem := range strings.Split(gpv, ",") {
+ log.Printf("gopreload: trying to open: %s", elem)
+ _, err := plugin.Open(elem)
+ if err != nil {
+ log.Printf("%v from GO_PRELOAD cannot be loaded: %v", elem, err)
+ continue
+ }
+ }
+}
diff --git a/irc/kcpd/vendor/github.com/google/gops/agent/agent.go b/irc/kcpd/vendor/github.com/google/gops/agent/agent.go
new file mode 100644
index 0000000..5708391
--- /dev/null
+++ b/irc/kcpd/vendor/github.com/google/gops/agent/agent.go
@@ -0,0 +1,237 @@
+// Copyright 2016 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 agent provides hooks programs can register to retrieve
+// diagnostics data by using gops.
+package agent
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "os"
+ gosignal "os/signal"
+ "runtime"
+ "runtime/pprof"
+ "runtime/trace"
+ "strconv"
+ "sync"
+ "time"
+
+ "bufio"
+
+ "github.com/google/gops/internal"
+ "github.com/google/gops/signal"
+ "github.com/kardianos/osext"
+)
+
+const defaultAddr = "127.0.0.1:0"
+
+var (
+ mu sync.Mutex
+ portfile string
+ listener net.Listener
+
+ units = []string{" bytes", "KB", "MB", "GB", "TB", "PB"}
+)
+
+// Options allows configuring the started agent.
+type Options struct {
+ // Addr is the host:port the agent will be listening at.
+ // Optional.
+ Addr string
+
+ // NoShutdownCleanup tells the agent not to automatically cleanup
+ // resources if the running process receives an interrupt.
+ // Optional.
+ NoShutdownCleanup bool
+}
+
+// Listen starts the gops agent on a host process. Once agent started, users
+// can use the advanced gops features. The agent will listen to Interrupt
+// signals and exit the process, if you need to perform further work on the
+// Interrupt signal use the options parameter to configure the agent
+// accordingly.
+//
+// Note: The agent exposes an endpoint via a TCP connection that can be used by
+// any program on the system. Review your security requirements before starting
+// the agent.
+func Listen(opts *Options) error {
+ mu.Lock()
+ defer mu.Unlock()
+
+ if opts == nil {
+ opts = &Options{}
+ }
+ if portfile != "" {
+ return fmt.Errorf("gops: agent already listening at: %v", listener.Addr())
+ }
+
+ gopsdir, err := internal.ConfigDir()
+ if err != nil {
+ return err
+ }
+ err = os.MkdirAll(gopsdir, os.ModePerm)
+ if err != nil {
+ return err
+ }
+ if !opts.NoShutdownCleanup {
+ gracefulShutdown()
+ }
+
+ addr := opts.Addr
+ if addr == "" {
+ addr = defaultAddr
+ }
+ ln, err := net.Listen("tcp", addr)
+ if err != nil {
+ return err
+ }
+ listener = ln
+ port := listener.Addr().(*net.TCPAddr).Port
+ portfile = fmt.Sprintf("%s/%d", gopsdir, os.Getpid())
+ err = ioutil.WriteFile(portfile, []byte(strconv.Itoa(port)), os.ModePerm)
+ if err != nil {
+ return err
+ }
+
+ go listen()
+ return nil
+}
+
+func listen() {
+ buf := make([]byte, 1)
+ for {
+ fd, err := listener.Accept()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "gops: %v", err)
+ if netErr, ok := err.(net.Error); ok && !netErr.Temporary() {
+ break
+ }
+ continue
+ }
+ if _, err := fd.Read(buf); err != nil {
+ fmt.Fprintf(os.Stderr, "gops: %v", err)
+ continue
+ }
+ if err := handle(fd, buf); err != nil {
+ fmt.Fprintf(os.Stderr, "gops: %v", err)
+ continue
+ }
+ fd.Close()
+ }
+}
+
+func gracefulShutdown() {
+ c := make(chan os.Signal, 1)
+ gosignal.Notify(c, os.Interrupt)
+ go func() {
+ // cleanup the socket on shutdown.
+ <-c
+ Close()
+ os.Exit(1)
+ }()
+}
+
+// Close closes the agent, removing temporary files and closing the TCP listener.
+// If no agent is listening, Close does nothing.
+func Close() {
+ mu.Lock()
+ defer mu.Unlock()
+
+ if portfile != "" {
+ os.Remove(portfile)
+ portfile = ""
+ }
+ if listener != nil {
+ listener.Close()
+ }
+}
+
+func formatBytes(val uint64) string {
+ var i int
+ var target uint64
+ for i = range units {
+ target = 1 << uint(10*(i+1))
+ if val < target {
+ break
+ }
+ }
+ if i > 0 {
+ return fmt.Sprintf("%0.2f%s (%d bytes)", float64(val)/(float64(target)/1024), units[i], val)
+ }
+ return fmt.Sprintf("%d bytes", val)
+}
+
+func handle(conn io.Writer, msg []byte) error {
+ switch msg[0] {
+ case signal.StackTrace:
+ return pprof.Lookup("goroutine").WriteTo(conn, 2)
+ case signal.GC:
+ runtime.GC()
+ _, err := conn.Write([]byte("ok"))
+ return err
+ case signal.MemStats:
+ var s runtime.MemStats
+ runtime.ReadMemStats(&s)
+ fmt.Fprintf(conn, "alloc: %v\n", formatBytes(s.Alloc))
+ fmt.Fprintf(conn, "total-alloc: %v\n", formatBytes(s.TotalAlloc))
+ fmt.Fprintf(conn, "sys: %v\n", formatBytes(s.Sys))
+ fmt.Fprintf(conn, "lookups: %v\n", s.Lookups)
+ fmt.Fprintf(conn, "mallocs: %v\n", s.Mallocs)
+ fmt.Fprintf(conn, "frees: %v\n", s.Frees)
+ fmt.Fprintf(conn, "heap-alloc: %v\n", formatBytes(s.HeapAlloc))
+ fmt.Fprintf(conn, "heap-sys: %v\n", formatBytes(s.HeapSys))
+ fmt.Fprintf(conn, "heap-idle: %v\n", formatBytes(s.HeapIdle))
+ fmt.Fprintf(conn, "heap-in-use: %v\n", formatBytes(s.HeapInuse))
+ fmt.Fprintf(conn, "heap-released: %v\n", formatBytes(s.HeapReleased))
+ fmt.Fprintf(conn, "heap-objects: %v\n", s.HeapObjects)
+ fmt.Fprintf(conn, "stack-in-use: %v\n", formatBytes(s.StackInuse))
+ fmt.Fprintf(conn, "stack-sys: %v\n", formatBytes(s.StackSys))
+ fmt.Fprintf(conn, "next-gc: when heap-alloc >= %v\n", formatBytes(s.NextGC))
+ lastGC := "-"
+ if s.LastGC != 0 {
+ lastGC = fmt.Sprint(time.Unix(0, int64(s.LastGC)))
+ }
+ fmt.Fprintf(conn, "last-gc: %v\n", lastGC)
+ fmt.Fprintf(conn, "gc-pause: %v\n", time.Duration(s.PauseTotalNs))
+ fmt.Fprintf(conn, "num-gc: %v\n", s.NumGC)
+ fmt.Fprintf(conn, "enable-gc: %v\n", s.EnableGC)
+ fmt.Fprintf(conn, "debug-gc: %v\n", s.DebugGC)
+ case signal.Version:
+ fmt.Fprintf(conn, "%v\n", runtime.Version())
+ case signal.HeapProfile:
+ pprof.WriteHeapProfile(conn)
+ case signal.CPUProfile:
+ if err := pprof.StartCPUProfile(conn); err != nil {
+ return err
+ }
+ time.Sleep(30 * time.Second)
+ pprof.StopCPUProfile()
+ case signal.Stats:
+ fmt.Fprintf(conn, "goroutines: %v\n", runtime.NumGoroutine())
+ fmt.Fprintf(conn, "OS threads: %v\n", pprof.Lookup("threadcreate").Count())
+ fmt.Fprintf(conn, "GOMAXPROCS: %v\n", runtime.GOMAXPROCS(0))
+ fmt.Fprintf(conn, "num CPU: %v\n", runtime.NumCPU())
+ case signal.BinaryDump:
+ path, err := osext.Executable()
+ if err != nil {
+ return err
+ }
+ f, err := os.Open(path)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ _, err = bufio.NewReader(f).WriteTo(conn)
+ return err
+ case signal.Trace:
+ trace.Start(conn)
+ time.Sleep(5 * time.Second)
+ trace.Stop()
+ }
+ return nil
+}
diff --git a/irc/kcpd/vendor/github.com/google/gops/internal/internal.go b/irc/kcpd/vendor/github.com/google/gops/internal/internal.go
new file mode 100644
index 0000000..1382822
--- /dev/null
+++ b/irc/kcpd/vendor/github.com/google/gops/internal/internal.go
@@ -0,0 +1,52 @@
+package internal
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/user"
+ "path/filepath"
+ "runtime"
+ "strings"
+)
+
+func ConfigDir() (string, error) {
+ if runtime.GOOS == "windows" {
+ return filepath.Join(os.Getenv("APPDATA"), "gops"), nil
+ }
+ homeDir := guessUnixHomeDir()
+ if homeDir == "" {
+ return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty")
+ }
+ return filepath.Join(homeDir, ".config", "gops"), nil
+}
+
+func guessUnixHomeDir() string {
+ usr, err := user.Current()
+ if err == nil {
+ return usr.HomeDir
+ }
+ return os.Getenv("HOME")
+}
+
+func PIDFile(pid int) (string, error) {
+ gopsdir, err := ConfigDir()
+ if err != nil {
+ return "", err
+ }
+ return fmt.Sprintf("%s/%d", gopsdir, pid), nil
+}
+
+func GetPort(pid int) (string, error) {
+ portfile, err := PIDFile(pid)
+ if err != nil {
+ return "", err
+ }
+ b, err := ioutil.ReadFile(portfile)
+ if err != nil {
+ return "", err
+ }
+ port := strings.TrimSpace(string(b))
+ return port, nil
+}
diff --git a/irc/kcpd/vendor/github.com/google/gops/signal/signal.go b/irc/kcpd/vendor/github.com/google/gops/signal/signal.go
new file mode 100644
index 0000000..b2bfbe1
--- /dev/null
+++ b/irc/kcpd/vendor/github.com/google/gops/signal/signal.go
@@ -0,0 +1,35 @@
+// Copyright 2016 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 signal contains signals used to communicate to the gops agents.
+package signal
+
+const (
+ // StackTrace represents a command to print stack trace.
+ StackTrace = byte(0x1)
+
+ // GC runs the garbage collector.
+ GC = byte(0x2)
+
+ // MemStats reports memory stats.
+ MemStats = byte(0x3)
+
+ // Version prints the Go version.
+ Version = byte(0x4)
+
+ // HeapProfile starts `go tool pprof` with the current memory profile.
+ HeapProfile = byte(0x5)
+
+ // CPUProfile starts `go tool pprof` with the current CPU profile
+ CPUProfile = byte(0x6)
+
+ // Stats returns Go runtime statistics such as number of goroutines, GOMAXPROCS, and NumCPU.
+ Stats = byte(0x7)
+
+ // Trace starts the Go execution tracer, waits 5 seconds and launches the trace tool.
+ Trace = byte(0x8)
+
+ // BinaryDump returns running binary file.
+ BinaryDump = byte(0x9)
+)
diff --git a/irc/kcpd/vendor/github.com/kardianos/osext/osext.go b/irc/kcpd/vendor/github.com/kardianos/osext/osext.go
new file mode 100644
index 0000000..17f380f
--- /dev/null
+++ b/irc/kcpd/vendor/github.com/kardianos/osext/osext.go
@@ -0,0 +1,33 @@
+// 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.
+
+// Extensions to the standard "os" package.
+package osext // import "github.com/kardianos/osext"
+
+import "path/filepath"
+
+var cx, ce = executableClean()
+
+func executableClean() (string, error) {
+ p, err := executable()
+ return filepath.Clean(p), err
+}
+
+// Executable returns an absolute path that can be used to
+// re-invoke the current program.
+// It may not be valid after the current program exits.
+func Executable() (string, error) {
+ return cx, ce
+}
+
+// Returns same path as Executable, returns just the folder
+// path. Excludes the executable name and any trailing slash.
+func ExecutableFolder() (string, error) {
+ p, err := Executable()
+ if err != nil {
+ return "", err
+ }
+
+ return filepath.Dir(p), nil
+}
diff --git a/irc/kcpd/vendor/github.com/kardianos/osext/osext_plan9.go b/irc/kcpd/vendor/github.com/kardianos/osext/osext_plan9.go
new file mode 100644
index 0000000..655750c
--- /dev/null
+++ b/irc/kcpd/vendor/github.com/kardianos/osext/osext_plan9.go
@@ -0,0 +1,20 @@
+// 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 osext
+
+import (
+ "os"
+ "strconv"
+ "syscall"
+)
+
+func executable() (string, error) {
+ f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+ return syscall.Fd2path(int(f.Fd()))
+}
diff --git a/irc/kcpd/vendor/github.com/kardianos/osext/osext_procfs.go b/irc/kcpd/vendor/github.com/kardianos/osext/osext_procfs.go
new file mode 100644
index 0000000..d59847e
--- /dev/null
+++ b/irc/kcpd/vendor/github.com/kardianos/osext/osext_procfs.go
@@ -0,0 +1,36 @@
+// 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 linux netbsd solaris dragonfly
+
+package osext
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "runtime"
+ "strings"
+)
+
+func executable() (string, error) {
+ switch runtime.GOOS {
+ case "linux":
+ const deletedTag = " (deleted)"
+ execpath, err := os.Readlink("/proc/self/exe")
+ if err != nil {
+ return execpath, err
+ }
+ execpath = strings.TrimSuffix(execpath, deletedTag)
+ execpath = strings.TrimPrefix(execpath, deletedTag)
+ return execpath, nil
+ case "netbsd":
+ return os.Readlink("/proc/curproc/exe")
+ case "dragonfly":
+ return os.Readlink("/proc/curproc/file")
+ case "solaris":
+ return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid()))
+ }
+ return "", errors.New("ExecPath not implemented for " + runtime.GOOS)
+}
diff --git a/irc/kcpd/vendor/github.com/kardianos/osext/osext_sysctl.go b/irc/kcpd/vendor/github.com/kardianos/osext/osext_sysctl.go
new file mode 100644
index 0000000..66da0bc
--- /dev/null
+++ b/irc/kcpd/vendor/github.com/kardianos/osext/osext_sysctl.go
@@ -0,0 +1,126 @@
+// 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 darwin freebsd openbsd
+
+package osext
+
+import (
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "syscall"
+ "unsafe"
+)
+
+var initCwd, initCwdErr = os.Getwd()
+
+func executable() (string, error) {
+ var mib [4]int32
+ switch runtime.GOOS {
+ case "freebsd":
+ mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
+ case "darwin":
+ mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
+ case "openbsd":
+ mib = [4]int32{1 /* CTL_KERN */, 55 /* KERN_PROC_ARGS */, int32(os.Getpid()), 1 /* KERN_PROC_ARGV */}
+ }
+
+ n := uintptr(0)
+ // Get length.
+ _, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
+ if errNum != 0 {
+ return "", errNum
+ }
+ if n == 0 { // This shouldn't happen.
+ return "", nil
+ }
+ buf := make([]byte, n)
+ _, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
+ if errNum != 0 {
+ return "", errNum
+ }
+ if n == 0 { // This shouldn't happen.
+ return "", nil
+ }
+
+ var execPath string
+ switch runtime.GOOS {
+ case "openbsd":
+ // buf now contains **argv, with pointers to each of the C-style
+ // NULL terminated arguments.
+ var args []string
+ argv := uintptr(unsafe.Pointer(&buf[0]))
+ Loop:
+ for {
+ argp := *(**[1 << 20]byte)(unsafe.Pointer(argv))
+ if argp == nil {
+ break
+ }
+ for i := 0; uintptr(i) < n; i++ {
+ // we don't want the full arguments list
+ if string(argp[i]) == " " {
+ break Loop
+ }
+ if argp[i] != 0 {
+ continue
+ }
+ args = append(args, string(argp[:i]))
+ n -= uintptr(i)
+ break
+ }
+ if n < unsafe.Sizeof(argv) {
+ break
+ }
+ argv += unsafe.Sizeof(argv)
+ n -= unsafe.Sizeof(argv)
+ }
+ execPath = args[0]
+ // There is no canonical way to get an executable path on
+ // OpenBSD, so check PATH in case we are called directly
+ if execPath[0] != '/' && execPath[0] != '.' {
+ execIsInPath, err := exec.LookPath(execPath)
+ if err == nil {
+ execPath = execIsInPath
+ }
+ }
+ default:
+ for i, v := range buf {
+ if v == 0 {
+ buf = buf[:i]
+ break
+ }
+ }
+ execPath = string(buf)
+ }
+
+ var err error
+ // execPath will not be empty due to above checks.
+ // Try to get the absolute path if the execPath is not rooted.
+ if execPath[0] != '/' {
+ execPath, err = getAbs(execPath)
+ if err != nil {
+ return execPath, err
+ }
+ }
+ // For darwin KERN_PROCARGS may return the path to a symlink rather than the
+ // actual executable.
+ if runtime.GOOS == "darwin" {
+ if execPath, err = filepath.EvalSymlinks(execPath); err != nil {
+ return execPath, err
+ }
+ }
+ return execPath, nil
+}
+
+func getAbs(execPath string) (string, error) {
+ if initCwdErr != nil {
+ return execPath, initCwdErr
+ }
+ // The execPath may begin with a "../" or a "./" so clean it first.
+ // Join the two paths, trailing and starting slashes undetermined, so use
+ // the generic Join function.
+ return filepath.Join(initCwd, filepath.Clean(execPath)), nil
+}
diff --git a/irc/kcpd/vendor/github.com/kardianos/osext/osext_windows.go b/irc/kcpd/vendor/github.com/kardianos/osext/osext_windows.go
new file mode 100644
index 0000000..72d282c
--- /dev/null
+++ b/irc/kcpd/vendor/github.com/kardianos/osext/osext_windows.go
@@ -0,0 +1,34 @@
+// 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 osext
+
+import (
+ "syscall"
+ "unicode/utf16"
+ "unsafe"
+)
+
+var (
+ kernel = syscall.MustLoadDLL("kernel32.dll")
+ getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
+)
+
+// GetModuleFileName() with hModule = NULL
+func executable() (exePath string, err error) {
+ return getModuleFileName()
+}
+
+func getModuleFileName() (string, error) {
+ var n uint32
+ b := make([]uint16, syscall.MAX_PATH)
+ size := uint32(len(b))
+
+ r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
+ n = uint32(r0)
+ if n == 0 {
+ return "", e1
+ }
+ return string(utf16.Decode(b[0:n])), nil
+}