In this TSNotify Series 16, we will learn how to trigger email notification based on the Windows Task Scheduler Event ID, getting prepared the necessary package dependencies such as Cobra CLI with step by step guide in Golang programming language.
#MaharlikansCode
#TSNotifySeries16
#TaskSchedulerEventIDNotify
#Golang
#LifeAsSoftwareDeveloper
#Maharlikans
#FilipinoSoftwareDeveloper
If you go with extra mile for buying me a cup of coffee, I appreciate it guys: https://ko-fi.com/maharlikanscode
Source Codes:
package main
import (
"flag"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
"unsafe"
"github.com/itrepablik/itrlog"
"github.com/kardianos/service"
"golang.org/x/sys/windows"
)
const TH32CS_SNAPPROCESS = 0x00000002
type Config struct {
Name, DisplayName, Description string
Dir string
Exec string
Args []string
Env []string
Stderr, Stdout string
}
var logger service.Logger
type program struct {
exit chan struct{}
service service.Service
*Config
cmd *exec.Cmd
}
func (p *program) Start(s service.Service) error {
fullExec, err := exec.LookPath(p.Exec)
if err != nil {
return fmt.Errorf("Failed to find executable %q: %v", p.Exec, err)
}
p.cmd = exec.Command(fullExec, p.Args...)
p.cmd.Dir = p.Dir
p.cmd.Env = append(os.Environ(), p.Env...)
go p.run()
return nil
}
func (p *program) run() {
logger.Info("Starting ", p.DisplayName)
itrlog.Info("Starting ", p.DisplayName)
defer func() {
if service.Interactive() {
p.Stop(p.service)
} else {
p.service.Stop()
}
}()
if p.Stderr != "" {
f, err := os.OpenFile(p.Stderr, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0777)
if err != nil {
logger.Warningf("Failed to open std err %q: %v", p.Stderr, err)
itrlog.Warnf("Failed to open std err %q: %v", p.Stderr, err)
return
}
defer f.Close()
p.cmd.Stderr = f
}
if p.Stdout != "" {
f, err := os.OpenFile(p.Stdout, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0777)
if err != nil {
logger.Warningf("Failed to open std out %q: %v", p.Stdout, err)
itrlog.Warnf("Failed to open std out %q: %v", p.Stdout, err)
return
}
defer f.Close()
p.cmd.Stdout = f
}
err := p.cmd.Run()
if err != nil {
logger.Warningf("Error running: %v", err)
itrlog.Warnf("Error running: %v", err)
}
return
}
func (p *program) Stop(s service.Service) error {
defer close(p.exit)
logger.Info("Stopping ", p.DisplayName)
itrlog.Info("Stopping ", p.DisplayName)
WindowsKillProcessByPID()
if p.cmd.ProcessState.Exited() == false {
p.cmd.Process.Kill()
}
if service.Interactive() {
os.Exit(0)
}
return nil
}
func WindowsKillProcessByPID() {
procs, err := processes()
if err != nil {
logger.Error(err)
itrlog.Error(err)
}
explorer := findProcessByName(procs, "tsnotify.exe")
if explorer != nil {
kill := exec.Command("taskkill", "/T", "/F", "/PID", strconv.Itoa(explorer.ProcessID))
err := kill.Run()
if err != nil {
logger.Error(err)
itrlog.Error(err)
}
}
}
type WindowsProcess struct {
ProcessID int
ParentProcessID int
Exe string
}
func processes() ([]WindowsProcess, error) {
handle, err := windows.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
if err != nil {
return nil, err
}
defer windows.CloseHandle(handle)
var entry windows.ProcessEntry32
entry.Size = uint32(unsafe.Sizeof(entry))
err = windows.Process32First(handle, &entry)
if err != nil {
return nil, err
}
results := make([]WindowsProcess, 0, 50)
for {
results = append(results, newWindowsProcess(&entry))
err = windows.Process32Next(handle, &entry)
if err != nil {
if err == syscall.ERROR_NO_MORE_FILES {
return results, nil
}
return nil, err
}
}
}
func findProcessByName(processes []WindowsProcess, name string) *WindowsProcess {
for _, p := range processes {
if strings.ToLower(p.Exe) == strings.ToLower(name) {
return &p
}
}
return nil
}
func newWindowsProcess(e *windows.ProcessEntry32) WindowsProcess {
end := 0
for {
if e.ExeFile[end] == 0 {
break
}
end++
}
return WindowsProcess{
ProcessID: int(e.ProcessID),
ParentProcessID: int(e.ParentProcessID),
Exe: syscall.UTF16ToString(e.ExeFile[:end]),
}
}
func main() {
// Call the kardianos OS service library here
svcFlag := flag.String("service", "", "Control the system service.")
flag.Parse()
svcConfig := &service.Config{
Name: "tsnotify",
DisplayName: "tsnotify",
Description: "Task scheduler status alert email notification app.",
}
// Get the current tsnotify.exe full path
fullexecpath, err := os.Executable()
if err != nil {
fmt.Println(err)
logger.Error(err)
itrlog.Error(err)
return
}
dir, _ := filepath.Split(fullexecpath)
execCMD := ""
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
execCMD = "C:\\windows\\system32\\cmd.exe"
}
configValues := &Config{
Dir: filepath.FromSlash(dir),
Exec: execCMD,
Args: []string{"/C", "tsnotify run"},
Env: []string{},
Stderr: "",
Stdout: "",
}
prg := &program{
exit: make(chan struct{}),
Config: configValues,
}
s, err := service.New(prg, svcConfig)
if err != nil {
logger.Error(err)
itrlog.Fatal(err)
}
Not enough space