]> git.lizzy.rs Git - micro.git/blob - cmd/micro/job.go
df9151b5490883331892dcbf6a3c38ddfe97e919
[micro.git] / cmd / micro / job.go
1 package main
2
3 import (
4         "bytes"
5         "io"
6         "os/exec"
7 )
8
9 // Jobs are the way plugins can run processes in the background
10 // A job is simply a process that gets executed asynchronously
11 // There are callbacks for when the job exits, when the job creates stdout
12 // and when the job creates stderr
13
14 // These jobs run in a separate goroutine but the lua callbacks need to be
15 // executed in the main thread (where the Lua VM is running) so they are
16 // put into the jobs channel which gets read by the main loop
17
18 // JobFunction is a representation of a job (this data structure is what is loaded
19 // into the jobs channel)
20 type JobFunction struct {
21         function func(string, ...string)
22         output   string
23         args     []string
24 }
25
26 // A CallbackFile is the data structure that makes it possible to catch stderr and stdout write events
27 type CallbackFile struct {
28         io.Writer
29
30         callback func(string, ...string)
31         args     []string
32 }
33
34 func (f *CallbackFile) Write(data []byte) (int, error) {
35         // This is either stderr or stdout
36         // In either case we create a new job function callback and put it in the jobs channel
37         jobFunc := JobFunction{f.callback, string(data), f.args}
38         jobs <- jobFunc
39         return f.Writer.Write(data)
40 }
41
42 // JobStart starts a shell command in the background with the given callbacks
43 // It returns an *exec.Cmd as the job id
44 func JobStart(cmd string, onStdout, onStderr, onExit string, userargs ...string) *exec.Cmd {
45         return JobSpawn("sh", []string{"-c", cmd}, onStdout, onStderr, onExit, userargs...)
46 }
47
48 // JobSpawn starts a process with args in the background with the given callbacks
49 // It returns an *exec.Cmd as the job id
50 func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit string, userargs ...string) *exec.Cmd {
51         // Set up everything correctly if the functions have been provided
52         proc := exec.Command(cmdName, cmdArgs...)
53         var outbuf bytes.Buffer
54         if onStdout != "" {
55                 proc.Stdout = &CallbackFile{&outbuf, LuaFunctionJob(onStdout), userargs}
56         } else {
57                 proc.Stdout = &outbuf
58         }
59         if onStderr != "" {
60                 proc.Stderr = &CallbackFile{&outbuf, LuaFunctionJob(onStderr), userargs}
61         } else {
62                 proc.Stderr = &outbuf
63         }
64
65         go func() {
66                 // Run the process in the background and create the onExit callback
67                 proc.Run()
68                 jobFunc := JobFunction{LuaFunctionJob(onExit), string(outbuf.Bytes()), userargs}
69                 jobs <- jobFunc
70         }()
71
72         return proc
73 }
74
75 // JobStop kills a job
76 func JobStop(cmd *exec.Cmd) {
77         cmd.Process.Kill()
78 }
79
80 // JobSend sends the given data into the job's stdin stream
81 func JobSend(cmd *exec.Cmd, data string) {
82         stdin, err := cmd.StdinPipe()
83         if err != nil {
84                 return
85         }
86
87         stdin.Write([]byte(data))
88 }