Skip to content

Conversation

@nojb
Copy link
Contributor

@nojb nojb commented Feb 20, 2023

On Windows, the process ocamlmerlin-server.exe is launched with the directory of the current source file as current working directory. This causes problems if one tries to move or delete that directory.

To avoid this problem, this PR makes sure to chdir to a different directory (TMPDIR) before launching ocamlmerlin-server.exe. Note that the same logic already exists for Unix:

/* Change directory to root, so that process still works if directory
* is delete. */
if (chdir("/") != 0)
failwith_perror("chdir");

cc @dra27 @MisterDA

Fixes #1474

@voodoos voodoos requested a review from let-def February 21, 2023 10:10
@voodoos
Copy link
Collaborator

voodoos commented Feb 21, 2023

@let-def was there a reason why we didn't chdir / in the Windows case like we do for unixes ?

@nojb
Copy link
Contributor Author

nojb commented Feb 21, 2023

@let-def was there a reason why we didn't chdir / in the Windows case like we do for unixes ?

What is / :) ?

@voodoos
Copy link
Collaborator

voodoos commented Feb 21, 2023

@nojb why not chdir to the root like in the unix cases ?

@voodoos
Copy link
Collaborator

voodoos commented Feb 21, 2023

What is / :) ?

Sorry, I was too lazy to write chdir("/") 😅

@nojb
Copy link
Contributor Author

nojb commented Feb 21, 2023

@nojb why not chdir to the root like in the unix cases ?

Yes, I also considered doing that by using PathCchStripToRoot, but did not see a clear win over GetTempPath. @MisterDA any opinions?

@nojb
Copy link
Contributor Author

nojb commented Feb 21, 2023

What is / :) ?

Sorry, I was too lazy to write chdir("/") 😅

I meant that / does not mean anything on Windows...

@MisterDA
Copy link
Contributor

No strong opinion. Switching to the temporary path seems fine.

@voodoos
Copy link
Collaborator

voodoos commented Feb 21, 2023

I meant that / does not mean anything on Windows...

Oh right, each drive has its own root. I would have expected that cd'ing to \ cd's to the current drive's root, though.

@let-def
Copy link
Contributor

let-def commented Feb 21, 2023

I meant that / does not mean anything on Windows...

Correct me if I am wrong, I remember trying that on windows at some point, and I remember the effect was to move to the root of the current drive (so C:\ or D:\). Though it might have been cygwin doing some magic, I can't tell and it is foggy. (And is it safe to assume that C:\ is always valid ?)

However I don't know why we were not doing this on Windows before.

@nojb
Copy link
Contributor Author

nojb commented Feb 21, 2023

@voodoos

I meant that / does not mean anything on Windows...

Oh right, each drive has its own root. I would have expected that cd'ing to \ cd's to the current drive's root, though.

@let-def

I meant that / does not mean anything on Windows...

Correct me if I am wrong, I remember trying that on windows at some point, and I remember the effect was to move to the root of the current drive (so C:\ or D:\).

Oops, you are both right; I misspoke.

Though it might have been cygwin doing some magic, I can't tell and it is foggy. (And is it safe to assume that C:\ is always valid ?)

If it works, using chdir("\\") may indeed be better as it is closer to what is done for Unix. I'll test it and (if it works) will update the PR accordingly.

@nojb
Copy link
Contributor Author

nojb commented Feb 21, 2023

If it works, using chdir("\\") may indeed be better as it is closer to what is done for Unix. I'll test it and (if it works) will update the PR accordingly.

Tried it, seems to work fine, and it is simpler. Updated the PR.

image

@MisterDA do you mind taking a second look, since you already did a first pass over the PR? Thanks!

@nojb nojb changed the title ocamlmerlin.c: chdir to TMPDIR to continue working if cwd is deleted ocamlmerlin.c: chdir to root also on Windows to continue working if cwd is deleted Feb 21, 2023
Copy link
Contributor

@MisterDA MisterDA left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I knew you could chdir to root, like this, that's good to know.
This version seems actually better than the last one, chdir is a CRT function and will set errno and failwith_perror uses perror and errno. The previous version was using Win32 functions, I don't think they set errno since the error code has to be retrieved with GetLastError (in that sense failwith_perror, a wrapper over CRT functions, is wrong for all Win32 functions…).
Reported the problem at #1570.

@nojb
Copy link
Contributor Author

nojb commented Feb 21, 2023

Thanks for the review @MisterDA! I updated CHANGES. This is ready to go as far as I am concerned.

