Programmer » Unison-ssh

The Unison-ssh utility is for users of the Unison file synchronizer under Windows. The thing is, Unison needs SSH and that isn't so straightforward under Windows. Many people instead use the almost-equivalent Plink and then have some kind of wrapper around it. Unison-ssh is a particularly convenient wrapper.

Using it

About it

Unison-ssh is a wrapper around plink with some nice features. (1) It's a drop-in replacement from the perspective of Unison, so you can just use the standard unison commands without any messing about. (2) It lets you type in your SSH password interactively if you want, rather than forcing you to run Pagent. Also, it's invisible when you type it in interactively, unlike most other unison/plink wrappers.

Notes. If you already have an ssh.exe on your system, then it probably works fine already and you don't need unison-ssh at all. Unison-ssh is merely a wrapper around Plink. If you already have plink installed somewhere on your path, then that will be used. If you don't, then unison-ssh contains a copy and will automatically install that copy in your windows directory.

Source code

This source code compiles with VS2005, BCB5 and g++. Also, the rest of this page shows the main source code.

#include <windows.h>
#include <stdio.h>
#include <string.h>
#ifdef DEBUG
void TRACE(const char *msg) {fprintf(stderr,"*** %s\n",msg);}
void TRACE(const char *msg, char *buf, int len) {for (int i=0; i<len; i++) {if (buf[i]<32) buf[i]='.';} buf[len]=0; fprintf(stderr,"*** %s [[[%s]]]\n",msg,buf);}
#else
inline void TRACE(const char *) {}
inline void TRACE(const char *, char *, int) {}
#endif

// UNISON-SSH, by Lucian Wischik 2005.
// This program is to help out the windows version of Unison, the file synchronisation tool.
// Unison assumes a command "ssh" has been installed which works in a particular way.
// The closest thing under Windows is "plink", but it's not quite the same.
// Hence this program UNISON-SSH, a wrapper around plink, which makes it act enough
// like "ssh" for Unison to be happy without needing any further workarounds.
//  (1) Unison expects to find a program called "ssh", but plink has a different name,
//      so UNISON-SSH is called "ssh"
//  (2) Unison uses the "-e none" argument, which plink lacks,
//      so UNISON-SSH gobbles up that argument before invoking plink
//  (3) Unison expects ssh not to emit any output of its own, but plink sometimes
//      prompts the user for the password, so UNISON-SSH manages the password
//      prompt on its own without feeding it through.
//  (4) Unison needs plink installed, but it's unpleasant to ask the user to install
//      too many different things, so UNISON-SSH will itself install plink into
//      the windows directory if it couldn't already be found on the path.
// Here's more detail on how UNISON-SSH does it:
// 
// (2) To gobble up the "-e none" argument, we parse the command line manually.
// We parse it as a single long text string, instead of using argc/argv. Why?
// because argc/argv have already lost information from the command line string
// (e.g. how many spaces there were, whether items had quotation marks around them)
// which it seemed unreasonable to lose. So we don't use argc/argv.
//
// (3) To manage the password, we run plink.exe with redirected input/output handles:
//    stdin -("inpump")->  hinwrite--pipe1-->hinread [plink] houtwrite--pipe2-->houtread -("outpump")-> stdout
// The two pumps ("inpump") and ("output") are run as their own threads. [plink] is run
// as a process. The inpump mainly forwards stuff straight through. One effect is so
// that we know when stdin ends. As for the output, if it encounters the string
// "Password: " in the first 1000 bytes of output, it displays a message to CONOUT$
// (nb. not the STDOUT, which has been redirected by unison). If we'd run SSH with console
// input, then now we leave the inpump to manage the password and send it
// via hinwrite to [plink]. Note that plink requires the entire password to be submitted
// in one go, not character-by-character. But if we'd redirected stdin, then we
// ask for the password from CONIN$ rather than stdin, and this asking is instead
// done in the outpump.
// Note: if the user has "pagent" installed, then the Password: prompt is bypassed
// and none of functionality is invoked. But the rest of it still works fine.
//
// (4) To install plink if necessary, we carry it as a binary payload. If our attempt
// to CreateProcess("plink.exe ...") fails, then we'll dump our payload into the windows
// directory and try again.
//
// (5) During the execution of this program, if it has been run without redirected stdin,
// then right from the start we set console input to "character-by-character"
// and we "disable-echo". For the password prompt, this means that the password isn't
// displayed onscreen as the user types it. For the main execution, it means that
// keypresses get sent to the remote machine as soon as they happen, and the remote
// machine does the echoing job. But if stdin had been redirected, then the only
// time we set console mode is to "line-by-line/disable-echo" immediately prior to
// asking for the password.
//
// (6) Sometimes when you run ssh you're doing it with a command that terminates (eg. "ls").
// In these cases, the plink process will terminate naturally, and we go on to close
// our input handles and output handles, and so the inpump and outpump come to a natural end,
// and the program finishes.
// Sometimes you're doing it with a command that goes on forever (eg. "unison -server").
// Presumably the user will close the input stream when they want it to end, or press ctrl-c.
// If they close the input stream then the inpump will come to an end. If this should happen
// before plink has finished, then we TerminateProcess(plink) to kill it. I'm worried that
// this is too abrubt, but I don't think there are any other options.



