Porting tmux to z/OS

Posted by Igor Todorovski on March 12, 2024

I’m excited to share my journey of porting tmux, a popular terminal multiplexer, to z/OS! This project builds upon my previous experience successfully porting GNU screen and making it available under the z/OS Open Tools umbrella. Following the success of GNU screen, the z/OS community expressed a strong desire for tmux in a recent poll, making it one of the most requested tools for z/OS.

So what is Tmux?

tmux is a terminal multiplexer. It lets you switch easily between several programs in one terminal, detach them (they keep running in the background) and reattach them to a different terminal.

tmux.cpp Tmux running on z/OS

Tmux has many advantages over GNU Screen, including mouse integration, supporting terminal splitting into both vertical and horizontal panes, and generally many more configuration options.

Why Tmux on z/OS?

For z/OS users, particularly system administrators and developers, efficient management of multiple shell sessions is critical for efficiency.

Tmux provides the following features:

  • Multi-pane Workspaces: Organize your terminal by splitting the window into panes, each running an independent program. This is great when you’re building and editing at the same time like me!
  • Detachable Sessions: Leave your work running on z/OS even after disconnecting. Reattach seamlessly from any terminal later. This means you can finish working, sleep and then resume where you left off the following morning.
  • Enhanced Workflow: Easily switch between tasks, monitor multiple processes concurrently, and maintain a clutter-free terminal environment. This is one vital for me since I sometimes work on 5+ different tasks during the day.

Why not run tmux on Linux? You can certainly run tmux remotely from a Linux machine to manage your screen sessions, but being able to run it locally on z/OS can reduce latency and is especially good for air-gapped environments.

How do I install it?

Grab it from z/OS Open Tools using the zopen package manager:

1
zopen install tmux -y

Then run it:

1
tmux

To learn more about the shortcuts, I recommend you read this page.

Porting Tmux to z/OS

Porting tmux to z/OS presented unique challenges due to the underlying differences between z/OS and traditional Unix-like systems. Let’s explore some key areas and the solutions implemented:

  • Missing dependencies: Tmux in itself does not have that many library dependencies. It depends on libevent and ncurses. Fortunately ncurses has already been ported for other projects like Vim and Less so I only had to focus on getting libevent ported.

  • I/O Handling: Standard Unix I/O operations might require special handling on z/OS. We leveraged functionalities provided by ZOSLIB (introduced in the previous blog post) to simplify I/O handling.

Conquering Dependency Challenges:

By leveraging the zopen build framework, I was able to port libevent fairly easily. The port is available at https://github.com/zopencommunity/libeventport. Since libevent is a library, I had to make sure I exposed it as a library when I was adding it as a dependency for Tmux with the following additions to its buildenv file:

1
2
3
4
  export ZOPEN_EXTRA_CFLAGS="\${ZOPEN_EXTRA_CFLAGS} -I\$PWD/include"
  export ZOPEN_EXTRA_CXXFLAGS="\${ZOPEN_EXTRA_CXXFLAGS} -I\$PWD/include"
  export ZOPEN_EXTRA_LDFLAGS="\${ZOPEN_EXTRA_LDFLAGS} -L\$PWD/lib"
  export ZOPEN_EXTRA_LIBS="\${ZOPEN_EXTRA_LIBS} -levent"

For a detailed look on how I built it, check out the entire buildenv configuration here https://github.com/zopencommunity/libeventport/blob/main/buildenv.

Now I was ready to build Tmux.

Building tmux

tmux’s buildenv configuration was relatively simple. We added the known dependencies (libevent, ncurses, make, and a few others) and chose the Clang compiler for building. To build it, I used the zopen build framework.

1
zopen build -v

Unfortunately, tmux had a number of issues at build and runtime and I’ll go through a few of them.

Overcoming the forkpty hurdle

forkpty is needed by tmux but is absent on z/OS, so I employed an alternative approach utilizing existing C LE apis. This involved creating a pseudo terminal by opening specific devices and manipulating terminal attributes to simulate a terminal environment for tmux sessions. You can find the patch here.

Testing and Debugging the Build

After testing the build of tmux, I was presented with garbled output immediately! To understand the issue, I leveraged tmux’s -v option for logging. After further investigation, I realized that the content written to the screen was actually readable and in ASCII format but it wasn’t being converted back to IBM-1047 for the terminal.

The culprit? writev was not respecting file tags and auto-conversion. This meant the text going to standard output wasn’t being automatically converted to the terminal’s CCSID (IBM-1047).

To resolve this, I modified the libevent code to use write instead of writev. This meant I had to create a patch for libevent here.

This enabled tmux to render text correctly, but I wasn’t done yet. The newline characters didn’t seem to register. This resulted in the prompt line being printed without a newline!

After comparing the stty output from a working terminal to that of the tmux created terminal, I noticed that the flags OPOST and ONLCR were missing. Adding them back in the tmux C code fixed it!

The last major issue was with regards to socketpair. Tmux uses socketpairs instead of pipes for interprocess communication, but socketpairs (as of z/OS 2.4) do not support auto-conversion. Changing the code to use pipes instead of socketpairs fixed this issue!

There’s still a lingering issue when using /bin/sh as the default shell. So for now I set my tmux default shell to bash to overcome it in my $HOME/.tmux.conf tmux config file as follows:

1
2
3
vim $HOME/.tmux.conf
# Now add the following line
set-option -g default-shell $ZOPEN_PKGINSTALL/bash/bash/bin/bash

For a more involved tmux.config file, you can check mine out here.

Next Steps

There’s still a few issues remaining, but for the most part tmux is pretty much functional. Here’s what remains:

  • Resolve issues when using /bin/sh as the default shell
  • Migrating the forkpty and writev changes to the common zoslib library

Thank you

Thanks for reading and thanks to Mike Fulton, Haritha D and the z/OS Open Tools contributors!