github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/builder/amazon/chroot/communicator.go (about) 1 package chroot 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "log" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "strings" 13 "syscall" 14 15 "github.com/hashicorp/packer/packer" 16 ) 17 18 // Communicator is a special communicator that works by executing 19 // commands locally but within a chroot. 20 type Communicator struct { 21 Chroot string 22 CmdWrapper CommandWrapper 23 } 24 25 func (c *Communicator) Start(cmd *packer.RemoteCmd) error { 26 command, err := c.CmdWrapper( 27 fmt.Sprintf("chroot %s /bin/sh -c \"%s\"", c.Chroot, cmd.Command)) 28 if err != nil { 29 return err 30 } 31 32 localCmd := ShellCommand(command) 33 localCmd.Stdin = cmd.Stdin 34 localCmd.Stdout = cmd.Stdout 35 localCmd.Stderr = cmd.Stderr 36 log.Printf("Executing: %s %#v", localCmd.Path, localCmd.Args) 37 if err := localCmd.Start(); err != nil { 38 return err 39 } 40 41 go func() { 42 exitStatus := 0 43 if err := localCmd.Wait(); err != nil { 44 if exitErr, ok := err.(*exec.ExitError); ok { 45 exitStatus = 1 46 47 // There is no process-independent way to get the REAL 48 // exit status so we just try to go deeper. 49 if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { 50 exitStatus = status.ExitStatus() 51 } 52 } 53 } 54 55 log.Printf( 56 "Chroot execution exited with '%d': '%s'", 57 exitStatus, cmd.Command) 58 cmd.SetExited(exitStatus) 59 }() 60 61 return nil 62 } 63 64 func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error { 65 dst = filepath.Join(c.Chroot, dst) 66 log.Printf("Uploading to chroot dir: %s", dst) 67 tf, err := ioutil.TempFile("", "packer-amazon-chroot") 68 if err != nil { 69 return fmt.Errorf("Error preparing shell script: %s", err) 70 } 71 defer os.Remove(tf.Name()) 72 73 if _, err := io.Copy(tf, r); err != nil { 74 return err 75 } 76 77 cpCmd, err := c.CmdWrapper(fmt.Sprintf("cp %s %s", tf.Name(), dst)) 78 if err != nil { 79 return err 80 } 81 82 return ShellCommand(cpCmd).Run() 83 } 84 85 func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { 86 // If src ends with a trailing "/", copy from "src/." so that 87 // directory contents (including hidden files) are copied, but the 88 // directory "src" is omitted. BSD does this automatically when 89 // the source contains a trailing slash, but linux does not. 90 if src[len(src)-1] == '/' { 91 src = src + "." 92 } 93 94 // TODO: remove any file copied if it appears in `exclude` 95 chrootDest := filepath.Join(c.Chroot, dst) 96 97 log.Printf("Uploading directory '%s' to '%s'", src, chrootDest) 98 cpCmd, err := c.CmdWrapper(fmt.Sprintf("cp -R '%s' %s", src, chrootDest)) 99 if err != nil { 100 return err 101 } 102 103 var stderr bytes.Buffer 104 cmd := ShellCommand(cpCmd) 105 cmd.Env = append(cmd.Env, "LANG=C") 106 cmd.Env = append(cmd.Env, os.Environ()...) 107 cmd.Stderr = &stderr 108 err = cmd.Run() 109 if err == nil { 110 return err 111 } 112 113 if strings.Contains(stderr.String(), "No such file") { 114 // This just means that the directory was empty. Just ignore it. 115 return nil 116 } 117 118 return err 119 } 120 121 func (c *Communicator) DownloadDir(src string, dst string, exclude []string) error { 122 return fmt.Errorf("DownloadDir is not implemented for amazon-chroot") 123 } 124 125 func (c *Communicator) Download(src string, w io.Writer) error { 126 src = filepath.Join(c.Chroot, src) 127 log.Printf("Downloading from chroot dir: %s", src) 128 f, err := os.Open(src) 129 if err != nil { 130 return err 131 } 132 defer f.Close() 133 134 if _, err := io.Copy(w, f); err != nil { 135 return err 136 } 137 138 return nil 139 }