HANDLE hstdin=INVALID_HANDLE_VALUE;
HANDLE hstdout=INVALID_HANDLE_VALUE;
HANDLE hinwrite=INVALID_HANDLE_VALUE;
HANDLE hinread=INVALID_HANDLE_VALUE;
HANDLE houtread=INVALID_HANDLE_VALUE;
HANDLE houtwrite=INVALID_HANDLE_VALUE;
HANDLE hinthread=0;
HANDLE houtthread=0;
bool stdin_is_console=false;
// All the above are initialized in main() before any threads are created.
// The following is set by outpump-thread upon encountering "Password:".
// It is read (and reset) by inpump-thread.
volatile bool inpump_passwording=false;


DWORD WINAPI inpump(void *)
{ // We'll use the same buffer both for normal inpumping,
  // and also for accumulating the password as the user types it in (if we're passwording).
  // For this second purposes, index "i" says how much of the buffer we've
  // filled with that password.
  DWORD red,writ; BOOL res; char buf[1026]; int off=0;
  for (;;)
  { TRACE("inpump ReadFile");
    res=ReadFile(hstdin,buf+off,1024-off,&red,0); if (!res || red==0) break;
    if (!inpump_passwording)
    { WriteFile(hinwrite,buf+off,red,&writ,0); TRACE("inpump WriteFile",buf,red);
      continue;
    } // can write directly
    off+=red; red=off;
    if (buf[red-1]!='\r' && buf[red-1]!='\n' && red<1000) continue;
    // plink expects the password to be \r\n terminated. Note that
    // the raw ReadFile might or might not have terminated it properly.
    if (buf[red-1]=='\r' || buf[red-1]=='\n') red--;
    if (buf[red-1]=='\r' || buf[red-1]=='\n') red--;
    buf[red]='\r'; buf[red+1]='\n'; red+=2;
    WriteFile(hinwrite,buf,red,&writ,0); TRACE("inpump submitting password ",buf,red);
    inpump_passwording=false; off=0;
    // Now print an empty line so it looks good to the user
    HANDLE hconout=CreateFile("CONOUT$",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);
    if (hconout!=INVALID_HANDLE_VALUE) {WriteFile(hconout,"\r\n",2,&writ,0); CloseHandle(hconout);}
  }
  TRACE("inpump done.");
  return 0;
}

DWORD WINAPI outpump(void *)
{ DWORD Countup=0; // when countup is above 1000, we won't look for Password: prompt any more.
  bool IgnoreComingCRLF=false; // if ever we submit a password then plink will generate a spurious crlf
  char buf[1024]; DWORD red,writ; BOOL res;
  //
  for (;;)
  { TRACE(IgnoreComingCRLF ? "outpump ReadFile IgnoreCRLF" : "outpump ReadFile");
    res=ReadFile(houtread,buf,1024,&red,0); if (!res || red==0) break;
    bool wasignore=IgnoreComingCRLF; IgnoreComingCRLF=false;
    if (wasignore && red>=2 && buf[0]=='\r' && buf[1]=='\n') {red-=2; if (red==0) continue; memcpy(&buf[0],&buf[2],red);}
    if (Countup>1000 || red<9 || red>10 || strncmp(buf,"Password:",9)!=0 || (red==10 && buf[9]!=' '))
    { WriteFile(hstdout,buf,red,&writ,0); TRACE("outpump WriteFile",buf,red);
      Countup+=red; continue;
    }
    // otherwise, plink has just asked us for the password... So tell the user.
    HANDLE hconout=CreateFile("CONOUT$",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);
    if (hconout!=INVALID_HANDLE_VALUE) WriteFile(hconout,"Password: ",10,&writ,0);
    // and obtain the password
    if (stdin_is_console)
    { // If at a console, the input-pump can manage getting+sending the password. We'll no longer watch
      inpump_passwording=true; Countup=1000; IgnoreComingCRLF=true;
      if (hconout!=INVALID_HANDLE_VALUE) CloseHandle(hconout);
      continue;
    }
    // Otherwise we'll grab the console, set it to secrecy, obtain the password.
    // Note: we don't bother freezing the inpump. If it had already got data, that would
    // have messed up password entry, so there's no sense trying to fight it.
    const char *err=0; HANDLE hconin=INVALID_HANDLE_VALUE; BOOL res=TRUE; DWORD mode=0;
    if (err==0) {hconin=CreateFile("CONIN$",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,0,0); if (hconin==INVALID_HANDLE_VALUE) err="Unable to read from console";}
    if (err==0) {res=GetConsoleMode(hconin,&mode); if (!res) err="Unable to get console input mode";}
    if (err==0) {res=SetConsoleMode(hconin,ENABLE_LINE_INPUT); if (!res) err="Unable to set console input mode";}
    if (err==0)
    { res=ReadFile(hconin,buf,1021,&red,0); 
      if (res && red>2) 
      { if (buf[red-1]=='\r' || buf[red-1]=='\n') red--;
        if (buf[red-1]=='\r' || buf[red-1]=='\n') red--;
        buf[red]='\r'; buf[red+1]='\n'; buf[red+2]=0; red+=2;
        WriteFile(hinwrite,buf,red,&writ,0);
        TRACE("outpump submitted password",buf,red);
        if (hconout!=INVALID_HANDLE_VALUE) WriteFile(hconout,"\r\n",2,&writ,0);
      }
    }
    else
    { void *msg; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&msg, 0,NULL);
      fprintf(stderr,"%s- %s\n",err,msg);
      LocalFree(msg);
    }
    if (mode!=0) SetConsoleMode(hconin,mode);
    if (hconin!=INVALID_HANDLE_VALUE) CloseHandle(hconin);
    if (hconout!=INVALID_HANDLE_VALUE) CloseHandle(hconout);
    Countup=1000; IgnoreComingCRLF=true;
    if (!res || red==0) break; // in case the user aborted at the password prompt
  }
  TRACE("outpump done.");
  return 0;
}

int main()
{ const char *cmd1 = GetCommandLine();
  TRACE(cmd1);
  char *cmd2 = new char[strlen(cmd1)+16384];
  const char *src=cmd1; char *dst=cmd2;
  // get past initial command
  if (*src=='\"') {src++; while (*src!='\"' && *src!=0) src++; if (*src=='\"') src++;}
  else {while (*src!=' ' && *src!=0) src++;}
  while (*src==' ') src++;
  dst[0]='p'; dst[1]='l'; dst[2]='i'; dst[3]='n'; dst[4]='k'; dst[5]=' '; dst+=6;
  // copy the rest, but skipping out "-e arg"
  bool blownit=false;
  while (*src!=0)
  { if (src[0]=='-' && src[1]=='e' && src[2]==' ' && !blownit)
    { src+=3; while (*src==' ') src++;
      if (*src=='\"') {src++; while (*src!='\"' && *src!=0) src++; if (*src=='\"') src++;}
      else {while (*src!=' ' && *src!=0) src++;}
      while (*src==' ') src++;
      blownit=true;
      continue;
    }
    if (*src=='\"')
    { *dst=*src; dst++; src++;
      while (*src!='\"' && *src!=0) {*dst=*src; dst++; src++;}
      if (*src=='\"') {*dst=*src; dst++; src++;}
      continue;
    }
    while (*src!=' ' && *src!=0)
    { *dst=*src; dst++; src++;
      continue;
    }
    while (*src==' ')
    { *dst=*src; dst++; src++;
      continue;
    }
  }
  *dst=0;


  BOOL res=FALSE; const char* err=0; DWORD tid; DWORD ret=1; DWORD red;
  HANDLE hconin=INVALID_HANDLE_VALUE; DWORD mode=0;
  SECURITY_ATTRIBUTES sa; ZeroMemory(&sa,sizeof(sa)); sa.nLength=sizeof(sa);
  sa.bInheritHandle=TRUE; sa.lpSecurityDescriptor=0;

  if (err==0) {hstdin=GetStdHandle(STD_INPUT_HANDLE); if (hstdin==INVALID_HANDLE_VALUE) err="Failed to open stdin";}
  if (err==0) {hstdout=GetStdHandle(STD_OUTPUT_HANDLE); if (hstdout==INVALID_HANDLE_VALUE) err="Failed to open stdout";}
  //
  if (err==0) stdin_is_console=(PeekConsoleInput(hstdin,0,0,&red) != 0); // http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/dnaraskdr/html/askgui07152003.asp
  if (err==0 && stdin_is_console) {hconin=CreateFile("CONIN$",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,0,0); if (hconin==INVALID_HANDLE_VALUE) err="Unable to setup read from console";}
  if (err==0 && stdin_is_console) {res=GetConsoleMode(hconin,&mode); if (!res) err="Unable to setup get console input mode";}
  if (err==0 && stdin_is_console) {res=SetConsoleMode(hconin,mode&~(ENABLE_ECHO_INPUT|ENABLE_LINE_INPUT)); if (!res) err="Unable to setup console input mode";}
  if (hconin!=INVALID_HANDLE_VALUE) CloseHandle(hconin); hconin=INVALID_HANDLE_VALUE;
  //
  if (err==0) {res=CreatePipe(&hinread,&hinwrite,&sa,0); if (!res) err="Failed to create input pipe";}
  if (err==0) {res=CreatePipe(&houtread,&houtwrite,&sa,0); if (!res) err="Failed to create output pipe";}
  if (err==0) {houtthread=CreateThread(0,0,outpump,0,0,&tid); if (houtthread==0) err="Failed to create output thread";}
  if (err==0) {hinthread=CreateThread(0,0,inpump,0,0,&tid); if (hinthread==0) err="Failed to create input thread";}

  STARTUPINFO si; ZeroMemory(&si,sizeof(si)); si.cb=sizeof(si);
  si.dwFlags=STARTF_USESTDHANDLES; 
  si.hStdInput=hinread; si.hStdOutput=houtwrite; si.hStdError=GetStdHandle(STD_ERROR_HANDLE);
  PROCESS_INFORMATION pi; ZeroMemory(&pi,sizeof(pi));
  if (err==0) res=CreateProcess(0,cmd2,0,0,TRUE,0,0,0,&si,&pi);
  if (err==0 && !res && GetLastError()==ERROR_FILE_NOT_FOUND)
  { char fn[MAX_PATH+12]; GetWindowsDirectory(fn,MAX_PATH); strcat(fn,"\\plink.exe");
    DWORD attr = GetFileAttributes(fn); if (attr==0xFFFFFFFF)
    { HRSRC hrsrc=0; HGLOBAL hglob=0; void *buf=0; HANDLE hf=INVALID_HANDLE_VALUE; DWORD size=0;
      if (err==0) {hrsrc=FindResource(GetModuleHandle(0),MAKEINTRESOURCE(1),RT_RCDATA); if (hrsrc==0) err="Ssh wasn't built with plink.exe resource";}
      if (err==0) {hglob=LoadResource(GetModuleHandle(0),hrsrc); if (hglob==0) err="Failed to load plink.exe resource";}
      if (err==0) {buf=LockResource(hglob); if (buf==0) err="Failed to lock plink.exe resource";}
      if (err==0) {size=SizeofResource(GetModuleHandle(0),hrsrc); if (size==0) err="Failed to size plink.exe resource";}
      if (err==0) {hf=CreateFile(fn,GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0); if (hf==INVALID_HANDLE_VALUE) err="Unable to create plink.exe in windows directory";}
      if (err==0) {DWORD writ; res=WriteFile(hf,buf,size,&writ,0); if (!res || writ!=size) err="Failed to properly write plink.exe in windows directory";}
      if (hf!=INVALID_HANDLE_VALUE) CloseHandle(hf);
      if (err==0) res=CreateProcess(0,cmd2,0,0,TRUE,0,0,0,&si,&pi);
    }
  }
  if (err==0 && !res) err="Failed to launch plink.exe";
  if (err==0)
  { TRACE("waiting for plink...");
    // Some remote commands terminate themselves. Others run on for ever.
    // To handle these we respond to a closure of the input stream by killing plink:
    HANDLE h[2]; h[0]=hinthread; h[1]=pi.hProcess;
    DWORD r = WaitForMultipleObjects(2,h,FALSE,INFINITE);
    if (r==0)
    { TRACE("stdin closed; terminating plink!");
      TerminateProcess(pi.hProcess,1); WaitForSingleObject(pi.hProcess,INFINITE);
    }
    GetExitCodeProcess(pi.hProcess,&ret);
    TRACE("plink has exited.");
  }
  else
  { void *msg; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&msg, 0,NULL);
    fprintf(stderr,"%s- %s\n",err,msg);
    LocalFree(msg); ret=1;
  }

  // Note: the order of closing stuff here is critical to avoid deadlock.
  // ...
  // by closing hstdin, the inpump's ReadFile will abort
  TRACE("closing hstdin");
  if (hstdin!=INVALID_HANDLE_VALUE) CloseHandle(hstdin);
  TRACE("closing hinwrite");
  if (hinwrite!=INVALID_HANDLE_VALUE) CloseHandle(hinwrite);
  TRACE("closing hinread");
  if (hinread!=INVALID_HANDLE_VALUE) CloseHandle(hinread);
  // For the output, I forget the reason for this order.
  // We're clearly closing it from the right hand side to the left.
  TRACE("closing hstdout");
  if (hstdout!=INVALID_HANDLE_VALUE) CloseHandle(hstdout);
  TRACE("closing houtwrite");
  if (houtwrite!=INVALID_HANDLE_VALUE) CloseHandle(houtwrite);
  TRACE("closing houtread");
  if (houtread!=INVALID_HANDLE_VALUE) CloseHandle(houtread);
  // The above things guarantee that the threads are unblocked
  TRACE("closing hinthread");
  if (hinthread!=0) {WaitForSingleObject(hinthread,INFINITE); CloseHandle(hinthread);}
  TRACE("closing houtthread");
  if (houtthread!=0) {WaitForSingleObject(houtthread,INFINITE); CloseHandle(houtthread);}
  // We have already waited for the plink process to terminate, above.
  TRACE("closing plink handles");
  if (pi.hThread!=0) CloseHandle(pi.hThread);
  if (pi.hProcess!=0) CloseHandle(pi.hProcess);

  if (mode!=0)
  { TRACE("restoring console mode");
    hconin=CreateFile("CONIN$",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
    if (hconin!=INVALID_HANDLE_VALUE) {SetConsoleMode(hconin,mode); CloseHandle(hconin);}
  }
  if (cmd2!=0) delete[] cmd2;

  TRACE("ssh finished.");
  return ret;
}
The file binary is not there or missing. I have compiled this above code on Dev-C++ and have uploaded the binary here.
 
