Bug: QtSingleApplication's UNIX socket sometimes disappears #4018

Closed
opened 2026-02-21 17:05:22 -05:00 by deekerman · 11 comments
Owner

Originally created by @vphantom on GitHub (Mar 5, 2016).

If I run qBittorrent, and then invoke it a second time (say, to add a torrent from another application or the command line for example) the full application is launched a second time in parallel (which I race to close to avoid conflicts!) instead of the already-running instance being told to add the specified magnet link or torrent file.

I can only imagine that QtSingleApplication::isRunning() is itself not detecting what it should? Maybe this is Qt4 behavior that is broken/absent from Qt5? I never touched Qt stuff before so I'm a bit lost. :(

qBittorrent: 3.3.3
Qt: 5.3.2
libtorrent: 1.0.8.0
boost: 1.55.0
OS version: Version? Mostly Debian Jessie, uname: 4.2.0-0.bpo.1-amd64 #1 SMP Debian 4.2.6-3~bpo8+2 (2015-12-14) x86_64 GNU/Linux

Originally created by @vphantom on GitHub (Mar 5, 2016). If I run qBittorrent, and then invoke it a second time (say, to add a torrent from another application or the command line for example) the full application is launched a second time in parallel (which I race to close to avoid conflicts!) instead of the already-running instance being told to add the specified magnet link or torrent file. I can only imagine that QtSingleApplication::isRunning() is itself not detecting what it should? Maybe this is Qt4 behavior that is broken/absent from Qt5? I never touched Qt stuff before so I'm a bit lost. :( qBittorrent: 3.3.3 Qt: 5.3.2 libtorrent: 1.0.8.0 boost: 1.55.0 OS version: Version? Mostly Debian Jessie, uname: 4.2.0-0.bpo.1-amd64 #1 SMP Debian 4.2.6-3~bpo8+2 (2015-12-14) x86_64 GNU/Linux
Author
Owner

@vphantom commented on GitHub (Mar 6, 2016):

Interesting information: it seems that the QtSingleApplication UNIX socket has vanished but the lock file still exists:

$ ls -la /tmp/qtsingleapp-qBit*
-rw-r--r-- 1 lis lis 0 Jan 25 07:28 /tmp/qtsingleapp-qBitto-1809-3e8-lockfile
$

Even weirder, I stopped and started qBittorrent numerous times since January 25th, yet that's the lockfile's timestamp. I'm starting to suspect that this is an issue with Qt5, but I couldn't find documentation to support this (I only found one cryptic Qt 4.4 document which would even mention the existence of QtSingleApplication::isRunning() at all.)

I finally decided to stop qBittorrent, delete the stale lockfile, and restart it. This time, a brand new socket and lockfile were created, and attempts to launch qBittorrent multiple times correctly spoke to the running instance instead of starting new ones.

Therefore, the core issue here is that the UNIX socket sometimes disappears, and when it does, the stale lockfile prevents qBittorrent from properly creating said socket next time it is run, making it vulnerable to being launched multiple times.

@vphantom commented on GitHub (Mar 6, 2016): Interesting information: it seems that the QtSingleApplication UNIX socket has vanished but the lock file still exists: ``` $ ls -la /tmp/qtsingleapp-qBit* -rw-r--r-- 1 lis lis 0 Jan 25 07:28 /tmp/qtsingleapp-qBitto-1809-3e8-lockfile $ ``` Even weirder, I stopped and started qBittorrent numerous times since January 25th, yet that's the lockfile's timestamp. I'm starting to suspect that this is an issue with Qt5, but I couldn't find documentation to support this (I only found one cryptic Qt 4.4 document which would even mention the existence of QtSingleApplication::isRunning() at all.) I finally decided to stop qBittorrent, delete the stale lockfile, and restart it. This time, a brand new socket and lockfile were created, and attempts to launch qBittorrent multiple times correctly spoke to the running instance instead of starting new ones. Therefore, the core issue here is that the UNIX socket sometimes disappears, and when it does, the stale lockfile prevents qBittorrent from properly creating said socket next time it is run, making it vulnerable to being launched multiple times.
Author
Owner

