mirror of
https://codeberg.org/frosty/modbot.git
synced 2024-09-19 11:46:34 -04:00
collection of changes
updated default config with go functions changed reader function implementations to use only a single function capture outputs in byte arrays and store outputs as byte arrays instead of strings add date reader move some main function code to separate functions move flags parsing to main
This commit is contained in:
parent
8c316dee38
commit
9ec3e94ae5
25
config.go
25
config.go
|
@ -23,30 +23,33 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
delim = "] ["
|
delim = []byte("] [")
|
||||||
prefix = "["
|
prefix = []byte("[")
|
||||||
suffix = "]"
|
suffix = []byte("]")
|
||||||
)
|
)
|
||||||
|
|
||||||
var modules = []Module{
|
var modules = []Module{
|
||||||
{
|
{
|
||||||
Func: readers.ReadExec("statusbar cpu"),
|
Func: readers.ReadCpuUsage(),
|
||||||
Interval: 5 * time.Second,
|
Interval: 5 * time.Second,
|
||||||
|
Template: `CPU {{printf "%.0f" .UsagePercent}}%`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Func: readers.ReadExec("statusbar volume"),
|
Func: readers.ReadBattery("BAT1"),
|
||||||
|
Interval: 60 * time.Second,
|
||||||
|
Template: "BAT {{.Capacity}}%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Func: readers.ReadExec("monitors volume"),
|
||||||
Signal: 1,
|
Signal: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Func: readers.ReadExec("statusbar battery"),
|
Func: readers.ReadDate("15:04:05"),
|
||||||
Interval: 60 * time.Second,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Func: readers.ReadExec("statusbar date"),
|
|
||||||
Interval: 1 * time.Second,
|
Interval: 1 * time.Second,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Func: readers.ReadExec("statusbar loadavg"),
|
Func: readers.ReadLoad(),
|
||||||
Interval: 5 * time.Second,
|
Interval: 5 * time.Second,
|
||||||
|
Template: "{{.OneMinute}}",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,8 @@ func BatteryTechnologyFromStr(technologyStr string) BatteryTechnology {
|
||||||
return TechnologyUnknown
|
return TechnologyUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
func readBattery(batteryName string) (interface{}, error) {
|
func ReadBattery(batteryName string) func() (interface{}, error) {
|
||||||
|
return func() (interface{}, error) {
|
||||||
capacityPath := fmt.Sprintf("/sys/class/power_supply/%s/capacity", batteryName)
|
capacityPath := fmt.Sprintf("/sys/class/power_supply/%s/capacity", batteryName)
|
||||||
statusPath := fmt.Sprintf("/sys/class/power_supply/%s/status", batteryName)
|
statusPath := fmt.Sprintf("/sys/class/power_supply/%s/status", batteryName)
|
||||||
technologyPath := fmt.Sprintf("/sys/class/power_supply/%s/technology", batteryName)
|
technologyPath := fmt.Sprintf("/sys/class/power_supply/%s/technology", batteryName)
|
||||||
|
@ -169,9 +170,4 @@ func readBattery(batteryName string) (interface{}, error) {
|
||||||
Technology: BatteryTechnologyFromStr(batteryTechnology),
|
Technology: BatteryTechnologyFromStr(batteryTechnology),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadBattery(batteryName string) func() (interface{}, error) {
|
|
||||||
return func() (interface{}, error) {
|
|
||||||
return readBattery(batteryName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,8 @@ import (
|
||||||
|
|
||||||
type CpuTemperatureInfo float32
|
type CpuTemperatureInfo float32
|
||||||
|
|
||||||
func readCpuTemperature(hwmonName, tempName string) (interface{}, error) {
|
func ReadCpuTemperature(hwmonName, tempName string) func() (interface{}, error) {
|
||||||
|
return func() (interface{}, error) {
|
||||||
tempPath := fmt.Sprintf("/sys/class/hwmon/%s/%s_input", hwmonName, tempName)
|
tempPath := fmt.Sprintf("/sys/class/hwmon/%s/%s_input", hwmonName, tempName)
|
||||||
|
|
||||||
file, err := os.Open(tempPath)
|
file, err := os.Open(tempPath)
|
||||||
|
@ -49,9 +50,4 @@ func readCpuTemperature(hwmonName, tempName string) (interface{}, error) {
|
||||||
|
|
||||||
return CpuTemperatureInfo(cpuTemperature), nil
|
return CpuTemperatureInfo(cpuTemperature), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadCpuTemperature(hwmonName, tempName string) func() (interface{}, error) {
|
|
||||||
return func() (interface{}, error) {
|
|
||||||
return readCpuTemperature(hwmonName, tempName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ func (cu CpuUsageInfo) String() string {
|
||||||
return fmt.Sprintf("%d%%", uint(cu.UsagePercent))
|
return fmt.Sprintf("%d%%", uint(cu.UsagePercent))
|
||||||
}
|
}
|
||||||
|
|
||||||
func readCpuUsage() (interface{}, error) {
|
func ReadCpuUsage() func() (interface{}, error) {
|
||||||
|
return func() (interface{}, error) {
|
||||||
file, err := os.Open("/proc/stat")
|
file, err := os.Open("/proc/stat")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CpuUsageInfo{}, err
|
return CpuUsageInfo{}, err
|
||||||
|
@ -81,9 +82,4 @@ func readCpuUsage() (interface{}, error) {
|
||||||
UsagePercent: float64(cpuInUse) * 100 / float64(cpuTotal),
|
UsagePercent: float64(cpuInUse) * 100 / float64(cpuTotal),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadCpuUsage() func() (interface{}, error) {
|
|
||||||
return func() (interface{}, error) {
|
|
||||||
return readCpuUsage()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
30
lib/readers/date.go
Normal file
30
lib/readers/date.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// modbot is a system information agregator
|
||||||
|
// Copyright (C) 2024 frosty <inthishouseofcards@gmail.com>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published
|
||||||
|
// by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package readers
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type DateInfo string
|
||||||
|
|
||||||
|
func ReadDate(format string) func() (interface{}, error) {
|
||||||
|
return func() (interface{}, error) {
|
||||||
|
date := time.Now()
|
||||||
|
|
||||||
|
// Reference: https://gosamples.dev/date-time-format-cheatsheet/
|
||||||
|
return date.Format(format), nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,8 @@ import (
|
||||||
|
|
||||||
type ExecInfo string
|
type ExecInfo string
|
||||||
|
|
||||||
func readExec(command string) (interface{}, error) {
|
func ReadExec(command string) func() (interface{}, error) {
|
||||||
|
return func() (interface{}, error) {
|
||||||
args := []string{"sh", "-c", command}
|
args := []string{"sh", "-c", command}
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
|
|
||||||
|
@ -36,10 +37,12 @@ func readExec(command string) (interface{}, error) {
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
return ExecInfo(""), err
|
return ExecInfo(""), err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.ProcessState.ExitCode() != 0 {
|
if cmd.ProcessState.ExitCode() != 0 {
|
||||||
return ExecInfo(""), errors.New("returned non-zero exit code")
|
return ExecInfo(""), errors.New("returned non-zero exit code")
|
||||||
}
|
}
|
||||||
|
if len(stderr.String()) > 0 {
|
||||||
|
return ExecInfo(""), errors.New("data in stderr found")
|
||||||
|
}
|
||||||
|
|
||||||
outputLines := strings.Split(stdout.String(), "\n")
|
outputLines := strings.Split(stdout.String(), "\n")
|
||||||
if len(outputLines) == 0 {
|
if len(outputLines) == 0 {
|
||||||
|
@ -48,11 +51,5 @@ func readExec(command string) (interface{}, error) {
|
||||||
|
|
||||||
outputString := outputLines[0]
|
outputString := outputLines[0]
|
||||||
return ExecInfo(outputString), nil
|
return ExecInfo(outputString), nil
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadExec(command string) func() (interface{}, error) {
|
|
||||||
return func() (interface{}, error) {
|
|
||||||
return readExec(command)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,8 @@ func (l LoadInfo) String() string {
|
||||||
return fmt.Sprintf("%v %v %v", l.OneMinute, l.FiveMinute, l.FifteenMinute)
|
return fmt.Sprintf("%v %v %v", l.OneMinute, l.FiveMinute, l.FifteenMinute)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readLoad() (interface{}, error) {
|
func ReadLoad() func() (interface{}, error) {
|
||||||
|
return func() (interface{}, error) {
|
||||||
const loadPath = "/proc/loadavg"
|
const loadPath = "/proc/loadavg"
|
||||||
|
|
||||||
file, err := os.Open(loadPath)
|
file, err := os.Open(loadPath)
|
||||||
|
@ -60,9 +61,4 @@ func readLoad() (interface{}, error) {
|
||||||
FifteenMinute: fields[2],
|
FifteenMinute: fields[2],
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadLoad() func() (interface{}, error) {
|
|
||||||
return func() (interface{}, error) {
|
|
||||||
return readLoad()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,8 @@ type MemoryInfo struct {
|
||||||
UsedPretty string
|
UsedPretty string
|
||||||
}
|
}
|
||||||
|
|
||||||
func readMemory() (interface{}, error) {
|
func ReadMemory() func() (interface{}, error) {
|
||||||
|
return func() (interface{}, error) {
|
||||||
file, err := os.Open("/proc/meminfo")
|
file, err := os.Open("/proc/meminfo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return MemoryInfo{}, err
|
return MemoryInfo{}, err
|
||||||
|
@ -89,9 +90,4 @@ func readMemory() (interface{}, error) {
|
||||||
UsedPretty: ui.PrettifyKib(memUsed, 2),
|
UsedPretty: ui.PrettifyKib(memUsed, 2),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadMemory() func() (interface{}, error) {
|
|
||||||
return func() (interface{}, error) {
|
|
||||||
return readMemory()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,8 @@ type OsInfo struct {
|
||||||
Version string
|
Version string
|
||||||
}
|
}
|
||||||
|
|
||||||
func readOs() (interface{}, error) {
|
func ReadOs() func() (interface{}, error) {
|
||||||
|
return func() (interface{}, error) {
|
||||||
const osPath = "/lib/os-release"
|
const osPath = "/lib/os-release"
|
||||||
|
|
||||||
file, err := os.Open(osPath)
|
file, err := os.Open(osPath)
|
||||||
|
@ -70,9 +71,4 @@ func readOs() (interface{}, error) {
|
||||||
Version: osVersion,
|
Version: osVersion,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadOs() func() (interface{}, error) {
|
|
||||||
return func() (interface{}, error) {
|
|
||||||
return readOs()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,8 @@ func (u UptimeInfo) String() string {
|
||||||
return builder.String()
|
return builder.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func readUptime() (interface{}, error) {
|
func ReadUptime() func() (interface{}, error) {
|
||||||
|
return func() (interface{}, error) {
|
||||||
const uptimePath = "/proc/uptime"
|
const uptimePath = "/proc/uptime"
|
||||||
|
|
||||||
file, err := os.Open(uptimePath)
|
file, err := os.Open(uptimePath)
|
||||||
|
@ -92,9 +93,4 @@ func readUptime() (interface{}, error) {
|
||||||
|
|
||||||
return UptimeInfo(uptime), nil
|
return UptimeInfo(uptime), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadUptime() func() (interface{}, error) {
|
|
||||||
return func() (interface{}, error) {
|
|
||||||
return readUptime()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
170
main.go
170
main.go
|
@ -36,10 +36,11 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
updateChan = make(chan int)
|
updateChan = make(chan int)
|
||||||
moduleOutputs = make([]string, len(modules))
|
moduleOutputs = make([][]byte, len(modules))
|
||||||
|
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
lastOutput string
|
|
||||||
|
sigChan = make(chan os.Signal, 1024)
|
||||||
|
signalMap = make(map[os.Signal][]*Module)
|
||||||
|
|
||||||
// X connection data
|
// X connection data
|
||||||
x *xgb.Conn
|
x *xgb.Conn
|
||||||
|
@ -62,17 +63,16 @@ type Module struct {
|
||||||
|
|
||||||
func (m *Module) Run() {
|
func (m *Module) Run() {
|
||||||
if m.pos < 0 || m.pos >= len(modules) {
|
if m.pos < 0 || m.pos >= len(modules) {
|
||||||
log.Printf("invalid module index %d\n", m.pos)
|
log.Printf("invalid module index: %d\n", m.pos)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var output bytes.Buffer
|
||||||
|
|
||||||
info, err := m.Func()
|
info, err := m.Func()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
moduleOutputs[m.pos] = "failed"
|
output.WriteString("failed")
|
||||||
return
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
var output string
|
|
||||||
if m.Template != "" {
|
if m.Template != "" {
|
||||||
// Parse the output and apply the provided template
|
// Parse the output and apply the provided template
|
||||||
tmpl, err := template.New("module").Parse(m.Template)
|
tmpl, err := template.New("module").Parse(m.Template)
|
||||||
|
@ -81,53 +81,24 @@ func (m *Module) Run() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
if err := tmpl.Execute(&output, info); err != nil {
|
||||||
if err := tmpl.Execute(&buf, info); err != nil {
|
|
||||||
log.Printf("template execution error: %v\n", err)
|
log.Printf("template execution error: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
output = buf.String()
|
|
||||||
} else {
|
} else {
|
||||||
// Use the output as is
|
// Use the output as is
|
||||||
output = fmt.Sprintf("%v", info)
|
fmt.Fprintf(&output, "%v", info)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
moduleOutputs[m.pos] = output
|
moduleOutputs[m.pos] = output.Bytes()
|
||||||
updateChan <- 1
|
updateChan <- 1
|
||||||
mutex.Unlock()
|
mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFlags() Flags {
|
func (m *Module) Init(pos int) {
|
||||||
var flags Flags
|
m.pos = pos
|
||||||
flag.BoolVar(&flags.SetXRootName, "x", false, "set x root window name")
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
return flags
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flags := parseFlags()
|
|
||||||
|
|
||||||
// Connect to X and get the root window if requested
|
|
||||||
if flags.SetXRootName {
|
|
||||||
var err error
|
|
||||||
x, err = xgb.NewConn()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("X connection failed: %s\n", err.Error())
|
|
||||||
}
|
|
||||||
root = xproto.Setup(x).DefaultScreen(x).Root
|
|
||||||
}
|
|
||||||
|
|
||||||
sigChan := make(chan os.Signal, 1024)
|
|
||||||
signalMap := make(map[os.Signal][]*Module)
|
|
||||||
|
|
||||||
// Initialize modules
|
|
||||||
for i := range modules {
|
|
||||||
go func(m *Module, i int) {
|
|
||||||
m.pos = i
|
|
||||||
|
|
||||||
if m.Signal != 0 {
|
if m.Signal != 0 {
|
||||||
sig := syscall.Signal(34 + m.Signal)
|
sig := syscall.Signal(34 + m.Signal)
|
||||||
|
@ -144,48 +115,97 @@ func main() {
|
||||||
m.Run()
|
m.Run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}(&modules[i], i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update output on difference
|
func grabXRootWindow() (*xgb.Conn, xproto.Window, error) {
|
||||||
go func() {
|
conn, err := xgb.NewConn()
|
||||||
for range updateChan {
|
if err != nil {
|
||||||
mutex.Lock()
|
return nil, 0, err
|
||||||
var combinedOutput string
|
}
|
||||||
for i, output := range moduleOutputs {
|
|
||||||
if output == "" {
|
root := xproto.Setup(conn).DefaultScreen(conn).Root
|
||||||
|
if conn == nil {
|
||||||
|
return nil, 0, fmt.Errorf("failed to create X connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, root, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createOutput(b *bytes.Buffer) {
|
||||||
|
b.Write(prefix)
|
||||||
|
first := true
|
||||||
|
for _, output := range moduleOutputs {
|
||||||
|
if output == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if i > 0 {
|
if !first {
|
||||||
combinedOutput += delim
|
b.Write(delim)
|
||||||
|
} else {
|
||||||
|
first = false
|
||||||
}
|
}
|
||||||
combinedOutput += output
|
b.Write(output)
|
||||||
}
|
}
|
||||||
combinedOutput = prefix + combinedOutput + suffix
|
b.Write(suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func monitorUpdates(setXRootName bool) {
|
||||||
|
var lastOutput []byte
|
||||||
|
var combinedOutput bytes.Buffer
|
||||||
|
|
||||||
|
for range updateChan {
|
||||||
|
mutex.Lock()
|
||||||
|
combinedOutput.Reset()
|
||||||
|
createOutput(&combinedOutput)
|
||||||
mutex.Unlock()
|
mutex.Unlock()
|
||||||
|
|
||||||
// Output to either X root window name or stdout based on flags
|
combinedOutputBytes := combinedOutput.Bytes()
|
||||||
if combinedOutput != lastOutput {
|
|
||||||
if flags.SetXRootName {
|
|
||||||
// Set the X root window name
|
|
||||||
outputBytes := []byte(combinedOutput)
|
|
||||||
xproto.ChangeProperty(x, xproto.PropModeReplace, root, xproto.AtomWmName, xproto.AtomString, 8, uint32(len(outputBytes)), outputBytes)
|
|
||||||
} else {
|
|
||||||
// Print to stdout
|
|
||||||
fmt.Printf("%v\n", combinedOutput)
|
|
||||||
}
|
|
||||||
lastOutput = combinedOutput
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Handle module signals
|
if !bytes.Equal(combinedOutputBytes, lastOutput) {
|
||||||
for sig := range sigChan {
|
if setXRootName {
|
||||||
go func(sig *os.Signal) {
|
// Set X root window name
|
||||||
ms := signalMap[*sig]
|
xproto.ChangeProperty(x, xproto.PropModeReplace, root, xproto.AtomWmName, xproto.AtomString, 8, uint32(len(combinedOutputBytes)), combinedOutputBytes)
|
||||||
|
} else {
|
||||||
|
// Send to stdout
|
||||||
|
fmt.Printf("%s\n", combinedOutputBytes)
|
||||||
|
}
|
||||||
|
lastOutput = append([]byte(nil), combinedOutputBytes...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSignal(sig os.Signal) {
|
||||||
|
ms := signalMap[sig]
|
||||||
for _, m := range ms {
|
for _, m := range ms {
|
||||||
go m.Run()
|
go m.Run()
|
||||||
}
|
}
|
||||||
}(&sig)
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Parse flags
|
||||||
|
var flags Flags
|
||||||
|
flag.BoolVar(&flags.SetXRootName, "x", false, "set x root window name")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Grab X root window if requested
|
||||||
|
if flags.SetXRootName {
|
||||||
|
var err error
|
||||||
|
x, root, err = grabXRootWindow()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error grabbing X root window: %v\n", err)
|
||||||
|
}
|
||||||
|
defer x.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize modules
|
||||||
|
for i := range modules {
|
||||||
|
go modules[i].Init(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monitor changes to the combined output
|
||||||
|
go monitorUpdates(flags.SetXRootName)
|
||||||
|
|
||||||
|
// Handle signals sent for modules
|
||||||
|
for sig := range sigChan {
|
||||||
|
go handleSignal(sig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue