const std = @import("std"); /// Embedded rsync binary functionality pub const EmbeddedRsync = struct { allocator: std.mem.Allocator, const Self = @This(); /// Extract embedded rsync binary to temporary location pub fn extractRsyncBinary(self: Self) ![]const u8 { const rsync_path = "/tmp/ml_rsync"; // Check if rsync binary already exists if (std.fs.cwd().openFile(rsync_path, .{})) |file| { file.close(); // Check if it's executable const stat = try std.fs.cwd().statFile(rsync_path); if (stat.mode & 0o111 != 0) { return try self.allocator.dupe(u8, rsync_path); } } else |_| { // Need to extract the binary try self.extractAndSetExecutable(rsync_path); } return try self.allocator.dupe(u8, rsync_path); } /// Extract rsync binary from embedded data and set executable permissions fn extractAndSetExecutable(self: Self, path: []const u8) !void { // Import embedded binary data const embedded_binary = @import("rsync_embedded_binary.zig"); // Get the embedded rsync binary data const binary_data = embedded_binary.getRsyncBinary(); // Debug output to show we're using embedded binary const debug_msg = try std.fmt.allocPrint(self.allocator, "Extracting embedded rsync binary to {s} (size: {d} bytes)", .{ path, binary_data.len }); defer self.allocator.free(debug_msg); std.log.debug("{s}", .{debug_msg}); // Write embedded binary to file system try std.fs.cwd().writeFile(.{ .sub_path = path, .data = binary_data }); // Set executable permissions using OS API const mode = 0o755; // rwxr-xr-x std.posix.fchmodat(std.fs.cwd().fd, path, mode, 0) catch |err| { std.log.warn("Failed to set executable permissions on {s}: {}", .{ path, err }); // Continue anyway - the script might still work }; } /// Sync using embedded rsync pub fn sync(self: Self, local_path: []const u8, remote_path: []const u8, ssh_port: u16) !void { const rsync_path = try self.extractRsyncBinary(); defer self.allocator.free(rsync_path); const port_str = try std.fmt.allocPrint(self.allocator, "{d}", .{ssh_port}); defer self.allocator.free(port_str); const ssh_opt = try std.fmt.allocPrint(self.allocator, "ssh -p {s}", .{port_str}); defer self.allocator.free(ssh_opt); // Build rsync command using embedded binary var child = std.process.Child.init( &[_][]const u8{ rsync_path, "-avz", "--delete", "-e", ssh_opt, local_path, remote_path, }, self.allocator, ); child.stdin_behavior = .Ignore; child.stdout_behavior = .Inherit; child.stderr_behavior = .Inherit; const term = try child.spawnAndWait(); switch (term) { .Exited => |code| { if (code != 0) { std.debug.print("rsync failed with exit code {d}\n", .{code}); return error.RsyncFailed; } }, .Signal => { return error.RsyncKilled; }, else => { return error.RsyncUnknownError; }, } } }; /// Public sync function that uses embedded rsync pub fn sync(allocator: std.mem.Allocator, local_path: []const u8, remote_path: []const u8, ssh_port: u16) !void { var embedded_rsync = EmbeddedRsync{ .allocator = allocator }; try embedded_rsync.sync(local_path, remote_path, ssh_port); std.debug.print("Synced {s} to {s} using embedded rsync\n", .{ local_path, remote_path }); }