summaryrefslogtreecommitdiff
path: root/sbuild/sbuild-auth-conv-tty.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sbuild/sbuild-auth-conv-tty.cc')
-rw-r--r--sbuild/sbuild-auth-conv-tty.cc320
1 files changed, 320 insertions, 0 deletions
diff --git a/sbuild/sbuild-auth-conv-tty.cc b/sbuild/sbuild-auth-conv-tty.cc
new file mode 100644
index 00000000..7701fdde
--- /dev/null
+++ b/sbuild/sbuild-auth-conv-tty.cc
@@ -0,0 +1,320 @@
+/* Copyright © 2005-2006 Roger Leigh <rleigh@debian.org>
+ *
+ * schroot is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * schroot is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ *********************************************************************/
+
+#include <config.h>
+
+#include "sbuild-auth-conv-tty.h"
+#include "sbuild-log.h"
+
+#include <iostream>
+
+#include <termios.h>
+#include <unistd.h>
+
+#include <boost/format.hpp>
+
+using std::cerr;
+using std::endl;
+using boost::format;
+using namespace sbuild;
+
+namespace
+{
+
+ typedef std::pair<auth_conv_tty::error_code,const char *> emap;
+
+ /**
+ * This is a list of the supported error codes. It's used to
+ * construct the real error codes map.
+ */
+ emap init_errors[] =
+ {
+ emap(auth_conv_tty::TIMEOUT, N_("Timed out")),
+ emap(auth_conv_tty::TIMEOUT_PENDING, N_("Time is running out...")),
+ emap(auth_conv_tty::TERMIOS, N_("Failed to get terminal settings")),
+ emap(auth_conv_tty::CONV_TYPE, N_("Unsupported conversation type"))
+ };
+
+ volatile sig_atomic_t timer_expired = false;
+
+ /**
+ * Disable the alarm and signal handler.
+ *
+ * @param orig_sa the signal handler to restore.
+ */
+ void
+ reset_alarm (struct sigaction *orig_sa)
+ {
+ // Stop alarm
+ alarm (0);
+ // Restore original handler
+ sigaction (SIGALRM, orig_sa, NULL);
+ }
+
+ /**
+ * Handle the SIGALRM signal.
+ *
+ * @param ignore the signal number (unused).
+ */
+ void
+ alarm_handler (int ignore)
+ {
+ timer_expired = true;
+ }
+
+ /**
+ * Set the SIGALARM handler, and set the timeout to delay seconds.
+ * The old signal handler is stored in orig_sa.
+ *
+ * @param delay the delay (in seconds) before SIGALRM is raised.
+ * @param orig_sa the location to store the original signal handler.
+ * @returns true on success, false on failure.
+ */
+ bool
+ set_alarm (int delay,
+ struct sigaction *orig_sa)
+ {
+ struct sigaction new_sa;
+ sigemptyset(&new_sa.sa_mask);
+ new_sa.sa_flags = 0;
+ new_sa.sa_handler = alarm_handler;
+
+ if (sigaction(SIGALRM, &new_sa, orig_sa) != 0)
+ {
+ return false;
+ }
+ if (alarm(delay) != 0)
+ {
+ sigaction(SIGALRM, orig_sa, NULL);
+ return false;
+ }
+
+ return true;
+ }
+
+}
+
+template<>
+custom_error<auth_conv_tty::error_code>::map_type
+custom_error<auth_conv_tty::error_code>::error_strings
+(init_errors,
+ init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
+
+auth_conv_tty::auth_conv_tty ():
+ warning_timeout(0),
+ fatal_timeout(0),
+ start_time(0)
+{
+}
+
+auth_conv_tty::~auth_conv_tty ()
+{
+}
+
+time_t
+auth_conv_tty::get_warning_timeout ()
+{
+ return this->warning_timeout;
+}
+
+void
+auth_conv_tty::set_warning_timeout (time_t timeout)
+{
+ this->warning_timeout = timeout;
+}
+
+time_t
+auth_conv_tty::get_fatal_timeout ()
+{
+ return this->fatal_timeout;
+}
+
+void
+auth_conv_tty::set_fatal_timeout (time_t timeout)
+{
+ this->fatal_timeout = timeout;
+}
+
+int
+auth_conv_tty::get_delay ()
+{
+ timer_expired = 0;
+ time (&this->start_time);
+
+ if (this->fatal_timeout != 0 &&
+ this->start_time >= this->fatal_timeout)
+ throw error(TIMEOUT);
+
+ if (this->warning_timeout != 0 &&
+ this->start_time >= this->warning_timeout)
+ {
+ error e(TIMEOUT_PENDING);
+ log_warning() << e.what() << endl;
+ return (this->fatal_timeout ?
+ this->fatal_timeout - this->start_time : 0);
+ }
+
+ if (this->warning_timeout != 0)
+ return this->warning_timeout - this->start_time;
+ else if (this->fatal_timeout != 0)
+ return this->fatal_timeout - this->start_time;
+ else
+ return 0;
+}
+
+std::string
+auth_conv_tty::read_string (std::string message,
+ bool echo)
+{
+ struct termios orig_termios, noecho_termios;
+ struct sigaction saved_signals;
+ sigset_t old_sigs, new_sigs;
+ bool use_termios = false;
+ std::string retval;
+
+ if (isatty(STDIN_FILENO))
+ {
+ use_termios = true;
+
+ if (tcgetattr(STDIN_FILENO, &orig_termios) != 0)
+ throw error(TERMIOS);
+
+ memcpy(&noecho_termios, &orig_termios, sizeof(struct termios));
+
+ if (echo == false)
+ noecho_termios.c_lflag &= ~(ECHO);
+
+ sigemptyset(&new_sigs);
+ sigaddset(&new_sigs, SIGINT);
+ sigaddset(&new_sigs, SIGTSTP);
+ sigprocmask(SIG_BLOCK, &new_sigs, &old_sigs);
+ }
+
+ char input[PAM_MAX_MSG_SIZE];
+
+ int delay = get_delay();
+
+ while (delay >= 0)
+ {
+ cerr << message;
+
+ if (use_termios == true)
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &noecho_termios);
+
+ if (delay > 0 && set_alarm(delay, &saved_signals) == false)
+ break;
+ else
+ {
+ int nchars = read(STDIN_FILENO, input, PAM_MAX_MSG_SIZE - 1);
+ if (use_termios)
+ {
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios);
+ if (echo == false && timer_expired == true)
+ cerr << endl;
+ }
+ if (delay > 0)
+ reset_alarm(&saved_signals);
+ if (timer_expired == true)
+ {
+ delay = get_delay();
+ }
+ else if (nchars > 0)
+ {
+ if (echo == false)
+ cerr << endl;
+
+ if (input[nchars-1] == '\n')
+ input[--nchars] = '\0';
+ else
+ input[nchars] = '\0';
+
+ retval = input;
+ break;
+ }
+ else if (nchars == 0)
+ {
+ if (echo == false)
+ cerr << endl;
+
+ retval = "";
+ break;
+ }
+ }
+ }
+
+ memset(input, 0, sizeof(input));
+
+ if (use_termios == true)
+ {
+ sigprocmask(SIG_SETMASK, &old_sigs, NULL);
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios);
+ }
+
+ return retval;
+}
+
+bool
+auth_conv_tty::conversation (message_list& messages)
+{
+ try
+ {
+ for (std::vector<auth_message>::iterator cur = messages.begin();
+ cur != messages.end();
+ ++cur)
+ {
+ switch (cur->message_type)
+ {
+ case auth_message::MESSAGE_PROMPT_NOECHO:
+ cur->response = read_string(cur->message, false);
+ break;
+ case auth_message::MESSAGE_PROMPT_ECHO:
+ cur->response = read_string(cur->message, true);
+ break;
+ case auth_message::MESSAGE_ERROR:
+ cerr << cur->message << endl;
+ break;
+ case auth_message::MESSAGE_INFO:
+ cerr << cur->message << endl;
+ break;
+ default:
+ {
+ format fmt("%1%");
+ fmt % cur->message_type;
+ error e(fmt.str(), CONV_TYPE);
+ log_error() << e.what() << endl;
+ return false;
+ }
+ break;
+ }
+ }
+ }
+ catch (error const& e)
+ {
+ log_error() << e.what() << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Local Variables:
+ * mode:C++
+ * End:
+ */