{"id":13395681,"url":"https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard","last_synced_at":"2025-03-13T22:30:42.222Z","repository":{"id":1419917,"uuid":"1528639","full_name":"ChrisJohnsen/tmux-MacOSX-pasteboard","owner":"ChrisJohnsen","description":"Notes and workarounds for accessing the Mac OS X pasteboard in tmux sessions. Note: The pu branch (“Proposed Updates”) may be rewound without notice.","archived":false,"fork":false,"pushed_at":"2020-10-10T04:22:59.000Z","size":87,"stargazers_count":2379,"open_issues_count":14,"forks_count":84,"subscribers_count":42,"default_branch":"master","last_synced_at":"2024-10-29T15:34:11.796Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ChrisJohnsen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-03-26T08:09:44.000Z","updated_at":"2024-10-28T23:54:56.000Z","dependencies_parsed_at":"2022-07-17T10:16:31.652Z","dependency_job_id":null,"html_url":"https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisJohnsen%2Ftmux-MacOSX-pasteboard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisJohnsen%2Ftmux-MacOSX-pasteboard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisJohnsen%2Ftmux-MacOSX-pasteboard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisJohnsen%2Ftmux-MacOSX-pasteboard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ChrisJohnsen","download_url":"https://codeload.github.com/ChrisJohnsen/tmux-MacOSX-pasteboard/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243493130,"owners_count":20299601,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T18:00:27.937Z","updated_at":"2025-03-13T22:30:41.946Z","avatar_url":"https://github.com/ChrisJohnsen.png","language":"C","funding_links":[],"categories":["Set up tmux","Terminal","C"],"sub_categories":["Oh My Tmux!","Packages"],"readme":"# Quick Summary\n\n* Using the Mac OS X programs *pbpaste* and *pbcopy* under old\n  versions of *tmux* may not work.  \n  Other services and unpatched builds of *screen* are also affected.\n\n* You may not even need the program from this repository!\n\n  *tmux* version 2.6 (2017 September) incorporated the functionality\n  of the wrapper program. If you are using a new enough *tmux*, you\n  may be able to skip using the wrapper in your *tmux*\n  configuration.\n\n* Certain undocumented, private API functions can fix the problem.\n\n* Because the functions are private, undocumented, and unstable (one\n  acquired an extra argument in 10.6), I think using a small wrapper\n  program might be better than patching *tmux*.\n\nThus, my wrapper-based workaround:\n\n1. Compile *reattach-to-user-namespace* from this repository.  \n   Make it available in your PATH (or use the absolute pathname in\n   the next step).\n\n        make reattach-to-user-namespace \u0026\u0026\n        cp reattach-to-user-namespace ~/bin\n\n   Alternatively, you can install it with [*MacPorts*][MacPorts] or\n   [*Homebrew*][Homebrew]:\n\n        port install tmux-pasteboard\n\n        brew install reattach-to-user-namespace\n\n[MacPorts]: http://www.macports.org\n[Homebrew]: http://brew.sh\n\n1. Configure *tmux* to use this wrapper program to start the shell\n   for each new window.\n\n    In `.tmux.conf`:\n\n        set-option -g default-command \"reattach-to-user-namespace -l zsh\"\n\n    See the file `Usage.md` for other configuration and usage\n    techniques (conditional use for cross-platform configurations,\n    fine-grained usage, etc.)\n\n1. Restart your *tmux* server (or start a new one, or just\n   reconfigure your existing one).\n\n    To kill your existing *tmux* server (and everything running\n    “inside” it!):\n\n        tmux kill-server\n\n1. Enjoy being able to use *pbpaste*, *pbcopy*, etc. in new shell\n   windows.\n\n# Purpose of These Programs\n\nThe programs in this repository were created to diagnose and enable\nreliable access to the Mac OS X pasteboard for programs run under\n[*tmux*][1] and unmodified versions of [*screen*][2].\n\nSee the “Beyond Pasteboard Access” section of the `Usage.md` file\nfor other programs that benefit from the wrapper.\n\n[1]: http://tmux.sourceforge.net/\n[2]: http://www.gnu.org/software/screen/\n\n# Mac OS X Pasteboard Access Under *tmux* And *screen*\n\n## The Problem\n\nThe most commonly reported broken behavior is that the [*pbcopy* and\n*pbpaste*][3] command-line programs that come with Mac OS X fail to\nfunction properly when run under *tmux* and (sometimes) under\n“unpatched” *screen*.\n\nApple has patched their builds of *screen* (included with Mac OS X)\nto fix the problem; the [*screen* “port”][4] in the [MacPorts][5]\nsystem has [adopted][6] Apple’s *screen* patches.\n\nTheir *screen* patch allows (for example) the user to create\na *screen* session under a normal GUI login session and access the\npasteboard (inside the *screen* session) anytime that user is logged\ninto the GUI. Programs that are run in a session of “unpatched” *screen* will\nonly encounter the problem when the *screen* session outlives its\nparent Mac OS X login session (e.g. a normal GUI login or an SSH\nlogin).\n\nThird-party programs (run under *tmux* or unpatched *screen*) are\nalso affected (e.g. non-GUI builds of [Vim][7] [7.3][8] can access\nthe pasteboard when compiled with the `+clipboard` feature).\n\n[3]: http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/pbcopy.1.html\n[4]: https://trac.macports.org/browser/trunk/dports/sysutils/screen/Portfile\n[5]: http://www.macports.org/\n[6]: https://trac.macports.org/browser/trunk/dports/sysutils/screen/files/patch-screen.c\n[7]: http://www.vim.org/\n[8]: http://vimhelp.appspot.com/version7.txt.html#added-7.3\n\n## Why Pasteboard Breaks\n\n### Access to the Mac OS X Pasteboard Service\n\nThe pasteboard service in Mac OS X is registered in a \"bootstrap\nnamespace\" (see Apple’s [TN2083][9]). The namespaces exist in\na hierarchy: “higher” namespaces include access to “lower”\nnamespaces. A process in a lower namespace can not access higher\nnamespaces. So, all process can access the lowest, “root” bootstrap\nnamespace, but only processes in a higher namespace can access that\nnamespace. Processes created as a part of a Mac OS X login session\nare automatically included in the user’s “per-user” bootstrap\nnamespace. The pasteboard service is only available to processes in\nthe per-user bootstrap namespace.\n\n[9]: http://developer.apple.com/library/mac/#technotes/tn2083/_index.html\n\n### Interaction with *tmux*\n\n*tmux* uses the *daemon(3)* library function when starting its\nserver process. In Mac OS X 10.5, Apple changed *daemon(3)* to move\nthe resulting process from its original bootstrap namespace to the\nroot bootstrap namespace. This means that the *tmux* server, and its\nchildren, will automatically and uncontrollably lose access to what\nwould have been their original bootstrap namespace (i.e. the one\nthat has access to the pasteboard service).\n\n### Interaction with Unpatched *screen*\n\nThe situation with *screen* is a bit different since it does not use\n*daemon(3)*. Unpatched *screen*, and its children, only lose access\nto the per-user bootstrap namespace when its parent login session\nexits.\n\n## Solution Space\n\nApple (and MacPorts) have already handled *screen*. Apple prevents\n*screen* from losing access to the per-user bootstrap namespace by\n“migrating to [the] background session” ([in 10.5][10] using\n*\\_vprocmgr\\_move\\_subset\\_to\\_user*) or “detach[ing] from console”\n([in 10.6][11] using *\\_vprocmgr\\_detach\\_from\\_console*). For the\npurposes of *screen*, both of these let the *screen* process access\nthe per-user bootstrap namespace even after the processes initial\nMac OS X login session has ended.\n\n[10]: http://www.opensource.apple.com/source/screen/screen-12/patches/screen.c.diff\n[11]: http://www.opensource.apple.com/source/screen/screen-19/screen/screen.c\n\n### Patch *tmux*?\n\nIdeally, we could port Apple’s patch to *tmux*. Practically, there\nare problems with a direct port.\n\nThe undocumented, private function used in Apple’s 10.6 patch,\n*\\_vprocmgr\\_detach\\_from\\_console*, is not effective if called before\n*daemon(3)* (since it forcibly moves the process to the root\nbootstrap namespace); if called after *daemon(3)*, it just returns\nan error.\n\nThe undocumented, private function used in Apple’s 10.5 patch,\n*\\_vprocmgr\\_move\\_subset\\_to\\_user*, is also available in 10.6 (though\nan extra parameter has been added to it in 10.6). Again, there is no\npoint in calling it before *daemon(3)*, but it is effective if\ncalled after *daemon(3)*.\n\nThe functionality of *\\_vprocmgr\\_move\\_subset\\_to\\_user* seems to be\na sort of superset of that of *\\_vprocmgr\\_detach\\_from\\_console* in\nthat both move to the `\"Background\"` session, but the former does\nsome extra work that can attach to a user namespace even if the\nprocess has been previously moved out of it.\n\nSo, another approach that works is to call either the private\nfunction after invoking a custom *daemon* that does not forcibly\nmove its resulting process to the root bootstrap namespace (*tmux*\neven already has one).\n\nThe fact that the signature of *\\_vprocmgr\\_move\\_subset\\_to\\_user*\nchanged between 10.5 and 10.6 is a strong indication that Apple sees\nthese functions as part of a private API that is liable to change or\nbecome available in any (major?) release. It seems inappropriate to\nask upstream *tmux* to incorporate calls to functions such as these.\nIt might be appropriate for MacPorts to apply a patch to its port\nthough.\n\n### Use a “Reattaching” Wrapper Program\n\nWhile it would be nice to have the *tmux* server itself reattached\nto the per-user bootstrap namespace, it is probably enough to\nselectively reattach just some of its children. A small wrapper\ncould do the work of reattaching to the appropriate namespace and\nthen execing some other program that will (eventually) need\naccess to the per-user namespace.\n\nSuch a wrapper could be used to run *pbcopy*, *pbpaste*, *vim*, et\ncetera. This would require the user to remember to use the wrapper\n(or write scripts/shell-functions/aliases to always do it; or notice\nit fail then re-run it under the wrapper).\n\nA more automated solution that probably covers most of the problem\nscenarios for most users would be to set *tmux*\u0026rsquo;s `default-command`\noption so that new windows start shells via the wrapper by default.\nThe major area this would not cover would be commands given directly\nto `new-session` and `new-window` (there are some other commands\nthat start new children, but those are the major ones).\n\n# Some New Programs For Your Consideration\n\n## The Wrapper Program\n\nThe *reattach-to-user-namespace* program implements the “wrapper”\nsolution described above.\n\n        reattach-to-user-namespace program args...\n\nIts `-l` option causes it to rewrite the execed program’s `argv[0]` to\nstart with a dash (`-`). Most shells take this as a signal that they should\nstart as “login” shells.\n\n        exec reattach-to-user-namespace -l \"$SHELL\"\n\nIn `.tmux.conf`:\n\n        set-option -g default-command \"reattach-to-user-namespace -l zsh\"\n\n## The Diagnostic Program\n\nThe *test* program was created to easily examine the effects and\ninteractions of some of the “functions of interest” (primarily\n*daemon(3)*, and the private “vproc” functions).\n\nIts arguments are interpreted as instructions to call various\nfunctions and/or display some result.\n\nExamples:\n\nEmulate calling *pbpaste* under plain *tmux*:\n\n        ./test daemon=sys system=pbpaste\n\nEmulate a *tmux* patch that would automatically reattach to the user\nnamespace (also equivalent to using the wrapper program under an\nunpatched *tmux*):\n\n        ./test daemon=sys move-to-user=10.6 system=pbpaste\n\nEmulate a *tmux* patch that uses compat/daemon.c and “detaches from\nthe console”:\n\n        ./test daemon=ours deatch system=pbpaste\n\nDemonstrate revocation of access to the per-user bootstrap namespace\nwhen the Mac OS X login session ends:\n\n        # while logged into the GUI\n    \n        # login session ends before pbpaste happens: failure\n        cp /dev/null /tmp/f \u0026\u0026\n        ssh localhost `pwd`/test \\\n          daemon=ours \\\n          msg=sleeping... sleep=1 msg='done\\ sleeping' \\\n          system=pbpaste 2\\\u003e /tmp/f \u0026\u0026\n        { cat /tmp/f; tail -f /tmp/f; }\n    \n        # pbpaste happens before login session ends: success\n        cp /dev/null /tmp/f \u0026\u0026\n        ssh localhost `pwd`/test \\\n          daemon=ours \\\n          msg=sleeping... msg='done\\ sleeping' \\\n          system=pbpaste 2\\\u003e /tmp/f \\; sleep 1 \u0026\u0026\n        { cat /tmp/f; tail -f /tmp/f; }\n\nTest workarounds to prevent the above end-of-login revocation:\n\n        # while logged into the GUI\n    \n        # emulate tmux patched to move to the user namespace\n        # or, equivalently, unpatched *tmux* and wrapper\n        cp /dev/null /tmp/f \u0026\u0026\n        ssh localhost `pwd`/test \\\n          daemon=sys \\\n          move-to-user=10.6 \\\n          msg=sleeping... sleep=1 msg='done\\ sleeping' \\\n          system=pbpaste 2\\\u003e /tmp/f \u0026\u0026\n        { cat /tmp/f; tail -f /tmp/f; }\n    \n        # emuate tmux patched to use compat/daemon + detach\n        cp /dev/null /tmp/f \u0026\u0026\n        ssh localhost `pwd`/test \\\n          daemon=ours \\\n          detach \\\n          msg=sleeping... sleep=1 msg='done\\ sleeping' \\\n          system=pbpaste 2\\\u003e /tmp/f \u0026\u0026\n        { cat /tmp/f; tail -f /tmp/f; }\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FChrisJohnsen%2Ftmux-MacOSX-pasteboard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FChrisJohnsen%2Ftmux-MacOSX-pasteboard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FChrisJohnsen%2Ftmux-MacOSX-pasteboard/lists"}