// defaultConfigDir returns the default configuration directory, as figured // out by various the environment variables present on each platform, or dies // trying. func defaultConfigDir() string { switch runtime.GOOS { case "windows": if p := os.Getenv("LocalAppData"); p != "" { return filepath.Join(p, "Syncthing") } return filepath.Join(os.Getenv("AppData"), "Syncthing") case "darwin": dir, err := osutil.ExpandTilde("~/Library/Application Support/Syncthing") if err != nil { l.Fatalln(err) } return dir default: if xdgCfg := os.Getenv("XDG_CONFIG_HOME"); xdgCfg != "" { return filepath.Join(xdgCfg, "syncthing") } dir, err := osutil.ExpandTilde("~/.config/syncthing") if err != nil { l.Fatalln(err) } return dir } }
func (s *apiService) getSystemBrowse(w http.ResponseWriter, r *http.Request) { qs := r.URL.Query() current := qs.Get("current") if current == "" { if roots, err := osutil.GetFilesystemRoots(); err == nil { sendJSON(w, roots) } else { http.Error(w, err.Error(), 500) } return } search, _ := osutil.ExpandTilde(current) pathSeparator := string(os.PathSeparator) if strings.HasSuffix(current, pathSeparator) && !strings.HasSuffix(search, pathSeparator) { search = search + pathSeparator } subdirectories, _ := osutil.Glob(search + "*") ret := make([]string, 0, len(subdirectories)) for _, subdirectory := range subdirectories { info, err := os.Stat(subdirectory) if err == nil && info.IsDir() { ret = append(ret, subdirectory+pathSeparator) } } sendJSON(w, ret) }
func (f FolderConfiguration) Path() string { // This is intentionally not a pointer method, because things like // cfg.Folders["default"].Path() should be valid. // Attempt tilde expansion; leave unchanged in case of error if path, err := osutil.ExpandTilde(f.RawPath); err == nil { f.RawPath = path } // Attempt absolutification; leave unchanged in case of error if !filepath.IsAbs(f.RawPath) { // Abs() looks like a fairly expensive syscall on Windows, while // IsAbs() is a whole bunch of string mangling. I think IsAbs() may be // somewhat faster in the general case, hence the outer if... if path, err := filepath.Abs(f.RawPath); err == nil { f.RawPath = path } } // Attempt to enable long filename support on Windows. We may still not // have an absolute path here if the previous steps failed. if runtime.GOOS == "windows" && filepath.IsAbs(f.RawPath) && !strings.HasPrefix(f.RawPath, `\\`) { return `\\?\` + f.RawPath } return f.RawPath }
func (f *FolderConfiguration) cleanedPath() string { cleaned := f.RawPath // Attempt tilde expansion; leave unchanged in case of error if path, err := osutil.ExpandTilde(cleaned); err == nil { cleaned = path } // Attempt absolutification; leave unchanged in case of error if !filepath.IsAbs(cleaned) { // Abs() looks like a fairly expensive syscall on Windows, while // IsAbs() is a whole bunch of string mangling. I think IsAbs() may be // somewhat faster in the general case, hence the outer if... if path, err := filepath.Abs(cleaned); err == nil { cleaned = path } } // Attempt to enable long filename support on Windows. We may still not // have an absolute path here if the previous steps failed. if runtime.GOOS == "windows" && filepath.IsAbs(cleaned) && !strings.HasPrefix(f.RawPath, `\\`) { return `\\?\` + cleaned } return cleaned }
func (s *apiSvc) getSystemStatus(w http.ResponseWriter, r *http.Request) { var m runtime.MemStats runtime.ReadMemStats(&m) tilde, _ := osutil.ExpandTilde("~") res := make(map[string]interface{}) res["myID"] = myID.String() res["goroutines"] = runtime.NumGoroutine() res["alloc"] = m.Alloc res["sys"] = m.Sys - m.HeapReleased res["tilde"] = tilde if cfg.Options().GlobalAnnEnabled && s.discoverer != nil { res["extAnnounceOK"] = s.discoverer.ExtAnnounceOK() } if relaySvc != nil { res["relayClientStatus"] = relaySvc.ClientStatus() } cpuUsageLock.RLock() var cpusum float64 for _, p := range cpuUsagePercent { cpusum += p } cpuUsageLock.RUnlock() res["cpuPercent"] = cpusum / float64(len(cpuUsagePercent)) / float64(runtime.NumCPU()) res["pathSeparator"] = string(filepath.Separator) res["uptime"] = int(time.Since(startTime).Seconds()) res["startTime"] = startTime w.Header().Set("Content-Type", "application/json; charset=utf-8") json.NewEncoder(w).Encode(res) }
// homeDir returns the user's home directory, or dies trying. func homeDir() string { home, err := osutil.ExpandTilde("~") if err != nil { l.Fatalln(err) } return home }
// expandLocations replaces the variables in the location map with actual // directory locations. func expandLocations() error { for key, dir := range locations { for varName, value := range baseDirs { dir = strings.Replace(dir, "${"+varName+"}", value, -1) } var err error dir, err = osutil.ExpandTilde(dir) if err != nil { return err } locations[key] = dir } return nil }
func (s *apiService) getSystemStatus(w http.ResponseWriter, r *http.Request) { var m runtime.MemStats runtime.ReadMemStats(&m) tilde, _ := osutil.ExpandTilde("~") res := make(map[string]interface{}) res["myID"] = myID.String() res["goroutines"] = runtime.NumGoroutine() res["alloc"] = m.Alloc res["sys"] = m.Sys - m.HeapReleased res["tilde"] = tilde if s.cfg.Options().LocalAnnEnabled || s.cfg.Options().GlobalAnnEnabled { res["discoveryEnabled"] = true discoErrors := make(map[string]string) discoMethods := 0 for disco, err := range s.discoverer.ChildErrors() { discoMethods++ if err != nil { discoErrors[disco] = err.Error() } } res["discoveryMethods"] = discoMethods res["discoveryErrors"] = discoErrors } if s.relayService != nil { res["relaysEnabled"] = true relayClientStatus := make(map[string]bool) relayClientLatency := make(map[string]int) for _, relay := range s.relayService.Relays() { latency, ok := s.relayService.RelayStatus(relay) relayClientStatus[relay] = ok relayClientLatency[relay] = int(latency / time.Millisecond) } res["relayClientStatus"] = relayClientStatus res["relayClientLatency"] = relayClientLatency } cpuUsageLock.RLock() var cpusum float64 for _, p := range cpuUsagePercent { cpusum += p } cpuUsageLock.RUnlock() res["cpuPercent"] = cpusum / float64(len(cpuUsagePercent)) / float64(runtime.NumCPU()) res["pathSeparator"] = string(filepath.Separator) res["uptime"] = int(time.Since(startTime).Seconds()) res["startTime"] = startTime res["themes"] = s.themes sendJSON(w, res) }
func generate(generateDir string) { dir, err := osutil.ExpandTilde(generateDir) if err != nil { l.Fatalln("generate:", err) } info, err := os.Stat(dir) if err == nil && !info.IsDir() { l.Fatalln(dir, "is not a directory") } if err != nil && os.IsNotExist(err) { err = osutil.MkdirAll(dir, 0700) if err != nil { l.Fatalln("generate:", err) } } certFile, keyFile := filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem") cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err == nil { l.Warnln("Key exists; will not overwrite.") l.Infoln("Device ID:", protocol.NewDeviceID(cert.Certificate[0])) } else { cert, err = tlsutil.NewCertificate(certFile, keyFile, tlsDefaultCommonName, bepRSABits) if err != nil { l.Fatalln("Create certificate:", err) } myID = protocol.NewDeviceID(cert.Certificate[0]) if err != nil { l.Fatalln("Load certificate:", err) } if err == nil { l.Infoln("Device ID:", protocol.NewDeviceID(cert.Certificate[0])) } } cfgFile := filepath.Join(dir, "config.xml") if _, err := os.Stat(cfgFile); err == nil { l.Warnln("Config exists; will not overwrite.") return } var myName, _ = os.Hostname() var newCfg = defaultConfig(myName) var cfg = config.Wrap(cfgFile, newCfg) err = cfg.Save() if err != nil { l.Warnln("Failed to save config", err) } }
// defaultConfigDir returns the default configuration directory, as figured // out by various the environment variables present on each platform, or dies // trying. func defaultConfigDir() string { switch runtime.GOOS { case "darwin": dir, err := osutil.ExpandTilde("~/Library/Application Support/SyncthingFUSE") if err != nil { l.Fatalln(err) } return dir case "linux": if xdgCfg := os.Getenv("XDG_CONFIG_HOME"); xdgCfg != "" { return filepath.Join(xdgCfg, "syncthing") } dir, err := osutil.ExpandTilde("~/.config/syncthingfuse") if err != nil { l.Fatalln(err) } return dir default: l.Fatalln("Only OS X and Linux supported right now!") } return "nil" }
func (s *apiService) getSystemBrowse(w http.ResponseWriter, r *http.Request) { qs := r.URL.Query() current := qs.Get("current") search, _ := osutil.ExpandTilde(current) pathSeparator := string(os.PathSeparator) if strings.HasSuffix(current, pathSeparator) && !strings.HasSuffix(search, pathSeparator) { search = search + pathSeparator } subdirectories, _ := osutil.Glob(search + "*") ret := make([]string, 0, 10) for _, subdirectory := range subdirectories { info, err := os.Stat(subdirectory) if err == nil && info.IsDir() { ret = append(ret, subdirectory+pathSeparator) if len(ret) > 9 { break } } } sendJSON(w, ret) }
func (s *apiSvc) getSystemBrowse(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") qs := r.URL.Query() current := qs.Get("current") search, _ := osutil.ExpandTilde(current) pathSeparator := string(os.PathSeparator) if strings.HasSuffix(current, pathSeparator) && !strings.HasSuffix(search, pathSeparator) { search = search + pathSeparator } subdirectories, _ := osutil.Glob(search + "*") ret := make([]string, 0, 10) for _, subdirectory := range subdirectories { info, err := os.Stat(subdirectory) if err == nil && info.IsDir() { ret = append(ret, subdirectory+pathSeparator) if len(ret) > 9 { break } } } json.NewEncoder(w).Encode(ret) }
func (f *FolderConfiguration) cleanedPath() string { if f.RawPath == "" { return "" } cleaned := f.RawPath // Attempt tilde expansion; leave unchanged in case of error if path, err := osutil.ExpandTilde(cleaned); err == nil { cleaned = path } // Attempt absolutification; leave unchanged in case of error if !filepath.IsAbs(cleaned) { // Abs() looks like a fairly expensive syscall on Windows, while // IsAbs() is a whole bunch of string mangling. I think IsAbs() may be // somewhat faster in the general case, hence the outer if... if path, err := filepath.Abs(cleaned); err == nil { cleaned = path } } // Attempt to enable long filename support on Windows. We may still not // have an absolute path here if the previous steps failed. if runtime.GOOS == "windows" && filepath.IsAbs(cleaned) && !strings.HasPrefix(f.RawPath, `\\`) { return `\\?\` + cleaned } // If we're not on Windows, we want the path to end with a slash to // penetrate symlinks. On Windows, paths must not end with a slash. if runtime.GOOS != "windows" && cleaned[len(cleaned)-1] != filepath.Separator { cleaned = cleaned + string(filepath.Separator) } return cleaned }
func main() { if runtime.GOOS == "windows" { // On Windows, we use a log file by default. Setting the -logfile flag // to "-" disables this behavior. flag.StringVar(&logFile, "logfile", "", "Log file name (use \"-\" for stdout)") // We also add an option to hide the console window flag.BoolVar(&noConsole, "no-console", false, "Hide console window") } else { flag.StringVar(&logFile, "logfile", "-", "Log file name (use \"-\" for stdout)") } flag.StringVar(&generateDir, "generate", "", "Generate key and config in specified dir, then exit") flag.StringVar(&guiAddress, "gui-address", guiAddress, "Override GUI address") flag.StringVar(&guiAPIKey, "gui-apikey", guiAPIKey, "Override GUI API key") flag.StringVar(&confDir, "home", "", "Set configuration directory") flag.IntVar(&logFlags, "logflags", logFlags, "Select information in log line prefix") flag.BoolVar(&noBrowser, "no-browser", false, "Do not start browser") flag.BoolVar(&noRestart, "no-restart", noRestart, "Do not restart; just exit") flag.BoolVar(&reset, "reset", false, "Reset the database") flag.BoolVar(&doUpgrade, "upgrade", false, "Perform upgrade") flag.BoolVar(&doUpgradeCheck, "upgrade-check", false, "Check for available upgrade") flag.BoolVar(&showVersion, "version", false, "Show version") flag.StringVar(&upgradeTo, "upgrade-to", upgradeTo, "Force upgrade directly from specified URL") flag.BoolVar(&auditEnabled, "audit", false, "Write events to audit file") flag.BoolVar(&verbose, "verbose", false, "Print verbose log output") flag.BoolVar(&paused, "paused", false, "Start with all devices paused") longUsage := fmt.Sprintf(extraUsage, baseDirs["config"], debugFacilities()) flag.Usage = usageFor(flag.CommandLine, usage, longUsage) flag.Parse() if noConsole { osutil.HideConsole() } if confDir != "" { // Not set as default above because the string can be really long. baseDirs["config"] = confDir } if err := expandLocations(); err != nil { l.Fatalln(err) } if guiAssets == "" { guiAssets = locations[locGUIAssets] } if logFile == "" { // Use the default log file location logFile = locations[locLogFile] } if showVersion { fmt.Println(LongVersion) return } l.SetFlags(logFlags) if generateDir != "" { dir, err := osutil.ExpandTilde(generateDir) if err != nil { l.Fatalln("generate:", err) } info, err := os.Stat(dir) if err == nil && !info.IsDir() { l.Fatalln(dir, "is not a directory") } if err != nil && os.IsNotExist(err) { err = osutil.MkdirAll(dir, 0700) if err != nil { l.Fatalln("generate:", err) } } certFile, keyFile := filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem") cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err == nil { l.Warnln("Key exists; will not overwrite.") l.Infoln("Device ID:", protocol.NewDeviceID(cert.Certificate[0])) } else { cert, err = tlsutil.NewCertificate(certFile, keyFile, tlsDefaultCommonName, tlsRSABits) if err != nil { l.Fatalln("Create certificate:", err) } myID = protocol.NewDeviceID(cert.Certificate[0]) if err != nil { l.Fatalln("Load certificate:", err) } if err == nil { l.Infoln("Device ID:", protocol.NewDeviceID(cert.Certificate[0])) } } cfgFile := filepath.Join(dir, "config.xml") if _, err := os.Stat(cfgFile); err == nil { l.Warnln("Config exists; will not overwrite.") return } var myName, _ = os.Hostname() var newCfg = defaultConfig(myName) var cfg = config.Wrap(cfgFile, newCfg) err = cfg.Save() if err != nil { l.Warnln("Failed to save config", err) } return } if info, err := os.Stat(baseDirs["config"]); err == nil && !info.IsDir() { l.Fatalln("Config directory", baseDirs["config"], "is not a directory") } // Ensure that our home directory exists. ensureDir(baseDirs["config"], 0700) if upgradeTo != "" { err := upgrade.ToURL(upgradeTo) if err != nil { l.Fatalln("Upgrade:", err) // exits 1 } l.Okln("Upgraded from", upgradeTo) return } if doUpgrade || doUpgradeCheck { releasesURL := "https://api.github.com/repos/syncthing/syncthing/releases?per_page=30" if cfg, _, err := loadConfig(locations[locConfigFile]); err == nil { releasesURL = cfg.Options().ReleasesURL } rel, err := upgrade.LatestRelease(releasesURL, Version) if err != nil { l.Fatalln("Upgrade:", err) // exits 1 } if upgrade.CompareVersions(rel.Tag, Version) <= 0 { l.Infof("No upgrade available (current %q >= latest %q).", Version, rel.Tag) os.Exit(exitNoUpgradeAvailable) } l.Infof("Upgrade available (current %q < latest %q)", Version, rel.Tag) if doUpgrade { // Use leveldb database locks to protect against concurrent upgrades _, err = leveldb.OpenFile(locations[locDatabase], &opt.Options{OpenFilesCacheCapacity: 100}) if err != nil { l.Infoln("Attempting upgrade through running Syncthing...") err = upgradeViaRest() if err != nil { l.Fatalln("Upgrade:", err) } l.Okln("Syncthing upgrading") return } err = upgrade.To(rel) if err != nil { l.Fatalln("Upgrade:", err) // exits 1 } l.Okf("Upgraded to %q", rel.Tag) } return } if reset { resetDB() return } if noRestart { syncthingMain() } else { monitorMain() } }