@sledgehammer999 commented on GitHub (May 13, 2018):

One would have thought that qtsingleapplication would test if the lockfile is still valid. And if not, remove it and create a new lockfile and UNIX socket pair.
I assume it already has this logic, but it is broken.

@sledgehammer999 commented on GitHub (May 13, 2018): One would have thought that qtsingleapplication would test if the lockfile is still valid. And if not, remove it and create a new lockfile and UNIX socket pair. I assume it already has this logic, but it is broken.
Author
Owner

@sledgehammer999 commented on GitHub (May 13, 2018):

Btw, does your distro ship a qtsingleapplication package or does qbittorrent use its own copy?

@sledgehammer999 commented on GitHub (May 13, 2018): Btw, does your distro ship a qtsingleapplication package or does qbittorrent use its own copy?
Author
Owner

@vedgy commented on GitHub (May 13, 2018):

Neither Debian nor Arch have its own qtsingleapplication package, so I suppose both @vphantom and @dreamhunt used the qbittorrent's copy. Not many distributions ship this library: https://repology.org/metapackage/qtsingleapplication/versions

I have the same issue with goldendict, and I believe that the lock file is not at fault for the second application instance. Quitting the application instance, which got its qtsingleapplication local server stopped (for unknown reason), helps. It doesn't matter whether I keep or delete the lock file. This explanation is compatible with @vphantom's comments above - his issue disappeared after quitting all application instances (including the longest-running one, whose local server had apparently stopped).

qtsingleapplication not removing the lock file has been reported for Windows years ago in QTSOLBUG-179. The leftover lock file seems to not have caused other issues to the reporter.

Goldendict uses a slightly newer version of qtsingleapplication and has the same issue. I also tried updating qtsingleapplication to the latest official version, but it didn't help either. There are very few changes to qtsingleapplication in the recent years. qtsingleapplication bug reports look like Qt developers don't care much about this old solution.

I've found two qtsingleapplication alternatives with comparable functionality:

I tried integrating these 2 alternatives into goldendict. Unfortunately they are not completely API-compatible and passing messages to the already running application instance doesn't work right away.

