summaryrefslogtreecommitdiff
path: root/libgsystem/gsystem-console.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgsystem/gsystem-console.c')
-rw-r--r--libgsystem/gsystem-console.c443
1 files changed, 0 insertions, 443 deletions
diff --git a/libgsystem/gsystem-console.c b/libgsystem/gsystem-console.c
deleted file mode 100644
index 35477ebaf..000000000
--- a/libgsystem/gsystem-console.c
+++ /dev/null
@@ -1,443 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2013 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#define _GSYSTEM_NO_LOCAL_ALLOC
-#include "libgsystem.h"
-
-/**
- * SECTION:gsconsole
- * @title: GSConsole
- * @short_description: Interact with standard input/output as well as terminal
- *
- * First, this class offers API to access the standard input and
- * output/error, streams as #GInputStream and #GOutputStream
- * respectively.
- *
- * In the case where the process is connected to a controlling
- * terminal, the gs_console_get() API is available, which exposes a
- * number of additional features such as no-echo password reading.
- *
- * Since: 2.36
- */
-
-#include "config.h"
-
-#include "gsystem-console.h"
-
-#include <string.h>
-#ifdef G_OS_UNIX
-#include <gio/gunixoutputstream.h>
-#include <gio/gfiledescriptorbased.h>
-#include <gio/gunixinputstream.h>
-#include <glib-unix.h>
-#include <unistd.h>
-#include <termios.h>
-#endif
-#include <fcntl.h>
-
-typedef GObjectClass GSConsoleClass;
-
-struct _GSConsole
-{
- GObject parent;
-
- gboolean in_status_line;
- gssize last_line_written;
-};
-
-G_DEFINE_TYPE (GSConsole, gs_console, G_TYPE_OBJECT);
-
-static void
-gs_console_init (GSConsole *self)
-{
- self->last_line_written = -1;
-}
-
-static void
-gs_console_class_init (GSConsoleClass *class)
-{
-}
-
-/**
- * gs_console_get:
- *
- * If the current process has an interactive console, return the
- * singleton #GSConsole instance. On Unix, this is equivalent to
- * isatty(). For all other cases, such as pipes, sockets, /dev/null,
- * this function will return %NULL.
- *
- * Returns: (transfer none): The console instance, or %NULL if not interactive
- *
- * Since: 2.36
- */
-GSConsole *
-gs_console_get (void)
-{
- static gsize checked = 0;
- static GSConsole *instance = NULL;
-
- if (g_once_init_enter (&checked))
- {
-#ifdef G_OS_UNIX
- if (isatty (0) && isatty (1))
- instance = g_object_new (GS_TYPE_CONSOLE, NULL);
-#endif
- g_once_init_leave (&checked, 1);
- }
-
- return (GSConsole*) instance;
-}
-
-/**
- * gs_console_get_stdin:
- *
- * Returns: (transfer none): The singleton stream connected to standard input
- */
-GInputStream *
-gs_console_get_stdin (void)
-{
-#ifdef G_OS_UNIX
- static gsize instance = 0;
-
- if (g_once_init_enter (&instance))
- g_once_init_leave (&instance, (gsize) g_unix_input_stream_new (0, FALSE));
-
- return (GInputStream*) instance;
-#else
- g_error ("not implemented");
-#endif
-}
-
-/**
- * gs_console_get_stdout:
- *
- * Returns: (transfer none): The singleton stream connected to standard output
- */
-GOutputStream *
-gs_console_get_stdout (void)
-{
-#ifdef G_OS_UNIX
- static gsize instance = 0;
-
- if (g_once_init_enter (&instance))
- g_once_init_leave (&instance, (gsize) g_unix_output_stream_new (1, FALSE));
-
- return (GOutputStream*) instance;
-#else
- g_error ("not implemented");
-#endif
-}
-
-/**
- * gs_console_get_stderr:
- *
- * Returns: (transfer none): The singleton stream connected to standard error
- */
-GOutputStream *
-gs_console_get_stderr (void)
-{
-#ifdef G_OS_UNIX
- static gsize instance = 0;
-
- if (g_once_init_enter (&instance))
- g_once_init_leave (&instance, (gsize) g_unix_output_stream_new (2, FALSE));
-
- return (GOutputStream*) instance;
-#else
- g_error ("not implemented");
-#endif
-}
-
-#ifdef G_OS_UNIX
-static inline void
-_set_error_from_errno (GError **error)
-{
- int errsv = errno;
- g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errsv),
- g_strerror (errsv));
-}
-#endif
-
-/**
- * gs_console_read_password:
- * @console: the #GSConsole
- * @prompt: A string to output before reading the password
- * @error: a #GError
- *
- * Write @prompt to standard output, then switch output echo off, read
- * a result string, then switch output echo back on.
- *
- * Returns: A string, or %NULL on error
- */
-char *
-gs_console_read_password (GSConsole *console,
- const char *prompt,
- GCancellable *cancellable,
- GError **error)
-{
-#ifdef G_OS_UNIX
- gboolean ret = FALSE;
- /* This code is modified from that found in
- * polkit/src/polkittextagentlistener.c, reused under the LGPL v2.1
- */
- int res;
- struct termios ts, ots;
- GInputStream *in;
- GOutputStream *out;
- GString *str = NULL;
- gsize bytes_written;
- gboolean reset_terminal = FALSE;
-
- in = gs_console_get_stdin ();
- out = gs_console_get_stdout ();
-
- if (!g_output_stream_write_all (out, prompt, strlen (prompt), &bytes_written,
- cancellable, error))
- goto out;
- if (!g_output_stream_flush (out, cancellable, error))
- goto out;
-
- /* TODO: We really ought to block SIGINT and STGSTP (and probably
- * other signals too) so we can restore the terminal (since we
- * turn off echoing). See e.g. Advanced Programming in the
- * UNIX Environment 2nd edition (Steves and Rago) section
- * 18.10, pg 660 where this is suggested. See also various
- * getpass(3) implementations
- *
- * However, since we are a library routine the user could have
- * multiple threads - in fact, typical usage of
- * PolkitAgentTextListener is to run it in a thread. And
- * unfortunately threads and POSIX signals is a royal PITA.
- *
- * Maybe we could fork(2) and ask for the password in the
- * child and send it back to the parent over a pipe? (we are
- * guaranteed that there is only one thread in the child
- * process).
- *
- * (Side benefit of doing this in a child process is that we
- * could avoid blocking the thread where the
- * PolkitAgentTextListener object is being serviced from. But
- * since this class is normally used in a dedicated thread
- * it doesn't really matter *anyway*.)
- *
- * Anyway, On modern Linux not doing this doesn't seem to be a
- * problem - looks like modern shells restore echoing anyway
- * on the first input. So maybe it's not even worth solving
- * the problem.
- */
-
- do
- res = tcgetattr (1, &ts);
- while (G_UNLIKELY (res == -1 && errno == EINTR));
- if (res == -1)
- {
- _set_error_from_errno (error);
- goto out;
- }
- ots = ts;
- ts.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
- do
- res = tcsetattr (1, TCSAFLUSH, &ts);
- while (G_UNLIKELY (res == -1 && errno == EINTR));
- if (res == -1)
- {
- _set_error_from_errno (error);
- goto out;
- }
-
- /* After this point, we'll need to clean up the terminal in case of
- * error.
- */
- reset_terminal = TRUE;
-
- str = g_string_new (NULL);
- while (TRUE)
- {
- gssize bytes_read;
- guint8 buf[1];
-
- /* FIXME - we should probably be converting from the system
- * codeset, in case it's not UTF-8.
- */
- bytes_read = g_input_stream_read (in, buf, sizeof (buf),
- cancellable, error);
- if (bytes_read < 0)
- goto out;
- else if (bytes_read == 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
- "End of stream while reading password");
- goto out;
- }
- else if (buf[0] == '\n')
- {
- break;
- }
- else
- {
- g_string_append_c (str, buf[0]);
- }
- }
-
- ret = TRUE;
- out:
- if (reset_terminal)
- {
- do
- res = tcsetattr (1, TCSAFLUSH, &ots);
- while (G_UNLIKELY (res == -1 && errno == EINTR));
- if (res == -1)
- {
- _set_error_from_errno (error);
- g_string_free (str, TRUE);
- return NULL;
- }
- }
-
- if (!ret)
- {
- g_string_free (str, TRUE);
- return NULL;
- }
- else
- {
- return g_string_free (str, FALSE);
- }
-#else
- g_error ("not implemented");
-#endif
-}
-
-/**
- * gs_console_begin_status_line:
- * @console: the #GSConsole
- * @line: String to output
- *
- * The primary use case for this function is to output periodic
- * "status" or "progress" information. The first time this function
- * is called, @line will be output normally. Subsequent invocations
- * will overwrite the previous.
- *
- * You must invoke gs_console_end_status_line() to return the console
- * to normal mode. In particular, concurrent use of this function and
- * the stream returned by gs_console_get_stdout() results in undefined
- * behavior.
- */
-gboolean
-gs_console_begin_status_line (GSConsole *console,
- const char *line,
- GCancellable *cancellable,
- GError **error)
-{
-#ifdef G_OS_UNIX
- gboolean ret = FALSE;
- gsize linelen;
- GOutputStream *out;
- gsize bytes_written;
-
- out = gs_console_get_stdout ();
-
- if (!console->in_status_line)
- {
- guint8 buf[3] = { (guint8)'\n', 0x1B, 0x37 };
- if (!g_output_stream_write_all (out, buf, sizeof (buf), &bytes_written,
- cancellable, error))
- goto out;
- console->in_status_line = TRUE;
- console->last_line_written = -1;
- }
-
- {
- guint8 buf[2] = { 0x1B, 0x38 };
- if (!g_output_stream_write_all (out, buf, sizeof (buf), &bytes_written,
- cancellable, error))
- goto out;
- }
-
- linelen = strlen (line);
- if (!g_output_stream_write_all (out, line, linelen, &bytes_written,
- cancellable, error))
- goto out;
-
- /* Now we need to pad with spaces enough to overwrite our last line
- */
- if (console->last_line_written >= 0
- && linelen < (gsize) console->last_line_written)
- {
- gsize towrite = console->last_line_written - linelen;
- const char c = ' ';
- while (towrite > 0)
- {
- if (!g_output_stream_write_all (out, &c, 1, &bytes_written,
- cancellable, error))
- goto out;
- towrite--;
- }
- }
-
- console->last_line_written = linelen;
-
- ret = TRUE;
- out:
- return ret;
-#else
- g_error ("not implemented");
-#endif
-}
-
-/**
- * gs_console_end_status_line:
- * @console: the #GSConsole
- *
- * Complete a series of invocations of gs_console_begin_status_line(),
- * returning the stream to normal mode. The last printed status line
- * remains on the console; if this is not desired, print an empty
- * string to clear it before invoking this function.
- */
-gboolean
-gs_console_end_status_line (GSConsole *console,
- GCancellable *cancellable,
- GError **error)
-{
-#ifdef G_OS_UNIX
- gboolean ret = FALSE;
- GOutputStream *out;
- gsize bytes_written;
- char c = '\n';
-
- g_return_val_if_fail (console->in_status_line, FALSE);
-
- out = gs_console_get_stdout ();
-
- if (!g_output_stream_write_all (out, &c, 1, &bytes_written,
- cancellable, error))
- goto out;
-
- console->in_status_line = FALSE;
-
- ret = TRUE;
- out:
- return ret;
-#else
- g_error ("not implemented");
-#endif
-}