avoid inheritance of file descriptors

Message ID 475D4D14.40609@fliegl.de
State New
Headers

Commit Message

Deti Fliegl Dec. 10, 2007, 2:28 p.m. UTC
  Hi,

I think there is a problem in calling external programs from plugins. If 
such a program takes some while for execution (even in background) it 
gets inherited all file descriptors of VDR. This prevents vdr from 
zapping to another channel or even from restarting properly. You will 
see messages like:

ERROR: /dev/dvb/adapter0/dvr0: Device or resource busy
or
ERROR (svdrp.c,84): Address already in use

Reason: By default unix inherits all file descriptors to child processes 
when calling exec*(...) or system(...). You can avoid this by setting 
FD_CLOEXEC on all file descriptors that should not be inherited.

Patch:


Comments, ideas? I would be happy to see this little patch applied to 
1.4 and 1.5 trunks of VDR.

Deti
  

Comments

Klaus Schmidinger Dec. 10, 2007, 6:32 p.m. UTC | #1
On 12/10/07 15:28, Deti Fliegl wrote:
> Hi,
> 
> I think there is a problem in calling external programs from plugins. If 
> such a program takes some while for execution (even in background) it 
> gets inherited all file descriptors of VDR. This prevents vdr from 
> zapping to another channel or even from restarting properly. You will 
> see messages like:
> 
> ERROR: /dev/dvb/adapter0/dvr0: Device or resource busy
> or
> ERROR (svdrp.c,84): Address already in use
> 
> Reason: By default unix inherits all file descriptors to child processes 
> when calling exec*(...) or system(...). You can avoid this by setting 
> FD_CLOEXEC on all file descriptors that should not be inherited.
> 
> Patch:
> ...
> Comments, ideas?

Doesn't SystemExec() (see tools.c) take care of this?

Klaus
  
Deti Fliegl Dec. 10, 2007, 8:07 p.m. UTC | #2
Klaus Schmidinger wrote:
> Doesn't SystemExec() (see tools.c) take care of this?
Yes you are right - it takes care internally but not for plugins like 
dvdswitch etc. In order to fix this problem you could patch every single 
plugin or just set the right file descriptor flag once. I think the 
latter does not cause any interference and should solves some issues.

Deti
  
Anssi Hannula Dec. 10, 2007, 9:02 p.m. UTC | #3
Deti Fliegl wrote:
> Klaus Schmidinger wrote:
>> Doesn't SystemExec() (see tools.c) take care of this?
> Yes you are right - it takes care internally but not for plugins like 
> dvdswitch etc. In order to fix this problem you could patch every single 
> plugin or just set the right file descriptor flag once. I think the 
> latter does not cause any interference and should solves some issues.

For the record, the latter creates a small race condition: an external
program could be launched before FD_CLOEXEC is set on an fd.
  
Deti Fliegl Dec. 10, 2007, 9:23 p.m. UTC | #4
Anssi Hannula wrote:
> Deti Fliegl wrote:
>> Klaus Schmidinger wrote:
>>> Doesn't SystemExec() (see tools.c) take care of this?
>> Yes you are right - it takes care internally but not for plugins like 
>> dvdswitch etc. In order to fix this problem you could patch every single 
>> plugin or just set the right file descriptor flag once. I think the 
>> latter does not cause any interference and should solves some issues.
> 
> For the record, the latter creates a small race condition: an external
> program could be launched before FD_CLOEXEC is set on an fd.
In VDR all file descriptors seem to be allocated at startup. IMHO it is 
not very likely that a race condition could happen.

Deti
  
Deti Fliegl Dec. 10, 2007, 9:30 p.m. UTC | #5
Deti Fliegl wrote:
> Anssi Hannula wrote:
>> Deti Fliegl wrote:
>>> Klaus Schmidinger wrote:
>>>> Doesn't SystemExec() (see tools.c) take care of this?
>>> Yes you are right - it takes care internally but not for plugins like 
>>> dvdswitch etc. In order to fix this problem you could patch every single 
>>> plugin or just set the right file descriptor flag once. I think the 
>>> latter does not cause any interference and should solves some issues.
>> For the record, the latter creates a small race condition: an external
>> program could be launched before FD_CLOEXEC is set on an fd.
> In VDR all file descriptors seem to be allocated at startup. IMHO it is 
> not very likely that a race condition could happen.
... except on dvr devices, I know.

Deti
  
Klaus Schmidinger Dec. 10, 2007, 9:49 p.m. UTC | #6
On 12/10/07 21:07, Deti Fliegl wrote:
> Klaus Schmidinger wrote:
>> Doesn't SystemExec() (see tools.c) take care of this?
> Yes you are right - it takes care internally but not for plugins like 
> dvdswitch etc. In order to fix this problem you could patch every single 
> plugin or just set the right file descriptor flag once. I think the 
> latter does not cause any interference and should solves some issues.

Plugins can call SystemExec() just as well when the want to execute
an external program.

IMHO it is no feasible solution to expect every file handle to
be opened with FD_CLOEXEC. Even if VDR itself would do this, there
could still be plugins that don't.

I'd say if some plugin wants to run an external program, it needs
to take care by itself that all unneeded file handles are closed.
That's what SystemExec() is for.

Klaus
  
Darren Salt Dec. 10, 2007, 10:59 p.m. UTC | #7
I demand that Deti Fliegl may or may not have written...

