mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
Adds windows build (#1040)
* fixes windows compilation, adds signal impl for windows, adds free space usage for windows, https://github.com/VictoriaMetrics/VictoriaMetrics/issues/70 https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1036 NOTE victoria metrics database still CANNOT work under windows system, only vmagent is supported. To completly port victoria metrics, you have to fix issues with separators, parsing and posix file removall * rollback separator * Adds windows setInformation api, it must behave like unix, need to test it. changes procutil * check for invlaid param * Fixes posix delete semantic * refactored a bit * fixes openbsd build * removed windows api call * Fixes code after windows add * Update lib/procutil/signal_windows.go Co-authored-by: Aliaksandr Valialkin <valyala@gmail.com>
This commit is contained in:
parent
975dac9086
commit
f3a03c4164
9 changed files with 305 additions and 42 deletions
|
@ -73,6 +73,10 @@ vmagent-ppc64le:
|
|||
vmagent-386:
|
||||
CGO_ENABLED=0 GOARCH=386 $(MAKE) vmagent-local-with-goarch
|
||||
|
||||
vmagent-windows:
|
||||
GOOS=windows CGO_ENABLED=0 $(MAKE) vmagent-local-with-goarch
|
||||
|
||||
|
||||
vmagent-local-with-goarch:
|
||||
APP_NAME=vmagent $(MAKE) app-local-with-goarch
|
||||
|
||||
|
|
42
lib/fs/fs.go
42
lib/fs/fs.go
|
@ -13,26 +13,15 @@ import (
|
|||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/filestream"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var tmpFileNum uint64
|
||||
|
||||
// MustSyncPath syncs contents of the given path.
|
||||
func MustSyncPath(path string) {
|
||||
d, err := os.Open(path)
|
||||
if err != nil {
|
||||
logger.Panicf("FATAL: cannot open %q: %s", path, err)
|
||||
}
|
||||
if err := d.Sync(); err != nil {
|
||||
_ = d.Close()
|
||||
logger.Panicf("FATAL: cannot flush %q to storage: %s", path, err)
|
||||
}
|
||||
if err := d.Close(); err != nil {
|
||||
logger.Panicf("FATAL: cannot close %q: %s", path, err)
|
||||
}
|
||||
mustSyncPath(path)
|
||||
}
|
||||
|
||||
var tmpFileNum uint64
|
||||
|
||||
// WriteFileAtomically atomically writes data to the given file path.
|
||||
//
|
||||
// WriteFileAtomically returns only after the file is fully written and synced
|
||||
|
@ -332,15 +321,7 @@ func MustWriteData(w io.Writer, data []byte) {
|
|||
// CreateFlockFile creates flock.lock file in the directory dir
|
||||
// and returns the handler to the file.
|
||||
func CreateFlockFile(dir string) (*os.File, error) {
|
||||
flockFile := dir + "/flock.lock"
|
||||
flockF, err := os.Create(flockFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create lock file %q: %w", flockFile, err)
|
||||
}
|
||||
if err := unix.Flock(int(flockF.Fd()), unix.LOCK_EX|unix.LOCK_NB); err != nil {
|
||||
return nil, fmt.Errorf("cannot acquire lock on file %q: %w", flockFile, err)
|
||||
}
|
||||
return flockF, nil
|
||||
return createFlockFile(dir)
|
||||
}
|
||||
|
||||
// MustGetFreeSpace returns free space for the given directory path.
|
||||
|
@ -372,18 +353,3 @@ type freeSpaceEntry struct {
|
|||
updateTime uint64
|
||||
freeSpace uint64
|
||||
}
|
||||
|
||||
func mustGetFreeSpace(path string) uint64 {
|
||||
d, err := os.Open(path)
|
||||
if err != nil {
|
||||
logger.Panicf("FATAL: cannot determine free disk space on %q: %s", path, err)
|
||||
}
|
||||
defer MustClose(d)
|
||||
|
||||
fd := d.Fd()
|
||||
var stat unix.Statfs_t
|
||||
if err := unix.Fstatfs(int(fd), &stat); err != nil {
|
||||
logger.Panicf("FATAL: cannot determine free disk space on %q: %s", path, err)
|
||||
}
|
||||
return freeSpace(stat)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,27 @@
|
|||
|
||||
package fs
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func mustGetFreeSpace(path string) uint64 {
|
||||
d, err := os.Open(path)
|
||||
if err != nil {
|
||||
logger.Panicf("FATAL: cannot determine free disk space on %q: %s", path, err)
|
||||
}
|
||||
defer MustClose(d)
|
||||
|
||||
fd := d.Fd()
|
||||
var stat unix.Statfs_t
|
||||
if err := unix.Fstatfs(int(fd), &stat); err != nil {
|
||||
logger.Panicf("FATAL: cannot determine free disk space on %q: %s", path, err)
|
||||
}
|
||||
return freeSpace(stat)
|
||||
}
|
||||
|
||||
func freeSpace(stat unix.Statfs_t) uint64 {
|
||||
return uint64(stat.Bavail) * uint64(stat.Bsize)
|
||||
|
|
|
@ -1,9 +1,27 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func mustGetFreeSpace(path string) uint64 {
|
||||
d, err := os.Open(path)
|
||||
if err != nil {
|
||||
logger.Panicf("FATAL: cannot determine free disk space on %q: %s", path, err)
|
||||
}
|
||||
defer MustClose(d)
|
||||
|
||||
fd := d.Fd()
|
||||
var stat unix.Statfs_t
|
||||
if err := unix.Fstatfs(int(fd), &stat); err != nil {
|
||||
logger.Panicf("FATAL: cannot determine free disk space on %q: %s", path, err)
|
||||
}
|
||||
return freeSpace(stat)
|
||||
}
|
||||
|
||||
func freeSpace(stat unix.Statfs_t) uint64 {
|
||||
return uint64(stat.F_bavail) * uint64(stat.F_bsize)
|
||||
}
|
||||
|
|
45
lib/fs/fs_unix.go
Normal file
45
lib/fs/fs_unix.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
// +build linux darwin freebsd openbsd
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func mmap(fd int, offset int64, length int) (data []byte, err error) {
|
||||
return unix.Mmap(fd, 0, length, unix.PROT_READ, unix.MAP_SHARED)
|
||||
|
||||
}
|
||||
func mUnmap(data []byte) error {
|
||||
return unix.Munmap(data)
|
||||
}
|
||||
|
||||
func mustSyncPath(path string) {
|
||||
d, err := os.Open(path)
|
||||
if err != nil {
|
||||
logger.Panicf("FATAL: cannot open %q: %s", path, err)
|
||||
}
|
||||
if err := d.Sync(); err != nil {
|
||||
_ = d.Close()
|
||||
logger.Panicf("FATAL: cannot flush %q to storage: %s", path, err)
|
||||
}
|
||||
if err := d.Close(); err != nil {
|
||||
logger.Panicf("FATAL: cannot close %q: %s", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
func createFlockFile(dir string) (*os.File, error) {
|
||||
flockFile := dir + "/flock.lock"
|
||||
flockF, err := os.Create(flockFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create lock file %q: %w", flockFile, err)
|
||||
}
|
||||
if err := unix.Flock(int(flockF.Fd()), unix.LOCK_EX|unix.LOCK_NB); err != nil {
|
||||
return nil, fmt.Errorf("cannot acquire lock on file %q: %w", flockFile, err)
|
||||
}
|
||||
return flockF, nil
|
||||
}
|
149
lib/fs/fs_windows.go
Normal file
149
lib/fs/fs_windows.go
Normal file
|
@ -0,0 +1,149 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
kernelDLL = windows.MustLoadDLL("kernel32.dll")
|
||||
procLock = kernelDLL.MustFindProc("LockFileEx")
|
||||
procEvent = kernelDLL.MustFindProc("CreateEventW")
|
||||
procDisk = kernelDLL.MustFindProc("GetDiskFreeSpaceExW")
|
||||
ntDLL = windows.MustLoadDLL("ntdll.dll")
|
||||
ntSetInformationProc = ntDLL.MustFindProc("NtSetInformationFile")
|
||||
)
|
||||
|
||||
// panic at windows, if file already open by another process.
|
||||
// one of possible solutions - change files opening process with correct flags.
|
||||
// https://github.com/dgraph-io/badger/issues/699
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers
|
||||
func mustSyncPath(string) {
|
||||
}
|
||||
|
||||
const (
|
||||
lockfileExclusiveLock = 2
|
||||
fileFlagNormal = 0x00000080
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_disposition_information_ex
|
||||
fileDispositionPosixSemantics = 0x00000002
|
||||
fileDispositionIgnoreReadonlyAttribute = 0x00000010
|
||||
)
|
||||
|
||||
// createFlockFile creates flock.lock file in the directory dir
|
||||
// and returns the handler to the file.
|
||||
// https://github.com/juju/fslock/blob/master/fslock_windows.go
|
||||
func createFlockFile(dir string) (*os.File, error) {
|
||||
flockFile := dir + "/flock.lock"
|
||||
name, err := windows.UTF16PtrFromString(flockFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
handle, err := windows.CreateFile(
|
||||
name,
|
||||
windows.GENERIC_READ|windows.DELETE,
|
||||
windows.FILE_SHARE_READ|windows.FILE_SHARE_DELETE,
|
||||
nil,
|
||||
windows.OPEN_ALWAYS,
|
||||
windows.FILE_FLAG_OVERLAPPED|fileFlagNormal,
|
||||
0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create lock file %q: %w", flockFile, err)
|
||||
}
|
||||
ol, err := newOverlapped()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create Overlapped handler: %w", err)
|
||||
}
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex
|
||||
// overlapped is dropped?
|
||||
r1, _, err := procLock.Call(uintptr(handle), uintptr(lockfileExclusiveLock), uintptr(0), uintptr(1), uintptr(0), uintptr(unsafe.Pointer(ol)))
|
||||
if r1 == 0 {
|
||||
return nil, err
|
||||
}
|
||||
return os.NewFile(uintptr(handle), flockFile), nil
|
||||
}
|
||||
|
||||
// stub
|
||||
func mmap(fd int, offset int64, length int) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// stub
|
||||
func mUnmap([]byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func mustGetFreeSpace(path string) uint64 {
|
||||
var freeBytes int64
|
||||
r, _, err := procDisk.Call(uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(path))),
|
||||
uintptr(unsafe.Pointer(&freeBytes)))
|
||||
if r == 0 {
|
||||
logger.Errorf("cannot get free space: %v", err)
|
||||
return 0
|
||||
}
|
||||
return uint64(freeBytes)
|
||||
}
|
||||
|
||||
// stub
|
||||
func fadviseSequentialRead(f *os.File, prefetch bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// copied from https://github.com/juju/fslock/blob/master/fslock_windows.go
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-overlapped
|
||||
func newOverlapped() (*windows.Overlapped, error) {
|
||||
event, err := createEvent(nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &windows.Overlapped{HEvent: event}, nil
|
||||
}
|
||||
|
||||
// copied from https://github.com/juju/fslock/blob/master/fslock_windows.go
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventa
|
||||
func createEvent(sa *windows.SecurityAttributes, name *uint16) (windows.Handle, error) {
|
||||
r0, _, err := procEvent.Call(uintptr(unsafe.Pointer(sa)), uintptr(1), uintptr(1), uintptr(unsafe.Pointer(name)))
|
||||
handle := windows.Handle(r0)
|
||||
if handle == windows.InvalidHandle {
|
||||
return 0, err
|
||||
}
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_disposition_information_ex
|
||||
type fileDispositionInformationEx struct {
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_io_status_block
|
||||
type ioStatusBlock struct {
|
||||
Status, Information uintptr
|
||||
}
|
||||
|
||||
// UpdateFileHandle - changes file deletion semantic at windows to posix-like.
|
||||
func UpdateFileHandle(path string) error {
|
||||
handle, err := windows.Open(path, windows.GENERIC_READ|windows.DELETE, windows.FILE_SHARE_READ|windows.FILE_SHARE_DELETE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setPosixDelete(handle)
|
||||
}
|
||||
|
||||
// supported starting with Windows 10, version 1709.
|
||||
// supported by NTFS only.
|
||||
func setPosixDelete(handle windows.Handle) error {
|
||||
var iosb ioStatusBlock
|
||||
flags := fileDispositionInformationEx{
|
||||
Flags: fileDispositionPosixSemantics | fileDispositionIgnoreReadonlyAttribute,
|
||||
}
|
||||
// class FileDispositionInformationEx, // 64
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ne-wdm-_file_information_class
|
||||
r0, _, err := ntSetInformationProc.Call(uintptr(handle), uintptr(unsafe.Pointer(&iosb)), uintptr(unsafe.Pointer(&flags)), unsafe.Sizeof(flags), uintptr(64))
|
||||
if r0 == 0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("cannot set file disposition information: NT_STATUS: 0x%X, error: %w", r0, err)
|
||||
}
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var disableMmap = flag.Bool("fs.disableMmap", is32BitPtr, "Whether to use pread() instead of mmap() for reading data files. "+
|
||||
|
@ -65,7 +64,7 @@ func (r *ReaderAt) MustReadAt(p []byte, off int64) {
|
|||
func (r *ReaderAt) MustClose() {
|
||||
fname := r.f.Name()
|
||||
if len(r.mmapData) > 0 {
|
||||
if err := unix.Munmap(r.mmapData[:cap(r.mmapData)]); err != nil {
|
||||
if err := mUnmap(r.mmapData[:cap(r.mmapData)]); err != nil {
|
||||
logger.Panicf("FATAL: cannot unmap data for file %q: %s", fname, err)
|
||||
}
|
||||
r.mmapData = nil
|
||||
|
@ -135,7 +134,7 @@ func mmapFile(f *os.File, size int64) ([]byte, error) {
|
|||
if size%4096 != 0 {
|
||||
size += 4096 - size%4096
|
||||
}
|
||||
data, err := unix.Mmap(int(f.Fd()), 0, int(size), unix.PROT_READ, unix.MAP_SHARED)
|
||||
data, err := mmap(int(f.Fd()), 0, int(size))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot mmap file with size %d: %w", size, err)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// +build !windows
|
||||
|
||||
package procutil
|
||||
|
||||
import (
|
||||
|
|
60
lib/procutil/signal_windows.go
Normal file
60
lib/procutil/signal_windows.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
// +build windows
|
||||
|
||||
package procutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// WaitForSigterm waits for either SIGTERM or SIGINT
|
||||
//
|
||||
// Returns the caught signal.
|
||||
//
|
||||
// Windows dont have SIGHUP syscall.
|
||||
func WaitForSigterm() os.Signal {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
|
||||
sig := <-ch
|
||||
return sig
|
||||
}
|
||||
|
||||
type sigHUPNotifier struct {
|
||||
lock sync.Mutex
|
||||
subscribers []chan<- os.Signal
|
||||
}
|
||||
|
||||
var notifier sigHUPNotifier
|
||||
|
||||
// https://golang.org/pkg/os/signal/#hdr-Windows
|
||||
// https://github.com/golang/go/issues/6948
|
||||
// SelfSIGHUP sends SIGHUP signal to the subscribed listeners.
|
||||
func SelfSIGHUP() {
|
||||
notifier.notify(syscall.SIGHUP)
|
||||
}
|
||||
|
||||
// NewSighupChan returns a channel, which is triggered on every SelfSIGHUP.
|
||||
func NewSighupChan() <-chan os.Signal {
|
||||
ch := make(chan os.Signal, 1)
|
||||
notifier.subscribe(ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
func (sn *sigHUPNotifier) subscribe(sub chan<- os.Signal) {
|
||||
sn.lock.Lock()
|
||||
defer sn.lock.Unlock()
|
||||
sn.subscribers = append(sn.subscribers, sub)
|
||||
}
|
||||
|
||||
func (sn *sigHUPNotifier) notify(sig os.Signal) {
|
||||
sn.lock.Lock()
|
||||
defer sn.lock.Unlock()
|
||||
for _, sub := range sn.subscribers {
|
||||
select {
|
||||
case sub <- sig:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue