github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/client/snapctl.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package client 21 22 import ( 23 "bytes" 24 "encoding/json" 25 "fmt" 26 "io" 27 "io/ioutil" 28 ) 29 30 // InternalSnapctlCmdNeedsStdin returns true if the given snapctl command 31 // needs data from stdin 32 func InternalSnapctlCmdNeedsStdin(name string) bool { 33 switch name { 34 case "fde-setup-result": 35 return true 36 default: 37 return false 38 } 39 } 40 41 // SnapCtlOptions holds the various options with which snapctl is invoked. 42 type SnapCtlOptions struct { 43 // ContextID is a string used to determine the context of this call (e.g. 44 // which context and handler should be used, etc.) 45 ContextID string `json:"context-id"` 46 47 // Args contains a list of parameters to use for this invocation. 48 Args []string `json:"args"` 49 } 50 51 // SnapCtlPostData is the data posted to the daemon /v2/snapctl endpoint 52 // TODO: this can be removed again once we no longer need to pass stdin data 53 // but instead use a real stdin stream 54 type SnapCtlPostData struct { 55 SnapCtlOptions 56 57 Stdin []byte `json:"stdin,omitempty"` 58 } 59 60 type snapctlOutput struct { 61 Stdout string `json:"stdout"` 62 Stderr string `json:"stderr"` 63 } 64 65 // protect against too much data via stdin 66 var stdinReadLimit = int64(4 * 1000 * 1000) 67 68 // RunSnapctl requests a snapctl run for the given options. 69 func (client *Client) RunSnapctl(options *SnapCtlOptions, stdin io.Reader) (stdout, stderr []byte, err error) { 70 // TODO: instead of reading all of stdin here we need to forward it to 71 // the daemon eventually 72 var stdinData []byte 73 if stdin != nil { 74 limitedStdin := &io.LimitedReader{R: stdin, N: stdinReadLimit + 1} 75 stdinData, err = ioutil.ReadAll(limitedStdin) 76 if err != nil { 77 return nil, nil, fmt.Errorf("cannot read stdin: %v", err) 78 } 79 if limitedStdin.N <= 0 { 80 return nil, nil, fmt.Errorf("cannot read more than %v bytes of data from stdin", stdinReadLimit) 81 } 82 } 83 84 b, err := json.Marshal(SnapCtlPostData{ 85 SnapCtlOptions: *options, 86 Stdin: stdinData, 87 }) 88 if err != nil { 89 return nil, nil, fmt.Errorf("cannot marshal options: %s", err) 90 } 91 92 var output snapctlOutput 93 _, err = client.doSync("POST", "/v2/snapctl", nil, nil, bytes.NewReader(b), &output) 94 if err != nil { 95 return nil, nil, err 96 } 97 98 return []byte(output.Stdout), []byte(output.Stderr), nil 99 }