gitee.com/mysnapcore/mysnapd@v0.1.0/daemon/api_snapctl.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2020 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 daemon 21 22 import ( 23 "net/http" 24 25 "github.com/jessevdk/go-flags" 26 27 "gitee.com/mysnapcore/mysnapd/client" 28 "gitee.com/mysnapcore/mysnapd/i18n" 29 "gitee.com/mysnapcore/mysnapd/jsonutil" 30 "gitee.com/mysnapcore/mysnapd/overlord/auth" 31 "gitee.com/mysnapcore/mysnapd/overlord/hookstate/ctlcmd" 32 ) 33 34 var ( 35 snapctlCmd = &Command{ 36 Path: "/v2/snapctl", 37 POST: runSnapctl, 38 WriteAccess: snapAccess{}, 39 } 40 ) 41 42 var ctlcmdRun = ctlcmd.Run 43 44 func runSnapctl(c *Command, r *http.Request, user *auth.UserState) Response { 45 var snapctlPostData client.SnapCtlPostData 46 47 if err := jsonutil.DecodeWithNumber(r.Body, &snapctlPostData); err != nil { 48 return BadRequest("cannot decode snapctl request: %s", err) 49 } 50 51 if len(snapctlPostData.Args) == 0 { 52 return BadRequest("snapctl cannot run without args") 53 } 54 55 ucred, err := ucrednetGet(r.RemoteAddr) 56 if err != nil { 57 return Forbidden("cannot get remote user: %s", err) 58 } 59 60 // Ignore missing context error to allow 'snapctl -h' without a context; 61 // Actual context is validated later by get/set. 62 context, _ := c.d.overlord.HookManager().Context(snapctlPostData.ContextID) 63 64 // make the data read from stdin available for the hook 65 // TODO: use a forwarded stdin here 66 if snapctlPostData.Stdin != nil { 67 context.Lock() 68 context.Set("stdin", snapctlPostData.Stdin) 69 context.Unlock() 70 } 71 72 stdout, stderr, err := ctlcmdRun(context, snapctlPostData.Args, ucred.Uid) 73 if err != nil { 74 if e, ok := err.(*ctlcmd.UnsuccessfulError); ok { 75 result := map[string]interface{}{ 76 "stdout": string(stdout), 77 "stderr": string(stderr), 78 "exit-code": e.ExitCode, 79 } 80 return &apiError{ 81 Status: 200, 82 Message: e.Error(), 83 Kind: client.ErrorKindUnsuccessful, 84 Value: result, 85 } 86 } 87 if e, ok := err.(*ctlcmd.ForbiddenCommandError); ok { 88 return Forbidden(e.Error()) 89 } 90 if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { 91 stdout = []byte(e.Error()) 92 } else { 93 return BadRequest("error running snapctl: %s", err) 94 } 95 } 96 97 if context != nil && context.IsEphemeral() { 98 context.Lock() 99 defer context.Unlock() 100 if err := context.Done(); err != nil { 101 return BadRequest(i18n.G("set failed: %v"), err) 102 } 103 } 104 105 result := map[string]string{ 106 "stdout": string(stdout), 107 "stderr": string(stderr), 108 } 109 110 return SyncResponse(result) 111 }