Files
windows_exporter/internal/ole/taskschd/taskschd_windows.go
T

140 lines
4.7 KiB
Go

// SPDX-License-Identifier: Apache-2.0
//
// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build windows && (amd64 || arm64)
// Package taskschd provides the Task Scheduler COM interfaces needed for task enumeration.
package taskschd
import (
"iter"
"runtime"
"unsafe"
"github.com/prometheus-community/windows_exporter/internal/ole"
"golang.org/x/sys/windows"
)
// Native method slots, including the seven inherited IUnknown/IDispatch slots.
// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/
const (
taskServiceGetFolder = 7
taskServiceConnect = 10
taskFolderPath = 8
taskFolderGetFolders = 10
taskFolderGetTasks = 14
taskCollectionCount = 7
taskCollectionItem = 8
registeredTaskName = 7
registeredTaskPath = 8
registeredTaskState = 9
registeredTaskEnabled = 10
registeredTaskLastResult = 16
registeredTaskMissedRuns = 17
)
type (
TaskService struct{ ole.Object }
TaskFolder struct{ ole.Object }
RegisteredTask struct{ ole.Object }
)
func NewTaskService() (*TaskService, error) {
return ole.Create[TaskService](
windows.GUID{Data1: 0x0f87369f, Data2: 0xa4e5, Data3: 0x4cfc, Data4: [8]byte{0xbd, 0x3e, 0x73, 0xe6, 0x15, 0x45, 0x72, 0xdd}},
windows.GUID{Data1: 0x2faba4c7, Data2: 0x4da9, Data3: 0x4013, Data4: [8]byte{0x96, 0x97, 0x20, 0xcc, 0x3f, 0xd4, 0x0f, 0x85}},
)
}
// Connect connects locally using the current security token. Empty VARIANTs
// supply the four optional arguments. The 24-byte values are passed indirectly
// on Windows amd64 and arm64, each using distinct caller-owned storage.
func (s *TaskService) Connect() error {
server := ole.NewEmptyVariant()
user := ole.NewEmptyVariant()
domain := ole.NewEmptyVariant()
password := ole.NewEmptyVariant()
fn, err := s.Method(taskServiceConnect)
if err != nil {
return err
}
hr := ole.Call(
fn,
uintptr(unsafe.Pointer(s)),
uintptr(unsafe.Pointer(server)),
uintptr(unsafe.Pointer(user)),
uintptr(unsafe.Pointer(domain)),
uintptr(unsafe.Pointer(password)),
)
runtime.KeepAlive(s)
return ole.ResultError(hr)
}
func (s *TaskService) Folder(path string) (*TaskFolder, error) {
return s.GetObjectStringArg[TaskFolder](taskServiceGetFolder, path)
}
// Folders returns the direct subfolders. Its reserved flags argument must be zero.
func (f *TaskFolder) Folders() (*taskCollection[TaskFolder], error) {
return f.GetObjectArg[taskCollection[TaskFolder]](taskFolderGetFolders, 0)
}
// Tasks includes hidden tasks (TASK_ENUM_HIDDEN).
func (f *TaskFolder) Tasks() (*taskCollection[RegisteredTask], error) {
return f.GetObjectArg[taskCollection[RegisteredTask]](taskFolderGetTasks, 1)
}
func (f *TaskFolder) Path() (string, error) { return f.String(taskFolderPath) }
func (t *RegisteredTask) Name() (string, error) { return t.String(registeredTaskName) }
func (t *RegisteredTask) Path() (string, error) { return t.String(registeredTaskPath) }
func (t *RegisteredTask) State() (int32, error) { return t.Get[int32](registeredTaskState) }
func (t *RegisteredTask) Enabled() (bool, error) {
value, err := t.Get[int16](registeredTaskEnabled)
return value != 0, err
}
func (t *RegisteredTask) LastTaskResult() (int32, error) {
return t.Get[int32](registeredTaskLastResult)
}
func (t *RegisteredTask) NumberOfMissedRuns() (int32, error) {
return t.Get[int32](registeredTaskMissedRuns)
}
type taskCollection[T TaskFolder | RegisteredTask] struct{ ole.Object }
func (c *taskCollection[T]) count() (int32, error) { return c.Get[int32](taskCollectionCount) }
func (c *taskCollection[T]) item(index int32) (*T, error) {
value := ole.NewInt32Variant(index + 1) // Task Scheduler uses one-based indices.
item, err := c.GetObjectArg[T](taskCollectionItem, uintptr(unsafe.Pointer(value)))
runtime.KeepAlive(value)
return item, err
}
// All yields borrowed interfaces, valid only in the loop body. It releases each
// item on advance, early exit, or panic; callers must not release the items.
// The collection remains owned by the caller and must be released separately.
func (c *taskCollection[T]) All() iter.Seq2[*T, error] {
return ole.BorrowedItems(c.count, c.item, func(item *T) { (*ole.Object)(unsafe.Pointer(item)).Release() })
}