// Discover discovers plugins. // // This looks in the directory of the executable and the CWD, in that // order for priority. func (c *Config) Discover() error { // Look in the cwd. if err := c.discover("."); err != nil { return err } // Look in the plugins directory. This will override any found // in the current directory. dir, err := ConfigDir() if err != nil { log.Printf("[ERR] Error loading config directory: %s", err) } else { if err := c.discover(filepath.Join(dir, "plugins")); err != nil { return err } } // Next, look in the same directory as the executable. Any conflicts // will overwrite those found in our current directory. exePath, err := osext.Executable() if err != nil { log.Printf("[ERR] Error loading exe directory: %s", err) } else { if err := c.discover(filepath.Dir(exePath)); err != nil { return err } } return nil }
func (c *config) pluginClient(path string) *plugin.Client { originalPath := path // First attempt to find the executable by consulting the PATH. path, err := exec.LookPath(path) if err != nil { // If that doesn't work, look for it in the same directory // as the `packer` executable (us). log.Printf("Plugin could not be found. Checking same directory as executable.") exePath, err := osext.Executable() if err != nil { log.Printf("Couldn't get current exe path: %s", err) } else { log.Printf("Current exe path: %s", exePath) path = filepath.Join(filepath.Dir(exePath), filepath.Base(originalPath)) } } // If everything failed, just use the original path and let the error // bubble through. if path == "" { path = originalPath } log.Printf("Creating plugin client for path: %s", path) var config plugin.ClientConfig config.Cmd = exec.Command(path) config.Managed = true config.MinPort = c.PluginMinPort config.MaxPort = c.PluginMaxPort return plugin.NewClient(&config) }
// Discover discovers plugins. // // Search the directory of the executable, then the plugins directory, and // finally the CWD, in that order. Any conflicts will overwrite previously // found plugins, in that order. // Hence, the priority order is the reverse of the search order - i.e., the // CWD has the highest priority. func (c *config) Discover() error { // First, look in the same directory as the executable. exePath, err := osext.Executable() if err != nil { log.Printf("[ERR] Error loading exe directory: %s", err) } else { if err := c.discover(filepath.Dir(exePath)); err != nil { return err } } // Next, look in the plugins directory. dir, err := ConfigDir() if err != nil { log.Printf("[ERR] Error loading config directory: %s", err) } else { if err := c.discover(filepath.Join(dir, "plugins")); err != nil { return err } } // Last, look in the CWD. if err := c.discover("."); err != nil { return err } return nil }
func pluginCmd(path string) *exec.Cmd { cmdPath := "" // If the path doesn't contain a separator, look in the same // directory as the Terraform executable first. if !strings.ContainsRune(path, os.PathSeparator) { exePath, err := osext.Executable() if err == nil { temp := filepath.Join( filepath.Dir(exePath), filepath.Base(path)) if _, err := os.Stat(temp); err == nil { cmdPath = temp } } // If we still haven't found the executable, look for it // in the PATH. if v, err := exec.LookPath(path); err == nil { cmdPath = v } } // If we still don't have a path, then just set it to the original // given path. if cmdPath == "" { cmdPath = path } // Build the command to execute the plugin return exec.Command(cmdPath) }
func pluginCmd(path string) *exec.Cmd { originalPath := path // First look for the provider on the PATH. path, err := exec.LookPath(path) if err != nil { // If that doesn't work, look for it in the same directory // as the executable that is running. exePath, err := osext.Executable() if err == nil { path = filepath.Join( filepath.Dir(exePath), filepath.Base(originalPath)) } } // If we still don't have a path set, then set it to the // original path and let any errors that happen bubble out. if path == "" { path = originalPath } // Build the command to execute the plugin return exec.Command(path) }
func realMain() int { flag.Parse() // If we have a token set, then we're in token handler mode. Do it! if *token != "" { return mainToken(*token, flag.Args()) } // Set our own path for later var err error selfPath, err = osext.Executable() if err != nil { log.Printf("[FATAL] Error getting executable path:%s", err) return 1 } http.HandleFunc("/", handleRoot) http.HandleFunc("/socket", handleWebSocket) log.Println("[INFO] Starting server...") log.Printf("[INFO] Executable (selfPath) = %s", selfPath) if err := http.ListenAndServe(*addr, nil); err != nil { log.Printf("[FATAL] error starting: %s", err) return 1 } return 0 }
// Discover discovers plugins. // // Search the directory of the executable, then the plugins directory, and // finally the CWD, in that order. Any conflicts will overwrite previously // found plugins, in that order. // Hence, the priority order is the reverse of the search order - i.e., the // CWD has the highest priority. func (c *config) Discover() error { // First, look in the same directory as the executable. exePath, err := osext.Executable() if err != nil { log.Printf("[ERR] Error loading exe directory: %s", err) } else { if err := c.discover(filepath.Dir(exePath)); err != nil { return err } } // Next, look in the plugins directory. dir, err := ConfigDir() if err != nil { log.Printf("[ERR] Error loading config directory: %s", err) } else { if err := c.discover(filepath.Join(dir, "plugins")); err != nil { return err } } // Next, look in the CWD. if err := c.discover("."); err != nil { return err } // Finally, try to use an internal plugin. Note that this will not override // any previously-loaded plugins. if err := c.discoverInternal(); err != nil { return err } return nil }
func TestPanicHandler(t *testing.T) { startTestServer() exePath, err := osext.Executable() if err != nil { t.Fatal(err) } // Use the same trick as panicwrap() to re-run ourselves. // In the init() block below, we will then panic. cmd := exec.Command(exePath, os.Args[1:]...) cmd.Env = append(os.Environ(), "BUGSNAG_API_KEY="+testAPIKey, "BUGSNAG_ENDPOINT="+testEndpoint) for i := range cmd.Env { if cmd.Env[i] == "bugsnag_wrapped=bugsnag_wrapped" { cmd.Env[i] = "please_panic=please_panic" } } if err = cmd.Start(); err != nil { t.Fatal(err) } if err = cmd.Wait(); err.Error() != "exit status 2" { t.Fatal(err) } json, err := simplejson.NewJson(<-postedJSON) if err != nil { t.Fatal(err) } event := json.Get("events").GetIndex(0) if event.Get("severity").MustString() != "error" { t.Errorf("severity should be error") } exception := event.Get("exceptions").GetIndex(0) if exception.Get("message").MustString() != "ruh roh" { t.Errorf("caught wrong panic") } if exception.Get("errorClass").MustString() != "panic" { t.Errorf("caught wrong panic") } frame := exception.Get("stacktrace").GetIndex(1) // Yeah, we just caught a panic from the init() function below and sent it to the server running above (mindblown) if frame.Get("inProject").MustBool() != true || frame.Get("file").MustString() != "panicwrap_test.go" || frame.Get("method").MustString() != "panick" || frame.Get("lineNumber").MustInt() == 0 { t.Errorf("stack trace seemed wrong") } }
func DiscoverPlugins() (*Plugins, error) { plugins := &Plugins{} // Look in the cwd. if err := discoverPluginsInDir(".", plugins); err != nil { return nil, err } // Next, look in the same directory as the executable. Any conflicts // will overwrite those found in our current directory. exePath, err := osext.Executable() if err != nil { log.Printf("[ERR] Error loading exe directory: %s", err) } else { if err := discoverPluginsInDir(filepath.Dir(exePath), plugins); err != nil { return nil, err } } return plugins, nil }
func (c *config) discoverInternal() error { // Get the packer binary path packerPath, err := osext.Executable() if err != nil { log.Printf("[ERR] Error loading exe directory: %s", err) return err } for builder := range command.Builders { _, found := (c.Builders)[builder] if !found { log.Printf("Using internal plugin for %s", builder) (c.Builders)[builder] = fmt.Sprintf("%s%splugin%spacker-builder-%s", packerPath, PACKERSPACE, PACKERSPACE, builder) } } for provisioner := range command.Provisioners { _, found := (c.Provisioners)[provisioner] if !found { log.Printf("Using internal plugin for %s", provisioner) (c.Provisioners)[provisioner] = fmt.Sprintf( "%s%splugin%spacker-provisioner-%s", packerPath, PACKERSPACE, PACKERSPACE, provisioner) } } for postProcessor := range command.PostProcessors { _, found := (c.PostProcessors)[postProcessor] if !found { log.Printf("Using internal plugin for %s", postProcessor) (c.PostProcessors)[postProcessor] = fmt.Sprintf( "%s%splugin%spacker-post-processor-%s", packerPath, PACKERSPACE, PACKERSPACE, postProcessor) } } return nil }
func main() { debug := os.Getenv("VAGRANT_DEBUG_LAUNCHER") != "" // Get the path to the executable. This path doesn't resolve symlinks // so we have to do that afterwards to find the real binary. path, err := osext.Executable() if err != nil { fmt.Fprintf(os.Stderr, "Failed to load Vagrant: %s\n", err) os.Exit(1) } if debug { log.Printf("launcher: path = %s", path) } for { fi, err := os.Lstat(path) if err != nil { fmt.Fprintf(os.Stderr, "Failed to stat executable: %s\n", err) os.Exit(1) } if fi.Mode()&os.ModeSymlink == 0 { break } // The executable is a symlink, so resolve it path, err = os.Readlink(path) if err != nil { fmt.Fprintf(os.Stderr, "Failed to load Vagrant: %s\n", err) os.Exit(1) } if debug { log.Printf("launcher: resolved symlink = %s", path) } } // Determine some basic directories that we use throughout path = filepath.Dir(filepath.Clean(path)) installerDir := filepath.Dir(path) embeddedDir := filepath.Join(installerDir, "embedded") if debug { log.Printf("launcher: installerDir = %s", installerDir) log.Printf("launcher: embeddedDir = %s", embeddedDir) } // Find the Vagrant gem gemPaths, err := filepath.Glob( filepath.Join(embeddedDir, "gems", "gems", "vagrant-*")) if err != nil { fmt.Fprintf(os.Stderr, "Failed to find Vagrant: %s\n", err) os.Exit(1) } if debug { log.Printf("launcher: gemPaths (initial) = %#v", gemPaths) } for i := 0; i < len(gemPaths); i++ { fullPath := filepath.Join(gemPaths[i], "lib", "vagrant", "pre-rubygems.rb") if _, err := os.Stat(fullPath); err != nil { if debug { log.Printf("launcher: bad gemPath += %s", fullPath) } gemPaths = append(gemPaths[:i], gemPaths[i+1:]...) i-- } } if len(gemPaths) == 0 { fmt.Fprintf(os.Stderr, "Failed to find Vagrant!\n") os.Exit(1) } gemPath := gemPaths[len(gemPaths)-1] vagrantExecutable := filepath.Join(gemPath, "bin", "vagrant") if debug { log.Printf("launcher: gemPaths (final) = %#v", gemPaths) log.Printf("launcher: gemPath = %s", gemPath) } // Setup the CPP/LDFLAGS so that native extensions can be // properly compiled into the Vagrant environment. cppflags := "-I" + filepath.Join(embeddedDir, "include") ldflags := "-L" + filepath.Join(embeddedDir, "lib") if original := os.Getenv("CPPFLAGS"); original != "" { cppflags = original + " " + cppflags } if original := os.Getenv("LDFLAGS"); original != "" { ldflags = original + " " + ldflags } // Set the PATH to include the proper paths into our embedded dir path = os.Getenv("PATH") if runtime.GOOS == "windows" { path = fmt.Sprintf( "%s;%s;%s", filepath.Join(embeddedDir, "bin"), filepath.Join(embeddedDir, "gnuwin32", "bin"), path) } else { path = fmt.Sprintf("%s:%s", filepath.Join(embeddedDir, "bin"), path) } // Allow users to specify a custom SSL cert sslCertFile := os.Getenv("SSL_CERT_FILE") if sslCertFile == "" { sslCertFile = filepath.Join(embeddedDir, "cacert.pem") } newEnv := map[string]string{ // Setup the environment to prefer our embedded dir over // anything the user might have setup on his/her system. "CPPFLAGS": cppflags, "GEM_HOME": filepath.Join(embeddedDir, "gems"), "GEM_PATH": filepath.Join(embeddedDir, "gems"), "GEMRC": filepath.Join(embeddedDir, "etc", "gemrc"), "LDFLAGS": ldflags, "PATH": path, "SSL_CERT_FILE": sslCertFile, // Environmental variables used by Vagrant itself "VAGRANT_EXECUTABLE": vagrantExecutable, "VAGRANT_INSTALLER_ENV": "1", "VAGRANT_INSTALLER_EMBEDDED_DIR": embeddedDir, "VAGRANT_INSTALLER_VERSION": "2", } // Unset any RUBYOPT, we don't want this bleeding into our runtime newEnv["RUBYOPT"] = "" // Unset any RUBYLIB, we don't want this bleeding into our runtime newEnv["RUBYLIB"] = "" // Store the "current" environment so Vagrant can restore it when shelling // out. for _, value := range os.Environ() { idx := strings.IndexRune(value, '=') key := fmt.Sprintf("%s_%s", envPrefix, value[:idx]) newEnv[key] = value[idx+1:] } if debug { keys := make([]string, 0, len(newEnv)) for k, _ := range newEnv { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { log.Printf("launcher: env %q = %q", k, newEnv[k]) } } // Set all the environmental variables for k, v := range newEnv { if err := os.Setenv(k, v); err != nil { fmt.Fprintf(os.Stderr, "Error setting env var %s: %s\n", k, err) os.Exit(1) } } // Determine the path to Ruby and then start the Vagrant process rubyPath := filepath.Join(embeddedDir, "bin", "ruby") if runtime.GOOS == "windows" { rubyPath += ".exe" } // Prior to starting the command, we ignore interrupts. Vagrant itself // handles these, so the launcher should just wait until that exits. signal.Ignore(os.Interrupt) cmd := exec.Command(rubyPath) cmd.Args = make([]string, len(os.Args)+1) cmd.Args[0] = "ruby" cmd.Args[1] = filepath.Join(gemPath, "lib", "vagrant", "pre-rubygems.rb") copy(cmd.Args[2:], os.Args[1:]) if debug { log.Printf("launcher: rubyPath = %s", rubyPath) log.Printf("launcher: args = %#v", cmd.Args) } cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Start(); err != nil { fmt.Fprintf(os.Stderr, "Exec error: %s\n", err) os.Exit(1) } exitCode := 0 if err := cmd.Wait(); err != nil { if exiterr, ok := err.(*exec.ExitError); ok { // The program has exited with an exit code != 0 // This works on both Unix and Windows. Although package // syscall is generally platform dependent, WaitStatus is // defined for both Unix and Windows and in both cases has // an ExitStatus() method with the same signature. if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { exitCode = status.ExitStatus() } } } os.Exit(exitCode) }
// Wrap wraps the current executable in a handler to catch panics. It // returns an error if there was an error during the wrapping process. // If the error is nil, then the int result indicates the exit status of the // child process. If the exit status is -1, then this is the child process, // and execution should continue as normal. Otherwise, this is the parent // process and the child successfully ran already, and you should exit the // process with the returned exit status. // // This function should be called very very early in your program's execution. // Ideally, this runs as the first line of code of main. // // Once this is called, the given WrapConfig shouldn't be modified or used // any further. func Wrap(c *WrapConfig) (int, error) { if c.Handler == nil { return -1, errors.New("Handler must be set") } if c.CookieKey == "" { c.CookieKey = DEFAULT_COOKIE_KEY } if c.CookieValue == "" { c.CookieValue = DEFAULT_COOKIE_VAL } if c.DetectDuration == 0 { c.DetectDuration = 300 * time.Millisecond } if c.Writer == nil { c.Writer = os.Stderr } // If the cookie key/value match our environment, then we are the // child, so just exit now and tell the caller that we're the child if os.Getenv(c.CookieKey) == c.CookieValue { return -1, nil } // Get the path to our current executable exePath, err := osext.Executable() if err != nil { return -1, err } // Pipe the stderr so we can read all the data as we look for panics stderr_r, stderr_w := io.Pipe() // doneCh is closed when we're done, signaling any other goroutines // to end immediately. doneCh := make(chan struct{}) // panicCh is the channel on which the panic text will actually be // sent. panicCh := make(chan string) // On close, make sure to finish off the copying of data to stderr defer func() { defer close(doneCh) stderr_w.Close() <-panicCh }() // Start the goroutine that will watch stderr for any panics go trackPanic(stderr_r, c.Writer, c.DetectDuration, panicCh) // Build a subcommand to re-execute ourselves. We make sure to // set the environmental variable to include our cookie. We also // set stdin/stdout to match the config. Finally, we pipe stderr // through ourselves in order to watch for panics. cmd := exec.Command(exePath, os.Args[1:]...) cmd.Env = append(os.Environ(), c.CookieKey+"="+c.CookieValue) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = stderr_w if err := cmd.Start(); err != nil { return 1, err } // Listen to signals and capture them forever. We allow the child // process to handle them in some way. sigCh := make(chan os.Signal) signal.Notify(sigCh, os.Interrupt) go func() { defer signal.Stop(sigCh) for { select { case <-doneCh: return case <-sigCh: } } }() if err := cmd.Wait(); err != nil { exitErr, ok := err.(*exec.ExitError) if !ok { // This is some other kind of subprocessing error. return 1, err } exitStatus := 1 if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { exitStatus = status.ExitStatus() } // Close the writer end so that the tracker goroutine ends at some point stderr_w.Close() // Wait on the panic data panicTxt := <-panicCh if panicTxt != "" { if !c.HidePanic { c.Writer.Write([]byte(panicTxt)) } c.Handler(panicTxt) } return exitStatus, nil } return 0, nil }