Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 235 lines (177 sloc) 9.466 kb
351e829 @ChrisJohnsen README.md
authored
1 # Purpose of These Programs
2
3 The programs in this repository were created to diagnose and enable
4 reliable access to the Mac OS X pasteboard for programs run under
5 [*tmux*][1] and unmodified versions of [*screen*][2].
6
7 [1]: http://tmux.sourceforge.net/
8 [2]: http://www.gnu.org/software/screen/
9
10 # Mac OS X Pasteboard Access Under *tmux* And *screen*
11
12 ## The Problem
13
14 The most commonly reported broken behavior is that the [*pbcopy* and
15 *pbpaste*][3] command-line programs that come with Mac OS X fail to
16 function properly when run under *tmux* and (sometimes) under
17 “unpatched” *screen*.
18
19 Apple has patched their builds of *screen* (included with Mac OS X)
20 to fix the problem; the [*screen* “port”][4] in the [MacPorts][5]
21 system has [adopted][6] Apple’s *screen* patches.
22
23 Their *screen* patch allows (for example) the user to create
24 a *screen* session under a normal GUI login session and access the
25 pasteboard (inside the *screen* session) anytime that user is logged
26 into the GUI. Programs that are run in a session of “unpatched” *screen* will
27 only encounter the problem when the *screen* session outlives its
28 parent Mac OS X login session (e.g. a normal GUI login or an SSH
29 login).
30
31 Third-party programs (run under *tmux* or unpatched *screen*) are
32 also affected (e.g. non-GUI builds of [Vim][7] [7.3][8] can access
33 the pasteboard when compiled with the `+clipboard` feature).
34
35 [3]: http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/pbcopy.1.html
36 [4]: https://trac.macports.org/browser/trunk/dports/sysutils/screen/Portfile
37 [5]: http://www.macports.org/
38 [6]: https://trac.macports.org/browser/trunk/dports/sysutils/screen/files/patch-screen.c
39 [7]: http://www.vim.org/
40 [8]: http://vimhelp.appspot.com/version7.txt.html#added-7.3
41
42 ## Why Pasteboard Breaks
43
44 ### Access to the Mac OS X Pasteboard Service
45
46 The pasteboard service in Mac OS X is registered in a "bootstrap
47 namespace" (see Apple’s [TN2083][9]). The namespaces exist in
48 a hierarchy: “higher” namespaces include access to “lower”
49 namespaces. A process in a lower namespace can not access higher
50 namespaces. So, all process can access the lowest, “root” bootstrap
51 namespace, but only processes in a higher namespace can access that
52 namespace. Processes created as a part of a Mac OS X login session
53 are automatically included in the user’s “per-user” bootstrap
54 namespace. The pasteboard service is only available to processes in
55 the per-user bootstrap namespace.
56
57 [9]: http://developer.apple.com/library/mac/#technotes/tn2083/_index.html
58
59 ### Interaction with *tmux*
60
61 *tmux* uses the *daemon(3)* library function when starting its
62 server process. In Mac OS X 10.5, Apple changed *daemon(3)* to move
63 the resulting process from its original bootstrap namespace to the
64 root bootstrap namespace. This means that the *tmux* server, and its
65 children, will automatically and uncontrollably lose access to what
66 would have been their original bootstrap namespace (i.e. the one
67 that has access to the pasteboard service).
68
69 ### Interaction with Unpatched *screen*
70
71 The situation with *screen* is a bit different since it does not use
72 *daemon(3)*. Unpatched *screen*, and its children, only lose access
73 to the per-user bootstrap namespace when its parent login session
74 exits.
75
76 ## Solution Space
77
78 Apple (and MacPorts) have already handled *screen*. Apple prevents
79 *screen* from losing access to the per-user bootstrap namespace by
80 “migrating to [the] background session” ([in 10.5][10] using
81 *_vprocmgr_move_subset_to_user*) or “detach[ing] from console”
82 ([in 10.6][11] using *_vprocmgr_detach_from_console*). For the
83 purposes of *screen*, both of these let the *screen* process access
84 the per-user bootstrap namespace even after the processes initial
85 Mac OS X login session has ended.
86
87 [10]: http://www.opensource.apple.com/source/screen/screen-12/patches/screen.c.diff
88 [11]: http://www.opensource.apple.com/source/screen/screen-19/screen/screen.c
89
90 ### Patch *tmux*?
91
92 Ideally, we could port Apple’s patch to *tmux*. Practically, there
93 are problems with a direct port.
94
95 The undocumented, private function used in Apple’s 10.6 patch,
96 *_vprocmgr_detach_from_console*, is not effective if called before
97 *daemon(3)* (since it forcibly moves the process to the root
98 bootstrap namespace); if called after *daemon(3)*, it just returns
99 an error.
100
101 The undocumented, private function used in Apple’s 10.5 patch,
102 *_vprocmgr_move_subset_to_user*, is also available in 10.6 (though
103 an extra parameter has been added to it in 10.6). Again, there is no
104 point in calling it before *daemon(3)*, but it is effective if
105 called after *daemon(3)*.
106
107 The functionality of *_vprocmgr_move_subset_to_user* seems to be
108 a sort of superset of that of *_vprocmgr_detach_from_console* in
109 that both move to the `"Background"` session, but the former does
110 some extra work that can attach to a user namespace even if the
111 process has been previously moved out of it.
112
113 So, another approach that works is to call either the private
114 function after invoking a custom *daemon* that does not forcibly
115 move its resulting process to the root bootstrap namespace (*tmux*
116 even already has one).
117
118 The fact that the signature of *_vprocmgr_move_subset_to_user*
119 changed between 10.5 and 10.6 is a strong indication that Apple sees
120 these functions as part of a private API that is liable to change or
121 become available in any (major?) release. It seems inappropriate to
122 ask upstream *tmux* to incorporate calls to functions such as these.
123 It might be appropriate for MacPorts to apply a patch to its port
124 though.
125
126 ### Use a “Reattaching” Wrapper Program
127
128 While it would be nice to have the *tmux* server itself reattached
129 to the per-user bootstrap namespace, it is probably enough to
130 selectively reattach just some of its children. A small wrapper
131 could do the work of reattaching to the appropriate namespace and
132 then execing some other program that will (eventually) need
133 access to the per-user namespace.
134
135 Such a wrapper could be used to run *pbcopy*, *pbpaste*, *vim*, et
136 cetera. This would require the user to remember to use the wrapper
137 (or write scripts/shell-functions/aliases to always do it; or notice
138 it fail then re-run it under the wrapper).
139
140 A more automated solution that probably covers most of the problem
141 scenarios for most users would be to set *tmux*’s `default-command`
142 option so that new windows start shells via the wrapper by default.
143 The major area this would not cover would be commands given directly
144 to `new-session` and `new-window` (there are some other commands
145 that start new children, but those are the major ones).
146
147 # Some New Programs For Your Consideration
148
149 ## The Wrapper Program
150
151 The *reattach-to-user-namespace* program implements the “wrapper”
152 solution described above.
153
154 reattach-to-user-namespace program args...
155
156 Its `-l` option causes it to rewrite the execed program’s `argv[0]` to
157 start with a dash (`-`). Most shells take this as a signal that they should
158 start as “login” shells.
159
160 exec reattach-to-user-namespace -l "$SHELL"
161
162 In `.tmux.conf`:
163
164 set-option -g default-command "reattach-to-user-namespace -l zsh"
165
166 ## The Diagnostic Program
167
168 The *test* program was created to easily examine the effects and
169 interactions of some of the “functions of interest” (primarily
170 *daemon(3)*, and the private “vproc” functions).
171
172 Its arguments are interpreted as instructions to call various
173 functions and/or display some result.
174
175 Examples:
176
177 Emulate calling *pbpaste* under plain *tmux*:
178
179 ./test daemon=sys system=pbpaste
180
181 Emulate a *tmux* patch that would automatically reattach to the user
182 namespace (also equivalent to using the wrapper program under an
183 unpatched *tmux*):
184
185 ./test daemon=sys move-to-user=10.6 system=pbpaste
186
187 Emulate a *tmux* patch that uses compat/daemon.c and “detaches from
188 the console”:
189
190 ./test daemon=ours deatch system=pbpaste
191
192 Demonstrate revocation of access to the per-user bootstrap namespace
193 when the Mac OS X login session ends:
194
195 # while logged into the GUI
196
197 # login session ends before pbpaste happens: failure
198 cp /dev/null /tmp/f &&
199 ssh localhost `pwd`/test \
200 daemon=ours \
201 msg=sleeping... sleep=1 msg='done\ sleeping' \
202 system=pbpaste 2\> /tmp/f &&
203 { cat /tmp/f; tail -f /tmp/f; }
204
205 # pbpaste happens before login session ends: success
206 cp /dev/null /tmp/f &&
207 ssh localhost `pwd`/test \
208 daemon=ours \
209 msg=sleeping... msg='done\ sleeping' \
210 system=pbpaste 2\> /tmp/f \; sleep 1 &&
211 { cat /tmp/f; tail -f /tmp/f; }
212
213 Test workarounds to prevent the above end-of-login revocation:
214
215 # while logged into the GUI
216
217 # emulate tmux patched to move to the user namespace
218 # or, equivalently, unpatched *tmux* and wrapper
219 cp /dev/null /tmp/f &&
220 ssh localhost `pwd`/test \
221 daemon=sys \
222 move-to-user=10.6 \
223 msg=sleeping... sleep=1 msg='done\ sleeping' \
224 system=pbpaste 2\> /tmp/f &&
225 { cat /tmp/f; tail -f /tmp/f; }
226
227 # emuate tmux patched to use compat/daemon + detach
228 cp /dev/null /tmp/f &&
229 ssh localhost `pwd`/test \
230 daemon=ours \
231 detach \
232 msg=sleeping... sleep=1 msg='done\ sleeping' \
233 system=pbpaste 2\> /tmp/f &&
234 { cat /tmp/f; tail -f /tmp/f; }
Something went wrong with that request. Please try again.