github.com/holochain/holochain-proto@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/cmd/common.go (about)

     1  // Copyright (C) 2013-2018, The MetaCurrency Project (Eric Harris-Braun, Arthur Brock, et. al.)
     2  // Use of this source code is governed by GPLv3 found in the LICENSE file
     3  //----------------------------------------------------------------------------------------
     4  
     5  // utilities for the holochain commands
     6  
     7  package cmd
     8  
     9  import (
    10  	"bytes"
    11  	"errors"
    12  	"fmt"
    13  	"github.com/urfave/cli"
    14  	"io"
    15  	"net"
    16  	"os"
    17  	"os/exec"
    18  	"os/user"
    19  	"path/filepath"
    20  	"syscall"
    21  	"time"
    22  
    23  	holo "github.com/holochain/holochain-proto"
    24  )
    25  
    26  var ErrServiceUninitialized = errors.New("service not initialized, run 'hcadmin init'")
    27  
    28  func MakeErr(c *cli.Context, text string) error {
    29  	if c != nil {
    30  		text = fmt.Sprintf("%s: %s", c.Command.Name, text)
    31  	}
    32  
    33  	if os.Getenv("HC_TESTING") != "" {
    34  		os.Setenv("HC_TESTING_EXITERR", fmt.Sprintf("%d", 1))
    35  		fmt.Printf(text)
    36  		return errors.New(text)
    37  	} else {
    38  		return cli.NewExitError(text, 1)
    39  	}
    40  }
    41  
    42  func MakeErrFromErr(c *cli.Context, err error) error {
    43  	return MakeErr(c, err.Error())
    44  }
    45  
    46  func GetCurrentDirectory() (dir string, err error) {
    47  	dir, err = os.Getwd()
    48  	return
    49  }
    50  
    51  func syscallExec(binaryFile string, args ...string) error {
    52  	return syscall.Exec(binaryFile, append([]string{binaryFile}, args...), os.Environ())
    53  }
    54  
    55  func ExecBinScript(script string, args ...string) (err error) {
    56  	var path string
    57  	path, err = GolangHolochainDir("bin", script)
    58  	if err != nil {
    59  		return
    60  	}
    61  
    62  	holo.Debugf("ExecBinScript: %v (%v)", path, args)
    63  
    64  	return syscallExec(path, args...)
    65  }
    66  
    67  func OsExecSilent(args ...string) error {
    68  	cmd := exec.Command(args[0], args[1:]...)
    69  	holo.Debugf("OsExecSilent: %v", cmd)
    70  
    71  	output, err := cmd.CombinedOutput()
    72  	if err != nil {
    73  		return err
    74  	}
    75  	holo.Debugf("OsExecSilent: %v", output)
    76  
    77  	return nil
    78  }
    79  
    80  // OsExecPipes executes a command as if we are in a shell, including user input
    81  func OsExecPipes(args ...string) *exec.Cmd {
    82  	cmd := OsExecPipes_noRun(args...)
    83  	holo.Debugf("OsExecPipes: %v", cmd)
    84  	cmd.Run()
    85  	return cmd
    86  }
    87  
    88  // OsExecPipes executes a command as if we are in a shell, including user input
    89  func OsExecPipes_noRun(args ...string) *exec.Cmd {
    90  	cmd := exec.Command(args[0], args[1:]...)
    91  	holo.Debugf("OsExecPipes_noRun: %v", cmd)
    92  
    93  	cmd.Stdout = os.Stdout
    94  	cmd.Stderr = os.Stderr
    95  	// cmd.Stdin = os.Stdin
    96  
    97  	return cmd
    98  }
    99  
   100  // RunAppWithStdoutCapture runs a cli.App and captures the stdout
   101  func RunAppWithStdoutCapture(app *cli.App, args []string, wait time.Duration) (out string, err error) {
   102  	os.Args = args
   103  
   104  	old := os.Stdout // keep backup of the real stdout
   105  	r, w, _ := os.Pipe()
   106  	os.Stdout = w
   107  
   108  	go func() { err = app.Run(os.Args) }()
   109  
   110  	outC := make(chan string)
   111  	// copy the output in a separate goroutine so printing can't block indefinitely
   112  	go func() {
   113  		var buf bytes.Buffer
   114  		io.Copy(&buf, r)
   115  		outC <- buf.String()
   116  	}()
   117  
   118  	time.Sleep(wait)
   119  
   120  	// back to normal state
   121  	w.Close()
   122  	os.Stdout = old // restoring the real stdout
   123  	out = <-outC
   124  	return
   125  }
   126  
   127  var configExtensionList []string
   128  
   129  func GetConfigExtensionList() (conExtList []string) {
   130  	if configExtensionList == nil {
   131  		configExtensionList = []string{"json", "toml", "yaml", "yml"}
   132  	}
   133  	return configExtensionList
   134  }
   135  
   136  // IsAppDir tests path to see if it's a properly set up holochain app
   137  // returns nil on success or error describing the problem
   138  func IsAppDir(path string) (err error) {
   139  	err = nil
   140  
   141  	for _, filename := range GetConfigExtensionList() {
   142  		info, err := os.Stat(filepath.Join(path, "dna", "dna."+filename))
   143  		if err != nil {
   144  			// err = fmt.Errorf("directory missing dna/%v file", filename)
   145  		} else {
   146  			if info.Mode().IsRegular() {
   147  				return nil
   148  			}
   149  		}
   150  	}
   151  	err = fmt.Errorf("HC: Holochain App directory missing dna/dna.xyz config file")
   152  	return err
   153  }
   154  
   155  // IsCoreDir tests path to see if it is contains Holochain Core source files
   156  // returns nil on success or an error describing the problem
   157  // func IsCoreDir(path string) error {
   158  // check for the existance of package.json
   159  //
   160  // return IsFile(filepath.Join(path, "package.json")
   161  // }
   162  
   163  // GetHolochainRoot returns either the path from the environment variable or the default
   164  func GetHolochainRoot(root string) (string, error) {
   165  	if root == "" {
   166  		root = os.Getenv("HOLOPATH")
   167  		if root == "" {
   168  			u, err := user.Current()
   169  			if err != nil {
   170  				return "", err
   171  			}
   172  			userPath := u.HomeDir
   173  			root = filepath.Join(userPath, holo.DefaultDirectoryName)
   174  		}
   175  	}
   176  	return root, nil
   177  }
   178  
   179  // GetService is a helper function to load the holochain service from default locations or a given path
   180  func GetService(root string) (service *holo.Service, err error) {
   181  	holo.InitializeHolochain()
   182  	if initialized := holo.IsInitialized(root); !initialized {
   183  		err = ErrServiceUninitialized
   184  	} else {
   185  		service, err = holo.LoadService(root)
   186  	}
   187  	return
   188  }
   189  
   190  // GetHolochain os a helper function to load a holochain from a directory or report an error based on a command name
   191  func GetHolochain(name string, service *holo.Service, cmd string) (h *holo.Holochain, err error) {
   192  	if service == nil {
   193  		err = ErrServiceUninitialized
   194  		return
   195  	}
   196  
   197  	if name == "" {
   198  		err = errors.New("missing required holochain-name argument to " + cmd)
   199  		return
   200  	}
   201  
   202  	h, err = service.Load(name)
   203  	if err != nil {
   204  		return
   205  	}
   206  
   207  	val := os.Getenv("HOLOCHAINCONFIG_ENABLENATUPNP")
   208  	if val != "" {
   209  		h.Config.EnableNATUPnP = val == "true"
   210  	}
   211  
   212  	if err = h.Prepare(); err != nil {
   213  		return
   214  	}
   215  	return
   216  }
   217  
   218  func Die(message string) {
   219  	fmt.Println(message)
   220  	os.Exit(1)
   221  }
   222  
   223  func GolangHolochainDir(subPath ...string) (path string, err error) {
   224  	err = nil
   225  	joinable := append([]string{os.Getenv("GOPATH"), "src/github.com/holochain/holochain-proto"}, subPath...)
   226  	path = filepath.Join(joinable...)
   227  	return
   228  }
   229  
   230  func IsFile(path ...string) bool {
   231  	return IsFileFromString(filepath.Join(path...))
   232  }
   233  func IsFileFromString(path string) bool {
   234  	info, err := os.Stat(path)
   235  	if err != nil {
   236  		return false
   237  	} else {
   238  		if !info.Mode().IsRegular() {
   239  			return false
   240  		}
   241  	}
   242  
   243  	return true
   244  }
   245  
   246  func IsDir(pathParts ...string) bool {
   247  	path := filepath.Join(pathParts...)
   248  	info, err := os.Stat(path)
   249  	return err == nil && info.Mode().IsDir()
   250  }
   251  
   252  func GetTmpDir(name string) (d string, err error) {
   253  	d = filepath.Join("/", "tmp", name)
   254  	//d, err = ioutil.TempDir("", d)
   255  	return
   256  }
   257  
   258  func MakeTmpDir(name string) (tmpHolochainCopyDir string, err error) {
   259  	tmpHolochainCopyDir, err = GetTmpDir(name)
   260  	if err != nil {
   261  		return
   262  	}
   263  	os.RemoveAll(tmpHolochainCopyDir)
   264  	err = os.MkdirAll(tmpHolochainCopyDir, 0770)
   265  	if err != nil {
   266  		return "", err
   267  	}
   268  	return
   269  }
   270  
   271  // Ask the kernel for a free open port that is ready to use
   272  func GetFreePort() (port int, err error) {
   273  	port = -1
   274  	err = nil
   275  
   276  	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
   277  	if err != nil {
   278  		return
   279  	}
   280  
   281  	l, err := net.ListenTCP("tcp", addr)
   282  	if err != nil {
   283  		return
   284  	}
   285  	defer l.Close()
   286  	port = l.Addr().(*net.TCPAddr).Port
   287  	return
   288  }
   289  
   290  func GetUnixTimestamp_secondsFromNow(seconds int) int64 {
   291  	return time.Now().Add(time.Duration(seconds) * time.Second).Unix()
   292  }
   293  func GetDuration_fromUnixTimestamp(timestamp int64) (duration time.Duration) {
   294  	duration = 0 * time.Second
   295  	targetTime := time.Unix(timestamp, 0)
   296  	duration = targetTime.Sub(time.Now())
   297  	return
   298  }
   299  
   300  func UpackageAppPackage(service *holo.Service, appPackagePath string, toPath string, appName string, encodingFormat string) (appPackage *holo.AppPackage, err error) {
   301  	sf, err := os.Open(appPackagePath)
   302  	if err != nil {
   303  		return
   304  	}
   305  	defer sf.Close()
   306  	decodingFormat := holo.EncodingFormat(appPackagePath)
   307  	appPackage, err = service.SaveFromAppPackage(sf, toPath, appName, nil, decodingFormat, encodingFormat, false)
   308  	return
   309  }
   310  
   311  // var syncWatcher fsnotify.Watcher
   312  // var created_syncWatcher bool
   313  
   314  // func SyncStart(syncName string) err error {
   315  //   err = nil
   316  
   317  //   syncDir := filepath.Join("/tmp", "hc.sync")
   318  //   if !DirExists(syncDir) {
   319  //     err = os.MkdirAll(syncDir, "0666")
   320  //     if err != nil {
   321  //       return err
   322  //     }
   323  //   }
   324  
   325  //   syncFile := filepath.Join(syncDir, syncName)
   326  //   if FileExists(syncPath) {
   327  //     return errors.New("HC: common.go: SyncStart(%v): file already exists", syncFile)
   328  //   }
   329  
   330  //   os.OpenFile(syncFile, os.O_RDONLY|os.O_CREATE, 0666)
   331  // }
   332  
   333  // func SyncOnRM(syncName string) (err error) {
   334  //   syncFile := filepath.Join("/tmp", "hc.sync", syncName)
   335  //   if !FileExists(syncFile) {
   336  //     return errors.New("HC: common.go: SyncOnRM(%v)", syncFile)
   337  //   }
   338  
   339  //   if ! created_syncWatcher {
   340  //     watcher, err := fsnotify.NewWatcher()
   341  //     if err != nil {
   342  //       log.Fatal(err)
   343  //     }
   344  //     defer watcher.Close()
   345  
   346  //     done := make(chan bool)
   347  //     go func() {
   348  //       for {
   349  //         select {
   350  //         case event := <-watcher.Events:
   351  //           log.Println("event:", event)
   352  //           if event.Op&fsnotify.Remove == fsnotify.Remove {
   353  //             log.Println("modified file:", event.Name)
   354  //           }
   355  //         case err := <-watcher.Errors:
   356  //           log.Println("error:", err)
   357  //         }
   358  //       }
   359  //     }()
   360  //     created_syncWatcher = true
   361  //   }
   362  
   363  //   err = watcher.Add(syncPath)
   364  //   if err != nil {
   365  //     return err
   366  //   }
   367  //   <-done
   368  // }
   369  
   370  // func