github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/snap/cmd_interfaces.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 main 21 22 import ( 23 "fmt" 24 25 "github.com/snapcore/snapd/client" 26 "github.com/snapcore/snapd/i18n" 27 28 "github.com/jessevdk/go-flags" 29 ) 30 31 type cmdInterfaces struct { 32 clientMixin 33 Interface string `short:"i"` 34 Positionals struct { 35 Query interfacesSlotOrPlugSpec `skip-help:"true"` 36 } `positional-args:"true"` 37 } 38 39 var shortInterfacesHelp = i18n.G("List interfaces' slots and plugs") 40 var longInterfacesHelp = i18n.G(` 41 The interfaces command lists interfaces available in the system. 42 43 By default all slots and plugs, used and offered by all snaps, are displayed. 44 45 $ snap interfaces <snap>:<slot or plug> 46 47 Lists only the specified slot or plug. 48 49 $ snap interfaces <snap> 50 51 Lists the slots offered and plugs used by the specified snap. 52 53 $ snap interfaces -i=<interface> [<snap>] 54 55 Filters the complete output so only plugs and/or slots matching the provided 56 details are listed. 57 58 NOTE this command is deprecated and has been replaced with the 'connections' 59 command. 60 `) 61 62 func init() { 63 cmd := addCommand("interfaces", shortInterfacesHelp, longInterfacesHelp, func() flags.Commander { 64 return &cmdInterfaces{} 65 }, map[string]string{ 66 // TRANSLATORS: This should not start with a lowercase letter. 67 "i": i18n.G("Constrain listing to specific interfaces"), 68 }, []argDesc{{ 69 // TRANSLATORS: This needs to begin with < and end with > 70 name: i18n.G("<snap>:<slot or plug>"), 71 // TRANSLATORS: This should not start with a lowercase letter. 72 desc: i18n.G("Constrain listing to a specific snap or snap:name"), 73 }}) 74 cmd.hidden = true 75 } 76 77 var interfacesDeprecationNotice = i18n.G("'snap interfaces' is deprecated; use 'snap connections'.") 78 79 func (x *cmdInterfaces) Execute(args []string) error { 80 if len(args) > 0 { 81 return ErrExtraArgs 82 } 83 84 opts := client.ConnectionOptions{ 85 All: true, 86 Snap: x.Positionals.Query.Snap, 87 } 88 ifaces, err := x.client.Connections(&opts) 89 if err != nil { 90 return err 91 } 92 if len(ifaces.Plugs) == 0 && len(ifaces.Slots) == 0 { 93 return fmt.Errorf(i18n.G("no interfaces found")) 94 } 95 96 defer fmt.Fprintln(Stderr, "\n"+fill(interfacesDeprecationNotice, 0)) 97 98 w := tabWriter() 99 defer w.Flush() 100 fmt.Fprintln(w, i18n.G("Slot\tPlug")) 101 102 wantedSnap := x.Positionals.Query.Snap 103 for _, slot := range ifaces.Slots { 104 if wantedSnap != "" { 105 var ok bool 106 if wantedSnap == slot.Snap { 107 ok = true 108 } 109 // Normally snap nicknames are handled internally in the snapd 110 // layer. This specific command is an exception as it does 111 // client-side filtering. As a special case, when the user asked 112 // for the snap "core" but we see the "system" nickname or the 113 // "snapd" snap, treat that as a match. 114 // 115 // The system nickname was returned in 2.35. 116 // The snapd snap is returned by 2.36+ if snapd snap is installed 117 // and is the host for implicit interfaces. 118 if (wantedSnap == "core" || wantedSnap == "snapd" || wantedSnap == "system") && (slot.Snap == "core" || slot.Snap == "snapd" || slot.Snap == "system") { 119 ok = true 120 } 121 122 for i := 0; i < len(slot.Connections) && !ok; i++ { 123 if wantedSnap == slot.Connections[i].Snap { 124 ok = true 125 } 126 } 127 if !ok { 128 continue 129 } 130 } 131 if x.Positionals.Query.Name != "" && x.Positionals.Query.Name != slot.Name { 132 continue 133 } 134 if x.Interface != "" && slot.Interface != x.Interface { 135 continue 136 } 137 // There are two special snaps, the "core" and "snapd" snaps are 138 // abbreviated to an empty snap name. The "system" snap name is still 139 // here in case we talk to older snapd for some reason. 140 if slot.Snap == "core" || slot.Snap == "snapd" || slot.Snap == "system" { 141 fmt.Fprintf(w, ":%s\t", slot.Name) 142 } else { 143 fmt.Fprintf(w, "%s:%s\t", slot.Snap, slot.Name) 144 } 145 for i := 0; i < len(slot.Connections); i++ { 146 if i > 0 { 147 fmt.Fprint(w, ",") 148 } 149 if slot.Connections[i].Name != slot.Name { 150 fmt.Fprintf(w, "%s:%s", slot.Connections[i].Snap, slot.Connections[i].Name) 151 } else { 152 fmt.Fprintf(w, "%s", slot.Connections[i].Snap) 153 } 154 } 155 // Display visual indicator for disconnected slots 156 if len(slot.Connections) == 0 { 157 fmt.Fprint(w, "-") 158 } 159 fmt.Fprintf(w, "\n") 160 } 161 // Plugs are treated differently. Since the loop above already printed each connected 162 // plug, the loop below focuses on printing just the disconnected plugs. 163 for _, plug := range ifaces.Plugs { 164 if wantedSnap != "" { 165 if wantedSnap != plug.Snap { 166 continue 167 } 168 } 169 if x.Positionals.Query.Name != "" && x.Positionals.Query.Name != plug.Name { 170 continue 171 } 172 if x.Interface != "" && plug.Interface != x.Interface { 173 continue 174 } 175 // Display visual indicator for disconnected plugs. 176 if len(plug.Connections) == 0 { 177 fmt.Fprintf(w, "-\t%s:%s\n", plug.Snap, plug.Name) 178 } 179 } 180 return nil 181 }