After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 652262 - gvfs-open exits too early
gvfs-open exits too early
Status: RESOLVED OBSOLETE
Product: gvfs
Classification: Core
Component: general
1.8.x
Other Linux
: Normal normal
: ---
Assigned To: gvfs-maint
gvfs-maint
Depends on: 678054
Blocks:
 
 
Reported: 2011-06-10 08:16 UTC by Tassilo Horn
Modified: 2018-08-30 21:11 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
gvfs-open: always use `g_app_info_launch_uris ()` (2.16 KB, patch)
2012-06-13 22:19 UTC, Vincent Bernat
none Details | Review
gvfs-open: add a `-w` flag to wait for the application to terminate (4.23 KB, patch)
2012-06-13 22:20 UTC, Vincent Bernat
none Details | Review
gvfs-open: add a `-w` flag to wait for the application to terminate (3.80 KB, patch)
2012-06-14 06:43 UTC, Vincent Bernat
none Details | Review
Patch to fix accidental infanticide (1.94 KB, patch)
2014-12-18 22:12 UTC, Reuben Thomas
needs-work Details | Review
Patch to fix accidental infanticide (2.19 KB, patch)
2015-01-20 21:28 UTC, Reuben Thomas
needs-work Details | Review
Patch to fix accidental infanticide (2.43 KB, patch)
2015-01-21 13:37 UTC, Reuben Thomas
reviewed Details | Review
Avoid accidentally killing child processes (2.98 KB, patch)
2015-01-23 20:39 UTC, Reuben Thomas
needs-work Details | Review

Description Tassilo Horn 2011-06-10 08:16:05 UTC
With gvfs-1.8.2, gvfs-open exits too early which causes problems when it is being used to open temporary files, for example when opening email attachments from mail clients.

Here's a recipe to show the behavior:

    echo foo > /tmp/test.txt; gvfs-open /tmp/test.txt; rm /tmp/test.txt

This opens an empty /tmp/test.txt file in an editor for me.  I would have
expected /tmp/test.txt containing "foo" to be opened.  Shouldn't gvfs-open
wait a bit for the target executable to launch before exiting?  Or at least
have a flag to do that, or even have a flag to wait until the launched
executable exits (or maybe do something like execve() with this flag)?

I think, my mail client deletes the temporary files as soon as the called app returns.  So either I'd need a synchronous call of gvfs-open, or gvfs-open should return the PID of the spawned app on that I can wait.

This report is a shameless copy of

  https://bugzilla.redhat.com/show_bug.cgi?id=472402

which reported the same issue for gnome-open in the redhat bugtracker.

BTW: Bug 556305 indicates that gnome-open is deprecated in favour of gvfs-open.  Is that true, or should I file another bug report for gnome-open, too?
Comment 1 Tomas Bzatek 2011-06-10 09:26:27 UTC
I don't see any sync versions of g_app_info_launch() and g_app_info_launch_uris() that are used for spawning associated apps. Probably the only way would be to use GAppLaunchContext or GdkAppLaunchContext but that would require initializing Gtk. Not sure if it would help at all. There's no waitfor() and similar functions.

