File Coverage

blib/lib/Test/NoTty.pm
Criterion Covered Total %
statement 32 46 69.5
branch 9 24 37.5
condition 3 3 100.0
subroutine 5 6 83.3
pod 1 1 100.0
total 50 80 62.5


line stmt bran cond sub pod time code
1             #!perl
2              
3 2     2   144625 use strict;
  2         12  
  2         55  
4 2     2   9 use warnings;
  2         3  
  2         69  
5              
6             package Test::NoTty;
7              
8 2     2   9 use parent qw(Exporter);
  2         4  
  2         9  
9 2     2   1102 use POSIX qw(setsid _exit WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG);
  2         12490  
  2         11  
10              
11             our @EXPORT = 'without_tty';
12             our $VERSION = '0.03';
13              
14             sub without_tty(&@) {
15 6     6 1 46071 my ($code, @args) = @_;
16 6 50       316 pipe my $reader, my $writer
17             or die "Can't pipe: $!";
18              
19             # So, "how to detach from your controlling terminal" is a subset of the "how
20             # to start a daemon" dance. In (reverse) you
21             #
22             # 2) Call setsid when your process is not a process group leader.
23             # This detaches you from any controlling terminal
24             # 1) fork, as the child process won't be a process group leader.
25             # (Your parent might be, and certainly will be if run interactively)
26             #
27             # The fun and games ensues because the code needs to run in the child, but
28             # really we'd like to fake it (as much as possible) that the code is running
29             # in the parent.
30              
31             # I'm not quite sure if how we deal with this correctly. Of if we really
32             # can. A child process is really supposed to call `exec` or `_exit`. But
33             # there's a chance here that we want to have real output
34              
35             # Perl before v5.14 didn't automatically load this:
36 6         101 require IO::File;
37 6         86 STDOUT->flush;
38 6         33 STDERR->flush;
39 6         7636 my $pid = fork;
40 6 50       319 die "Couldn't fork: $!"
41             unless defined $pid;
42              
43 6 50       89 unless ($pid) {
44             # We are in the child
45              
46             # We use the pipe to send (and rethrow) any regular exception.
47             # By implication, we can't deal with exception objects.
48 0         0 close $reader;
49              
50 0         0 eval {
51 0 0       0 die "setsid failed: $!"
52             if setsid == -1;
53              
54             # Likewise, a limitation is that the only function return value we
55             # can easily support is an integer process exit code:
56 0         0 my $exitcode = $code->(@args);
57 0         0 STDOUT->flush;
58 0         0 STDERR->flush;
59 0 0       0 _exit(defined $exitcode ? $exitcode : 0);
60             };
61              
62             # If you get here it's an error:
63 0 0       0 print $writer $@
64             or warn "print to error message handle failed: $!";
65 0 0       0 close $writer
66             or warn "close error message handle failed: $!";
67 0         0 STDOUT->flush;
68 0         0 STDERR->flush;
69 0         0 kill 'ABRT', $$;
70             }
71             # We are in the parent
72              
73             # Try very hard to relay signals to the child. For example, if it sleeps or
74             # churns forever, we want ^C to interrupt it, not take us out but leave it
75             # running in the background. This isn't foolproof, but seems better than
76             # doing nothing:
77              
78 6   100     1263 my @sigs = grep { !/^__/ && !/^CH?LD$/ } keys %SIG;
  414         2081  
79 6         4658 local @SIG{@sigs};
80 6         58 for my $sig (@sigs) {
81             $SIG{$sig} = sub {
82 0 0   0   0 kill $sig, $pid
83             or warn "kill $sig $pid failed: $!";
84 396         5303 };
85             }
86 6         102 close $writer;
87              
88             # "Setup" done. Let's see what the child tried to tell us:
89 6 50       586397 waitpid $pid, 0
90             or die "waitpid $pid, 0 failed: $!";
91 6         199 local $/;
92 6         383 my $error = <$reader>;
93 6 100       1885 die $error
94             if length $error;
95              
96             # This is the common case:
97 4 100       1856 return WEXITSTATUS(${^CHILD_ERROR_NATIVE})
98             if WIFEXITED(${^CHILD_ERROR_NATIVE});
99              
100 1 50       701 die "Code called by without_tty() died with signal " . WTERMSIG(${^CHILD_ERROR_NATIVE})
101             if WTERMSIG(${^CHILD_ERROR_NATIVE});
102              
103 0           die "Code called by without_tty() exited with unknown status ${^CHILD_ERROR_NATIVE}";
104             }
105              
106             1;
107              
108             __END__