f3a6904ecc267356c2b3ad30e4553ea6c603f96e
[debian/amanda] / perl / Amanda / Interactivity / tty.pm
1 # Copyright (c) 2010 Zmanda, Inc.  All Rights Reserved.
2 #
3 # This program is free software; you can redistribute it and/or modify it
4 # under the terms of the GNU General Public License version 2 as published
5 # by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful, but
8 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
10 # for more details.
11 #
12 # You should have received a copy of the GNU General Public License along
13 # with this program; if not, write to the Free Software Foundation, Inc.,
14 # 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
15 #
16 # Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
17 # Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
18
19 package Amanda::Interactivity::tty;
20
21 use strict;
22 use warnings;
23 use POSIX qw( :errno_h );
24 use vars qw( @ISA );
25 @ISA = qw( Amanda::Interactivity );
26
27 use Amanda::Paths;
28 use Amanda::Util;
29 use Amanda::Debug qw( debug );
30 use Amanda::Changer;
31 use Amanda::MainLoop qw( :GIOCondition );
32
33 =head1 NAME
34
35 Amanda::Interactivity::tty -- Interactivity class to read user request from /dev/tty
36
37 =head1 SYNOPSIS
38
39 Amanda::Interactivity class to write user request on /dev/tty and read reply
40 from /dev/tty.
41
42 =cut
43
44 sub new {
45     my $class = shift;
46     my $property = shift;
47
48     my $input;
49     my $output;
50     my $abort_message;
51
52     my $r = open($input, '<', "/dev/tty");
53     if (!$r) {
54         $abort_message = "Failed to open /dev/tty: $!";
55     } else {
56         $r = open($output, '>', "/dev/tty");
57         if (!$r) {
58             $abort_message = "Failed to open /dev/tty: $!";
59             close $input;
60             $input = undef;
61         }
62     }
63     my $self = {
64         input_src     => undef,
65         input         => $input,
66         output        => $output,
67         property      => $property,
68         abort_message => $abort_message,
69     };
70     return bless ($self, $class);
71 }
72
73 sub DESTROY {
74     my $self = shift;
75
76     close($self->{'input'})  if $self->{'input'};
77     close($self->{'output'}) if $self->{'output'};
78 }
79
80 sub abort {
81     my $self = shift @_;
82
83     if ($self->{'input_src'}) {
84         $self->{'input_src'}->remove();
85         $self->{'input_src'} = undef;
86     }
87 }
88
89 sub user_request {
90     my $self = shift @_;
91     my %params = @_;
92     my $buffer = "";
93
94     my $message    = $params{'message'};
95     my $label      = $params{'label'};
96     my $new_volume = $params{'new_volume'};
97     my $err        = $params{'err'};
98     my $chg_name   = $params{'chg_name'};
99
100     my $data_in = sub {
101         my $b;
102         my $n_read = POSIX::read(fileno($self->{'input'}), $b, 1);
103         if (!defined $n_read) {
104             return if ($! == EINTR);
105             $self->abort();
106             return $params{'request_cb'}->(
107                         Amanda::Changer::Error->new('fatal',
108                                 message => "Fail to read from /dev/tty"));
109         } elsif ($n_read == 0) {
110             $self->abort();
111             return $params{'request_cb'}->(
112                         Amanda::Changer::Error->new('fatal',
113                                 message => "Aborted by user"));
114         } else {
115             $buffer .= $b;
116             if ($b eq "\n") {
117                 my $line = $buffer;
118                 chomp $line;
119                 $buffer = "";
120                 $self->abort();
121                 return $params{'request_cb'}->(undef, $line);
122             }
123         }
124     };
125
126     if ($self->{'abort_message'}) {
127         return $params{'request_cb'}->(
128                         Amanda::Changer::Error->new('fatal',
129                                 message => $self->{'abort_message'}));
130     }
131
132     print {$self->{'output'}} "$err\n";
133     if ($label && $new_volume) {
134         print {$self->{'output'}} "Insert volume labeled '$label' or a new volume in $chg_name\n";
135     } elsif ($label) {
136         print {$self->{'output'}} "Insert volume labeled '$label' in $chg_name\n";
137     } else {
138         print {$self->{'output'}} "Insert a new volume in $chg_name\n";
139     }
140     print {$self->{'output'}} "and press <enter> enter, or ^D to abort.\n";
141
142     $self->{'input_src'} = Amanda::MainLoop::fd_source(
143                                                 fileno($self->{'input'}),
144                                                 $G_IO_IN|$G_IO_HUP|$G_IO_ERR);
145     $self->{'input_src'}->set_callback($data_in);
146     return;
147 }
148
149 1;