(In reply to comment #0)
> BTW: Bug 556305 indicates that gnome-open is deprecated in favour of gvfs-open.
>  Is that true, or should I file another bug report for gnome-open, too?
Yes, that's correct, gnome-open is dead already.
Comment 2 Tassilo Horn 2011-06-10 09:39:02 UTC
> Probably the only way would be to use GAppLaunchContext or GdkAppLaunchContext
> but that would require initializing Gtk.

People using gvfs-open are most likely GNOME (or XFCE) users, anyway, so would that be a big drawback?
Comment 3 Rex Dieter 2011-06-10 17:16:05 UTC
kde's kde-open has a --tempfile option, which handles some(all?) the use-cases here, would implementing something like that help?
Comment 4 Tassilo Horn 2011-06-10 19:33:59 UTC
(In reply to comment #3)
> kde's kde-open has a --tempfile option, which handles some(all?) the use-cases
> here, would implementing something like that help?

I don't know what that option does exactly.  My main aim is to be able to put a call pattern into my ~/.mailcap that works for anything, so there mustn't be a need to chose different patterns for normal and temp files.
Comment 5 Tassilo Horn 2011-09-02 09:19:22 UTC
(In reply to comment #3)
> kde's kde-open has a --tempfile option, which handles some(all?) the use-cases
> here, would implementing something like that help?

I've now checked on a computer with kde what that option means:

KDE-tempfile options:
  --tempfile                The files/URLs opened by the application
                            will be deleted after use

That's not really what I'd need, and somehow it doesn't do what it says anyway.

  $ kde-open --tempfile foo.pdf
  # it does not return before the app (okular in this case) is opened
  $ ls foo.pdf
  foo.pdf

So the file is not deleted although the help text says so.

But in my use-case, the situation is that my mail app saves an attachment to /tmp/, then fires up gvfs-open <file>, and deletes that file after gvfs-open returns.  So I need a way to make gvfs-open not return before the spawned app returns.  Or as a workaround for wrapping gvfs-open in a shell script, I somehow need to get the PID of the launched app on that I can wait.

@Tomas: Checking the gvfs-open code and http://developer.gnome.org/gio/2.29/GAppInfo.html#g-app-info-launch it says that this functions sets the environment variable GIO_LAUNCHED_DESKTOP_FILE_PID.  But doing

  $ gvfs-open foo.pdf && echo $GIO_LAUNCHED_DESKTOP_FILE_PID

prints just the empty string.  That seems to be because NULL is provided as launch context...

So I've tried initializing one

  GAppLaunchContext *context = g_app_launch_context_new ();

and passing that in the 2 launch calls.  However, the command

  $ gvfs-open foo.pdf && echo $GIO_LAUNCHED_DESKTOP_FILE_PID

still only prints an empty line. :-(
Comment 6 Rex Dieter 2011-09-02 11:58:37 UTC
I think you misunderstand how "kde-open --tempfile" works (the help text is wrong/misleading, imo).  Example:

cp foo.txt temp.txt; kde-open temp.txt ; rm -f temp.txt

will sometimes (depending on speed, race-conditions of course), give the "temp.txt not found" error, but

cp foo.txt temp.txt; kde-open --tempfile temp.txt ; rm -f temp.txt

will not (ie, it opens temporary *copy* of the file in question).
Comment 7 Tassilo Horn 2011-09-02 12:16:11 UTC
(In reply to comment #6)
> I think you misunderstand how "kde-open --tempfile" works (the help text is
> wrong/misleading, imo).  Example:
> 
> cp foo.txt temp.txt; kde-open temp.txt ; rm -f temp.txt
> 
> will sometimes (depending on speed, race-conditions of course), give the
> "temp.txt not found" error, but
> 
> cp foo.txt temp.txt; kde-open --tempfile temp.txt ; rm -f temp.txt
> 
> will not (ie, it opens temporary *copy* of the file in question).

Ah, ok.  That makes sense.  Not sure if that's a good thing, though.  I mean, my mail client already creates a copy which would be copied again.  That doesn't sound to scale very well if the file in question is big.  Well, that's not really an issue for the email attachment issue.  I hope nobody is ever going to send me a DVD image.
Comment 8 Rex Dieter 2012-01-05 19:33:58 UTC
confirmed still an issue on fedora 16 using gvfs-1.10.1-2.fc16.x86_64
Comment 9 Tassilo Horn 2012-02-02 20:06:14 UTC
> So I need a way to make gvfs-open not return before the spawned app
> returns.  Or as a workaround for wrapping gvfs-open in a shell script, I
> somehow need to get the PID of the launched app on that I can wait.

Ok, for all suffering this problem, here's a shell script that I now use as universal open command in place of gvfs-open.  It queries gvfs to get the right application and then fires that up and waits till it finished.

There's one restriction: it assumes that a foo.desktop's file Exec command is foo.  In my case, that seems to be true for all apps I use, so I didn't bother sophisticating the script any further.

It's a ZSH script, but I think you can replace the shebang with bash as well, and it'll still work.

-----------------------------------------------------------------------
#!/bin/zsh

if [[ ${#} != 1 ]]; then
    echo "Usage: open <file>"
    exit 1
fi

mime_type=`gvfs-info 12-02-02-cpp-unicode.pdf | \
  grep "standard::content-type" | \
  cut -d' ' -f4`

# echo "The mime type of ${1} is ${mime_type}."

desktop_file=`gvfs-mime --query ${mime_type} | \
  grep "Default application for" | \
  cut -d: -f2 | tr -d ' '`

# echo "The default desktop file for ${mime_type} is ${desktop_file}."

command=`echo ${desktop_file} | sed -e s/\.desktop//`

${command} ${1}
-----------------------------------------------------------------------
Comment 10 Tassilo Horn 2012-02-08 13:53:46 UTC
Ups, of course, you need to replace the 12-02-02-cpp-unicode.pdf with ${1} in the script above.
Comment 11 Vincent Bernat 2012-06-13 22:19:52 UTC
Created attachment 216352 [details] [review]
gvfs-open: always use `g_app_info_launch_uris ()`
Comment 12 Vincent Bernat 2012-06-13 22:20:30 UTC
Created attachment 216353 [details] [review]
gvfs-open: add a `-w` flag to wait for the application to terminate

Please, find attached two patches to implement a `-w` flag to let gvfs-open waits for the application to terminate. It uses a deprecated function but I hope that the bug 678054 asking for a synchronous version of g_app_info_launch() will get a positive answer in the future.
Comment 13 Vincent Bernat 2012-06-14 06:43:29 UTC
Created attachment 216376 [details] [review]
gvfs-open: add a `-w` flag to wait for the application to terminate

We don't really need to record the PID of the launched application. We can use `waitpid(-1)` instead. We still need `g_desktop_app_info_launch_uris_as_manager()` because we need the `G_SPAWN_DO_NOT_REAP_CHILD` flag.
Comment 14 Colin Walters 2012-07-14 18:13:48 UTC
Review of attachment 216376 [details] [review]:

Note this patch will still break for applications that are single instance (via GtkApplication, libunique, or whatever).  The executed binary will send a message to the running instance and then exit.
Comment 15 Reuben Thomas 2014-03-14 22:49:29 UTC
There is a related problem also caused by gvfs-open exiting too early: if gvfs-open is run under a new pty, then it is the session group leader, and when it exits, its children are killed. There's a race condition: sometimes they can have had time to detach themselves (I've seen this happen when running gvfs-open under ltrace); other times, gvfs-open completes successfully, but the child is killed.

The fix is to have gvfs-open call setsid() to put it in its own process group.

I came across the problem when running gvfs-open from xdg-open (which is a shell script), and I was able to work around it by calling gvfs-open as "setsid gvfs-open". Of course, that doesn't help when gvfs-open is invoked directly.

I'd be happy to make a patch for gvfs-open.c if it is acceptable.

(I didn't file a separate bug because I thought it would quite possibly simply be marked as a dup; I could easily imagine making that mistake myself if I were a maintainer.)
Comment 16 Ondrej Holy 2014-03-27 14:19:57 UTC
(In reply to comment #15)
> I'd be happy to make a patch for gvfs-open.c if it is acceptable.

Please do so.
Comment 17 Reuben Thomas 2014-12-18 22:10:57 UTC
I attach the patch I proposed in comment #15. I stole the code from util-linux's setsid.c, as documented. You can see the original here: https://github.com/karelzak/util-linux/blob/master/sys-utils/setsid.c
Comment 18 Reuben Thomas 2014-12-18 22:12:50 UTC
Created attachment 293008 [details] [review]
Patch to fix accidental infanticide
Comment 19 Ondrej Holy 2015-01-19 11:03:42 UTC
Review of attachment 293008 [details] [review]:

Thanks for your patch and sorry for the delay. Not sure what this actually fixes and how to test it, seems not working for me... Please give me reproducer. 

Also the patch needs some changes.

Please follow the guidelines how to write commit message:
https://wiki.gnome.org/Git/CommitMessages

::: programs/gvfs-open.c
@@ +63,3 @@
+  /* setsid so that we do not accidentally kill children when we exit.
+     If necessary, first fork a child (see setsid(2)).
+     See bug #652262.

Please write preferably url instead of bug number.

@@ +65,3 @@
+     See bug #652262.
+     The following code was adapted from util-linux's setsid.c. */
+  if (getpgrp() == getpid()) {

Please use coding style as it is used in the file (so the curly brackets have to be on newlines, space have to be between function name and parentheses...).

@@ +69,3 @@
+    switch (pid) {
+    case -1:
+      g_printerr(_("fork failed"));

Would be nice to have those error messages in same format as others (so program name, description and new line). Wouldn't be better to use break here?

@@ +86,3 @@
+  }
+  if (setsid() < 0) {
+    /* cannot happen */

This comment doesn't make sence - why we are checking this if this can't happend?
Comment 20 Ondrej Holy 2015-01-19 11:18:14 UTC
Review of attachment 293008 [details] [review]:

::: programs/gvfs-open.c
@@ +82,3 @@
+        return WEXITSTATUS(status);
+      g_printerr(_("child %d did not exit normally"), pid);
+      return status;

Is undefined value returned here?
Comment 21 Reuben Thomas 2015-01-19 12:15:53 UTC
Thanks for the review. Before addressing it, it seems sensible to deal with your first point: "Not sure what this actually fixes and how to test it, seems not working for me... Please give me reproducer."

First, what is unclear about the original report, and the RedHat bug it links to?

Secondly, did you mean "seems working for me"? Or did you mean that the patch doesn't fix it for you?

Thirdly, are you saying that the recipe given in comment #1 does not reproduce the bug for you?

Once that is cleared up, I can rewrite the patch.
Comment 22 Ondrej Holy 2015-01-19 14:07:46 UTC
(In reply to comment #21)
> Thanks for the review. Before addressing it, it seems sensible to deal with
> your first point: "Not sure what this actually fixes and how to test it, seems
> not working for me... Please give me reproducer."
> 
> First, what is unclear about the original report, and the RedHat bug it links
> to?

Thanks for response. Sorry I wrongly interpreted your first sentence in Comment #15. I thought you are trying to fix just some subproblem. Original report seems clear to me...

> Secondly, did you mean "seems working for me"? Or did you mean that the patch
> doesn't fix it for you?

However it seems the patch doesn't fix it for me.

> Thirdly, are you saying that the recipe given in comment #1 does not reproduce
> the bug for you?

I can reproduce the problem using: 
echo foo > /tmp/test.txt; gvfs-open /tmp/test.txt; rm /tmp/test.txt

The file is opened empty, unfortunately with the patch also...

Maybe I'm facing also another bug, because sometimes the file isn't opened at all...
Comment 23 Reuben Thomas 2015-01-20 21:27:47 UTC
I'm sorry, I had forgotten the context since I originally posted the patch. Indeed, I am not trying here to fix the original bug. I tried to explain the problem I am fixing in comment #15. It is hard to give a recipe to reproduce it, because it is a race condition: as I said there, when gvfs-open is run under a new pty, sometimes it dies before its child process (or processes) have detached; since it is the process group leader, they are automatically killed.

I found this bug myself when using xdg-open from Emacs via M-x async-shell-command.

I hope this explains what I'm trying to achieve with the patch.

I attach a new version of the patch that tries to address your points.

In reply to your question, can an undefined value be returned, the answer is no: status is set by wait.
Comment 24 Reuben Thomas 2015-01-20 21:28:52 UTC
Created attachment 295050 [details] [review]
Patch to fix accidental infanticide

Updated following review.
Comment 25 Ondrej Holy 2015-01-21 09:41:29 UTC
I'm sorry also, I didn't read the comment 15 carefully. I think the fix is correct, but I'd like to find a way how to test it before committing...
Comment 26 Ondrej Holy 2015-01-21 09:42:42 UTC
Review of attachment 295050 [details] [review]:

It looks good otherwise, thanks.

The error message should be something like:
"gvfs-open: use setsid to avoid killing children

gvfs-open is the session group leader if it is run under a new pty,
therefore its children can be killed, when gvfs-open exits. The fix
is to call setuid() to put it in its own process group.

https://bugzilla.gnome.org/review?bug=652262"

::: programs/gvfs-open.c
@@ +71,3 @@
+        {
+        case -1:
+          g_printerr (_("%s: fork failed\n"), g_get_prgname ());

Would be good to add comments for translators before each translation to know what is the %s, something like:
/* Translators: the %s is the program name */

@@ +74,3 @@
+          break;
+
+       case 0:

wrong indentation (missing one space)

@@ +78,3 @@
+          break;
+
+       default:

dtto
Comment 27 Ondrej Holy 2015-01-21 09:45:11 UTC
Comment on attachment 216352 [details] [review]
gvfs-open: always use `g_app_info_launch_uris ()`

Marking the patch as obsolete, because the code was changed and we are currently using only g_app_info_launch_uris...
Comment 28 Reuben Thomas 2015-01-21 13:37:45 UTC
Created attachment 295099 [details] [review]
Patch to fix accidental infanticide

Updated as per review; thanks again!
Comment 29 Reuben Thomas 2015-01-21 13:39:46 UTC
I understand your wish to test the fix before committing. Do you use Emacs, or could you use it to perform the test I mentioned in comment #23? That fails reliably for me without the fix when opening a PDF file (which happens on my system with Evince).

If you're happy to try that test, and need more details of exactly what to do, do ask.
Comment 30 Ondrej Holy 2015-01-23 15:27:14 UTC
Review of attachment 295099 [details] [review]:

Ok, I've made first experience with emacs, hmm :-D So, I can reproduce it and the patch fixes it... You didn't fix the commit message as I wanted, but I suppose you don't have commit rights, so I will change it before commit.
Comment 31 Reuben Thomas 2015-01-23 20:38:50 UTC
Apologies, I had forgotten about fixing the commit message. I have updated the patch, changing only the message, as per the page you pointed me to above.
Comment 32 Reuben Thomas 2015-01-23 20:39:27 UTC
Created attachment 295305 [details] [review]
Avoid accidentally killing child processes
Comment 33 Ross Lagerwall 2015-01-26 00:14:28 UTC
Review of attachment 295305 [details] [review]:

I don't think this is the correct solution because it is a workaround to
paper over a more fundamental design decision: should the started
process be treated like a daemon or as part of the current (shell)
session.  Also, if this change were done at all, it should really
be part of GAppInfo.

The processes are killed with SIGHUP because emacs closes the tty.

Either:
We should keep it as is, letting the processes handle SIGHUP how they
like, since they're still connected to the same session and controlling
tty.  Traditionally, one would use nohup before gvfs-open to make the child
process ignore the SIGHUP signal. (1)

Or:
Alternatively, g_app_info_launch_default_for_uri should run the child in
a clean state, as a child of init, in its own session and process group
(i.e. like a daemon).  Since gvfs-open would mostly
be used for running GUI apps, this is probably OK.  (But it kind of
contradicts the original bug which wants to wait for the app to exit.)


(1) Unfortunately, this doesn't currently work because the SIGHUP
disposition is set back to default for the child that is run.
I'm not sure why, I need to find out...
Comment 34 Ross Lagerwall 2015-01-26 09:13:58 UTC
Note that this same sort of behavior can be reproduced quite simply:
In a gnome-terminal
$ eog somepicture.png &

and then close the terminal with the X.
Comment 35 Reuben Thomas 2015-01-27 22:52:09 UTC
Thanks for the analysis, Ross.

We have conflicting requirements: some users/uses want to start the child asynchronously (e.g. nautilus, or users "launching" files from the command line); some want to wait until it terminates (e.g. terminal programs like Mutt that want to launch helpers synchronously).

It might be argued that users should use "&" and programs running gvfs-open should fork, but it's too late: users and code already rely on the current behaviour.

Therefore, we should support both. For the asynchronous case, your suggestion that "g_app_info_launch_default_for_uri should run the child in a clean state" works nicely.

For the synchronous case, add a -w flag as suggested in Comment #12. As pointed out in Comment #14, a simple implementation won't work for single instance applications; support would have to be added to cover that case, such as already exists in Emacs as exploited by emacsclient.

The implementation approach is then threefold:

1. Run the child in a clean state.

2. Add a simple "-w" flag.

3. Add support for waiting on messages to single instance applications.

Some might consider 2 not worth doing without 3.

Finally, using nohup is not a good idea, for two reasons:

1. It creates an output file.

2. It makes child processes insensitive to NOHUP, rather than simply preventing NOHUP being propagated from gvfs-open to its children.

Further, consider the example in Comment #34: running "eog foo.png &" and then closing the terminal will ALWAYS close eog. However, running "gvfs-open foo.png" and then closing the terminal will close eog if it is done very quickly, but not if gvfs-open has already exited.

In other words, there's a definite race condition here, which should be solved. Both parts of the solution above fix it: when used asynchronously, gvfs-open will no longer cause the command it launches to be SIGHUP'd on exit (because it won't be in the same session); when used synchronously, gvfs-open will always still be there to be SIGHUP'd itself, and so the child will always get SIGHUP if gvfs-open's controlling tty is closed.
Comment 36 Reuben Thomas 2015-01-27 22:57:11 UTC
(When I said "We have conflicting requirements" above, I meant "There are conflicting requirements", not "You and I have conflicting requirements"!)
Comment 37 Reuben Thomas 2015-01-28 00:25:26 UTC
I had a look to see what might be involved in making these changes. Looking at the source to glib, in order to get the child to run in its own session, we could call g_desktop_app_info_launch_uris_as_manager with the setsid code I posted above, in a user_setup (GSpawnChildSetupFunc) function. (If application launching occurs via D-Bus, user_setup is ignored, but it will still be asynchronous, and there's no need for setsid.)

Since g_desktop_app_info_launch_uris_as_manager has no synchronous option, launching synchronously would require some other route or deeper patching.

The use of g_desktop_app_info_launch_uris_as_manager would prevent gvfs-open from working with any other implementations of GAppInfo. At present there don't seem to be any; I don't know how serious this limitation might prove; equally, I can't see a way to get the required functionality through the GAppInfo interface.
Comment 38 Reuben Thomas 2015-01-28 00:35:47 UTC
Indeed, the attached patch to implement -w has to wait for the launched process itself rather than using a synchronous launch.

As far as I can see, G[tk]Application has no functionality like Emacs's server-edit command (which notifies a client when all its buffers have been marked "done"), so that would take some work to define a new API and add it (and presumably this would be something to do via D-Bus).
Comment 39 Ondrej Holy 2016-10-04 09:08:52 UTC
New gio cmd tool has been added in GLib and thus GVfs cmd tools has been deprecated, see Bug 769378. Please test this issue against the new gio tool and file a new bug report under GLib product (GIO component) if it is still valid.
Comment 40 Reuben Thomas 2018-08-30 21:11:16 UTC
I filed bug #772397 after this, which became bug #1208 on GitLab: https://gitlab.gnome.org/GNOME/glib/issues/1208