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