// Returns true if the file was changed in any way. public Task <bool> Download(string source, Manifest.SyncItem syncItem, string modPath, Catflap.Repository.DownloadProgressChanged dpc, Catflap.Repository.DownloadEnd de, Catflap.Repository.DownloadMessage dm, CancellationTokenSource cts, string overrideDestination = null) { var ct = cts.Token; stdErr = ""; return(Task.Run <bool>(delegate() { currentRunWasChanged = false; var p = RunRSync(source, syncItem, modPath, dpc, dm, overrideDestination); (Application.Current as App).TrackProcess(p); // Wait for the pid to appear. while (0 == p.Id && !p.HasExited) { ; } while (!p.HasExited) { if (ct.IsCancellationRequested) { cancelled = true; dm.Invoke("<cancelando (paciência!)>", true); /* Try Ctrl+C first so we can catch --replace/partial transfers */ SIGTERM(p.Id); /* Lets wait for a generous amount of time to wait for rsync to gracefully * terminate. This can happen on slow disks. */ p.WaitForExit(20000); App.KillProcessAndChildren(p.Id); p.WaitForExit(); ct.ThrowIfCancellationRequested(); } else { Thread.Sleep(100); } } p.WaitForExit(); de.Invoke(p.ExitCode != 0, stdErr, bytesOnNetwork); return currentRunWasChanged; }, ct)); }
private Task <bool> RunSyncItem(Manifest.SyncItem f, bool verify, bool simulate, DownloadProgressChanged dpc, DownloadEnd de, DownloadMessage dm, CancellationTokenSource cts, string overrideDestination = null) { switch (f.type) { case "rsync": RSyncDownloader dd = new RSyncDownloader(this); dd.appPath = AppPath; dd.tmpPath = TmpPath; dd.VerifyChecksums = verify; dd.Simulate = simulate; return(dd.Download(LatestManifest.rsyncUrl + "/" + f.name, f, RootPath, dpc, de, dm, cts, overrideDestination)); case "delete": return(Task <bool> .Run(() => { if (f.name.EndsWith("/") && Directory.Exists(RootPath + "/" + f.name)) { dm.Invoke("Deleting directory " + f.name); Directory.Delete(RootPath + "/" + f.name, true); } else if (File.Exists(RootPath + "/" + f.name)) { dm.Invoke("Deleting file " + f.name); File.Delete(RootPath + "/" + f.name); } return true; })); default: return(null); } }
private Process RunRSync(String rsyncUrl, Manifest.SyncItem syncItem, string modPath, Catflap.Repository.DownloadProgressChanged dpc, Catflap.Repository.DownloadMessage dm, string overrideDestination = null) { var targetFileName = syncItem.name; string targetDir = modPath + "\\" + Path.GetDirectoryName(targetFileName); Directory.CreateDirectory(targetDir); string rsyncTargetSpec = overrideDestination != null ? overrideDestination :"."; bool isDir = targetFileName.EndsWith("/"); if (this.repository.Username != null) { rsyncUrl = rsyncUrl.Replace("%user%", this.repository.Username); } string va = rsyncFlags + " " + "\"" + rsyncUrl + "\"" + " " + "\"" + rsyncTargetSpec.ShellEscape() + "\""; if (VerifyChecksums) { va += " " + rsyncFlagsVerify; } if (!VerifyChecksums) { va += " " + rsyncFlagsNoVerify; } if (Simulate) { va += " --dry-run"; } if (isDir) { va += " " + rsyncFlagsDirectory; } if (syncItem.ignoreExisting.GetValueOrDefault()) { va += " --ignore-existing"; } // Only ever allow purge on directories, obviously. if (isDir && syncItem.purge.GetValueOrDefault()) { va += " --delete-delay"; } if (syncItem.ignoreCase.GetValueOrDefault()) { va += " --ignore-case"; } if (syncItem.fuzzy.GetValueOrDefault()) { va += " --fuzzy"; } va += " \"--temp-dir=" + tmpPath.ShellEscape() + "\""; switch (syncItem.mode) { case "inplace": va += " --inplace"; break; default: // "replace" /* We cannot keep partials for ignore-existing .. */ if (!syncItem.ignoreExisting.GetValueOrDefault()) { va += " --partial-dir=catflap.partials"; } va += " --delay-updates"; break; } long thisFileTotalSize = 0; string thisFilename = targetFileName; dm.Invoke("(rsync) " + va); Process pProcess = new System.Diagnostics.Process(); pProcess.StartInfo.FileName = appPath + "\\rsync.exe"; pProcess.StartInfo.Arguments = va; pProcess.StartInfo.CreateNoWindow = true; pProcess.StartInfo.UseShellExecute = false; pProcess.StartInfo.RedirectStandardOutput = true; pProcess.StartInfo.RedirectStandardError = true; pProcess.StartInfo.RedirectStandardInput = true; pProcess.StartInfo.WorkingDirectory = targetDir; if (pProcess.StartInfo.EnvironmentVariables.ContainsKey("PATH")) { pProcess.StartInfo.EnvironmentVariables.Remove("PATH"); } if (pProcess.StartInfo.EnvironmentVariables.ContainsKey("CYGWIN")) { pProcess.StartInfo.EnvironmentVariables.Remove("CYGWIN"); } pProcess.StartInfo.EnvironmentVariables.Add("CYGWIN", "nodosfilewarning"); if (this.repository.Password != null) { pProcess.StartInfo.EnvironmentVariables.Add("RSYNC_PASSWORD", this.repository.Password); } Console.WriteLine("VA = " + va); pProcess.OutputDataReceived += (s, ee) => { if (ee.Data != null) { /* Send everything to the log as-is. */ dm.Invoke("(stdout) " + ee.Data); switch (ee.Data) { case "receiving file list ... ": case "receiving incremental file list": dm.Invoke("<receiving list>", true); break; default: Match mr; // Progress indicator if ((mr = rxRsyncProgress.Match(ee.Data)).Success) { long bytesDone = long.Parse(mr.Groups[1].Value, CultureInfo.InvariantCulture); int percentage = int.Parse(mr.Groups[2].Value, CultureInfo.InvariantCulture); double rate = double.Parse(mr.Groups[3].Value, CultureInfo.InvariantCulture); string rateDesc = mr.Groups[4].Value; string eta = mr.Groups[5].Value; if (rateDesc == "kB/s") { rate *= 1024; } if (rateDesc == "MB/s") { rate *= 1024 * 1024; } dpc.Invoke(cancelled ? "<cancelando>" : thisFilename, percentage, bytesDone, thisFileTotalSize, (int)rate); } // new file else if ((mr = rxRsyncNewFile.Match(ee.Data)).Success) { string flags = mr.Groups[1].Value; thisFileTotalSize = long.Parse(mr.Groups[2].Value, CultureInfo.InvariantCulture); string fname = mr.Groups[3].Value; //if (isDir) if (fname == "." || fname == "./") { fname = targetFileName; } else if (isDir) { fname = targetFileName + fname; } else { fname = targetFileName; } // YXcstpogz // X: update type (< > c .) // Y: filetype (f d L D S) // c: checksum, s: size, t: time, var action = ""; switch (flags[0]) { case '*': action = "deleting"; break; case '<': action = "sending"; break; case '>': action = ""; break; case 'c': action = "creating"; break; } /*var typeStr = ""; * switch (flags[1]) * { * case 'f': typeStr = "file"; break; * case 'd': typeStr = "directory"; break; * }*/ thisFilename = action + " " + fname; var flagStr = flags.Substring(2).Replace(".", "").Replace("+", "").Trim(); if (flagStr != "") { thisFilename += " [" + flagStr + "]"; } dm.Invoke(thisFilename, true); } // Literal data: xx else if ((mr = rxRsyncLiteralData.Match(ee.Data)).Success) { if (mr.Groups[1].Value != "0") { currentRunWasChanged = true; } } // Total bytes received else if ((mr = rxRsyncTotalBytesReceived.Match(ee.Data)).Success) { if (mr.Groups[1].Value != "0") { bytesOnNetwork += long.Parse(mr.Groups[1].Value, CultureInfo.InvariantCulture); } } // Total bytes sent else if ((mr = rxRsyncTotalBytesSent.Match(ee.Data)).Success) { if (mr.Groups[1].Value != "0") { bytesOnNetwork += long.Parse(mr.Groups[1].Value, CultureInfo.InvariantCulture); } } break; } } }; pProcess.ErrorDataReceived += (s, ee) => { if (ee.Data != null) { stdErr += ee.Data + "\n"; Console.WriteLine("STDERR: " + ee.Data); dm.Invoke("ERRO: " + ee.Data); } }; dm.Invoke("Verificando " + syncItem.name + " (checksumming/esperando o server)", true); pProcess.Start(); pProcess.BeginOutputReadLine(); pProcess.BeginErrorReadLine(); // This eats "Password:"******"\n"); return(pProcess); }