예제 #1
0
파일: shell.go 프로젝트: tillberg/gut
func Shutdown(reason string, exitcode int) {
	shutdownLock.Lock()
	for i := 0; i < shutdownChanLen; i++ {
		shutdownChan <- true
	}
	status := log.New(os.Stderr, "", 0)
	if reason != "" {
		status.Printf("%s ", reason)
	}
	status.Printf("Stopping all subprocesses...\n")
	done := make(chan bool)
	for _, _ctx := range AllSyncContexts {
		go func(ctx *SyncContext) {
			if ctx.IsConnected() {
				ctx.KillAllSessions()
				// This generally shouldn't *do* anything other than
				// clean up the PID files, as the killing would have
				// been done already in KillAllSessions.
				ctx.KillAllViaPidfiles()
				ctx.Close()
			}
			done <- true
		}(_ctx)
	}
	for range AllSyncContexts {
		select {
		case <-done:
		case <-time.After(3 * time.Second):
		}
	}
	status.Printf("Exiting.")
	os.Stderr.WriteString("\n")
	os.Exit(exitcode)
}
예제 #2
0
파일: shell.go 프로젝트: tillberg/gut
func printUsageInfoAndExit() {
	status := log.New(os.Stderr, "", 0)
	status.Println("")
	status.Println("Usage: gut sync [option]... path [{ [user@]host:path | path }]...")
	status.Println("")
	status.Println("Options:")
	status.Println("      --no-color: Disable ANSI colors")
	status.Println("       --verbose: Show all commands executed")
	status.Println("    --build-deps: Build gut-commands from git source instead of downloading tarball")
	status.Println("--build-parallel: Build gut-commands in parallel via make -j {num_cores}")
	status.Println("")
	status.Println("Examples:")
	status.Println("   Sync folder with one remote: gut sync ~/stuff/ [email protected]:~/stuff/")
	status.Println("  Sync folder with two remotes: gut sync stuff/ remotehost1.com:~/work/ [email protected]:/tmp/sync")
	status.Println("          Sync folders locally: gut sync ~/mywork /mnt/backup/mywork/")
	status.Println("Just track changes, no syncing: gut sync ~/mywork")
	status.Println("")
	os.Exit(0)
}
예제 #3
0
파일: shell.go 프로젝트: tillberg/gut
func main() {
	log.EnableColorTemplate()
	log.AddAnsiColorCode("error", 31)
	log.AddAnsiColorCode("commit", 32)
	log.AddAnsiColorCode("path", 36)
	var args []string = os.Args[1:]
	if len(args) == 0 {
		fmt.Println("You must specify a gut-command, e.g. `gut sync ...`")
		os.Exit(1)
	}
	var cmd = args[0]
	if IsGitCommand(cmd) {
		if IsDangerousGitCommand(cmd) {
			if len(args) < 2 || args[1] != "--danger" {
				status := log.New(os.Stderr, "", 0)
				status.Printf("@(dim:In order to prevent damage caused by accidentally using `)gut %s ...@(dim:`)\n", cmd)
				status.Printf("@(dim:in cases where `)git %s ...@(dim:` was intended, you must append `)--danger@(dim:`)\n", cmd)
				status.Printf("@(dim:immediately after the command, i.e. `)gut %s --danger ...@(dim:`.)\n", cmd)
				status.Printf("@(dim:Alternatively, you could invoke) gut @(dim:directly at) @(path:%s)@(dim:.)\n", GutExePath)
				status.Printf("@(dim:The commands that require this flag are:) %s\n", strings.Join(DangerousGitCommands, " "))
				os.Exit(1)
			}
			// Split the "--danger" flag out before handing off the args list to the gut-command:
			if len(args) > 2 {
				args = append(args[:1], args[2:]...)
			} else {
				args = args[:1]
			}
		}
		homeDir, err := homedir.Dir()
		if err != nil {
			log.Bail(err)
		}
		var gutExe = path.Join(homeDir, GutExePath[2:])
		syscall.Exec(gutExe, append([]string{gutExe}, args...), os.Environ())
		fmt.Printf("Failed to exec %s", gutExe)
		os.Exit(1)
	}
	autorestart.CleanUpChildZombiesQuietly()
	go autorestart.RestartOnChange()
	status := log.New(os.Stderr, "", 0)
	args = args[1:]
	parser := flags.NewParser(&OptsCommon, flags.IgnoreUnknown)
	var argsRemaining, err = parser.ParseArgs(args)
	if err != nil {
		printUsageInfoAndExit()
	}
	if OptsCommon.NoColor {
		log.DisableColor()
	}
	if OptsCommon.Version {
		status.Printf("gut-sync %s\n", GutVersion)
		os.Exit(0)
	}
	bismuth.SetVerbose(OptsCommon.Verbose)

	go func() {
		sigintChan := make(chan os.Signal, 1)
		signal.Notify(sigintChan, os.Interrupt)
		<-sigintChan
		Shutdown("Received SIGINT.", 1)
	}()
	go func() {
		sighupChan := autorestart.NotifyOnSighup()
		<-sighupChan
		Shutdown("Received SIGHUP.", 0)
	}()

	if cmd == "build" {
		var local = NewSyncContext()
		err := local.Connect()
		if err != nil {
			status.Bail(err)
		}
		err = local.CheckLocalDeps()
		if err != nil {
			status.Bail(err)
		}
		didSomething, err := EnsureBuild(local, local)
		if err != nil {
			status.Bail(err)
		}
		if !didSomething {
			status.Printf("@(dim:gut) " + GitVersion + " @(dim:has already been built.)\n")
		}
	} else if cmd == "sync" {
		var remoteArgs, err = flags.ParseArgs(&OptsSync, argsRemaining)
		if err != nil {
			printUsageInfoAndExit()
		}

		ready := make(chan error)

		local := NewSyncContext()
		err = local.ParseSyncPath(OptsSync.Positional.LocalPath)
		if err != nil {
			status.Bail(err)
		}
		if len(remoteArgs) > 0 && os.Getenv("SSH_AUTH_SOCK") == "" {
			log.Printf("@(error:SSH_AUTH_SOCK is not set in environment. Start up an ssh agent first before running gut-sync.)\n")
			Shutdown("", 1)
		}
		go func() {
			err = local.Connect()
			if err != nil {
				status.Bail(err)
			}
			err = local.CheckLocalDeps()
			if err != nil {
				status.Bail(err)
			}
			local.KillAllViaPidfiles()
			local.SaveDaemonPid("gut", os.Getpid())
			ready <- nil
		}()

		remotes := []*SyncContext{}
		for _, remotePath := range remoteArgs {
			remote := NewSyncContext()
			remotes = append(remotes, remote)
			err = remote.ParseSyncPath(remotePath)
			if err != nil {
				status.Bail(err)
			}
			go func(_remote *SyncContext) {
				err = _remote.Connect()
				if err != nil {
					status.Printf("@(error:Failed to connect to %s: %s)\n", remote.Hostname(), err)
					Shutdown("", 1)
				}
				_remote.KillAllViaPidfiles()
				err = _remote.CheckRemoteDeps()
				if err != nil {
					status.Bail(err)
				}
				ready <- nil
			}(remote)
		}

		for i := 0; i < len(remotes)+1; i++ {
			<-ready
		}

		err = Sync(local, remotes)
		if err != nil {
			status.Bail(err)
		}
	}
}