> Anssi Hannula wrote:
>> Deti Fliegl wrote:
>>> Klaus Schmidinger wrote:
>>>> Doesn't SystemExec() (see tools.c) take care of this?
>>> Yes you are right - it takes care internally but not for plugins like 
>>> dvdswitch etc. In order to fix this problem you could patch every single 
>>> plugin or just set the right file descriptor flag once. I think the 
>>> latter does not cause any interference and should solves some issues.
>> For the record, the latter creates a small race condition: an external
>> program could be launched before FD_CLOEXEC is set on an fd.

> In VDR all file descriptors seem to be allocated at startup. IMHO it is 
> not very likely that a race condition could happen.

>From open(2):

  O_CLOEXEC (Since Linux 2.6.23)
        Enable the close-on-exec  flag  for  the  new  file  descriptor.
        Specifying  this  flag  permits a program to avoid an additional
        fcntl(2) F_SETFD operation to set the  FD_CLOEXEC  flag.   Addi-
        tionally,  use  of  this flag is essential in some multithreaded
        programs since using a separate fcntl(2)  F_SETFD  operation  to
        set  the  FD_CLOEXEC  flag does not suffice to avoid race condi-
        tions where one thread opens a file descriptor at the same  time
        as another thread does a fork(2) plus execve(2).

No use *now*, I know - too many people not yet using a new-enough kernel...
  
Deti Fliegl Dec. 11, 2007, 7:39 a.m. UTC | #8
Klaus Schmidinger wrote:
> Plugins can call SystemExec() just as well when the want to execute
> an external program.
Yes, but would you point every single developer on this issue?

> IMHO it is no feasible solution to expect every file handle to
> be opened with FD_CLOEXEC. Even if VDR itself would do this, there
> could still be plugins that don't.
It is enough to let VDR set this flag on its own file handles - the main 
problem is that while an external script/program is running and because 
of inherited DVB handles
- zapping is blocked
- a restart of VDR is impossible.

> I'd say if some plugin wants to run an external program, it needs
> to take care by itself that all unneeded file handles are closed.
> That's what SystemExec() is for.
Whatever - if you don't see the point I cannot help.

Deti
  
Klaus Schmidinger Dec. 11, 2007, 8:21 a.m. UTC | #9
On 12/11/2007 08:39 AM, Deti Fliegl wrote:
> Klaus Schmidinger wrote:
>> Plugins can call SystemExec() just as well when the want to execute
>> an external program.
> Yes, but would you point every single developer on this issue?
> 
>> IMHO it is no feasible solution to expect every file handle to
>> be opened with FD_CLOEXEC. Even if VDR itself would do this, there
>> could still be plugins that don't.
> It is enough to let VDR set this flag on its own file handles - the main 
> problem is that while an external script/program is running and because 
> of inherited DVB handles
> - zapping is blocked
> - a restart of VDR is impossible.
> 
>> I'd say if some plugin wants to run an external program, it needs
>> to take care by itself that all unneeded file handles are closed.
>> That's what SystemExec() is for.
> Whatever - if you don't see the point I cannot help.

Why is this suddenly such a big problem?
If a plugin wants to run an external program it can simply
use SystemExec().

Besides, as Darren Salt pointed out, this flag is apparently
only available in the very latest kernel version, which I'm
pretty sure is not that widely in use yet.

Klaus
  
Darren Salt Dec. 11, 2007, 12:09 p.m. UTC | #10
I demand that Klaus Schmidinger may or may not have written...

[snip; FD_CLOEXEC]
> Why is this suddenly such a big problem?
> If a plugin wants to run an external program it can simply use
> SystemExec().

> Besides, as Darren Salt pointed out, this flag is apparently only available
> in the very latest kernel version, which I'm pretty sure is not that widely
> in use yet.

Er, no: FD_CLOEXEC has been available for years (see fcntl(2)). You're
confusing it with O_CLOEXEC, which is new.
  
Klaus Schmidinger Dec. 11, 2007, 12:16 p.m. UTC | #11
On 12/11/2007 01:09 PM, Darren Salt wrote:
> I demand that Klaus Schmidinger may or may not have written...
> 
> [snip; FD_CLOEXEC]
>> Why is this suddenly such a big problem?
>> If a plugin wants to run an external program it can simply use
>> SystemExec().
> 
>> Besides, as Darren Salt pointed out, this flag is apparently only available
>> in the very latest kernel version, which I'm pretty sure is not that widely
>> in use yet.
> 
> Er, no: FD_CLOEXEC has been available for years (see fcntl(2)). You're
> confusing it with O_CLOEXEC, which is new.

Oh, sorry.

Nevertheless, SystemExec() (or something similar) is what a plugin
should use when launching an external program.

Klaus
  

Patch

--- dvbdevice.c~        2007-12-10 15:19:51.116943936 +0100
+++ dvbdevice.c 2007-12-10 15:19:51.120944682 +0100
@@ -63,6 +63,7 @@ 
    int fd = open(FileName, Mode);
    if (fd < 0 && ReportError)
       LOG_ERROR_STR(FileName);
+  fcntl(fd, F_SETFD, FD_CLOEXEC);
    return fd;
  }

--- svdrp.c~    2007-12-10 15:20:12.476929058 +0100
+++ svdrp.c     2007-12-10 15:20:12.480929804 +0100
@@ -91,7 +91,7 @@ 
          LOG_ERROR;
          return false;
          }
-     oldflags |= O_NONBLOCK;
+     oldflags |= O_NONBLOCK|FD_CLOEXEC;
       if (fcntl(sock, F_SETFL, oldflags) < 0) {
          LOG_ERROR;
          return false;