/bin/screen-server-backend

https://github.com/gitpan/Enbugger · Perl · 159 lines · 86 code · 24 blank · 49 comment · 14 complexity · 5ee76bed8974f809f3601b9872282a87 MD5 · raw file

  1. #!perl -w
  2. =head1 NAME
  3. screen-server-backend - TODO
  4. =head1 SYNOPSIS
  5. screen-server-backend [options]
  6. --help TODO
  7. --debug
  8. --name <screen name> TODO
  9. --daemonize (Default)
  10. --no-daemonize
  11. =head1 DESCRIPTION
  12. TODO
  13. =cut
  14. use strict;
  15. # Seemingly socat doesn't seem to transmit STDERR automatically so
  16. # re-route it myself.
  17. #
  18. *STDERR = *STDOUT;
  19. # Option handling
  20. #
  21. use Getopt::Long ();
  22. Getopt::Long::GetOptions(
  23. help => \&pod2usage,
  24. debug => \ my $debug,
  25. name => \ my $screen_name,
  26. 'daemonize!' => \ my $daemonize,
  27. )
  28. or pod2usage();
  29. # Daemonization by default
  30. #
  31. if ( $daemonize ) {
  32. fork && exit;
  33. fork && exit;
  34. umask 0;
  35. chdir '/';
  36. }
  37. # Automatically clean up zombie children
  38. #
  39. $SIG{CHLD} = 'IGNORE';
  40. require File::Temp;
  41. my ( $log_fh, $log_fn ) = File::Temp::tempfile();
  42. my $top_pid = $$;
  43. my $child_pid = fork;
  44. if ( ! defined $child_pid ) {
  45. # Oops, failure. Is there a fork bomb going on?
  46. #
  47. die "Can't fork: $!";
  48. }
  49. elsif ( 0 == $child_pid ) {
  50. # Double-fork so our parent can reap us immediately and the
  51. # exec() below will be reaped by init.
  52. #
  53. fork && exit;
  54. fork && exit;
  55. # Wait until either the parent socat has exited or it has logged
  56. # the right thing.
  57. #
  58. require Time::HiRes;
  59. Time::HiRes::sleep( 0.05 )
  60. while ! -s $log_fh
  61. || kill 0, $top_pid;
  62. # Read the PTY from the socat logfile and have /usr/bin/screen
  63. # start a window against it. If all goes well, we'll exec() right
  64. # out of this loop and never finish it.
  65. #
  66. while ( my $l = <$log_fh> ) {
  67. # socat under `-d -d' flags will print a line like the
  68. # following. In the parent, socat is configured to write its
  69. # debugging log to a $log_fn which we have a handle to in
  70. # $log_fh.
  71. #
  72. # PTY is /dev/pts/4
  73. #
  74. if ( $l =~ m{PTY is (/dev/pts/\d+)} ) {
  75. my $pty = $1;
  76. # Clean up the log file. We don't need it anymore. socat
  77. # itself may keep it open however. This is unfortunate.
  78. #
  79. truncate $log_fh, 0;
  80. close $log_fh;
  81. unlink $log_fn;
  82. # exec screen to open a new window using the PTY allocated
  83. # by socat.
  84. #
  85. my @screen_opts;
  86. if ( $screen_name ) {
  87. push @screen_opts, '-S' => $screen_name;
  88. }
  89. my @cmd = (
  90. 'screen',
  91. @screen_opts,
  92. '-X' => 'screen', $pty
  93. );
  94. if ( $debug ) {
  95. print STDERR "exec( @cmd )\n";
  96. }
  97. exec @cmd;
  98. }
  99. }
  100. # This is an error condition.
  101. #
  102. # I was unable to read any "PTY is /dev/pts/#" lines in the
  103. # socat log so something has gone wrong. Kill off the socat if
  104. # it's still present.
  105. #
  106. # TODO: check for actual death, wait between signals
  107. close $log_fh;
  108. unlink $log_fn;
  109. kill -2, $top_pid; # SIGINT
  110. kill -15, $top_pid; # SIGTERM
  111. kill -9, $top_pid; # SIGKILL
  112. }
  113. elsif ( $child_pid ) {
  114. # Tie our input to a new PTY and write the PTY's name to a log
  115. # file so the child can pick it up.
  116. #
  117. # I request two levels of -d debug to get the a message "PTY is
  118. # /dev/pts/#" in the log file at -lf. The child process is going
  119. # to delete this log file.
  120. #
  121. my @cmd = (
  122. 'socat',
  123. '-d', '-d',
  124. "-lf$log_fn",
  125. '-' => 'PTY',
  126. );
  127. if ( $debug ) {
  128. print STDERR "exec( @cmd )\n";
  129. }
  130. exec @cmd;
  131. }
  132. sub pod2usage {
  133. require Pod::Usage;
  134. goto &Pod::Usage::pod2usage;
  135. }