@voodoos
Copy link
Collaborator

voodoos commented Feb 21, 2023

The windows CI failure is a bit surprising.
There is a warning:

(cd _build/default/src/frontend/ocamlmerlin && D:\cygwin\bin\x86_64-w64-mingw32-gcc.exe -O2 -fno-strict-aliasing -fwrapv -mms-bitfields -oocamlmerlin.exe ocamlmerlin.c)
ocamlmerlin.c: In function 'start_server':
ocamlmerlin.c:293:7: warning: implicit declaration of function '_chdir'; did you mean 'chdir'? [-Wimplicit-function-declaration]
  293 |   if (_chdir("\\") == -1)
      |       ^~~~~~
      |       chdir
      ```
      
But also a number of different results in the test suite. I will have a look later.

@nojb
Copy link
Contributor Author

nojb commented Feb 21, 2023

There is a warning:

(cd _build/default/src/frontend/ocamlmerlin && D:\cygwin\bin\x86_64-w64-mingw32-gcc.exe -O2 -fno-strict-aliasing -fwrapv -mms-bitfields -oocamlmerlin.exe ocamlmerlin.c)
ocamlmerlin.c: In function 'start_server':
ocamlmerlin.c:293:7: warning: implicit declaration of function '_chdir'; did you mean 'chdir'? [-Wimplicit-function-declaration]
  293 |   if (_chdir("\\") == -1)
      |       ^~~~~~
      |       chdir
      ```

It may have been a missing include. I pushed a possible fix.

But also a number of different results in the test suite. I will have a look later.

Thanks!

@voodoos
Copy link
Collaborator

voodoos commented Feb 21, 2023

So, one of the failing test is calling:

   $ ocamlmerlin server errors -filename test.ml -cmi-path sub < test.ml

And it looks like with that PR the argument -cmi-path sub is ignored (it prints an error saying that some module is not in path).

Not sure what is causing that, there nothing obvious in the diff.

@voodoos
Copy link
Collaborator

voodoos commented Feb 21, 2023

Right, maybe there is something wrong with how Merlin treat the relative path given as an argument now that the server is not spawned at the same place. @let-def does that ring a 🔔 ?

@nojb
Copy link
Contributor Author

nojb commented Feb 21, 2023

Right, maybe there is something wrong with how Merlin treat the relative path given as an argument now that the server is not spawned at the same place. @let-def does that ring a 🔔 ?

I think I now what is going on. In the Unix code, we only change the working directory for the child process, but in the present verseion of this PR we are changing the working directory also for the parent process.

I am working on a fix.

@dra27
Copy link
Member

dra27 commented Feb 21, 2023

FWIW (sorry for late entry), chdir("\\") isn't strictly as on Unix... it's not impossible to be on a network or removable drive, and then Merlin is part of the "Windows cannot eject this drive right now" problem (i.e. this could give you G:\ which could be unmounted, whereas / cannot for the most part be unmounted) 🙂 Given that there's no need to write to it, I'd go with GetSystemDirectory which is guaranteed to exist on a non-removeable drive, or the GetTempPath2 but we're in hair-splitting territory!

@dra27
Copy link
Member

dra27 commented Feb 21, 2023

