diff --git a/config.go b/config.go index a987aaa3..de0a6f4a 100644 --- a/config.go +++ b/config.go @@ -47,6 +47,7 @@ type graphConfig struct { Noop []NoopType `yaml:"noop"` File []FileType `yaml:"file"` Service []ServiceType `yaml:"service"` + Exec []ExecType `yaml:"exec"` } `yaml:"types"` Collector []collectorTypeConfig `yaml:"collect"` Edges []edgeConfig `yaml:"edges"` @@ -79,18 +80,31 @@ func ParseConfigFromFile(filename string) *graphConfig { return &config } -func UpdateGraphFromConfig(config *graphConfig, hostname string, g *Graph, etcdO *EtcdWObject) { +// XXX: we need to fix this function so that it either fails without modifying +// the graph, passes successfully and modifies it, or basically panics i guess +// this way an invalid compilation can leave the old graph running, and we we +// don't modify a partial graph. so we really need to validate, and then perform +// whatever actions are necessary +// finding some way to do this on a copy of the graph, and then do a graph diff +// and merge the new data into the old graph would be more appropriate, in +// particular if we can ensure the graph merge can't fail. As for the putting +// of stuff into etcd, we should probably store the operations to complete in +// the new graph, and keep retrying until it succeeds, thus blocking any new +// etcd operations until that time. +func UpdateGraphFromConfig(config *graphConfig, hostname string, g *Graph, etcdO *EtcdWObject) bool { var NoopMap map[string]*Vertex = make(map[string]*Vertex) var FileMap map[string]*Vertex = make(map[string]*Vertex) var ServiceMap map[string]*Vertex = make(map[string]*Vertex) + var ExecMap map[string]*Vertex = make(map[string]*Vertex) var lookup map[string]map[string]*Vertex = make(map[string]map[string]*Vertex) lookup["noop"] = NoopMap lookup["file"] = FileMap lookup["service"] = ServiceMap + lookup["exec"] = ExecMap - //fmt.Printf("%+v\n", config) // debug + //log.Printf("%+v", config) // debug g.SetName(config.Graph) // set graph name @@ -140,6 +154,17 @@ func UpdateGraphFromConfig(config *graphConfig, hostname string, g *Graph, etcdO keep = append(keep, v) // append } + for _, t := range config.Types.Exec { + obj := NewExecType(t.Name, t.Cmd, t.Shell, t.Timeout, t.WatchCmd, t.WatchShell, t.IfCmd, t.IfShell, t.PollInt, t.State) + v := g.GetVertexMatch(obj) + if v == nil { // no match found + v = NewVertex(obj) + g.AddVertex(v) // call standalone in case not part of an edge + } + ExecMap[obj.Name] = v // used for constructing edges + keep = append(keep, v) // append + } + // lookup from etcd graph // do all the graph look ups in one single step, so that if the etcd // database changes, we don't have a partial state of affairs... @@ -186,6 +211,19 @@ func UpdateGraphFromConfig(config *graphConfig, hostname string, g *Graph, etcdO } for _, e := range config.Edges { + if _, ok := lookup[e.From.Type]; !ok { + return false + } + if _, ok := lookup[e.To.Type]; !ok { + return false + } + if _, ok := lookup[e.From.Type][e.From.Name]; !ok { + return false + } + if _, ok := lookup[e.To.Type][e.To.Name]; !ok { + return false + } g.AddEdge(lookup[e.From.Type][e.From.Name], lookup[e.To.Type][e.To.Name], NewEdge(e.Name)) } + return true } diff --git a/configwatch.go b/configwatch.go index 5544ca50..9dd91930 100644 --- a/configwatch.go +++ b/configwatch.go @@ -51,7 +51,7 @@ func ConfigWatch(file string) chan bool { if current == "" { // the empty string top is the root dir ("/") current = "/" } - log.Printf("Watching: %v\n", current) // attempting to watch... + log.Printf("Watching: %v", current) // attempting to watch... // initialize in the loop so that we can reset on rm-ed handles err = watcher.Add(current) @@ -61,10 +61,10 @@ func ConfigWatch(file string) chan bool { } else if err == syscall.ENOSPC { // XXX: occasionally: no space left on device, // XXX: probably due to lack of inotify watches - log.Printf("Lack of watches for config(%v) error: %+v\n", file, err.Error) // 0x408da0 + log.Printf("Lack of watches for config(%v) error: %+v", file, err.Error) // 0x408da0 log.Fatal(err) } else { - log.Printf("Unknown config(%v) error:\n", file) + log.Printf("Unknown config(%v) error:", file) log.Fatal(err) } index = int(math.Max(1, float64(index))) @@ -92,7 +92,7 @@ func ConfigWatch(file string) chan bool { // event.Name: /tmp/mgmt/f3 and current: /tmp/mgmt/f2 continue } - //log.Printf("The delta depth is: %v\n", delta_depth) + //log.Printf("The delta depth is: %v", delta_depth) // if we have what we wanted, awesome, send an event... if event.Name == safename { diff --git a/etcd.go b/etcd.go index d0f0c3db..707145ce 100644 --- a/etcd.go +++ b/etcd.go @@ -127,12 +127,13 @@ func (etcdO *EtcdWObject) EtcdWatch() chan etcdMsg { tmult := 2 // multiplier for exponential delay tmax := 16000 // max delay watcher := kapi.Watcher("/exported/", &etcd.WatcherOptions{Recursive: true}) + etcdch := etcdO.EtcdChannelWatch(watcher, etcd_context.Background()) for { log.Printf("Etcd: Watching...") var resp *etcd.Response = nil var err error = nil select { - case out := <-etcdO.EtcdChannelWatch(watcher, etcd_context.Background()): + case out := <-etcdch: etcdO.SetState(etcdNil) resp, err = out.resp, out.err diff --git a/examples/graph7.yaml b/examples/graph7.yaml new file mode 100644 index 00000000..0a866d88 --- /dev/null +++ b/examples/graph7.yaml @@ -0,0 +1,17 @@ +--- +graph: mygraph +types: + noop: + - name: noop1 + exec: + - name: exec1 + cmd: sleep 10s + shell: '' + timeout: 0 + watchcmd: '' + watchshell: '' + ifcmd: '' + ifshell: '' + pollint: 0 + state: present +edges: diff --git a/examples/graph8.yaml b/examples/graph8.yaml new file mode 100644 index 00000000..b1d86301 --- /dev/null +++ b/examples/graph8.yaml @@ -0,0 +1,61 @@ +--- +graph: mygraph +types: + noop: + - name: noop1 + exec: + - name: exec1 + cmd: sleep 10s + shell: '' + timeout: 0 + watchcmd: '' + watchshell: '' + ifcmd: '' + ifshell: '' + pollint: 0 + state: present + - name: exec2 + cmd: sleep 10s + shell: '' + timeout: 0 + watchcmd: '' + watchshell: '' + ifcmd: '' + ifshell: '' + pollint: 0 + state: present + - name: exec3 + cmd: sleep 10s + shell: '' + timeout: 0 + watchcmd: '' + watchshell: '' + ifcmd: '' + ifshell: '' + pollint: 0 + state: present + - name: exec4 + cmd: sleep 10s + shell: '' + timeout: 0 + watchcmd: '' + watchshell: '' + ifcmd: '' + ifshell: '' + pollint: 0 + state: present +edges: +- name: e1 + from: + type: exec + name: exec1 + to: + type: exec + name: exec2 +- name: e2 + from: + type: exec + name: exec2 + to: + type: exec + name: exec3 diff --git a/exec.go b/exec.go new file mode 100644 index 00000000..5a45f50b --- /dev/null +++ b/exec.go @@ -0,0 +1,333 @@ +// Mgmt +// Copyright (C) 2013-2016+ James Shubin and the project contributors +// Written by James Shubin and the project contributors +// +// 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 . + +package main + +import ( + "bufio" + "bytes" + "log" + "os/exec" + "strings" +) + +type ExecType struct { + BaseType `yaml:",inline"` + State string `yaml:"state"` // state: exists/present?, absent, (undefined?) + Cmd string `yaml:"cmd"` // the command to run + Shell string `yaml:"shell"` // the (optional) shell to use to run the cmd + Timeout int `yaml:"timeout"` // the cmd timeout in seconds + WatchCmd string `yaml:"watchcmd"` // the watch command to run + WatchShell string `yaml:"watchshell"` // the (optional) shell to use to run the watch cmd + IfCmd string `yaml:"ifcmd"` // the if command to run + IfShell string `yaml:"ifshell"` // the (optional) shell to use to run the if cmd + PollInt int `yaml:"pollint"` // the poll interval for the ifcmd + isStateOK bool // whether the state is okay based on events or not +} + +func NewExecType(name, cmd, shell string, timeout int, watchcmd, watchshell, ifcmd, ifshell string, pollint int, state string) *ExecType { + // FIXME if path = nil, path = name ... + return &ExecType{ + BaseType: BaseType{ + Name: name, + events: make(chan Event), + vertex: nil, + }, + Cmd: cmd, + Shell: shell, + Timeout: timeout, + WatchCmd: watchcmd, + WatchShell: watchshell, + IfCmd: ifcmd, + IfShell: ifshell, + PollInt: pollint, + State: state, + } +} + +func (obj *ExecType) GetType() string { + return "Exec" +} + +// validate if the params passed in are valid data +// FIXME: where should this get called ? +func (obj *ExecType) Validate() bool { + if obj.Cmd == "" { // this is the only thing that is really required + return false + } + + // if we have a watch command, then we don't poll with the if command! + if obj.WatchCmd != "" && obj.PollInt > 0 { + return false + } + + return true +} + +// wraps the scanner output in a channel +func (obj *ExecType) BufioChanScanner(scanner *bufio.Scanner) (chan string, chan error) { + ch, errch := make(chan string), make(chan error) + go func() { + for scanner.Scan() { + ch <- scanner.Text() // blocks here ? + if e := scanner.Err(); e != nil { + errch <- e // send any misc errors we encounter + //break // TODO ? + } + } + close(ch) + errch <- scanner.Err() // eof or some err + close(errch) + }() + return ch, errch +} + +// Exec watcher +func (obj *ExecType) Watch() { + if obj.IsWatching() { + return + } + obj.SetWatching(true) + defer obj.SetWatching(false) + + var send = false // send event? + bufioch, errch := make(chan string), make(chan error) + //vertex := obj.GetVertex() // stored with SetVertex + + if obj.WatchCmd != "" { + var cmdName string + var cmdArgs []string + if obj.WatchShell == "" { + // call without a shell + // FIXME: are there still whitespace splitting issues? + split := strings.Fields(obj.WatchCmd) + cmdName = split[0] + //d, _ := os.Getwd() // TODO: how does this ever error ? + //cmdName = path.Join(d, cmdName) + cmdArgs = split[1:len(split)] + } else { + cmdName = obj.Shell // usually bash, or sh + cmdArgs = []string{"-c", obj.WatchCmd} + } + cmd := exec.Command(cmdName, cmdArgs...) + //cmd.Dir = "" // look for program in pwd ? + + cmdReader, err := cmd.StdoutPipe() + if err != nil { + log.Printf("%v[%v]: Error creating StdoutPipe for Cmd: %v", obj.GetType(), obj.GetName(), err) + log.Fatal(err) // XXX: how should we handle errors? + } + scanner := bufio.NewScanner(cmdReader) + + defer cmd.Wait() // XXX: is this necessary? + defer cmd.Process.Kill() // TODO: is this necessary? + if err := cmd.Start(); err != nil { + log.Printf("%v[%v]: Error starting Cmd: %v", obj.GetType(), obj.GetName(), err) + log.Fatal(err) // XXX: how should we handle errors? + } + + bufioch, errch = obj.BufioChanScanner(scanner) + } + + for { + select { + case text := <-bufioch: + obj.SetState(typeNil) + + // each time we get a line of output, we loop! + log.Printf("%v[%v]: Watch output: %s", obj.GetType(), obj.GetName(), text) + if text != "" { + send = true + } + + case err := <-errch: + obj.SetState(typeNil) // XXX ? + if err == nil { // EOF + // FIXME: add an "if watch command ends/crashes" + // restart or generate error option + log.Printf("%v[%v]: Reached EOF", obj.GetType(), obj.GetName()) + return + } + log.Printf("%v[%v]: Error reading input?: %v", obj.GetType(), obj.GetName(), err) + log.Fatal(err) + // XXX: how should we handle errors? + + case event := <-obj.events: + obj.SetState(typeNil) + if ok := obj.ReadEvent(&event); !ok { + return // exit + } + send = true + + case _ = <-TimeAfterOrBlock(obj.ctimeout): + obj.SetState(typeConvergedTimeout) + obj.converged <- true + continue + } + + // do all our event sending all together to avoid duplicate msgs + if send { + send = false + obj.isStateOK = false // something made state dirty + obj.Process(obj) // XXX: rename this function + } + } +} + +// TODO: expand the IfCmd to be a list of commands +func (obj *ExecType) StateOK() bool { + + // if there is a watch command, but no if command, run based on state + if b := obj.isStateOK; obj.WatchCmd != "" && obj.IfCmd == "" { + obj.isStateOK = true // reset + //if !obj.isStateOK { obj.isStateOK = true; return false } + return b + + // if there is no watcher, but there is an onlyif check, run it to see + } else if obj.IfCmd != "" { // && obj.WatchCmd == "" + // there is a watcher, but there is also an if command + //} else if obj.IfCmd != "" && obj.WatchCmd != "" { + + if obj.PollInt > 0 { // && obj.WatchCmd == "" + // XXX have the Watch() command output onlyif poll events... + // XXX we can optimize by saving those results for returning here + // return XXX + } + + var cmdName string + var cmdArgs []string + if obj.IfShell == "" { + // call without a shell + // FIXME: are there still whitespace splitting issues? + split := strings.Fields(obj.IfCmd) + cmdName = split[0] + //d, _ := os.Getwd() // TODO: how does this ever error ? + //cmdName = path.Join(d, cmdName) + cmdArgs = split[1:len(split)] + } else { + cmdName = obj.IfShell // usually bash, or sh + cmdArgs = []string{"-c", obj.IfCmd} + } + err := exec.Command(cmdName, cmdArgs...).Run() + if err != nil { + // TODO: check exit value + return true // don't run + } + return false // just run + + // if there is no watcher and no onlyif check, assume we should run + } else { // if obj.WatchCmd == "" && obj.IfCmd == "" { + return false // just run + } +} + +func (obj *ExecType) Apply() bool { + log.Printf("%v[%v]: Apply", obj.GetType(), obj.GetName()) + var cmdName string + var cmdArgs []string + if obj.Shell == "" { + // call without a shell + // FIXME: are there still whitespace splitting issues? + // TODO: we could make the split character user selectable...! + split := strings.Fields(obj.Cmd) + cmdName = split[0] + //d, _ := os.Getwd() // TODO: how does this ever error ? + //cmdName = path.Join(d, cmdName) + cmdArgs = split[1:len(split)] + } else { + cmdName = obj.Shell // usually bash, or sh + cmdArgs = []string{"-c", obj.Cmd} + } + cmd := exec.Command(cmdName, cmdArgs...) + //cmd.Dir = "" // look for program in pwd ? + var out bytes.Buffer + cmd.Stdout = &out + + if err := cmd.Start(); err != nil { + log.Printf("%v[%v]: Error starting Cmd: %v", obj.GetType(), obj.GetName(), err) + return false + } + + timeout := obj.Timeout + if timeout == 0 { // zero timeout means no timer, so disable it + timeout = -1 + } + done := make(chan error) + go func() { done <- cmd.Wait() }() + + select { + case err := <-done: + if err != nil { + log.Printf("%v[%v]: Error waiting for Cmd: %v", obj.GetType(), obj.GetName(), err) + return false + } + + case <-TimeAfterOrBlock(timeout): + log.Printf("%v[%v]: Timeout waiting for Cmd", obj.GetType(), obj.GetName()) + //cmd.Process.Kill() // TODO: is this necessary? + return false + } + + // TODO: if we printed the stdout while the command is running, this + // would be nice, but it would require terminal log output that doesn't + // interleave all the parallel parts which would mix it all up... + if s := out.String(); s == "" { + log.Printf("Exec[%v]: Command output is empty!", obj.Name) + } else { + log.Printf("Exec[%v]: Command output is:", obj.Name) + log.Printf(out.String()) + } + // XXX: return based on exit value!! + return true +} + +func (obj *ExecType) Compare(typ Type) bool { + switch typ.(type) { + case *ExecType: + typ := typ.(*ExecType) + if obj.Name != typ.Name { + return false + } + if obj.Cmd != typ.Cmd { + return false + } + if obj.Shell != typ.Shell { + return false + } + if obj.Timeout != typ.Timeout { + return false + } + if obj.WatchCmd != typ.WatchCmd { + return false + } + if obj.WatchShell != typ.WatchShell { + return false + } + if obj.IfCmd != typ.IfCmd { + return false + } + if obj.PollInt != typ.PollInt { + return false + } + if obj.State != typ.State { + return false + } + default: + return false + } + return true +} diff --git a/file.go b/file.go index 710241fe..e7382e25 100644 --- a/file.go +++ b/file.go @@ -20,7 +20,6 @@ package main import ( "crypto/sha256" "encoding/hex" - "fmt" "gopkg.in/fsnotify.v1" //"github.com/go-fsnotify/fsnotify" // git master of "gopkg.in/fsnotify.v1" "io" @@ -106,7 +105,7 @@ func (obj *FileType) Watch() { //var recursive bool = false //var isdir = (obj.GetPath()[len(obj.GetPath())-1:] == "/") // dirs have trailing slashes - //fmt.Printf("IsDirectory: %v\n", isdir) + //log.Printf("IsDirectory: %v", isdir) //vertex := obj.GetVertex() // stored with SetVertex var safename = path.Clean(obj.GetPath()) // no trailing slash @@ -127,7 +126,7 @@ func (obj *FileType) Watch() { if current == "" { // the empty string top is the root dir ("/") current = "/" } - log.Printf("Watching: %v\n", current) // attempting to watch... + log.Printf("Watching: %v", current) // attempting to watch... // initialize in the loop so that we can reset on rm-ed handles err = watcher.Add(current) @@ -137,10 +136,10 @@ func (obj *FileType) Watch() { } else if err == syscall.ENOSPC { // XXX: occasionally: no space left on device, // XXX: probably due to lack of inotify watches - log.Printf("Lack of watches for file[%v] error: %+v\n", obj.Name, err.Error) // 0x408da0 + log.Printf("Lack of watches for file[%v] error: %+v", obj.Name, err.Error) // 0x408da0 log.Fatal(err) } else { - log.Printf("Unknown file[%v] error:\n", obj.Name) + log.Printf("Unknown file[%v] error:", obj.Name) log.Fatal(err) } index = int(math.Max(1, float64(index))) @@ -169,7 +168,7 @@ func (obj *FileType) Watch() { // event.Name: /tmp/mgmt/f3 and current: /tmp/mgmt/f2 continue } - //log.Printf("The delta depth is: %v\n", delta_depth) + //log.Printf("The delta depth is: %v", delta_depth) // if we have what we wanted, awesome, send an event... if event.Name == safename { @@ -294,7 +293,7 @@ func (obj *FileType) StateOKFile() bool { } sha256sum := hex.EncodeToString(hash.Sum(nil)) - //fmt.Printf("sha256sum: %v\n", sha256sum) + //log.Printf("sha256sum: %v", sha256sum) if obj.HashSHA256fromContent() == sha256sum { return true @@ -314,7 +313,7 @@ func (obj *FileType) StateOKDir() bool { } func (obj *FileType) Apply() bool { - fmt.Printf("Apply->File[%v]\n", obj.Name) + log.Printf("%v[%v]: Apply", obj.GetType(), obj.GetName()) if PathIsDir(obj.GetPath()) { return obj.ApplyDir() @@ -330,7 +329,7 @@ func (obj *FileType) ApplyFile() bool { } if obj.State == "absent" { - log.Printf("About to remove: %v\n", obj.GetPath()) + log.Printf("About to remove: %v", obj.GetPath()) err := os.Remove(obj.GetPath()) if err != nil { return false @@ -338,7 +337,7 @@ func (obj *FileType) ApplyFile() bool { return true } - //fmt.Println("writing: " + filename) + //log.Println("writing: " + filename) f, err := os.Create(obj.GetPath()) if err != nil { log.Println("error:", err) diff --git a/main.go b/main.go index 7fb482ee..c102ef81 100644 --- a/main.go +++ b/main.go @@ -18,7 +18,6 @@ package main import ( - "fmt" "github.com/codegangsta/cli" "log" "os" @@ -49,7 +48,6 @@ func waitForSignal(exit chan bool) { select { case e := <-signals: // any signal will do if e == os.Interrupt { - fmt.Println() // put ^C char from terminal on its own line log.Println("Interrupted by ^C") } else { log.Println("Interrupted by signal") @@ -64,8 +62,8 @@ func run(c *cli.Context) { var wg sync.WaitGroup exit := make(chan bool) // exit signal converged := make(chan bool) // converged signal - log.Printf("This is: %v, version: %v\n", program, version) - log.Printf("Start: %v\n", start) + log.Printf("This is: %v, version: %v", program, version) + log.Printf("Main: Start: %v", start) G := NewGraph("Graph") // give graph a default name // exit after `max-runtime` seconds for no reason at all... @@ -102,7 +100,7 @@ func run(c *cli.Context) { go func() { startchan <- struct{}{} }() file := c.String("file") configchan := ConfigWatch(file) - log.Printf("Starting etcd...\n") + log.Printf("Etcd: Starting...") etcdchan := etcdO.EtcdWatch() first := true // first loop or not for { @@ -144,8 +142,10 @@ func run(c *cli.Context) { // build the graph from a config file // build the graph on events (eg: from etcd) - UpdateGraphFromConfig(config, hostname, G, etcdO) - log.Printf("Graph: %v\n", G) // show graph + if !UpdateGraphFromConfig(config, hostname, G, etcdO) { + log.Fatal("Config: We borked the graph.") // XXX + } + log.Printf("Graph: %v", G) // show graph err := G.ExecGraphviz(c.String("graphviz-filter"), c.String("graphviz")) if err != nil { log.Printf("Graphviz: %v", err) @@ -200,7 +200,7 @@ func run(c *cli.Context) { }() } - log.Println("Running...") + log.Println("Main: Running...") waitForSignal(exit) // pass in exit channel to watch @@ -208,9 +208,9 @@ func run(c *cli.Context) { if DEBUG { for i := range G.GetVerticesChan() { - fmt.Printf("Vertex: %v\n", i) + log.Printf("Vertex: %v", i) } - fmt.Printf("Graph: %v\n", G) + log.Printf("Graph: %v", G) } wg.Wait() // wait for primary go routines to exit diff --git a/pgraph.go b/pgraph.go index d6ecdac8..3ee8e80a 100644 --- a/pgraph.go +++ b/pgraph.go @@ -373,7 +373,7 @@ func (g *Graph) FilterGraph(name string, vertices []*Vertex) *Graph { for k1, x := range g.Adjacency { for k2, e := range x { - //fmt.Printf("Filter: %v -> %v # %v\n", k1.Name, k2.Name, e.Name) + //log.Printf("Filter: %v -> %v # %v", k1.Name, k2.Name, e.Name) if Contains(vertices, k1) || Contains(vertices, k2) { newgraph.AddEdge(k1, k2, e) } @@ -548,7 +548,7 @@ func (g *Graph) Start(wg *sync.WaitGroup) { // start or continue go func(vv *Vertex) { defer wg.Done() vv.Type.Watch() - log.Printf("Finish: %v", vv.GetName()) + log.Printf("%v[%v]: Exited", vv.GetType(), vv.GetName()) }(v) } diff --git a/service.go b/service.go index 69e94415..140804aa 100644 --- a/service.go +++ b/service.go @@ -98,14 +98,14 @@ func (obj *ServiceType) Watch() { // firstly, does service even exist or not? loadstate, err := conn.GetUnitProperty(service, "LoadState") if err != nil { - log.Printf("Failed to get property: %v\n", err) + log.Printf("Failed to get property: %v", err) invalid = true } if !invalid { var notFound = (loadstate.Value == dbus.MakeVariant("not-found")) if notFound { // XXX: in the loop we'll handle changes better... - log.Printf("Failed to find service: %v\n", service) + log.Printf("Failed to find service: %v", service) invalid = true // XXX ? } } @@ -115,7 +115,7 @@ func (obj *ServiceType) Watch() { } if invalid { - log.Printf("Waiting for: %v\n", service) // waiting for service to appear... + log.Printf("Waiting for: %v", service) // waiting for service to appear... if activeSet { activeSet = false set.Remove(service) // no return value should ever occur @@ -125,7 +125,7 @@ func (obj *ServiceType) Watch() { case _ = <-buschan: // XXX wait for new units event to unstick obj.SetState(typeNil) // loop so that we can see the changed invalid signal - log.Printf("Service[%v]->DaemonReload()\n", service) + log.Printf("Service[%v]->DaemonReload()", service) case event := <-obj.events: obj.SetState(typeNil) @@ -144,24 +144,24 @@ func (obj *ServiceType) Watch() { set.Add(service) // no return value should ever occur } - log.Printf("Watching: %v\n", service) // attempting to watch... + log.Printf("Watching: %v", service) // attempting to watch... select { case event := <-subChannel: - log.Printf("Service event: %+v\n", event) + log.Printf("Service event: %+v", event) // NOTE: the value returned is a map for some reason... if event[service] != nil { // event[service].ActiveState is not nil if event[service].ActiveState == "active" { - log.Printf("Service[%v]->Started()\n", service) + log.Printf("Service[%v]->Started()", service) } else if event[service].ActiveState == "inactive" { - log.Printf("Service[%v]->Stopped!()\n", service) + log.Printf("Service[%v]->Stopped!()", service) } else { log.Fatal("Unknown service state: ", event[service].ActiveState) } } else { // service stopped (and ActiveState is nil...) - log.Printf("Service[%v]->Stopped\n", service) + log.Printf("Service[%v]->Stopped", service) } send = true @@ -204,14 +204,14 @@ func (obj *ServiceType) StateOK() bool { loadstate, err := conn.GetUnitProperty(service, "LoadState") if err != nil { - log.Printf("Failed to get load state: %v\n", err) + log.Printf("Failed to get load state: %v", err) return false } // NOTE: we have to compare variants with other variants, they are really strings... var notFound = (loadstate.Value == dbus.MakeVariant("not-found")) if notFound { - log.Printf("Failed to find service: %v\n", service) + log.Printf("Failed to find service: %v", service) return false } @@ -241,7 +241,7 @@ func (obj *ServiceType) StateOK() bool { } func (obj *ServiceType) Apply() bool { - fmt.Printf("Apply->Service[%v]\n", obj.Name) + log.Printf("%v[%v]: Apply", obj.GetType(), obj.GetName()) if !util.IsRunningSystemd() { log.Fatal("Systemd is not running.") @@ -264,7 +264,7 @@ func (obj *ServiceType) Apply() bool { err = nil } if err != nil { - log.Printf("Unable to change startup status: %v\n", err) + log.Printf("Unable to change startup status: %v", err) return false } diff --git a/types.go b/types.go index 709154a0..3712228e 100644 --- a/types.go +++ b/types.go @@ -18,7 +18,6 @@ package main import ( - "fmt" "log" "time" ) @@ -286,7 +285,7 @@ func (obj *NoopType) StateOK() bool { } func (obj *NoopType) Apply() bool { - fmt.Printf("Apply->Noop[%v]\n", obj.Name) + log.Printf("%v[%v]: Apply", obj.GetType(), obj.GetName()) return true }