sparrow icon indicating copy to clipboard operation
sparrow copied to clipboard

force only a single instance to be opened at the same time

Open AndySchroder opened this issue 11 months ago • 5 comments

Sparrow wallet can be opened multiple times, which can lead to confusion and problems. For example, changing settings only seems to save whatever the last instance closed has set.

People using slow old computers or people who struggle with double clicking can open the application multiple times by accident. Would be nice to have a setting option that only allows a single instance to be opened at a time. If someone tries to open the application a second time, then just switch the focus to the instance that was already running.

Or, possibly make it so that that is the only way Sparrow wallet works and not have a setting to choose.

AndySchroder avatar Mar 08 '24 19:03 AndySchroder

Sparrow wallet can be opened multiple times, which can lead to confusion and problems.

Actually, no it can't by design. The first instance of Sparrow opens a specific port that further instances check when launching.

How are you achieving this? Possibly there is a thread race that can achieve this if multiple instances are opened at once, but it's difficult to do much about this without better support from the OS.

craigraw avatar Mar 09 '24 08:03 craigraw

I just click the icon in the launcher, wait a minute and do it again. Or, I double click on a desktop icon twice. Then I get a bunch of "Error opening wallet, Wallet file may already be in use. Make sure the application is not running elsewhere." popups. Also,

$ ps -ef|grep Sparrow
andy       29773    4536 28 11:15 ?        00:00:22 /opt/sparrow/bin/Sparrow
andy       29915    4536 12 11:15 ?        00:00:08 /opt/sparrow/bin/Sparrow
andy       30097   30075  0 11:16 pts/84   00:00:00 grep --color=auto Sparrow
$

What kind of port are you opening?

AndySchroder avatar Mar 09 '24 16:03 AndySchroder

I just click the icon in the launcher, wait a minute and do it again.

I can't recreate this - what Linux flavour and desktop are you using?

What kind of port are you opening?

Sparrow will search for an open port on the loopback interface, starting from 7221 - details here: https://github.com/sparrowwallet/sparrow/blob/master/src/main/java/com/sparrowwallet/sparrow/instance/Instance.java

craigraw avatar Mar 11 '24 06:03 craigraw

I am using https://xubuntu.org/ . I think I've seen this on windows too, but it was on someone else's computer and I didn't have time to try and reproduce.

Trying to understand why you are using a TCP port? Is that just a way for inter process communication? Seems like this is a bad idea since some people might fail to have a firewall installed.

Wondering why you don't use UNIX sockets instead? Here is an a (python) example: https://github.com/AndySchroder/StaticWire/blob/2e007c0db6ec8c65e2f0ba2478a31c6bff16ef82/staticIP#L49

AndySchroder avatar Mar 12 '24 03:03 AndySchroder

Is that just a way for inter process communication?

Yes, effectively.

Seems like this is a bad idea since some people might fail to have a firewall installed.

A firewall on the loopback interface?

Wondering why you don't use UNIX sockets instead?

Unfortunately UNIX sockets have their own issues. They are obviously not really cross platform, and implementations differ in their approach I have found and are not consistently reliable. I had to switch from UNIX sockets for Sparrow's internal Tor back to TCP to resolve Tor startup issues.

It would be helpful if you can try debug this. My suspicion is that your system is giving you a different value for it's temporary file directory every time. Sparrow uses this location to store a file called (for example) com.sparrowwallet.sparrow.mainnet.lock which contains the TCP port. For my Ubuntu system, this is in /tmp.

craigraw avatar Mar 12 '24 06:03 craigraw

If you bind to the loopback interface, can't other users on the system access the port still? Seems like there should be some authentication cookie required like bitcoind or X11. Also, it seems like the cookie should be present in the user's home folder and not in /tmp/

Here is the lock file that is created when the first instance starts.

$ cat /tmp/com.sparrowwallet.sparrow.mainnet.lock 
7221

I am not seeing anything useful in the sparrow log file related to this problem other than that all the wallet files are locked and they can't be read (which is the same as the pop up errors that I'm getting).

AndySchroder avatar Mar 15 '24 05:03 AndySchroder

If you bind to the loopback interface, can't other users on the system access the port still?

Yes, but what would they gain? One can assume that an application with access to the loopback interface can also for example send a URI to operating system, which will cause a second instance of Sparrow to launch and pass to the first (running) instance. So restricting access to the port does not really secure much I don't think.

Here is the lock file that is created when the first instance starts

Ok - so the file is created in a standard place, and the first instance of Sparrow should bind to that port. Subsequent runs should read that file and attempt to connect to the port. Unfortunately it's difficult to tell where this is going wrong on your system. Can you check if the port is actually bound?