Perhaps a new D-Bus qtsingleapplication backend is called for (see a related discussion here: https://forum.qt.io/topic/71778/what-happened-to-qtsingleapplication/7).

@vedgy commented on GitHub (May 13, 2018): Neither Debian nor Arch have its own *qtsingleapplication* package, so I suppose both @vphantom and @dreamhunt used the qbittorrent's copy. Not many distributions ship this library: https://repology.org/metapackage/qtsingleapplication/versions I have the same issue with goldendict, and I believe that the lock file is not at fault for the second application instance. Quitting the application instance, which got its *qtsingleapplication* local server stopped (for unknown reason), helps. It doesn't matter whether I keep or delete the lock file. This explanation is compatible with @vphantom's comments above - his issue disappeared after quitting all application instances (including the longest-running one, whose local server had apparently stopped). *qtsingleapplication* not removing the lock file has been reported for Windows years ago in [QTSOLBUG-179](https://bugreports.qt.io/browse/QTSOLBUG-179). The leftover lock file seems to not have caused other issues to the reporter. Goldendict uses a slightly newer version of *qtsingleapplication* and has the same issue. I also tried updating *qtsingleapplication* to the latest official version, but it didn't help either. There are very few changes to *qtsingleapplication* in the recent years. [*qtsingleapplication* bug reports](https://bugreports.qt.io/browse/QTSOLBUG-193?jql=project%20%3D%20QTSOLBUG%20AND%20component%20%3D%20%22Single%20Application%22) look like Qt developers don't care much about this old solution. I've found two *qtsingleapplication* alternatives with comparable functionality: * Qt Creator maintains its own *qtsingleapplication* fork embedded in its repository. * There is also a fresh Qt5 library implementation: https://github.com/itay-grudev/SingleApplication. I tried integrating these 2 alternatives into goldendict. Unfortunately they are not completely API-compatible and passing messages to the already running application instance doesn't work right away. Perhaps a new D-Bus *qtsingleapplication* backend is called for (see a related discussion here: https://forum.qt.io/topic/71778/what-happened-to-qtsingleapplication/7).
Author
Owner

@sledgehammer999 commented on GitHub (May 13, 2018):

By looking at the qtsingleapplication source I can see one possible place where it fails. And it should log a warning in the console.
In file qtlocalpeer.cpp:

bool QtLocalPeer::isClient()
{
   .....
    if (!res)
        qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
    ....
}

@vedgy @dreamhunt can you run your 1st/primary instance from the console? Does any message/warning appear there in the form of:

QtSingleCoreApplication: listen on local socket failed

If so, it should be accompanied by a reason/error string. Can you paste it here?

@sledgehammer999 commented on GitHub (May 13, 2018): By looking at the qtsingleapplication source I can see one possible place where it fails. And it should log a warning in the console. In file `qtlocalpeer.cpp`: ```c++ bool QtLocalPeer::isClient() { ..... if (!res) qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); .... } ``` @vedgy @dreamhunt can you run your 1st/primary instance from the console? Does any message/warning appear there in the form of: >QtSingleCoreApplication: listen on local socket failed If so, it should be accompanied by a reason/error string. Can you paste it here?
Author
Owner

@vedgy commented on GitHub (May 13, 2018):

@sledgehammer999, I started goldendict from terminal to monitor its output once the second instance gets launched. But I don't think that your suggestion is going to work. If I understand correctly, the primary application instance runs isClient() only while starting. So this function's failure should manifest itself immediately. This is not what I observe. When other instances are started, they detect this primary running instance correctly for a while. But after this same primary instance runs for some time (for a few days perhaps), its server stops and other instances no longer detect it.

@vedgy commented on GitHub (May 13, 2018): @sledgehammer999, I started goldendict from terminal to monitor its output once the second instance gets launched. But I don't think that your suggestion is going to work. If I understand correctly, the primary application instance runs `isClient()` only while starting. So this function's failure should manifest itself immediately. This is not what I observe. When other instances are started, they detect this primary running instance correctly for a while. But after this same primary instance runs for some time (for a few days perhaps), its server stops and other instances no longer detect it.
Author
Owner

@sledgehammer999 commented on GitHub (May 13, 2018):

If you have verified that the issue is that the server has stopped, then I don't think the bug can be fixed. Why? Because the QLocalServer class doesn't have a signal for that. It has only the newConnection signal. The only possible way to detect that something's wrong, is for the primary instance to check QLocalServer::serverError() in a continuous timeout loop. And that won't be pretty.

PS: Since you are able to reproduce, there is a way to discover if the server is the problem:

  1. Look at QtLocalPeer::QtLocalPeer() and calculate yourself the value of socketName (or put a print to see what it truly is)
  2. Make a simple program which will do QLocalSocket::connectToServer(socketName). Then inspect the state of the socket and the possible error string. Don't forget to let the mainloop run for a while too.
@sledgehammer999 commented on GitHub (May 13, 2018): If you have verified that the issue is that the server has stopped, then I don't think the bug can be fixed. Why? Because the QLocalServer class doesn't have a signal for that. It has only the `newConnection` signal. The only possible way to detect that something's wrong, is for the primary instance to check `QLocalServer::serverError()` in a continuous timeout loop. And that won't be pretty. PS: Since you are able to reproduce, there is a way to discover if the server is the problem: 1. Look at `QtLocalPeer::QtLocalPeer()` and calculate yourself the value of `socketName` (or put a print to see what it truly is) 2. Make a simple program which will do `QLocalSocket::connectToServer(socketName)`. Then inspect the state of the socket and the possible error string. Don't forget to let the mainloop run for a while too.
Author
Owner

@vedgy commented on GitHub (May 21, 2018):

I have tested what happens to the failing local server with a simple client reusing some of the qtsingleapplication code as @sledgehammer999 suggested. The test project: QLocalSocketTest.zip. The output after the goldendict's qtsingleapplication files are deleted from /tmp:

Waiting more...
false QLocalSocket::ServerNotFoundError "QLocalSocket::connectToServer: Invalid name"

There is no suspicious stdout/stderr output from the failed goldendict instance.

Finally I found the culprit of this bug! Quitting yet another application that uses qtsingleapplication - octopi - deletes ALL qtsingleapplication files, including lock files, from /tmp. So quitting octopi allows launching a second instance of goldendict, qbittorrent and qtcreator -client. Quitting any of these other 3 qtsingleapplication-using applications does not impact files from independent applications. So this is clearly a bug in octopi.
@dreamhunt, do you use octopi? If yes, please confirm that the second qbittorrent instance can be launched after quitting octopi.
I'm looking for the bug in the octopi code right now.

EDIT: found the actual bug: UnixCommand::removeTemporaryFiles() deletes "unneeded" files far too aggressively. I'll report this bug shortly.

@vedgy commented on GitHub (May 21, 2018): I have tested what happens to the failing local server with a simple client reusing some of the *qtsingleapplication* code as @sledgehammer999 suggested. The test project: [QLocalSocketTest.zip](https://github.com/qbittorrent/qBittorrent/files/2022137/QLocalSocketTest.zip). The output after the goldendict's *qtsingleapplication* files are deleted from /tmp: > Waiting more... false QLocalSocket::ServerNotFoundError "QLocalSocket::connectToServer: Invalid name" There is no suspicious stdout/stderr output from the failed goldendict instance. Finally I found the culprit of this bug! Quitting yet another application that uses *qtsingleapplication* - [octopi](https://github.com/aarnt/octopi/) - deletes ALL *qtsingleapplication* files, including lock files, from /tmp. So quitting *octopi* allows launching a second instance of *goldendict*, *qbittorrent* and *qtcreator -client*. Quitting any of these other 3 *qtsingleapplication*-using applications does not impact files from independent applications. So this is clearly a bug in *octopi*. @dreamhunt, do you use *octopi*? If yes, please confirm that the second *qbittorrent* instance can be launched after quitting octopi. I'm looking for the bug in the *octopi* code right now. *EDIT*: found the actual bug: [UnixCommand::removeTemporaryFiles()](https://github.com/aarnt/octopi/blob/c413caadd11883ffcebf13ce515fb17bc23ebd27/src/unixcommand.cpp#L651) deletes "unneeded" files far too aggressively. I'll report this bug shortly.
Author
Owner

@mozo78 commented on GitHub (May 21, 2018):

Yes, I use Octopi on a daily basis. Great you find this annoyance!!!

@mozo78 commented on GitHub (May 21, 2018): Yes, I use Octopi on a daily basis. Great you find this annoyance!!!
Author
Owner

@sledgehammer999 commented on GitHub (May 27, 2018):

@vedgy thank you for tracking this down. It seems that itsn't our fault and probably nothing we can do about it.

@dreamhunt I hope your problem has the same root cause.

@sledgehammer999 commented on GitHub (May 27, 2018): @vedgy thank you for tracking this down. It seems that itsn't our fault and probably nothing we can do about it. @dreamhunt I hope your problem has the same root cause.
Author
Owner

@dreamhunt commented on GitHub (Jun 10, 2018):

The bug has finally fixed:
github.com/aarnt/octopi@be5fa78234

@dreamhunt commented on GitHub (Jun 10, 2018): The bug has finally fixed: https://github.com/aarnt/octopi/commit/be5fa78234e230d81f485fe7a9fafcaa30eb2488
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/qBittorrent#4018
No description provided.