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  }