Fix ktmux_helper for tmux 1.8 with run_shell -b.
authorIain Patterson <me@iain.cx>
Wed, 18 Jul 2012 07:46:17 +0000 (08:46 +0100)
committerIain Patterson <me@iain.cx>
Wed, 2 Aug 2017 09:26:29 +0000 (10:26 +0100)
opt/bin/ktmux_helper

index e2b58a5..fb12672 100755 (executable)
@@ -2,11 +2,14 @@
 #
 # ktmux_helper: Run krenew in the background for tmux.
 # Usage: ktmux_helper [options]
-# Options: -I <path>   Specify path to kinit.
-#          -L <path>   Specify path to klist.
-#          -R <path>   Specify path to krenew.
-#          -T <path>   Specify path to tmux.
-# Notes: Doesn't handle multiple sessions properly.
+# Options: -I <path>      Specify path to kinit.
+#          -L <path>      Specify path to klist.
+#          -R <path>      Specify path to krenew.
+#          -T <path>      Specify path to tmux.
+#          -p <pid>       Specify tmux PID.
+#          -s <session>   Specify tmux session number.
+# Notes: The -p and -s flags are only used to identify instances when
+#        viewed in ps output.
 #
 
 use FindBin;
@@ -15,20 +18,38 @@ use POSIX ":sys_wait_h";
 
 my $PROG = $FindBin::Script;
 
-# Ensure tmux is our parent and find its PID.
-our $tmux_pid = &get_tmux_pid;
+# Remember args for re-exec.
+my @execargs;
+foreach my $arg (@ARGV) {
+  push @execargs, $arg;
+}
+
+my %opts;
+getopts('I:L:R:T:p:s:', \%opts);
+
+# Ensure tmux is our parent and find its PID if not already known.
+our $tmux_pid = &get_tmux_pid($opts{'p'});
 unless ($tmux_pid) {
   print STDERR "$PROG: Not a child of tmux!\n";
   exit 100;
 }
 
+# Find session if possible.
+our $tmux_session = &get_tmux_session($opts{'s'});
+
+# Fix the environment.
+$ENV{TMUX} =~ s/,\d+,-?\d+$/,$tmux_pid,$tmux_session/;
+
 # Ensure there isn't already a helper running for this tmux.
 my $tmux_helper = &get_tmux_helper;
 exit 0 if $tmux_helper;
 $tmux_helper = $$;
 
-my %opts;
-getopts('I:L:R:T:', \%opts);
+# Re-exec ourselves purely so the session and PID are set in the environment.
+my @args;
+push @args, "-p", $tmux_pid unless defined $opts{'p'};
+push @args, "-s", $tmux_session unless defined $opts{'s'};
+exec $0, @args, @execargs if @args;
 
 my $kinit = $opts{'I'} || "kinit";
 my $klist = $opts{'L'} || "klist";
@@ -64,21 +85,95 @@ LOOP: while (&ping_tmux) {
   else {
     exit 1 if &check_credentials;
     exec $krenew, "-K", "60";
-    print "$PROG: Can't run krenew: $!\n";
+    print STDERR "$PROG: Can't run krenew: $!\n";
     exit 111;
   }
 }
 
 sub get_tmux_pid {
-  my $pid = getppid;
+  # Set from command line?
+  my $pid = shift;
+
+  # Set from environment?
+  unless ($pid) {
+    if ($ENV{TMUX} =~ /,(\d+),[^,]+$/) {
+      $pid = $1;
+    }
+  }
+
+  my $ppid = getppid;
+
+  if ($pid) {
+    # Check it really is our parent.
+    if ($pid == $ppid) {
+      # Check that it's still running.
+      return $pid if kill -0, $pid;
+    }
+  }
+
+  return undef;
+
+  # Everything below probably can't happen so should be removed.
   my $cmd = `/bin/ps -o args= -p $pid`;
   return $pid if $cmd =~ /\btmux\b/;
   return undef;
 }
 
+sub get_tmux_session {
+  # Set from command line?
+  my $session = shift;
+
+  # Set from environment?
+  unless ($session) {
+    # The session identifier will be -1 if we were launched from run-shell
+    # because a run-shell command doesn't count as being part of a session.
+    if ($ENV{TMUX} =~ /,(-?\d+)$/) {
+      $session = $1;
+    }
+  }
+
+  return $session;
+}
+
+# Check that a given process was launched from our session.
+sub check_tmux_session {
+  my $pid = shift;
+
+  return undef unless defined $tmux_session;
+
+  my $cmd;
+  if ($^O eq "linux") {
+    $cmd = "xargs -0 -n 1 < /proc/$pid/environ";
+  }
+  elsif ($^O eq "solaris") {
+    $cmd = "pargs -Fe $pid";
+  }
+  else {
+    # Don't know how to check on this OS.
+    return undef;
+  }
+
+  return undef unless (open IN, "$cmd | ");
+
+  while (<IN>) {
+    chomp;
+    next unless /TMUX=.+,(-?\d+)/;
+    # Abort if the running helper doesn't know what session it's for.
+    if ($1 eq $tmux_session || $1 eq "-1") {
+      close IN;
+      return 1;
+    }
+  }
+
+  close IN;
+  return 0;
+}
+
 sub check_kinit_child {
   foreach my $pid (`/bin/ps -o ppid= -C kinit`) {
-    return 1 if $pid =~ /^\s*$tmux_pid\s*$/;
+    next unless $pid =~ /^\s*$tmux_pid\s*$/;
+    next if &check_tmux_session($pid);
+    return 1;
   }
   return 0;
 }
@@ -91,11 +186,11 @@ sub get_tmux_helper {
       s/[^\d]//g;
       next if $_ == $$;
       $pid = $_;
-      last;
+      return $pid if &check_tmux_session($pid);
     }
     close IN;
   }
-  return $pid;
+  return undef;
 }
 
 sub ping_tmux {