1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
package tschexec
import (
"context"
"encoding/xml"
"fmt"
"github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1"
"github.com/rs/zerolog"
)
const (
TaskXMLDurationFormat = "2006-01-02T15:04:05.9999999Z"
TaskXMLHeader = `<?xml version="1.0" encoding="UTF-16"?>`
)
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/0d6383e4-de92-43e7-b0bb-a60cfa36379f
type triggers struct {
XMLName xml.Name `xml:"Triggers"`
TimeTriggers []taskTimeTrigger `xml:"TimeTrigger,omitempty"`
}
type taskTimeTrigger struct {
XMLName xml.Name `xml:"TimeTrigger"`
StartBoundary string `xml:"StartBoundary,omitempty"` // Derived from time.Time
EndBoundary string `xml:"EndBoundary,omitempty"` // Derived from time.Time; must be > StartBoundary
Enabled bool `xml:"Enabled"`
}
type idleSettings struct {
XMLName xml.Name `xml:"IdleSettings"`
StopOnIdleEnd bool `xml:"StopOnIdleEnd"`
RestartOnIdle bool `xml:"RestartOnIdle"`
}
type settings struct {
XMLName xml.Name `xml:"Settings"`
Enabled bool `xml:"Enabled"`
Hidden bool `xml:"Hidden"`
DisallowStartIfOnBatteries bool `xml:"DisallowStartIfOnBatteries"`
StopIfGoingOnBatteries bool `xml:"StopIfGoingOnBatteries"`
AllowHardTerminate bool `xml:"AllowHardTerminate"`
RunOnlyIfNetworkAvailable bool `xml:"RunOnlyIfNetworkAvailable"`
AllowStartOnDemand bool `xml:"AllowStartOnDemand"`
WakeToRun bool `xml:"WakeToRun"`
RunOnlyIfIdle bool `xml:"RunOnlyIfIdle"`
StartWhenAvailable bool `xml:"StartWhenAvailable"`
Priority int `xml:"Priority,omitempty"` // 1 to 10 inclusive
MultipleInstancesPolicy string `xml:"MultipleInstancesPolicy,omitempty"`
ExecutionTimeLimit string `xml:"ExecutionTimeLimit,omitempty"`
DeleteExpiredTaskAfter string `xml:"DeleteExpiredTaskAfter,omitempty"` // Derived from time.Duration
IdleSettings idleSettings `xml:"IdleSettings,omitempty"`
}
type actionExec struct {
XMLName xml.Name `xml:"Exec"`
Command string `xml:"Command"`
Arguments string `xml:"Arguments"`
}
type actions struct {
XMLName xml.Name `xml:"Actions"`
Context string `xml:"Context,attr"`
Exec []actionExec `xml:"Exec,omitempty"`
}
type principals struct {
XMLName xml.Name `xml:"Principals"`
Principals []principal `xml:"Principal,omitempty"`
}
type principal struct {
XMLName xml.Name `xml:"Principal"`
ID string `xml:"id,attr"`
UserID string `xml:"UserId"`
RunLevel string `xml:"RunLevel"`
}
type task struct {
XMLName xml.Name `xml:"Task"`
TaskVersion string `xml:"version,attr"`
TaskNamespace string `xml:"xmlns,attr"`
//TimeTriggers []taskTimeTrigger `xml:"Triggers>TimeTrigger,omitempty"` // TODO: triggers type
Triggers triggers `xml:"Triggers"`
Actions actions `xml:"Actions"`
Principals principals `xml:"Principals"`
Settings settings `xml:"Settings"`
}
// registerTask serializes and submits the provided task structure
func (mod *Module) registerTask(ctx context.Context, taskDef task, taskPath string) (path string, err error) {
var taskXml string
log := zerolog.Ctx(ctx).With().
Str("module", "tsch").
Str("func", "createTask").Logger()
// Generate task XML content. See https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/0d6383e4-de92-43e7-b0bb-a60cfa36379f
{
doc, err := xml.Marshal(taskDef)
if err != nil {
log.Error().Err(err).Msg("failed to marshal task XML")
return "", fmt.Errorf("marshal task: %w", err)
}
taskXml = TaskXMLHeader + string(doc)
log.Debug().Str("content", taskXml).Msg("Generated task XML")
}
// Submit task
{
response, err := mod.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{
Path: taskPath,
XML: taskXml,
Flags: 0, // TODO
LogonType: 0, // TASK_LOGON_NONE
CredsCount: 0,
Creds: nil,
})
if err != nil {
log.Error().Err(err).Msg("Failed to register task")
return "", fmt.Errorf("register task: %w", err)
}
log.Info().Str("path", taskPath).Msg("Task created successfully")
path = response.ActualPath
}
return
}
func (mod *Module) deleteTask(ctx context.Context, taskPath string) (err error) {
log := zerolog.Ctx(ctx).With().
Str("module", "tsch").
Str("path", taskPath).
Str("func", "deleteTask").Logger()
if _, err = mod.tsch.Delete(ctx, &itaskschedulerservice.DeleteRequest{Path: taskPath}); err != nil {
log.Error().Err(err).Msg("Failed to delete task")
return fmt.Errorf("delete task: %w", err)
}
log.Info().Msg("Task deleted successfully")
return
}
|