github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/client/daemonservice/command/command.go (about) 1 // Copyright 2017 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package command provides a relatively thin wrapper around exec.Cmd, adding support 16 // for communicating with the dependent process using a channel.Channel. 17 package command 18 19 import ( 20 "fmt" 21 "os" 22 "os/exec" 23 24 "github.com/google/fleetspeak/fleetspeak/src/client/channel" 25 ) 26 27 // Command is a wrapper around exec.Cmd which adds needed operations which are 28 // os specific. Notably, it knows how to attach a channel.Channel. 29 type Command struct { 30 exec.Cmd 31 32 filesToClose []*os.File // Files to close after starting cmd. 33 } 34 35 // Start refines exec.Cmd.Start. 36 func (cmd *Command) Start() error { 37 // Delegate to the wrapped struct. 38 if err := cmd.Cmd.Start(); err != nil { 39 return err 40 } 41 42 // Close our copy of the passed pipe file descrptors. 43 for _, f := range cmd.filesToClose { 44 f.Close() 45 } 46 47 return nil 48 } 49 50 // SoftKill sends SIGINT to cmd. 51 func (cmd *Command) SoftKill() error { 52 return cmd.softKill() 53 } 54 55 // Kill sends SIGKILL to cmd. 56 func (cmd *Command) Kill() error { 57 return cmd.kill() 58 } 59 60 // AddEnvVar passes the given environment variable to the process. 61 // 62 // The environment variables are passed when the process is spawned, so this 63 // method should only be called before .Start is called. 64 // 65 // The argument kv is a key value pair in the form "key=value". 66 func (cmd *Command) AddEnvVar(kv string) { 67 if cmd.Env == nil { 68 // Append to current environment instead of overriding it. That's also how 69 // os/exec handles .Env == nil - see golang.org/src/os/exec/exec.go 70 cmd.Env = os.Environ() 71 } 72 73 cmd.Env = append(cmd.Env, kv) 74 } 75 76 // addInPipeFD creates a pipe passed to the process as a file descriptor. The 77 // process receives the read end of the pipe. 78 // 79 // The file descriptor is passed when the process is spawned, so this method 80 // should only be called before .Start is called. 81 // 82 // Returns the write end of the pipe and the file descriptor number that the 83 // process will receive. 84 func (cmd *Command) addInPipeFD() (*os.File, int, error) { 85 return cmd.addInPipeFDImpl() 86 } 87 88 // addOutPipeFD creates a pipe passed to the process as a file descriptor. The 89 // process receives the write end of the pipe. 90 // 91 // The file descriptor is passed when the process is spawned, so this method 92 // should only be called before .Start is called. 93 // 94 // Returns the read end of the pipe and the file descriptor number that the 95 // process will receive. 96 func (cmd *Command) addOutPipeFD() (*os.File, int, error) { 97 return cmd.addOutPipeFDImpl() 98 } 99 100 // SetupCommsChannel prepares a daemonServiceCommand to communicate with a 101 // DaemonService locally over an interprocess pipe. 102 func (cmd *Command) SetupCommsChannel() (*channel.Channel, error) { 103 pw, inFd, err := cmd.addInPipeFD() 104 if err != nil { 105 return nil, fmt.Errorf("failed to create an input pipe: %v", err) 106 } 107 108 pr, outFd, err := cmd.addOutPipeFD() 109 if err != nil { 110 return nil, fmt.Errorf("failed to create an output pipe: %v", err) 111 } 112 113 // Note the FD numbers in cmd's environment. 114 cmd.AddEnvVar(fmt.Sprintf("FLEETSPEAK_COMMS_CHANNEL_INFD=%d", inFd)) 115 cmd.AddEnvVar(fmt.Sprintf("FLEETSPEAK_COMMS_CHANNEL_OUTFD=%d", outFd)) 116 117 return channel.New(pr, pw), nil 118 }