github.com/askholme/packer@v0.7.2-0.20140924152349-70d9566a6852/packer/plugin/server.go (about) 1 // The plugin package provides the functionality to both expose a Packer 2 // plugin binary and to connect to an existing Packer plugin binary. 3 // 4 // Packer supports plugins in the form of self-contained external static 5 // Go binaries. These binaries behave in a certain way (enforced by this 6 // package) and are connected to in a certain way (also enforced by this 7 // package). 8 package plugin 9 10 import ( 11 "errors" 12 "fmt" 13 "github.com/mitchellh/packer/packer" 14 packrpc "github.com/mitchellh/packer/packer/rpc" 15 "io/ioutil" 16 "log" 17 "net" 18 "os" 19 "os/signal" 20 "runtime" 21 "strconv" 22 "sync/atomic" 23 ) 24 25 // This is a count of the number of interrupts the process has received. 26 // This is updated with sync/atomic whenever a SIGINT is received and can 27 // be checked by the plugin safely to take action. 28 var Interrupts int32 = 0 29 30 const MagicCookieKey = "PACKER_PLUGIN_MAGIC_COOKIE" 31 const MagicCookieValue = "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2" 32 33 // The APIVersion is outputted along with the RPC address. The plugin 34 // client validates this API version and will show an error if it doesn't 35 // know how to speak it. 36 const APIVersion = "4" 37 38 // Server waits for a connection to this plugin and returns a Packer 39 // RPC server that you can use to register components and serve them. 40 func Server() (*packrpc.Server, error) { 41 log.Printf("Plugin build against Packer '%s'", packer.GitCommit) 42 43 if os.Getenv(MagicCookieKey) != MagicCookieValue { 44 return nil, errors.New( 45 "Please do not execute plugins directly. Packer will execute these for you.") 46 } 47 48 // If there is no explicit number of Go threads to use, then set it 49 if os.Getenv("GOMAXPROCS") == "" { 50 runtime.GOMAXPROCS(runtime.NumCPU()) 51 } 52 53 minPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MIN_PORT"), 10, 32) 54 if err != nil { 55 return nil, err 56 } 57 58 maxPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MAX_PORT"), 10, 32) 59 if err != nil { 60 return nil, err 61 } 62 63 log.Printf("Plugin minimum port: %d\n", minPort) 64 log.Printf("Plugin maximum port: %d\n", maxPort) 65 66 listener, err := serverListener(minPort, maxPort) 67 if err != nil { 68 return nil, err 69 } 70 defer listener.Close() 71 72 // Output the address to stdout 73 log.Printf("Plugin address: %s %s\n", 74 listener.Addr().Network(), listener.Addr().String()) 75 fmt.Printf("%s|%s|%s\n", 76 APIVersion, 77 listener.Addr().Network(), 78 listener.Addr().String()) 79 os.Stdout.Sync() 80 81 // Accept a connection 82 log.Println("Waiting for connection...") 83 conn, err := listener.Accept() 84 if err != nil { 85 log.Printf("Error accepting connection: %s\n", err.Error()) 86 return nil, err 87 } 88 89 // Eat the interrupts 90 ch := make(chan os.Signal, 1) 91 signal.Notify(ch, os.Interrupt) 92 go func() { 93 var count int32 = 0 94 for { 95 <-ch 96 newCount := atomic.AddInt32(&count, 1) 97 log.Printf("Received interrupt signal (count: %d). Ignoring.", newCount) 98 } 99 }() 100 101 // Serve a single connection 102 log.Println("Serving a plugin connection...") 103 return packrpc.NewServer(conn), nil 104 } 105 106 func serverListener(minPort, maxPort int64) (net.Listener, error) { 107 if runtime.GOOS == "windows" { 108 return serverListener_tcp(minPort, maxPort) 109 } 110 111 return serverListener_unix() 112 } 113 114 func serverListener_tcp(minPort, maxPort int64) (net.Listener, error) { 115 for port := minPort; port <= maxPort; port++ { 116 address := fmt.Sprintf("127.0.0.1:%d", port) 117 listener, err := net.Listen("tcp", address) 118 if err == nil { 119 return listener, nil 120 } 121 } 122 123 return nil, errors.New("Couldn't bind plugin TCP listener") 124 } 125 126 func serverListener_unix() (net.Listener, error) { 127 tf, err := ioutil.TempFile("", "packer-plugin") 128 if err != nil { 129 return nil, err 130 } 131 path := tf.Name() 132 133 // Close the file and remove it because it has to not exist for 134 // the domain socket. 135 if err := tf.Close(); err != nil { 136 return nil, err 137 } 138 if err := os.Remove(path); err != nil { 139 return nil, err 140 } 141 142 return net.Listen("unix", path) 143 }