// expireCommands loads commands in the inflight directory // and terminate the expired ones func expireCommands(ctx Context) (err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("expireCommands() -> %v", e) } ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: "leaving expireCommands()"}.Debug() }() dir, err := os.Open(ctx.Directories.Command.InFlight) dirContent, err := dir.Readdir(-1) if err != nil { panic(err) } // loop over the content of the directory for _, DirEntry := range dirContent { if !DirEntry.Mode().IsRegular() { // ignore non file continue } filename := ctx.Directories.Command.InFlight + "/" + DirEntry.Name() _, err = os.Stat(filename) if err != nil { // file is already gone, probably consumed by a concurrent returning command // ignore and continue continue } err := waitForFileOrDelete(filename, 3) if err != nil { ctx.Channels.Log <- mig.Log{Desc: fmt.Sprintf("error while reading '%s': %v", filename, err)}.Err() continue } cmd, err := mig.CmdFromFile(filename) if err != nil { // failing to load this file, log and skip it ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: fmt.Sprintf("failed to load inflight command file %s", filename)}.Err() continue } if time.Now().After(cmd.Action.ExpireAfter) { desc := fmt.Sprintf("expiring command '%s' on agent '%s'", cmd.Action.Name, cmd.Agent.Name) ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, CommandID: cmd.ID, ActionID: cmd.Action.ID, Desc: desc} cmd.Status = "expired" cmd.FinishTime = time.Now().UTC() // write it into the returned command directory data, err := json.Marshal(cmd) if err != nil { panic(err) } dest := fmt.Sprintf("%s/%.0f-%.0f.json", ctx.Directories.Command.Returned, cmd.Action.ID, cmd.ID) err = safeWrite(ctx, dest, data) if err != nil { panic(err) } //ctx.Directories.Command.Returned os.Remove(filename) } } dir.Close() return }
// loadReturnedCommands walks through the returned commands directory and loads // the commands into the scheduler func loadReturnedCommands(ctx Context) (err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("loadReturnedCommands() -> %v", e) } ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: "leaving loadReturnedCommands()"}.Debug() }() dir, err := os.Open(ctx.Directories.Command.Returned) dirContent, err := dir.Readdir(-1) if err != nil { panic(err) } // loop over the content of the directory for _, DirEntry := range dirContent { if !DirEntry.Mode().IsRegular() { // ignore non file continue } filename := ctx.Directories.Command.Returned + "/" + DirEntry.Name() if strings.HasSuffix(filename, ".fail") { // skip files with invalid commands continue } _, err = os.Stat(filename) if err != nil { // file is already gone, probably consumed by the file notifier // ignore and continue continue } err := waitForFileOrDelete(filename, 3) if err != nil { ctx.Channels.Log <- mig.Log{Desc: fmt.Sprintf("error while reading '%s': %v", filename, err)}.Err() continue } cmd, err := mig.CmdFromFile(filename) if err != nil { // failing to load this file, log and skip it ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: fmt.Sprintf("failed to load returned command file %s", filename)}.Err() continue } // queue it ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, CommandID: cmd.ID, ActionID: cmd.Action.ID, Desc: fmt.Sprintf("loading returned command '%s'", cmd.Action.Name)} ctx.Channels.CommandReturned <- filename } dir.Close() return }
// returnCommands is called when commands have returned // it stores the result of a command and mark it as completed/failed and then // send a message to the Action completion routine to update the action status func returnCommands(cmdFiles []string, ctx Context) (err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("returnCommands() -> %v", e) } ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: "leaving returnCommands()"}.Debug() }() for _, cmdFile := range cmdFiles { // load and parse the command. If this fail, skip it and continue. cmd, err := mig.CmdFromFile(cmdFile) if err != nil { // if CmdFromFile fails, rename the cmdFile and skip. desc := fmt.Sprintf("Command in %s failed, renaming to %s.fail", cmdFile, cmdFile) ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: desc}.Debug() os.Rename(cmdFile, cmdFile+".fail") continue } cmd.FinishTime = time.Now().UTC() // update command in database go func() { err = ctx.DB.FinishCommand(cmd) if err != nil { desc := fmt.Sprintf("command results insertion in database failed with error: %v", err) ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, ActionID: cmd.Action.ID, CommandID: cmd.ID, Desc: desc}.Err() } else { ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, ActionID: cmd.Action.ID, CommandID: cmd.ID, Desc: "command updated in database"}.Debug() } // pass the command over to the Command Done channel ctx.Channels.CommandDone <- cmd }() // remove command from inflight dir inflightPath := fmt.Sprintf("%s/%.0f-%.0f.json", ctx.Directories.Command.InFlight, cmd.Action.ID, cmd.ID) os.Remove(inflightPath) // remove command from Returned dir os.Remove(cmdFile) } return }
func main() { var err error var Usage = func() { fmt.Fprintf(os.Stderr, "Mozilla InvestiGator Action Verifier\n"+ "usage: %s <-a action file> <-c command file>\n\n"+ "Command line to verify an action *or* command.\n"+ "Options:\n", os.Args[0]) flag.PrintDefaults() } hasaction := false hascommand := false homedir := client.FindHomedir() // command line options var actionfile = flag.String("a", "/path/to/action", "Load action from file") var commandfile = flag.String("c", "/path/to/command", "Load command from file") var config = flag.String("conf", homedir+"/.migrc", "Load configuration from file") var showversion = flag.Bool("V", false, "Show build version and exit") flag.Parse() if *showversion { fmt.Println(mig.Version) os.Exit(0) } conf, err := client.ReadConfiguration(*config) if err != nil { panic(err) } // if a file is defined, load action from that if *actionfile != "/path/to/action" { hasaction = true } if *commandfile != "/path/to/command" { hascommand = true } if (hasaction && hascommand) || (!hasaction && !hascommand) { fmt.Println("[error] either an action file or a command file must be provided") Usage() os.Exit(1) } var a mig.Action if hasaction { a, err = mig.ActionFromFile(*actionfile) if err != nil { panic(err) } } else { c, err := mig.CmdFromFile(*commandfile) if err != nil { panic(err) } a = c.Action } err = a.Validate() if err != nil { fmt.Println(err) } pubringFile, err := os.Open(conf.GPG.Home + "/pubring.gpg") if err != nil { panic(err) } defer pubringFile.Close() // syntax checking err = a.VerifySignatures(pubringFile) if err != nil { fmt.Println("[error]", err) } else { fmt.Println("Valid signature") } }