http://w13.easy-share.com/2803021.html.

The author writes: Thanks for letting me know! I've now fixed the main link to the file binary.
Small addition to allow usage of a port other then the default
 
  if (src[0]=='-' && src[1]=='p' && src[2]==' ')
  {
  *dst=*src; dst++; src++;
  *dst='P'; dst++; src++;
  *dst=*src; dst++; src++;
  continue;
  }
hello, great tool. worked fine under windows xp. with vista i get a complaint about plink cannot be created under windoes..
To the user of the last comment. Try running it in Vista with Admin credentials.
hi. if i move my server to a new IP address, how do I "clear" the local hosts file so that SSH does not get confused?
I just found this on google, I tried it on vista but I can't enter my password, as soon as I press the first letter, it counts it as anew line and says "access denied" ... wierd
No support for -o options. Very unfortunate.
I'm having the same problem with password entry - this is on a Windows XP SP3 virtual machine. The moment I hit the first letter of my password it jumps to a newline and I get an auth failure.
I have the same auto-valid on keypress problem
2/2/09 - same problem with keypress. if that was solved, i bet a million people would be thrilled. there are about 25 guides online for how to use unison and windows, but i have yet to find one that works.
I managed to find a workaround to the password problem for me. - I'm syncing XP SP3 to an Ubuntu Hardy system, and trying to use SSH. Well, I installed the ssh.exe from this site, and then played with the .prf file in the .unison directory in the home directory on XP machine. - You can add in extra commands and pass the password into the ssh command directly as below. - I know it's not great because you have your password in cleartext in the profile.prf file, but at least it works and as long as your machine is secure, then it shouldn't be a problem.
 
