1 |
On Sat, Oct 29, 2005 at 07:49:49PM +0900, Jason Stubbs wrote: |
2 |
> This patch simplifies the moving around and closing of file descriptors when |
3 |
> setting up the pipe. The addition of fd_unused to the parameter list is in |
4 |
> preparation of a later patch. |
5 |
|
6 |
> +++ portage_exec.py 2005-10-29 19:07:21.000000000 +0900 |
7 |
> - # this may look ugly, but basically it moves file descriptors around to ensure no |
8 |
> - # handles that are needed are accidentally closed during the final dup2 calls. |
9 |
> - trg_fd=[] |
10 |
> - if type(fd_pipes)==types.DictType: |
11 |
> - src_fd=[] |
12 |
> - k=fd_pipes.keys() |
13 |
> - k.sort() |
14 |
> - for x in k: |
15 |
> - trg_fd.append(x) |
16 |
> - src_fd.append(fd_pipes[x]) |
17 |
> - for x in range(0,len(trg_fd)): |
18 |
> - if trg_fd[x] == src_fd[x]: |
19 |
> - continue |
20 |
> - if trg_fd[x] in src_fd[x+1:]: |
21 |
> - new=os.dup2(trg_fd[x],max(src_fd) + 1) |
22 |
> - os.close(trg_fd[x]) |
23 |
> - try: |
24 |
> - while True: |
25 |
> - src_fd[s.index(trg_fd[x])]=new |
26 |
> - except SystemExit, e: |
27 |
> - raise |
28 |
> - except: |
29 |
> - pass |
30 |
> - for x in range(0,len(trg_fd)): |
31 |
> - if trg_fd[x] != src_fd[x]: |
32 |
> - os.dup2(src_fd[x], trg_fd[x]) |
33 |
> - else: |
34 |
> - trg_fd=[0,1,2] |
35 |
|
36 |
Above *does* need reworking, knew that when I wrote it- but the reason |
37 |
it's so damn ugly is to cover against dup2 calls stomping handles that |
38 |
are needed. Must be a collect, then re-arrange algo, not single pass |
39 |
|
40 |
fd_pipes = [1:2, 2:1] |
41 |
|
42 |
> + for x in fd_pipes: |
43 |
> + if x != fd_pipes[x]: |
44 |
> + os.dup2(fd_pipes[x], x) |
45 |
> + if fd_pipes[x] not in fd_pipes and fd_pipes[x] not in fd_unused: |
46 |
> + fd_unused.append(fd_pipes[x]) |
47 |
> + for x in fd_unused: |
48 |
> + os.close(x) |
49 |
|
50 |
Replacement code will fail with the simple stdout <-> stderr switch. |
51 |
dup2 will set the actual fd 2 handle to fd 1, but *prior* to |
52 |
duplicating fd 2 to a throw away handle so it remains open, end result |
53 |
is that fd 2 is now fd 1, so the 2 -> 1 move does nothing. |
54 |
|
55 |
Code above will result in 1 == 1, 2 == 1, rather then 1 == 2, 2 == 1 |
56 |
|
57 |
Which is a regression, although existing code _probably_ won't choke |
58 |
on it- the protection I added made it robust so that it handles the |
59 |
corner case without tripping up any dev writing the code, rather then |
60 |
silently tieing the fd's together. |
61 |
|
62 |
|
63 |
fd_unused doesn't cut it either also, or at the very least introduces |
64 |
an ass biter hidden behaviour into spawn- consider |
65 |
|
66 |
fd_pipes={} |
67 |
|
68 |
Previous code *would* close all fd's, and execute the process as such. |
69 |
With fd_unused addition, it won't anymore- meaning at the very least a |
70 |
chunk of the regen code will need updating (closes stdin last I |
71 |
looked). Very least, all callers of spawn with fd_pipes != {0:0, 1:1, |
72 |
2:2} need verification, since the handles they were having explicitly |
73 |
closed prior, are now left open. |
74 |
|
75 |
With the fd_unused route, there is no way clean way to have all fd's |
76 |
closed. You don't know what fd's are open when you spawn (nor is |
77 |
there any way to know, beyond testing each potential fd, or overriding |
78 |
the file builtin); going the fd_unused route means fd's the process |
79 |
shouldn't have access to, will have access to. |
80 |
|
81 |
Mind you the fd_unused comments are about this patch and the one +2 |
82 |
down the set. |
83 |
|
84 |
So... regressions, both in fd_pipes handling, and in open handles |
85 |
leaking to the spawned processes |
86 |
|
87 |
The original code is ugly, and knowing a bit more this time around I'd |
88 |
write it a bit differently, that said the protections/behaviour |
89 |
originally in it should be left- it's more robust, and is expected |
90 |
now. |
91 |
~harring |