Merge pull request #36 from UhuruSoftware/master

Fix tail for windows os
This commit is contained in:
Sridhar Ratnakumar 2014-07-28 13:45:15 -07:00
commit 5fc9335f68
7 changed files with 151 additions and 2 deletions

View File

@ -104,7 +104,7 @@ func TailFile(filename string, config Config) (*Tail, error) {
if t.MustExist {
var err error
t.file, err = os.Open(t.Filename)
t.file, err = OpenFile(t.Filename)
if err != nil {
return nil, err
}
@ -149,7 +149,7 @@ func (tail *Tail) reopen() error {
}
for {
var err error
tail.file, err = os.Open(tail.Filename)
tail.file, err = OpenFile(tail.Filename)
if err != nil {
if os.IsNotExist(err) {
tail.Logger.Printf("Waiting for %s to appear...", tail.Filename)

11
tail_linux.go Normal file
View File

@ -0,0 +1,11 @@
// +build linux
package tail
import (
"os"
)
func OpenFile(name string) (file *os.File, err error) {
return os.Open(name)
}

12
tail_windows.go Normal file
View File

@ -0,0 +1,12 @@
// +build windows
package tail
import (
"github.com/ActiveState/tail/winfile"
"os"
)
func OpenFile(name string) (file *os.File, err error) {
return winfile.OpenFile(name, os.O_RDONLY, 0)
}

View File

@ -51,6 +51,8 @@ func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, origFi os.FileInfo) *Fi
go func() {
defer changes.Close()
var retry int = 0
prevSize := fw.Size
for {
select {
@ -67,6 +69,11 @@ func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, origFi os.FileInfo) *Fi
changes.NotifyDeleted()
return
}
if permissionErrorRetry(err, &retry) {
continue
}
// XXX: report this error back to the user
util.Fatal("Failed to stat file %v: %v", fw.Filename, err)
}

9
watch/polling_linux.go Normal file
View File

@ -0,0 +1,9 @@
// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
// +build linux
package watch
func permissionErrorRetry(err error, retry *int) bool {
// No need for this on linux, don't retry
return false
}

18
watch/polling_windows.go Normal file
View File

@ -0,0 +1,18 @@
// +build windows
package watch
import (
"os"
)
const permissionDeniedRetryCount int = 5
func permissionErrorRetry(err error, retry *int) bool {
if os.IsPermission(err) && *retry < permissionDeniedRetryCount {
// While pooling a file that does not exist yet, but will be created by another process we can get Permission Denied
(*retry)++
return true
}
return false
}

92
winfile/winfile.go Normal file
View File

@ -0,0 +1,92 @@
// +build windows
package winfile
import (
"os"
"syscall"
"unsafe"
)
// issue also described here
//https://codereview.appspot.com/8203043/
// https://github.com/jnwhiteh/golang/blob/master/src/pkg/syscall/syscall_windows.go#L218
func Open(path string, mode int, perm uint32) (fd syscall.Handle, err error) {
if len(path) == 0 {
return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
}
pathp, err := syscall.UTF16PtrFromString(path)
if err != nil {
return syscall.InvalidHandle, err
}
var access uint32
switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) {
case syscall.O_RDONLY:
access = syscall.GENERIC_READ
case syscall.O_WRONLY:
access = syscall.GENERIC_WRITE
case syscall.O_RDWR:
access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
}
if mode&syscall.O_CREAT != 0 {
access |= syscall.GENERIC_WRITE
}
if mode&syscall.O_APPEND != 0 {
access &^= syscall.GENERIC_WRITE
access |= syscall.FILE_APPEND_DATA
}
sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE)
var sa *syscall.SecurityAttributes
if mode&syscall.O_CLOEXEC == 0 {
sa = makeInheritSa()
}
var createmode uint32
switch {
case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
createmode = syscall.CREATE_NEW
case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC):
createmode = syscall.CREATE_ALWAYS
case mode&syscall.O_CREAT == syscall.O_CREAT:
createmode = syscall.OPEN_ALWAYS
case mode&syscall.O_TRUNC == syscall.O_TRUNC:
createmode = syscall.TRUNCATE_EXISTING
default:
createmode = syscall.OPEN_EXISTING
}
h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, syscall.FILE_ATTRIBUTE_NORMAL, 0)
return h, e
}
// https://github.com/jnwhiteh/golang/blob/master/src/pkg/syscall/syscall_windows.go#L211
func makeInheritSa() *syscall.SecurityAttributes {
var sa syscall.SecurityAttributes
sa.Length = uint32(unsafe.Sizeof(sa))
sa.InheritHandle = 1
return &sa
}
// https://github.com/jnwhiteh/golang/blob/master/src/pkg/os/file_windows.go#L133
func OpenFile(name string, flag int, perm os.FileMode) (file *os.File, err error) {
r, e := Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
if e != nil {
return nil, e
}
return os.NewFile(uintptr(r), name), nil
}
// https://github.com/jnwhiteh/golang/blob/master/src/pkg/os/file_posix.go#L61
func syscallMode(i os.FileMode) (o uint32) {
o |= uint32(i.Perm())
if i&os.ModeSetuid != 0 {
o |= syscall.S_ISUID
}
if i&os.ModeSetgid != 0 {
o |= syscall.S_ISGID
}
if i&os.ModeSticky != 0 {
o |= syscall.S_ISVTX
}
// No mapping for Go's ModeTemporary (plan9 only).
return
}