craigraw avatar Mar 25 '24 14:03 craigraw

Here is what I am getting. Please tell me a more useful message to try and send to the port besides quit.

$ telnet localhost 7221
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
quit
!com.sparrowwallet.sparrow.mainnetConnection closed by foreign host.

AndySchroder avatar Mar 26 '24 02:03 AndySchroder

If you bind to the loopback interface, can't other users on the system access the port still?

Yes, but what would they gain? One can assume that an application with access to the loopback interface can also for example send a URI to operating system, which will cause a second instance of Sparrow to launch and pass to the first (running) instance. So restricting access to the port does not really secure much I don't think.

Access to the wallet.

Not sure how the example you provided is relevant.

Consider this:

  1. User a launches sparrow wallet and opens wallets
  2. User b launches sparrow wallet and then that second instance connects to localhost port 7221.
  3. User b can now use user a's wallets, right?

AndySchroder avatar Mar 26 '24 02:03 AndySchroder

User b can now use user a's wallets, right?

No - there is no access to a user's wallets from that port. You can pass in file paths and URIs (ones that are specifically supported) to that port for Sparrow to handle, which might be annoying but I think very low risk. Certainly you can't extract information.

That said, if you have malicious software running on the same operating system (and very often, the same user) as your Bitcoin software, you're at risk from a whole host of things that are difficult to defend against.

craigraw avatar Mar 26 '24 06:03 craigraw

Here is what I am getting. Please tell me a more useful message to try and send to the port besides quit.

Ok - looks like the port is getting bound. Looks like I'll need to install Xubuntu to try figure out what is going on.

craigraw avatar Mar 26 '24 07:03 craigraw

User b can now use user a's wallets, right?

No - there is no access to a user's wallets from that port. You can pass in file paths and URIs (ones that are specifically supported) to that port for Sparrow to handle, which might be annoying but I think very low risk. Certainly you can't extract information.

That said, if you have malicious software running on the same operating system (and very often, the same user) as your Bitcoin software, you're at risk from a whole host of things that are difficult to defend against.

So, confused what this function is actually trying to do again? Does the second process

  1. tell the first process to open a new window and then exit, or
  2. open a new window, stay open, and communicate the first process for everything it needs?

In either case, how does the first process know what user the second process is running under? If the first process is given commands from the second process to open specific file paths, how does that work if the first process doesn't have access to the second process's files?

AndySchroder avatar Mar 26 '24 14:03 AndySchroder

Here is what I am getting. Please tell me a more useful message to try and send to the port besides quit.

Ok - looks like the port is getting bound. Looks like I'll need to install Xubuntu to try figure out what is going on.

FYI, if you already have regular ubuntu, you can run sudo apt install xubuntu-desktop to install xubuntu on top of your existing installation. It's a little messier because you have two desktop environments and all their dependencies installed at the same time, but it works.

AndySchroder avatar Mar 26 '24 14:03 AndySchroder

tell the first process to open a new window and then exit

This one - although it will likely be a new tab

how does that work if the first process doesn't have access to the second process's files

It will fail - which is probably what you want in this situation. The use case is that both Sparrow instances are created by the same user.

craigraw avatar Mar 26 '24 14:03 craigraw

@craigraw I have a machine running xubuntu if you need info / help with this. FWIW I just tried and it does let me launch multiple copies of Sparrow:

image

PrinceOfEgypt avatar Mar 26 '24 14:03 PrinceOfEgypt

ok so this is interesting - the same thing is also possible on windows - I was just able to launch two copies on windows 10

PrinceOfEgypt avatar Mar 26 '24 14:03 PrinceOfEgypt

how does that work if the first process doesn't have access to the second process's files

It will fail - which is probably what you want in this situation. The use case is that both Sparrow instances are created by the same user.

That is what I want, but how does it fail if it is communicating on an unauthenticated TCP port?

Basically all user b can do is try to open a new window/tab on user a's desktop, but user b can never see that window/tab and user a can never access user b's filesystem, so that is how things are safe?

Seems like this design limits user a and user b from both using Sparrow at the same time on the same machine. Does this limitation also apply to the terminal version of Sparrow?

AndySchroder avatar Mar 26 '24 14:03 AndySchroder

tell the first process to open a new window and then exit

This one - although it will likely be a new tab

What new tab will be opened if just launching Sparrow normally? Normally sparrow doesn't load any specific wallets when launching unless "Load Recent Wallets" is selected, but if this is the second lunch of sparrow, those wallets should already be opened. Will it really just change window focus to the existing sparrow wallet window?

AndySchroder avatar Mar 26 '24 14:03 AndySchroder