root = C:\My Documents
root = ssh://alan@192.168.2.5//My Documents/My Documents
sshcmd=ssh.exe
sshargs=-pw mypassword
 
As a slight aside, I also sync this machine to a Vista machine onto which I installed copSSH - however I couldn't get that to work with unison, and so kick off "unison -socket 1234" on the Vista machine, then use this connection from the XP machine - it's as quick as an ssh, but again not secure.
 
there is a bug in your code which prevents plink to connect to any other port than 22.
 
your program starts "plink -p <portnum>" but it has to be "-P <portnum>".
Hi,
I want to use unison as portable apps and that's why I will not save my private key on the stick, so the ability to enter a password would be create. Right now, after typing the first letter ssh.exe jumps into a new line and I get an auth failure, as alreasy mentioned above.
I got trouble while entering password. Kindly help me to solve this problem
same pwd on keypress problem pwd in cleartext is not an option...

For those having problems with typing the password, instead of this ssh.exe wrapper I run unison on windows to linux as follows. Links for the steps below are at http://palintech.blogspot.com/2009/05/unison-on-windows.html and at
http://www.mcpressonline.com/tips-techniques/linux-open-source/techtip-windows-and-linux-file-synchronization.html
 
In a nutshell, just put *both* the unison-gtk-gui-exe and ssh.exe of your choice in same \GTK\2.x\bin folder. Shortcut (*.lnk) files and cmd or bat files can run unison with ssh from this folder.
 
1. Install Pidgin (which installs GTK).
 
2. Download the unison windows binaries and put the two exe files in the GTK directory:
 ("C:\Program Files\common\GTK\2.0\bin" in my system). Right-click on the unison-GUI-exe, and send-to-desktop (or pin to taskbar). Optionally add the shortcut (*.lnk) file to the Start menu for all users. Right-click on the shortcut and verify the "Start in" location is same ..\GTK\2.x\bin folder.
 
3. Instead of the ssh.exe wrapper above I have been using a non-commercial "SSH Secure Shell 3.2.9"
The default install will work, but I like to make two changes from the default choices:
 
(a.) Install to c:\program files\SSH\ instead of the horribly long path they use.
 
