gitee.com/mysnapcore/mysnapd@v0.1.0/wrappers/binaries.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-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 wrappers is used to generate wrappers and service units and also desktop files for snap applications.
    21  package wrappers
    22  
    23  import (
    24  	"bufio"
    25  	"os"
    26  	"regexp"
    27  
    28  	"gitee.com/mysnapcore/mysnapd/dirs"
    29  	"gitee.com/mysnapcore/mysnapd/osutil"
    30  	"gitee.com/mysnapcore/mysnapd/snap"
    31  	"gitee.com/mysnapcore/mysnapd/strutil"
    32  )
    33  
    34  type completionMode int
    35  
    36  const (
    37  	noCompletion completionMode = iota
    38  	legacyCompletion
    39  	normalCompletion
    40  )
    41  
    42  // detectCompletion verifies if and how completion should be installed.
    43  // If `complete.sh` is not available then completion is disabled.
    44  // If bash-completion less than 2.2, bash-completion does not support
    45  // XDG_DATA_DIRS. So we select legacy path installation.
    46  // If it fails to detect the version of bash-completion, it disables
    47  // completion.
    48  func detectCompletion(base string) (string, completionMode) {
    49  	completeSh := dirs.CompleteShPath(base)
    50  
    51  	if !osutil.FileExists(completeSh) {
    52  		return "", noCompletion
    53  	}
    54  
    55  	fd, err := os.Open(dirs.BashCompletionScript)
    56  	if err != nil {
    57  		// Cannot read file, disable completion
    58  		return "", noCompletion
    59  	}
    60  	defer fd.Close()
    61  
    62  	// Up to 2.5
    63  	releaseOld := regexp.MustCompile(`^# *RELEASE: ([0-9.]+)$`)
    64  	// 2.6 and later
    65  	releaseNew := regexp.MustCompile(`^BASH_COMPLETION_VERSINFO=`)
    66  	s := bufio.NewScanner(fd)
    67  	var matched []string
    68  	for s.Scan() {
    69  		line := s.Text()
    70  		matched = releaseOld.FindStringSubmatch(line)
    71  		if matched == nil {
    72  			if releaseNew.MatchString(line) {
    73  				// It must be 2.6 or later
    74  				return completeSh, normalCompletion
    75  			}
    76  		} else {
    77  			break
    78  		}
    79  	}
    80  	if err := s.Err(); err != nil {
    81  		// Cannot read file, disable completion
    82  		return "", noCompletion
    83  	}
    84  	if matched == nil {
    85  		// Unknown version: disable completion
    86  		return "", noCompletion
    87  	}
    88  
    89  	versionComp, err := strutil.VersionCompare(matched[1], "2.2")
    90  	if err != nil {
    91  		// Cannot parse version, disable completion
    92  		return "", noCompletion
    93  	}
    94  
    95  	if versionComp < 0 {
    96  		if !osutil.IsWritable(dirs.LegacyCompletersDir) {
    97  			return "", noCompletion
    98  		} else {
    99  			return completeSh, legacyCompletion
   100  		}
   101  	} else {
   102  		return completeSh, normalCompletion
   103  	}
   104  }
   105  
   106  // AddSnapBinaries writes the wrapper binaries for the applications from the snap which aren't services.
   107  func AddSnapBinaries(s *snap.Info) (err error) {
   108  	var created []string
   109  	defer func() {
   110  		if err == nil {
   111  			return
   112  		}
   113  		for _, fn := range created {
   114  			os.Remove(fn)
   115  		}
   116  	}()
   117  
   118  	if err := os.MkdirAll(dirs.SnapBinariesDir, 0755); err != nil {
   119  		return err
   120  	}
   121  
   122  	completeSh, completion := detectCompletion(s.Base)
   123  
   124  	for _, app := range s.Apps {
   125  		if app.IsService() {
   126  			continue
   127  		}
   128  
   129  		wrapperPath := app.WrapperPath()
   130  		if err := os.Remove(wrapperPath); err != nil && !os.IsNotExist(err) {
   131  			return err
   132  		}
   133  		if err := os.Symlink("/usr/bin/snap", wrapperPath); err != nil {
   134  			return err
   135  		}
   136  		created = append(created, wrapperPath)
   137  
   138  		if completion == normalCompletion {
   139  			legacyCompPath := app.LegacyCompleterPath()
   140  			if dirs.IsCompleteShSymlink(legacyCompPath) {
   141  				os.Remove(legacyCompPath)
   142  			}
   143  		}
   144  
   145  		if completion == noCompletion || app.Completer == "" {
   146  			continue
   147  		}
   148  		// symlink the completion snippet
   149  		compDir := dirs.CompletersDir
   150  		if completion == legacyCompletion {
   151  			compDir = dirs.LegacyCompletersDir
   152  		}
   153  		if err := os.MkdirAll(compDir, 0755); err != nil {
   154  			return err
   155  		}
   156  		compPath := app.CompleterPath()
   157  		if completion == legacyCompletion {
   158  			compPath = app.LegacyCompleterPath()
   159  		}
   160  		if err := os.Symlink(completeSh, compPath); err == nil {
   161  			created = append(created, compPath)
   162  		} else if !os.IsExist(err) {
   163  			return err
   164  		}
   165  	}
   166  
   167  	return nil
   168  }
   169  
   170  // RemoveSnapBinaries removes the wrapper binaries for the applications from the snap which aren't services from.
   171  func RemoveSnapBinaries(s *snap.Info) error {
   172  	for _, app := range s.Apps {
   173  		os.Remove(app.WrapperPath())
   174  		if app.Completer == "" {
   175  			continue
   176  		}
   177  		compPath := app.CompleterPath()
   178  		if dirs.IsCompleteShSymlink(compPath) {
   179  			os.Remove(compPath)
   180  		}
   181  		legacyCompPath := app.LegacyCompleterPath()
   182  		if dirs.IsCompleteShSymlink(legacyCompPath) {
   183  			os.Remove(legacyCompPath)
   184  		}
   185  	}
   186  
   187  	return nil
   188  }