2

Problem

I need to find what process is running on a particular window in screen (in a reasonable amount of time).

Scenario

I need to use the Session Name and Window Title to find the process running therein. It needs to not be super slow.

Also potentially noteworthy: I'm using byobu as a wrapper for screen.

What I've tried

  • Searching the internet
  • Reading the screen man page (not for the faint of heart). (Okay, I didn't read all of it, but I did most of the relevant sections and searched it very thoroughly for anything that might be useful.)
    • What I learned:
      • The only way to gain the information I might need from screen (by calling screen) is through the use of it's command line flags.
        • -Q will allow you to query certain commands, but none of these provided everything that I need. The one that I'm using returns the Window number.
          • number - what I'm using to get the window number
          • windowlist - allows you to get a custom-formatted string of information, but the session PID is not one of the things you can ask for
          • echo, info, lastmsg, select, time, title are the other ones and none of these looked useful
        • -ls lists the active sessions. It prepends the PID to the session name, so this is how I'm currently getting the session PID.
      • Once I have the PID of the shell running in a specific Window, I can check its environment variables for WINDOW. This variable is set to the window number. This is what I'm using to match the process to the window.
      • There is no single command that will allow me to return the session PID and a map of the window titles to window numbers. Also, I could find no way to deterministically find the session id and window title to window number map outside of calling screen.
  • Trial and error / digging through environment variables
  • Writing a script

My Script

I wrote a script that seems to successfully solve the problem, but it takes a little over 0.75 seconds to run. This is far too long for what I need done, but more importantly, far too long when a server is waiting for its completion to send the response to an HTML request.

Here is the script:

#!/bin/bash
# Accept the name of a GNU/screen session & window and return the process running in its shell
SessionName=$1
TabName=$2

# ====== Averages 0.370 seconds ======
# This finds the window number given the session name and window title
# The screen command queries screen for the window number of the specified 
# window title in the specified session.
# Example output of screen command: 1 (Main)
# Example output after cut command: 1
TargetTabNum=$(screen -S $SessionName -p $TabName -Q number | cut -d ' ' -f1)

# ====== Averages 0.370 seconds ======
# This finds the session PID given the session name.
# The screen command prints the list of session IDs
# Example output of screen command:
#     There is a screen on:
#             29676.byobu     (12/09/2019 10:23:19 AM)        (Attached)
#     1 Socket in /run/screen/S-{username here}.
# Example output after sed command: 29676
SessionPID=$(screen -ls | sed -n "s/\s*\([0-9]*\)\.$SessionName\t.*/\1/p")

# ====== The rest averages 0.025 seconds ======
# This gets all the processes that have the session as a parent,
# loops through them checking the WINDOW environment variable for
# each until it finds the one that matches the window title, and
# then finds the process with that process as a parent and prints its
# command and arguments (or null if there are no matching processes)
ProcessArray=( $(ps -o pid --ppid $SessionPID --no-headers) )
for i in "${ProcessArray[@]}"
do
    ProcTabNum=$(tr '\0' '\n' < /proc/$i/environ | grep ^WINDOW= | cut -d '=' -f2)
    if [ ! -z "$ProcTabNum" ] && [ "$TargetTabNum" -eq "$ProcTabNum" ]; then
        ProcInTab=$(ps -o cmd --ppid $i --no-headers)
        if [[ $? -eq 1 ]]; then
            ProcInTab=NULL
        fi
        echo $ProcInTab
        exit 0
    fi
done
echo "Couldn't find the specified Tab: $TabName" >&2
exit 1

As you can see, the problem commands are the two screen commands. I can get rid of one of them by searching for a screen process launched with the session name, but this feels kind of flaky and I'm not sure it would be deterministic:

SessionPID=$(ps -eo pid,command --no-headers | grep -i "[0-9]* screen.*\-s $SessionName " | grep -v grep | cut -d ' ' -f1)

Goal

I would like to have a fast, reliable way to determine the process currently running in a specific screen window. I feel like I'm just missing something, so I would be very grateful if one of you spot it!

(I'm still fairly knew to StackExchange, so any feedback on my question is welcome!)

  • bash is actually slow compared to other shells. Try to write in POSIX sh and run it with a faster shell like ksh. Alternatively you can write it in C using int system(const char *command); or another fast compiled language. Those should speed it up as long as the screen command itself is not the actual bottleneck. That seems to be the case, but I would test the screen command on its own (no piping) before concluding this. If the screen command is the botleneck then you can try to use an screen API if it exists, or find a feature of the process that indicates is running in screen. – jsb Dec 10 '19 at 19:47
  • I just tested the screen commands by themselves and they still average around 370ms. I hadn't considered just checking all the shell processes for the relevant environment variables. It seems kind of brute force, but may actually still be faster. (Also, I'm definitely being lazy using bash.. I've had it on my to-do list to learn the bash-isms and avoid them, but haven't gotten to it :) ) – UrsineRaven Dec 10 '19 at 19:57
  • 1
    Feedback on the question: this is a fairly well-written question, but a couple tips that would help it be even better and more likely to get an answer: if you include the screen commands that you did find that are closest to what you wanted, and say what is wrong with them, and/or mention the section of the documentation you read through that was most relevant and anything useful you found in there, I think it would help. (This isn't a demanded part of using this site, it's just my personal opinion. I use screen a lot myself but I don't feel like reverse engineering your script.) :) – Wildcard Dec 10 '19 at 20:34
  • 1
    Another potential solution is to run the script periodically and cache the result, then use the cached answer. This would likely be the easiest if it is valid for your use case. Specifically to accomplish this you can run the command on a cronjob or you can run the script in the background withing an infinite while loop coupled with a sleep command. – jsb Dec 10 '19 at 20:41
  • 1
    @Wildcard, Thanks! I've updated my post to give more useful comments in the script (to hopefully prevent the need to reverse engineer it), and I also added more info on what I learned from the man pages. – UrsineRaven Dec 10 '19 at 22:10
0

The only way to gain the information I might need from screen (by calling screen) is through the use of it's command line flags.

Nooo. You are looking at screen the wrong way. screen is a virtual pseudo-terminal (hence it hooks to /dev/pts/*), and can be interacted with in a hundred different ways. The moment you run screen you end up with a socket visible in netstat that you can interact with, and it is how you manage this socket communication will determine how long a given exchange takes. The best way to make this communication as fast as possible is to take advantage of things that are already being reported back, instead of sending a request and waiting for a reply.

In your case, the issue can be solved by running w as follows:

shark@tank:~$ sudo w
 20:39:11 up 2 days,  1:24,  2 users,  load average: 0.64, 0.41, 0.44
USER     TTY      FROM             LOGIN@   IDLE       WHAT
shark    tty7     :0               Sun19    2days      /usr/lib/xorg/Xorg :0 *
fish     pts/1    :pts/6:S.0       20:31    7:42       htop

as you can see, the second user fish operating on screen socket connected to tty on /dev/pts/1 which belongs to user shark as can be seen by running ls -la /dev/pts/ and that socket is listening to /dev/pts/6 and there is an htop running there.

How can we double-check that this is in fact an actual socket connection? By running:

sudo file /run/screen/*/*
/run/screen/S-fish/98633.pts-6.tank: socket

and the number that we see corresponds to

ps aux | grep 98633 -B 1 -A 1 
fish      98632  0.0  0.0   6732  2964 pts/6   S+   20:31   0:00 screen
fish      98633  0.0  0.0   7032  2576 ?        Ss   20:31   0:00 SCREEN
fish      98634  0.0  0.0   8660  5400 pts/1   Ss+  20:31   0:00 /bin/bash

The command polls this info from /proc and /var/run/utmp and the hit is minimal, as can be seen by running:

$ sudo usr/bin/time -f "\t%E real\n\t%U user\n\t%S sys\n" w > /dev/null
    0:00.01 real
    0.00 user
    0.01 sys
|improve this answer|||||
  • So, this is definitely the kind of thing I was looking for, but there are still a couple of issues that I need to address before I'm able to accept it as an answer to my question: 1. How do I associate the running command with the screen window title? 2. Running w when the screen session is detatched doesn't return anything. Will I be able to work around this (my screen session starts on boot)? – UrsineRaven Dec 11 '19 at 15:40
  • Yes, the answer to both is coming right up. Ill update my answer accordingly. – BarBar1234 Dec 11 '19 at 18:22
  • Cool, thanks. It would definitely be incredibly helpful to me! I'm using my script in more places than I anticipated, so it's slowing a lot down. – UrsineRaven Dec 17 '19 at 14:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.