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