diff options
author | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
---|---|---|
committer | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
commit | bae9f6d2fd5eb5bc80929bd393932b23f14d7c93 (patch) | |
tree | ca9ab12a7d78b1fc27a8f734729081357ce6d252 /vendor/github.com/hashicorp/go-plugin/server.go | |
parent | 254c495b6bebab3fb72a243c4bce858d79e6ee99 (diff) | |
download | terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.gz terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.zst terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.zip |
Initial transfer of provider code
Diffstat (limited to 'vendor/github.com/hashicorp/go-plugin/server.go')
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/server.go | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/go-plugin/server.go b/vendor/github.com/hashicorp/go-plugin/server.go new file mode 100644 index 0000000..b5c5270 --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/server.go | |||
@@ -0,0 +1,222 @@ | |||
1 | package plugin | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "fmt" | ||
6 | "io/ioutil" | ||
7 | "log" | ||
8 | "net" | ||
9 | "os" | ||
10 | "os/signal" | ||
11 | "runtime" | ||
12 | "strconv" | ||
13 | "sync/atomic" | ||
14 | ) | ||
15 | |||
16 | // CoreProtocolVersion is the ProtocolVersion of the plugin system itself. | ||
17 | // We will increment this whenever we change any protocol behavior. This | ||
18 | // will invalidate any prior plugins but will at least allow us to iterate | ||
19 | // on the core in a safe way. We will do our best to do this very | ||
20 | // infrequently. | ||
21 | const CoreProtocolVersion = 1 | ||
22 | |||
23 | // HandshakeConfig is the configuration used by client and servers to | ||
24 | // handshake before starting a plugin connection. This is embedded by | ||
25 | // both ServeConfig and ClientConfig. | ||
26 | // | ||
27 | // In practice, the plugin host creates a HandshakeConfig that is exported | ||
28 | // and plugins then can easily consume it. | ||
29 | type HandshakeConfig struct { | ||
30 | // ProtocolVersion is the version that clients must match on to | ||
31 | // agree they can communicate. This should match the ProtocolVersion | ||
32 | // set on ClientConfig when using a plugin. | ||
33 | ProtocolVersion uint | ||
34 | |||
35 | // MagicCookieKey and value are used as a very basic verification | ||
36 | // that a plugin is intended to be launched. This is not a security | ||
37 | // measure, just a UX feature. If the magic cookie doesn't match, | ||
38 | // we show human-friendly output. | ||
39 | MagicCookieKey string | ||
40 | MagicCookieValue string | ||
41 | } | ||
42 | |||
43 | // ServeConfig configures what sorts of plugins are served. | ||
44 | type ServeConfig struct { | ||
45 | // HandshakeConfig is the configuration that must match clients. | ||
46 | HandshakeConfig | ||
47 | |||
48 | // Plugins are the plugins that are served. | ||
49 | Plugins map[string]Plugin | ||
50 | } | ||
51 | |||
52 | // Serve serves the plugins given by ServeConfig. | ||
53 | // | ||
54 | // Serve doesn't return until the plugin is done being executed. Any | ||
55 | // errors will be outputted to the log. | ||
56 | // | ||
57 | // This is the method that plugins should call in their main() functions. | ||
58 | func Serve(opts *ServeConfig) { | ||
59 | // Validate the handshake config | ||
60 | if opts.MagicCookieKey == "" || opts.MagicCookieValue == "" { | ||
61 | fmt.Fprintf(os.Stderr, | ||
62 | "Misconfigured ServeConfig given to serve this plugin: no magic cookie\n"+ | ||
63 | "key or value was set. Please notify the plugin author and report\n"+ | ||
64 | "this as a bug.\n") | ||
65 | os.Exit(1) | ||
66 | } | ||
67 | |||
68 | // First check the cookie | ||
69 | if os.Getenv(opts.MagicCookieKey) != opts.MagicCookieValue { | ||
70 | fmt.Fprintf(os.Stderr, | ||
71 | "This binary is a plugin. These are not meant to be executed directly.\n"+ | ||
72 | "Please execute the program that consumes these plugins, which will\n"+ | ||
73 | "load any plugins automatically\n") | ||
74 | os.Exit(1) | ||
75 | } | ||
76 | |||
77 | // Logging goes to the original stderr | ||
78 | log.SetOutput(os.Stderr) | ||
79 | |||
80 | // Create our new stdout, stderr files. These will override our built-in | ||
81 | // stdout/stderr so that it works across the stream boundary. | ||
82 | stdout_r, stdout_w, err := os.Pipe() | ||
83 | if err != nil { | ||
84 | fmt.Fprintf(os.Stderr, "Error preparing plugin: %s\n", err) | ||
85 | os.Exit(1) | ||
86 | } | ||
87 | stderr_r, stderr_w, err := os.Pipe() | ||
88 | if err != nil { | ||
89 | fmt.Fprintf(os.Stderr, "Error preparing plugin: %s\n", err) | ||
90 | os.Exit(1) | ||
91 | } | ||
92 | |||
93 | // Register a listener so we can accept a connection | ||
94 | listener, err := serverListener() | ||
95 | if err != nil { | ||
96 | log.Printf("[ERR] plugin: plugin init: %s", err) | ||
97 | return | ||
98 | } | ||
99 | defer listener.Close() | ||
100 | |||
101 | // Create the channel to tell us when we're done | ||
102 | doneCh := make(chan struct{}) | ||
103 | |||
104 | // Create the RPC server to dispense | ||
105 | server := &RPCServer{ | ||
106 | Plugins: opts.Plugins, | ||
107 | Stdout: stdout_r, | ||
108 | Stderr: stderr_r, | ||
109 | DoneCh: doneCh, | ||
110 | } | ||
111 | |||
112 | // Output the address and service name to stdout so that core can bring it up. | ||
113 | log.Printf("[DEBUG] plugin: plugin address: %s %s\n", | ||
114 | listener.Addr().Network(), listener.Addr().String()) | ||
115 | fmt.Printf("%d|%d|%s|%s\n", | ||
116 | CoreProtocolVersion, | ||
117 | opts.ProtocolVersion, | ||
118 | listener.Addr().Network(), | ||
119 | listener.Addr().String()) | ||
120 | os.Stdout.Sync() | ||
121 | |||
122 | // Eat the interrupts | ||
123 | ch := make(chan os.Signal, 1) | ||
124 | signal.Notify(ch, os.Interrupt) | ||
125 | go func() { | ||
126 | var count int32 = 0 | ||
127 | for { | ||
128 | <-ch | ||
129 | newCount := atomic.AddInt32(&count, 1) | ||
130 | log.Printf( | ||
131 | "[DEBUG] plugin: received interrupt signal (count: %d). Ignoring.", | ||
132 | newCount) | ||
133 | } | ||
134 | }() | ||
135 | |||
136 | // Set our new out, err | ||
137 | os.Stdout = stdout_w | ||
138 | os.Stderr = stderr_w | ||
139 | |||
140 | // Serve | ||
141 | go server.Accept(listener) | ||
142 | |||
143 | // Wait for the graceful exit | ||
144 | <-doneCh | ||
145 | } | ||
146 | |||
147 | func serverListener() (net.Listener, error) { | ||
148 | if runtime.GOOS == "windows" { | ||
149 | return serverListener_tcp() | ||
150 | } | ||
151 | |||
152 | return serverListener_unix() | ||
153 | } | ||
154 | |||
155 | func serverListener_tcp() (net.Listener, error) { | ||
156 | minPort, err := strconv.ParseInt(os.Getenv("PLUGIN_MIN_PORT"), 10, 32) | ||
157 | if err != nil { | ||
158 | return nil, err | ||
159 | } | ||
160 | |||
161 | maxPort, err := strconv.ParseInt(os.Getenv("PLUGIN_MAX_PORT"), 10, 32) | ||
162 | if err != nil { | ||
163 | return nil, err | ||
164 | } | ||
165 | |||
166 | for port := minPort; port <= maxPort; port++ { | ||
167 | address := fmt.Sprintf("127.0.0.1:%d", port) | ||
168 | listener, err := net.Listen("tcp", address) | ||
169 | if err == nil { | ||
170 | return listener, nil | ||
171 | } | ||
172 | } | ||
173 | |||
174 | return nil, errors.New("Couldn't bind plugin TCP listener") | ||
175 | } | ||
176 | |||
177 | func serverListener_unix() (net.Listener, error) { | ||
178 | tf, err := ioutil.TempFile("", "plugin") | ||
179 | if err != nil { | ||
180 | return nil, err | ||
181 | } | ||
182 | path := tf.Name() | ||
183 | |||
184 | // Close the file and remove it because it has to not exist for | ||
185 | // the domain socket. | ||
186 | if err := tf.Close(); err != nil { | ||
187 | return nil, err | ||
188 | } | ||
189 | if err := os.Remove(path); err != nil { | ||
190 | return nil, err | ||
191 | } | ||
192 | |||
193 | l, err := net.Listen("unix", path) | ||
194 | if err != nil { | ||
195 | return nil, err | ||
196 | } | ||
197 | |||
198 | // Wrap the listener in rmListener so that the Unix domain socket file | ||
199 | // is removed on close. | ||
200 | return &rmListener{ | ||
201 | Listener: l, | ||
202 | Path: path, | ||
203 | }, nil | ||
204 | } | ||
205 | |||
206 | // rmListener is an implementation of net.Listener that forwards most | ||
207 | // calls to the listener but also removes a file as part of the close. We | ||
208 | // use this to cleanup the unix domain socket on close. | ||
209 | type rmListener struct { | ||
210 | net.Listener | ||
211 | Path string | ||
212 | } | ||
213 | |||
214 | func (l *rmListener) Close() error { | ||
215 | // Close the listener itself | ||
216 | if err := l.Listener.Close(); err != nil { | ||
217 | return err | ||
218 | } | ||
219 | |||
220 | // Remove the file | ||
221 | return os.Remove(l.Path) | ||
222 | } | ||