github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/snapstate/backend/aliases.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2017 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 backend
    21  
    22  import (
    23  	"fmt"
    24  	"os"
    25  	"path/filepath"
    26  	"strings"
    27  	"syscall"
    28  
    29  	"github.com/snapcore/snapd/dirs"
    30  )
    31  
    32  // Alias represents a command alias with a name and its application target.
    33  type Alias struct {
    34  	Name   string `json:"name"`
    35  	Target string `json:"target"`
    36  }
    37  
    38  // MatchingAliases returns the subset of aliases that exist on disk and have the expected targets.
    39  
    40  // UpdateAliases adds and removes the given aliases.
    41  func (b Backend) UpdateAliases(add []*Alias, remove []*Alias) error {
    42  	removed := make(map[string]bool, len(remove))
    43  	for _, alias := range remove {
    44  		err := os.Remove(filepath.Join(dirs.SnapBinariesDir, alias.Name))
    45  		if err != nil && !os.IsNotExist(err) {
    46  			return fmt.Errorf("cannot remove alias symlink: %v", err)
    47  		}
    48  		removed[alias.Name] = true
    49  		completer := filepath.Join(dirs.CompletersDir, alias.Name)
    50  		target, err := os.Readlink(completer)
    51  		if err != nil || target != alias.Target {
    52  			continue
    53  		}
    54  		os.Remove(completer)
    55  	}
    56  
    57  	for _, alias := range add {
    58  		p := filepath.Join(dirs.SnapBinariesDir, alias.Name)
    59  
    60  		if !removed[alias.Name] {
    61  			if err := os.Remove(p); err != nil && !os.IsNotExist(err) {
    62  				return fmt.Errorf("cannot remove alias symlink: %v", err)
    63  			}
    64  		}
    65  
    66  		err := os.Symlink(alias.Target, p)
    67  		if err != nil {
    68  			return fmt.Errorf("cannot create alias symlink: %v", err)
    69  		}
    70  
    71  		if dirs.IsCompleteShSymlink(filepath.Join(dirs.CompletersDir, alias.Target)) {
    72  			os.Symlink(alias.Target, filepath.Join(dirs.CompletersDir, alias.Name))
    73  		}
    74  	}
    75  	return nil
    76  }
    77  
    78  // RemoveSnapAliases removes all the aliases targeting the given snap.
    79  func (b Backend) RemoveSnapAliases(snapName string) error {
    80  	removeSymlinksTo(dirs.CompletersDir, snapName)
    81  	return removeSymlinksTo(dirs.SnapBinariesDir, snapName)
    82  }
    83  
    84  func removeSymlinksTo(dir, snapName string) error {
    85  	cands, err := filepath.Glob(filepath.Join(dir, "*"))
    86  	if err != nil {
    87  		return err
    88  	}
    89  	prefix := fmt.Sprintf("%s.", snapName)
    90  	var firstErr error
    91  	// best effort
    92  	for _, cand := range cands {
    93  		target, err := os.Readlink(cand)
    94  		if err, ok := err.(*os.PathError); ok && err.Err == syscall.EINVAL {
    95  			continue
    96  		}
    97  		if err != nil {
    98  			if firstErr == nil {
    99  				firstErr = err
   100  			}
   101  			continue
   102  		}
   103  		if target == snapName || strings.HasPrefix(target, prefix) {
   104  			err := os.Remove(cand)
   105  			if err != nil && firstErr == nil {
   106  				firstErr = fmt.Errorf("cannot remove alias symlink: %v", err)
   107  			}
   108  		}
   109  	}
   110  	return firstErr
   111  }