Windows Event Log, error output

Adding more Event Log support, and modifying the loggers so they return
errors in their operational functions.
This commit is contained in:
2022-01-16 02:05:42 -05:00
parent 3d0d420454
commit 39e0a1fd43
18 changed files with 612 additions and 220 deletions

View File

@@ -3,17 +3,29 @@ package logging
import (
`errors`
`fmt`
`os`
`os/exec`
`syscall`
`golang.org/x/sys/windows/registry`
`golang.org/x/sys/windows/svc/eventlog`
`r00t2.io/sysutils/paths`
)
// Setup sets up/configures a WinLogger and prepares it for use.
func (l *WinLogger) Setup() {
/*
Setup sets up/configures a WinLogger and prepares it for use.
This will fail with an Access Denied (the first time, at least) unless running with elevated permissions unless WinLogger.Prefix is
a registered Event Log source.
var err error
If a failure occurs while trying to open the log with the given WinLogger.Prefix ("source"), a new Event Log source will be registered.
If WinLogger.Executable is not empty at the time of calling WinLogger.Setup (or WinLogger.ForceService is true),
eventlog.Install will be used (with the WinLogger.ExpandKey field).
Otherwise eventlog.InstallAsEventCreate will be used.
*/
func (l *WinLogger) Setup() (err error) {
/*
First a sanity check on the EventIDs.
A sanity check on the EventIDs.
Since we use eventcreate, all Event IDs must be 1 <= eid <= 1000.
*/
for _, eid := range []uint32{
@@ -26,45 +38,100 @@ func (l *WinLogger) Setup() {
l.eids.Notice,
l.eids.Warning,
} {
if !((eid <= 1000) && (1 <= eid)) {
err = errors.New("event IDs must be between 1 and 1000 inclusive")
panic(err)
if !((eid <= EIDMax) && (EIDMin <= eid)) {
err = ErrBadEid
return
}
}
if err = eventlog.InstallAsEventCreate(l.Prefix, eventlog.Error|eventlog.Warning|eventlog.Info); err != nil {
if idx := ptrnSourceExists.FindStringIndex(err.Error()); idx == nil {
// It's an error we want to panic on.
panic(err)
} else {
// It already exists, so ignore the error.
err = nil
}
if err = l.Install(); err != nil {
return
}
if l.elog, err = eventlog.Open(l.Prefix); err != nil {
panic(err)
return
}
return
}
// Install installs/registers the WinLogger Event Log interface. You most likely do not need to run this directly.
func (l *WinLogger) Install() (err error) {
var exists bool
var doNotCreate bool
var useEventCreate bool = true
if doNotCreate, err = l.Exists(); err != nil {
return
} else if !doNotCreate {
if l.Executable != "" {
if l.Executable, err = exec.LookPath(l.Executable); err != nil {
return
}
useEventCreate = false
} else if l.ForceService {
if l.Executable, err = exec.LookPath(os.Args[0]); err != nil {
return
}
useEventCreate = false
}
if !useEventCreate {
if exists, err = paths.RealPathExists(&l.Executable); err != nil {
return
} else if !exists {
err = ErrBadBinPath
return
}
if err = eventlog.Install(l.Prefix, l.Executable, l.ExpandKey, eventlog.Error|eventlog.Warning|eventlog.Info); err != nil {
return
}
} else {
if err = eventlog.InstallAsEventCreate(l.Prefix, eventlog.Error|eventlog.Warning|eventlog.Info); err != nil {
return
}
}
}
return
}
// Shutdown cleanly shuts down a WinLogger.
func (l *WinLogger) Shutdown() {
var err error
if err = l.elog.Close(); err != nil {
panic(err)
}
// Remove uninstalls a registered WinLogger source.
func (l *WinLogger) Remove() (err error) {
if err = eventlog.Remove(l.Prefix); err != nil {
panic(err)
return
}
return
}
// GetPrefix returns the prefix used by this WinLogger.
func (l *WinLogger) GetPrefix() (prefix string) {
/*
Shutdown cleanly shuts down a WinLogger but keep the source registered. Use WinLogger.Remove
(or set WinLogger.RemoveOnClose to true before calling WinLogger.Shutdown) to remove the registered source.
*/
func (l *WinLogger) Shutdown() (err error) {
if err = l.elog.Close(); err != nil {
// TODO: check for no access or file not exists syscall errors?
return
}
if l.RemoveOnClose {
if err = l.Remove(); err != nil {
return
}
}
return
}
/*
GetPrefix returns the prefix used by this WinLogger.
err will always be nil; it's there for interface-compat.
*/
func (l *WinLogger) GetPrefix() (prefix string, err error) {
prefix = l.Prefix
@@ -74,43 +141,55 @@ func (l *WinLogger) GetPrefix() (prefix string) {
/*
DoDebug sets the debug state of this WinLogger.
Note that this merely acts as a *safety filter* for debug messages to avoid sensitive information being written to the log.
err will always be nil; it's there for interface-compat.
*/
func (l *WinLogger) DoDebug(d bool) {
func (l *WinLogger) DoDebug(d bool) (err error) {
l.EnableDebug = d
return
}
// SetPrefix sets the prefix for this WinLogger.
func (l *WinLogger) SetPrefix(prefix string) {
func (l *WinLogger) SetPrefix(prefix string) (err error) {
var err error
// To properly change the prefix, we need to tear down the old event log and create a new one.
if err = l.Shutdown(); err != nil {
return
}
l.Prefix = prefix
// To properly change the prefix, we need to tear down the old event log and create a new one.
if err = l.elog.Close(); err != nil {
panic(err)
if err = l.Setup(); err != nil {
return
}
if err = eventlog.Remove(l.Prefix); err != nil {
panic(err)
}
return
}
if err = eventlog.InstallAsEventCreate(l.Prefix, eventlog.Error|eventlog.Warning|eventlog.Info); err != nil {
if idx := ptrnSourceExists.FindStringIndex(err.Error()); idx == nil {
// It's an error we want to panic on.
panic(err)
} else {
// It already exists, so ignore the error.
// Exists indicates if the WinLogger.Prefix is a registered source or not.
func (l *WinLogger) Exists() (e bool, err error) {
var regKey registry.Key
var subKey registry.Key
if regKey, err = registry.OpenKey(registry.LOCAL_MACHINE, eventLogRegistryKey, registry.READ); err != nil {
return
}
defer regKey.Close()
if subKey, err = registry.OpenKey(regKey, l.Prefix, registry.READ); err != nil {
if errors.Is(err, syscall.ERROR_FILE_NOT_FOUND) {
e = false
err = nil
}
return
}
defer subKey.Close()
if l.elog, err = eventlog.Open(l.Prefix); err != nil {
panic(err)
}
e = true
return
}
// Alert writes an ALERT-level message to this WinLogger.