sbuild-auth-conv-tty.cc

Go to the documentation of this file.
00001 /* Copyright © 2005-2006  Roger Leigh <rleigh@debian.org>
00002  *
00003  * schroot is free software; you can redistribute it and/or modify it
00004  * under the terms of the GNU General Public License as published by
00005  * the Free Software Foundation; either version 2 of the License, or
00006  * (at your option) any later version.
00007  *
00008  * schroot is distributed in the hope that it will be useful, but
00009  * WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011  * General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program; if not, write to the Free Software
00015  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00016  * MA  02111-1307  USA
00017  *
00018  *********************************************************************/
00019 
00020 #include <config.h>
00021 
00022 #include "sbuild-auth-conv-tty.h"
00023 #include "sbuild-log.h"
00024 
00025 #include <iostream>
00026 
00027 #include <signal.h>
00028 #include <termios.h>
00029 #include <unistd.h>
00030 
00031 #include <boost/format.hpp>
00032 
00033 using std::cerr;
00034 using std::endl;
00035 using boost::format;
00036 using namespace sbuild;
00037 
00038 namespace
00039 {
00040 
00041   typedef std::pair<auth_conv_tty::error_code,const char *> emap;
00042 
00047   emap init_errors[] =
00048     {
00049       emap(auth_conv_tty::TIMEOUT,         N_("Timed out")),
00050       // TRANSLATORS: Please use an ellipsis e.g. U+2026
00051       emap(auth_conv_tty::TIMEOUT_PENDING, N_("Time is running out...")),
00052       emap(auth_conv_tty::TERMIOS,         N_("Failed to get terminal settings")),
00053       // TRANSLATORS: %1% = integer
00054       emap(auth_conv_tty::CONV_TYPE,       N_("Unsupported conversation type '%1%'"))
00055     };
00056 
00057   volatile sig_atomic_t timer_expired = false;
00058 
00064   void
00065   reset_alarm (struct sigaction *orig_sa)
00066   {
00067     // Stop alarm
00068     alarm (0);
00069     // Restore original handler
00070     sigaction (SIGALRM, orig_sa, 0);
00071   }
00072 
00078   void
00079   alarm_handler (int ignore)
00080   {
00081     timer_expired = true;
00082   }
00083 
00092   bool
00093   set_alarm (int delay,
00094              struct sigaction *orig_sa)
00095   {
00096     struct sigaction new_sa;
00097     sigemptyset(&new_sa.sa_mask);
00098     new_sa.sa_flags = 0;
00099     new_sa.sa_handler = alarm_handler;
00100 
00101     if (sigaction(SIGALRM, &new_sa, orig_sa) != 0)
00102       {
00103         return false;
00104       }
00105     if (alarm(delay) != 0)
00106       {
00107         sigaction(SIGALRM, orig_sa, 0);
00108         return false;
00109       }
00110 
00111     return true;
00112   }
00113 
00114 }
00115 
00116 template<>
00117 error<auth_conv_tty::error_code>::map_type
00118 error<auth_conv_tty::error_code>::error_strings
00119 (init_errors,
00120  init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
00121 
00122 auth_conv_tty::auth_conv_tty ():
00123   warning_timeout(0),
00124   fatal_timeout(0),
00125   start_time(0)
00126 {
00127 }
00128 
00129 auth_conv_tty::~auth_conv_tty ()
00130 {
00131 }
00132 
00133 time_t
00134 auth_conv_tty::get_warning_timeout ()
00135 {
00136   return this->warning_timeout;
00137 }
00138 
00139 void
00140 auth_conv_tty::set_warning_timeout (time_t timeout)
00141 {
00142   this->warning_timeout = timeout;
00143 }
00144 
00145 time_t
00146 auth_conv_tty::get_fatal_timeout ()
00147 {
00148   return this->fatal_timeout;
00149 }
00150 
00151 void
00152 auth_conv_tty::set_fatal_timeout (time_t timeout)
00153 {
00154   this->fatal_timeout = timeout;
00155 }
00156 
00157 int
00158 auth_conv_tty::get_delay ()
00159 {
00160   timer_expired = 0;
00161   time (&this->start_time);
00162 
00163   if (this->fatal_timeout != 0 &&
00164       this->start_time >= this->fatal_timeout)
00165     throw error(TIMEOUT);
00166 
00167   if (this->warning_timeout != 0 &&
00168       this->start_time >= this->warning_timeout)
00169     {
00170       error e(TIMEOUT_PENDING);
00171       log_exception_warning(e);
00172       return (this->fatal_timeout ?
00173               this->fatal_timeout - this->start_time : 0);
00174     }
00175 
00176   if (this->warning_timeout != 0)
00177     return this->warning_timeout - this->start_time;
00178   else if (this->fatal_timeout != 0)
00179     return this->fatal_timeout - this->start_time;
00180   else
00181     return 0;
00182 }
00183 
00184 std::string
00185 auth_conv_tty::read_string (std::string message,
00186                             bool        echo)
00187 {
00188   // TODO: Read from controlling TTY.
00189 
00190   struct termios orig_termios, noecho_termios;
00191   struct sigaction saved_signals;
00192   sigset_t old_sigs, new_sigs;
00193   bool use_termios = false;
00194   std::string retval;
00195 
00196   if (isatty(STDIN_FILENO))
00197     {
00198       use_termios = true;
00199 
00200       if (tcgetattr(STDIN_FILENO, &orig_termios) != 0)
00201         throw error(TERMIOS);
00202 
00203       memcpy(&noecho_termios, &orig_termios, sizeof(struct termios));
00204 
00205       if (echo == false)
00206         noecho_termios.c_lflag &= ~(ECHO);
00207 
00208       sigemptyset(&new_sigs);
00209       sigaddset(&new_sigs, SIGINT);
00210       sigaddset(&new_sigs, SIGTSTP);
00211       sigprocmask(SIG_BLOCK, &new_sigs, &old_sigs);
00212     }
00213 
00214   char input[PAM_MAX_MSG_SIZE];
00215 
00216   int delay = get_delay();
00217 
00218   while (delay >= 0)
00219     {
00220       cerr << message;
00221 
00222       if (use_termios == true)
00223         tcsetattr(STDIN_FILENO, TCSAFLUSH, &noecho_termios);
00224 
00225       if (delay > 0 && set_alarm(delay, &saved_signals) == false)
00226         break;
00227       else
00228         {
00229           int nchars = read(STDIN_FILENO, input, PAM_MAX_MSG_SIZE - 1);
00230           if (use_termios)
00231             {
00232               tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios);
00233               if (echo == false && timer_expired == true)
00234                 cerr << endl;
00235             }
00236           if (delay > 0)
00237             reset_alarm(&saved_signals);
00238           if (timer_expired == true)
00239             {
00240               delay = get_delay();
00241             }
00242           else if (nchars > 0)
00243             {
00244               if (echo == false)
00245                 cerr << endl;
00246 
00247               if (input[nchars-1] == '\n')
00248                 input[--nchars] = '\0';
00249               else
00250                 input[nchars] = '\0';
00251 
00252               retval = input;
00253               break;
00254             }
00255           else if (nchars == 0)
00256             {
00257               if (echo == false)
00258                 cerr << endl;
00259 
00260               retval = "";
00261               break;
00262             }
00263         }
00264     }
00265 
00266   memset(input, 0, sizeof(input));
00267 
00268   if (use_termios == true)
00269     {
00270       sigprocmask(SIG_SETMASK, &old_sigs, 0);
00271       tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios);
00272     }
00273 
00274   return retval;
00275 }
00276 
00277 void
00278 auth_conv_tty::conversation (auth_conv::message_list& messages)
00279 {
00280   log_debug(DEBUG_NOTICE) << "PAM TTY conversation handler started" << endl;
00281 
00282   for (std::vector<auth_message>::iterator cur = messages.begin();
00283        cur != messages.end();
00284        ++cur)
00285     {
00286       switch (cur->type)
00287         {
00288         case auth_message::MESSAGE_PROMPT_NOECHO:
00289           log_debug(DEBUG_NOTICE) << "PAM TTY input prompt (noecho)" << endl;
00290           cur->response = read_string(cur->message, false);
00291           break;
00292         case auth_message::MESSAGE_PROMPT_ECHO:
00293           log_debug(DEBUG_NOTICE) << "PAM TTY input prompt (echo)" << endl;
00294           cur->response = read_string(cur->message, true);
00295           break;
00296         case auth_message::MESSAGE_ERROR:
00297           log_debug(DEBUG_NOTICE) << "PAM TTY output error" << endl;
00298           // TODO: Log to controlling TTY.
00299           log_error() << cur->message << endl;
00300           break;
00301         case auth_message::MESSAGE_INFO:
00302           log_debug(DEBUG_NOTICE) << "PAM TTY output info" << endl;
00303           // TODO: Log to controlling TTY.
00304           log_info() << cur->message << endl;
00305           break;
00306         default:
00307           throw error(cur->type, CONV_TYPE);
00308           break;
00309         }
00310     }
00311 
00312   log_debug(DEBUG_NOTICE) << "PAM TTY conversation handler ended" << endl;
00313 }

Generated on Sat Jan 27 16:11:03 2007 for schroot by  doxygen 1.5.1