I decided to rewrite the instance implementation to be more modern.

What Sparrow now does is:

  • On launch, create a UNIX socket in Sparrow home which acts as both a lock file for that instance and an interprocess communication protocol for passing files and URIs to that instance. Since access to Sparrow home is restricted to the user starting the instance, this should resolve any privacy/security concerns. This means it's possible to run multiple instances of Sparrow using different Sparrow home locations with the -d argument. If Sparrow starts and the lock file is already present, any file or URI arguments will be passed to the already running instance via the UNIX socket.
  • In addition, Sparrow will create a symlink (or on Windows, a file containing the path) to the lock file in the user-specific default Sparrow home location. This means that even if Sparrow is started with a non-default home, the OS can still pass files and URIs to it as Sparrow will be able to find the current instance. The first instance started will be the one to receive them.

d1a353ae and subsequent commits.

It's interesting to note that macOS automatically prevents multiple instances from opening from the same installation, which is why I probably didn't notice any issues previously.

craigraw avatar Mar 28 '24 08:03 craigraw

Consider this scenario:

  • First instance started uses a different location with the -d argument. First instance places the symlink the default sparrow home
  • Second instance started uses the default sparrow home location, but explicitly using the -d argument.

In this case, can the second instance overwrite the symlink for the first with its lock file? Or, does the second instance just follow the symlink?

Seems like the second instance will probably follow the symlink.

So, it seems like if using -d for the first instance to a non-default sparrow home location, no other instance can use the default home location. This is probably fine, but it is just worth noting.

AndySchroder avatar Mar 28 '24 13:03 AndySchroder

  • This means that even if Sparrow is started with a non-default home, the OS can still pass files and URIs to it as Sparrow will be able to find the current instance. The first instance started will be the one to receive them.

Generally, I like this approach. With multiple profiles in firefox, I get the same type of experience. In firefox, the first profile started I consider to be my main instance where links will open and any subsequent profiles are special ones that are self contained and don't receive any messages from the rest of the operating system. The same thought process can apply well to multiple instances of sparrow.

AndySchroder avatar Mar 28 '24 14:03 AndySchroder

In this case, can the second instance overwrite the symlink for the first with its lock file?

No - and the symlink is only deleted once the first instance exits.

Or, does the second instance just follow the symlink?

The only time the symlink is followed is if there are file or URI arguments to the program. This is how the OS will pass them to Sparrow. This is not normally how Sparrow is ever run by a user, although it's perfectly ok to use it in this manner. The second instance, launched by the OS, will exit after communicating these arguments to the first instance.

If there are no file or URI arguments, and the second instance is trying to use the same Sparrow home as the first, it will simply exit.

craigraw avatar Mar 28 '24 15:03 craigraw

If there are no file or URI arguments, and the second instance is trying to use the same Sparrow home as the first, it will simply exit.

Wondering in this case if you can make the first instance come to the foreground if it is minimized or in the background? For example, what if you have sparrow running in another workspace and you don't realize it, then you will keep trying to launch sparrow and wonder why nothing happens.

AndySchroder avatar Mar 28 '24 15:03 AndySchroder

Good idea, implemented in 6b4c3014.

craigraw avatar Mar 29 '24 07:03 craigraw

Good idea, implemented in 6b4c301.

Just tested in version 1.8.5 and I am no longer getting multiple instances but if the window is minimized or in the background it is not coming to the foreground when re-launching sparrow.

AndySchroder avatar Apr 14 '24 18:04 AndySchroder

if the window is minimized

Added this behaviour in 5a0df265.

in the background

This I can't reproduce - I get the existing window coming to the foreground when the second instance is launched.

craigraw avatar Apr 15 '24 10:04 craigraw

if the window is minimized

Added this behaviour in 5a0df26.

This works for me in the same workspace.

If in a different workspace, the window is moved to the current workspace and restored, but it can be behind an existing window.

AndySchroder avatar Jun 13 '24 20:06 AndySchroder

in the background

This I can't reproduce - I get the existing window coming to the foreground when the second instance is launched.

Still not getting it to come to the foreground if it is already restored (un-minimized), it just stays behind whatever window is already in the foreground.

Which desktop environments are you testing with?

AndySchroder avatar Jun 13 '24 20:06 AndySchroder

If in a different workspace, the window is moved to the current workspace and restored, but it can be behind an existing window.

This is probably implementation-specific to the windowing system in use. Might be difficult to solve this in general.

Which desktop environments are you testing with?

It would definitely have been tested on stock Ubuntu 20.01 - I may have tested on other environments as well. Which is it not working on?

craigraw avatar Jun 16 '24 08:06 craigraw