Oo, except GetTempPath2 is really recent (if that hasn't already been spotted), so GetSystemDirectory is probably the better one.

@nojb
Copy link
Contributor Author

nojb commented Feb 21, 2023

Thanks @dra27 for the suggestion!

Switched to GetSystemDirectory; also, I made it so that only the current directory of the child process is modified (and not the parent's). This should fix the test suite errors. I also had to do a tiny fix to the code in New_merlin to restore the "old" working directory after handling a request.

Review welcome! cc @voodoos @dra27 @MisterDA

@nojb
Copy link
Contributor Author

nojb commented Feb 21, 2023

(By the way, this PR fixes #1474.)

@nojb
Copy link
Contributor Author

nojb commented Feb 22, 2023

Friendly ping.

try Sys.chdir wd; Printf.sprintf "changed directory to %S" wd
with _ -> Printf.sprintf "cannot change working directory to %S" wd
let oldwd = Sys.getcwd () in
try Sys.chdir wd; Printf.sprintf "changed directory to %S" wd, restore_cwd oldwd run
Copy link
Collaborator

@voodoos voodoos Feb 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, do I understand well that without these changes we update the server cwd before running a command without restoring it after that. Which means that the first cd to / is useless ?

Could you add a test illustrating this change's effect ?

Copy link
Contributor Author

@nojb nojb Feb 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, do I understand well that without these changes we update the server cwd before running a command without restoring it after that.

Yes.

Which means that the first cd to / is useless ?

No, it is still necessary: otherwise after handling a request the server is left on the directory where it was first launched, and this can cause problems.

Could you add a test illustrating this change's effect ?

I'm not sure how I would go about it, since we are talking about correctly (re)setting the "current directory" of the server process, but there should be no observable difference for the user. But if you have any suggestions, I'm happy to give it a try.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arf, I just realized I edited your comment instead of replying to it... My answer:

Which means that the first cd to / is useless ?

No, it is still necessary: otherwise after handling a request the server is left on the directory where it was first > launched, and this can cause problems.

Sorry, my formulation was unclear, I meant "so the current (without these changes) cd to / done in the Unix case is useless because of this issue".

Could you add a test illustrating this change's effect ?

I'm not sure how I would go about it, since we are talking about correctly (re)setting the "current directory" of the server process, but there should be no observable difference for the user. But if you have any suggestions, I'm happy to give it a try.

Maybe by triggering the original issue: start the server, move the dir, perform a query, observe a crash ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, my formulation was unclear, I meant "so the current (without these changes) cd to / done in the Unix case is useless because of this issue".

Yes, exactly.

Maybe by triggering the original issue: start the server, move the dir, perform a query, observe a crash ?

I'll give it a try and report back.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a test that shows that the current working directory was not being restored correctly before. I didn't try to exhibit a server misbehaviour because this was harder, it depends on the operating system, etc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thank you !

@voodoos voodoos added this to the Next release milestone Feb 24, 2023
@voodoos
Copy link
Collaborator

voodoos commented Feb 24, 2023

Thanks a lot @nojb !

@voodoos voodoos merged commit 7ca211e into ocaml:master Feb 24, 2023
voodoos added a commit to voodoos/merlin that referenced this pull request Feb 24, 2023
from nojb/win32_chdir

ocamlmerlin.c: chdir to root also on Windows to continue working if cwd is deleted
voodoos added a commit to voodoos/opam-repository that referenced this pull request Feb 24, 2023
CHANGES:

Fri Feb 24 16:55:42 CEST 2023

  + merlin binary
    - Recognize OCaml 5.0 cmi magic number in compiler version mismatch message
      (ocaml/merlin#1554, fixes ocaml/merlin#1553)
    - Upgrade Merlin from the RC2 to the stable 5.0.0 compiler release (ocaml/merlin#1559,
      fixes ocaml/merlin#1558)
    - Improve type-enclosing behaviour when used on records' labels (ocaml/merlin#1565,
      fixes ocaml/merlin#1564)
    - Restore compatibility with the compiler's command line by accepting the
      `-safe-string` flag as a no-op instead of rejecting it (ocaml/merlin#1544, fixes
      ocaml/merlin#1518)
    - Traverse aliases when jumping to declaration. This matches
      jump-to-definition's behavior (ocaml/merlin#1563)
    - Improve locate's behavior in various ill-typed expressions (ocaml/merlin#1546, fixes
      ocaml/merlin#1567 and partially ocaml/merlin#1543)
    - Correctly traverse patterns when looking for docs in the typedtree (ocaml/merlin#1572)
    - Get documentation when the declaration or definition is selected (ocaml/merlin#1542,
      fixes ocaml/merlin#1540)
    - On Windows, change to a harmless directory when launching server to avoid
      locking down current directory (ocaml/merlin#1569, fixes ocaml/merlin#1474)
  + editor modes
    - emacs: Fix misuse of `eq` comparison (ocaml/merlin#1549, @mattiase)
    - emacs: xref works from context menus; better highlighting of xref matches;
      xref recognises operators and binding operators at the cursor position;
      bad locations are filtered out (ocaml/merlin#1385, fixes ocaml/merlin#1410, @mattiase)
  + test suite
    - Add a test for incorrect alert defaults (ocaml/merlin#1559)
    - Add multiple tests for locate over ill-typed expressions (ocaml/merlin#1546)
    - Add non-regression tests for other fixes in this release
voodoos added a commit to voodoos/opam-repository that referenced this pull request Feb 24, 2023
CHANGES:

Fri Feb 24 16:55:42 CEST 2023

  + merlin binary
    - Update internal typer to match OCaml 4.14.1 release (ocaml/merlin#1557)
    - Improve type-enclosing behaviour when used on records' labels (ocaml/merlin#1565,
      fixes ocaml/merlin#1564)
    - Restore compatibility with some OCaml compiler's debug flags that were
      incorrectly rejected by Merlin (ocaml/merlin#1556)
    - Traverse aliases when jumping to declaration. This matches
      jump-to-definition's behavior (ocaml/merlin#1563)
    - Improve locate's behavior in various ill-typed expressions (ocaml/merlin#1546, fixes
      ocaml/merlin#1567 and partially ocaml/merlin#1543)
    - Correctly traverse patterns when looking for docs in the typedtree (ocaml/merlin#1572)
    - Get documentation when the declaration or definition is selected (ocaml/merlin#1542,
      fixes ocaml/merlin#1540)
    - On Windows, change to a harmless directory when launching server to avoid
      locking down current directory (ocaml/merlin#1569, fixes ocaml/merlin#1474)
  + test suite
    - Add multiple tests for locate over ill-typed expressions (ocaml/merlin#1546)
    - Add non-regression tests for other fixes in this release
voodoos added a commit to voodoos/opam-repository that referenced this pull request Feb 24, 2023
CHANGES:

Fri Feb 24 16:55:42 CEST 2023

  + merlin binary
    - Recognize OCaml 5.0 cmi magic number in compiler version mismatch message
      (ocaml/merlin#1554, fixes ocaml/merlin#1553)
    - Upgrade Merlin from the RC2 to the stable 5.0.0 compiler release (ocaml/merlin#1559,
      fixes ocaml/merlin#1558)
    - Improve type-enclosing behaviour when used on records' labels (ocaml/merlin#1565,
      fixes ocaml/merlin#1564)
    - Restore compatibility with the compiler's command line by accepting the
      `-safe-string` flag as a no-op instead of rejecting it (ocaml/merlin#1544, fixes
      ocaml/merlin#1518)
    - Traverse aliases when jumping to declaration. This matches
      jump-to-definition's behavior (ocaml/merlin#1563)
    - Improve locate's behavior in various ill-typed expressions (ocaml/merlin#1546, fixes
      ocaml/merlin#1567 and partially ocaml/merlin#1543)
    - Correctly traverse patterns when looking for docs in the typedtree (ocaml/merlin#1572)
    - Get documentation when the declaration or definition is selected (ocaml/merlin#1542,
      fixes ocaml/merlin#1540)
    - On Windows, change to a harmless directory when launching server to avoid
      locking down current directory (ocaml/merlin#1569, fixes ocaml/merlin#1474)
  + editor modes
    - emacs: Fix misuse of `eq` comparison (ocaml/merlin#1549, @mattiase)
    - emacs: xref works from context menus; better highlighting of xref matches;
      xref recognises operators and binding operators at the cursor position;
      bad locations are filtered out (ocaml/merlin#1385, fixes ocaml/merlin#1410, @mattiase)
  + test suite
    - Add a test for incorrect alert defaults (ocaml/merlin#1559)
    - Add multiple tests for locate over ill-typed expressions (ocaml/merlin#1546)
    - Add non-regression tests for other fixes in this release
voodoos added a commit to voodoos/opam-repository that referenced this pull request Feb 24, 2023
CHANGES:

Fri Feb 24 16:55:42 CEST 2023

  + merlin binary
    - Update internal typer to match OCaml 4.14.1 release (ocaml/merlin#1557)
    - Improve type-enclosing behaviour when used on records' labels (ocaml/merlin#1565,
      fixes ocaml/merlin#1564)
    - Restore compatibility with some OCaml compiler's debug flags that were
      incorrectly rejected by Merlin (ocaml/merlin#1556)
    - Traverse aliases when jumping to declaration. This matches
      jump-to-definition's behavior (ocaml/merlin#1563)
    - Improve locate's behavior in various ill-typed expressions (ocaml/merlin#1546, fixes
      ocaml/merlin#1567 and partially ocaml/merlin#1543)
    - Correctly traverse patterns when looking for docs in the typedtree (ocaml/merlin#1572)
    - Get documentation when the declaration or definition is selected (ocaml/merlin#1542,
      fixes ocaml/merlin#1540)
    - On Windows, change to a harmless directory when launching server to avoid
      locking down current directory (ocaml/merlin#1569, fixes ocaml/merlin#1474)
  + test suite
    - Add multiple tests for locate over ill-typed expressions (ocaml/merlin#1546)
    - Add non-regression tests for other fixes in this release
@nojb nojb deleted the win32_chdir branch February 25, 2023 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Running ocamlmerlin-server.exe does not allow moving directory under Windows

5 participants