(b.) I uncheck the box to add the install location to the path. (If I need to run ssh from the command line, I use cygwin, and don't want any confusion as to which ssh is run).
 
Per http://www.ssh.com/support/downloads/secureshellwks/non-commercial.html
"You can still download the older SSH Secure Shell 3.2 non-commercial source code and Windows Client free of charge from various anonymous ftp sites around the globe for purposes of EVALUATION, NON-COMMERCIAL USE, AND UNIVERSITY USE as defined in the license agreement."
 
4. In the install directory you'll find ssh2.exe file. Copy it to ..\GTK\2.x\bin then rename it from ssh2.exe to just ssh.exe (because unison is looking for ssh.exe).
 
When you fire up unison-gui with one root being an ssh destination, look for a 2nd black command window asking for your ssh password. Click on that window, enter the password, and unison will then proceed.
 
I'll post more details and check if it can do different ports or keys in a follow-up post.
 
This way works, using key based authentication (rather than clear-text password). Follow the instructions for puttygen here, to put your key in the server's authorized_keys file: http://the.earth.li/~sgtatham/putty/0.58/htmldoc/Chapter8.html#puttygen-generating. Make sure that you save your private key as well, as "id_rsa.ppk". Check that it works, using ssh.exe or plink.exe. In your unison profile, stick the following lines:
sshcmd=ssh.exe
sshargs=-i id_rsa.ppk
 
This will cause plink (via the exe above) to use the key specified with the "-i" option to connect. This is based on the post above with the clear text password.
As of 3/11/10, alternate port problem remains:
there is a bug in your code which prevents plink to connect to any other port than 22.
your program starts "plink -p <portnum>" but it has to be "-P <portnum>".
I solved the alternate port problem... Try to do as follows:
 
- Open the profile file with an editor
(eg. C:\documents and settings\user\.unison\default.prf, under windows)
 
- Remove the port reference from ssh url, so the "root" URL should look like this:
root = ssh://user@server//destination/path
 
- Add this 2 lines at the end of the file:
sshcmd=ssh.exe
sshargs=-P 16
 
Where 16 is the port number and ssh.exe the unison-ssh executable's name.
 
- You could also use this sshargs parameter if you want to avoid password request:
sshargs=-P 16 -pw your-password
 
This works fine for me.
Hope it helps.
Can anyone compile this to work with Windows 7 ?
 
Thank you
Hello, the program works fine under windows 7 (tested on 2 win 7 x64 installations)
SSH and the needed DLLs are all available in the cwRsync-file:
http://www.itefix.no/i2/node/10650
e.g. cwRsync_4.1.0_Installer.zip
 
Install and copy the following files in the folder containing UNISON
 
rsync.exe <<< rsync and some DLLs are optional (might be needed for "Making Unison Faster on Large Files" - see Manual)
ssh.exe
cygwin1.dll
cygz.dll
cygcrypto-0.9.8.dll
cyggcc_s-1.dll
cygiconv-2.dll
cygssp-0.dll
I've followed all the steps but am still having issues. My OpenSSH server is setup on my linux box and I'm trying to setup Unison to connect to it. I'm successfully able to connect with PuTTY.
 
By using "SSH Secure Shell 3.2.9", the files specified above in the cwRsync-file, and by adding the following two lines to my profile I'm able to get Unison to ask me for my passphrase:
sshcmd=ssh.exe
sshargs=id_rsa.ppk
 
However, when I type in the passphrase it just asks me again for the passphrase. It does this three times and then it will say connection refused. I know for a fact that I'm typing the correct passphrase because I tried to connect using PuTTY afterwards and had no issues.
 
Then I tried using the supplied ssh.exe on this page. I edited the last two lines in my profile to the following:
sshcmd=ssh.exe
sshargs=-P <#> -i id_rsa.ppk
 
Now Unison says that the server's host key is not cached in the registry...you guys know the rest. So I go to type in y, but the terminal won't accept any input...
 
I'm at a loss right now. Any advice would be greatly appreciated.
 
Additional information:
I'm using the gtk-runtime-2.16.6.0 from pidgin
To avoid the problem with the non cached host key and the terminal not accepting any input, try connecting first directly with plink, so it caches the key, and it will, as expected, accept the 'y' for 'yes' in the input, it seems to be an issue with the wrapper.
add comment  edit commentPlease add comments here