summaryrefslogtreecommitdiff
path: root/console/console.c
diff options
context:
space:
mode:
Diffstat (limited to 'console/console.c')
-rw-r--r--console/console.c511
1 files changed, 511 insertions, 0 deletions
diff --git a/console/console.c b/console/console.c
new file mode 100644
index 00000000..ac7c807d
--- /dev/null
+++ b/console/console.c
@@ -0,0 +1,511 @@
+/* console.c - The device independant part of a console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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, USA. */
+
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <cthreads.h>
+
+#include "console.h"
+#include "display.h"
+#include "input.h"
+
+
+struct vcons
+{
+ /* Protected by cons_list_lock. */
+ vcons_t next;
+ vcons_t prev;
+ int refcnt;
+
+ /* The following members remain constant over the lifetime of the
+ object and don't need to be locked. */
+ cons_t cons;
+ int id;
+ void *display_console;
+
+ struct mutex lock;
+ /* Indicates if OWNER_ID is initialized. */
+ int has_owner;
+ /* Specifies the ID of the process that should receive the WINCH
+ signal for this virtual console. */
+ int owner_id;
+
+ /* The output queue holds the characters that are to be outputted.
+ The display driver might refuse to handle some incomplete
+ multi-byte or composed character at the end of the buffer, so we
+ have to keep them around. */
+ struct mutex output_lock;
+ int output_stopped;
+ struct condition output_resumed;
+ char *output;
+ size_t output_allocated;
+ size_t output_size;
+
+ struct mutex input_lock;
+ /* XXX input queue. */
+ char *input;
+ size_t input_allocated;
+ size_t input_size;
+};
+
+
+struct cons
+{
+ /* Protected by cons_list_lock. */
+ cons_t prev;
+ cons_t next;
+ int refcnt;
+ vcons_t vcons_list;
+ size_t vcons_length;
+ vcons_t vcons_active;
+
+ /* The following members are static and don't need to be locked. */
+ char *name;
+ display_ops_t display_ops;
+};
+
+
+/* The lock protects the console list, all virtual console lists of
+ all consoles, and their reference counters. */
+struct mutex cons_list_lock;
+cons_t cons_list;
+size_t cons_length;
+
+
+struct mutex config_lock;
+/* The default encoding. */
+const char *config_encoding;
+display_ops_t config_display;
+
+
+/* Lookup the console with name NAME, acquire a reference for it, and
+ return it in R_CONS. If NAME doesn't exist, return ESRCH. */
+error_t
+cons_lookup (const char *name, cons_t *r_cons)
+{
+ cons_t cons;
+
+ mutex_lock (&cons_list_lock);
+ cons = cons_list;
+ while (cons)
+ {
+ if (!strcmp (name, cons->name))
+ {
+ cons->refcnt++;
+ mutex_unlock (&cons_list_lock);
+ *r_cons = cons;
+ return 0;
+ }
+ cons = cons->next;
+ }
+ mutex_unlock (&cons_list_lock);
+ return ESRCH;
+}
+
+
+/* Release a reference to CONS. */
+void
+cons_release (cons_t cons)
+{
+ mutex_lock (&cons_list_lock);
+ cons->refcnt--;
+ mutex_unlock (&cons_list_lock);
+}
+
+
+/* Lookup the virtual console with number ID in the console CONS,
+ acquire a reference for it, and return it in R_VCONS. If CREATE is
+ true, the virtual console will be created if it doesn't exist yet.
+ If CREATE is true, and ID 0, the first free virtual console id is
+ used. */
+error_t
+vcons_lookup (cons_t cons, int id, int create, vcons_t *r_vcons)
+{
+ error_t err;
+ vcons_t previous_cons = 0;
+ vcons_t vcons;
+
+ if (!id && !create)
+ return EINVAL;
+
+ mutex_lock (&cons_list_lock);
+ if (id)
+ {
+ if (cons->vcons_list && cons->vcons_list->id <= id)
+ {
+ previous_cons = cons->vcons_list;
+ while (previous_cons->next && previous_cons->next->id <= id)
+ previous_cons = previous_cons->next;
+ if (previous_cons->id == id)
+ {
+ previous_cons->refcnt++;
+ mutex_unlock (&cons_list_lock);
+ *r_vcons = previous_cons;
+ return 0;
+ }
+ else if (!create)
+ {
+ mutex_unlock (&cons_list_lock);
+ return ESRCH;
+ }
+ }
+ }
+ else
+ {
+ id = 1;
+ if (cons->vcons_list && cons->vcons_list->id == 1)
+ {
+ previous_cons = cons->vcons_list;
+ while (previous_cons && previous_cons->id == id)
+ {
+ id++;
+ previous_cons = previous_cons->next;
+ }
+ }
+ }
+
+ vcons = calloc (1, sizeof (struct vcons));
+ if (!vcons)
+ {
+ mutex_unlock (&cons_list_lock);
+ return ENOMEM;
+ }
+ mutex_init (&vcons->lock);
+ mutex_init (&vcons->output_lock);
+ condition_init (&vcons->output_resumed);
+ vcons->refcnt = 1;
+ err = (*(cons->display_ops->create)) (&vcons->display_console,
+ config_encoding);
+ if (err)
+ {
+ mutex_unlock (&cons_list_lock);
+ free (vcons);
+ return err;
+ }
+ /* XXX Set up keyboard input etc. */
+ vcons->cons = cons;
+ cons->refcnt++;
+ cons->vcons_length++;
+ /* Insert the virtual console into the doubly linked list. */
+ if (previous_cons)
+ {
+ vcons->prev = previous_cons;
+ if (previous_cons->next)
+ {
+ previous_cons->next->prev = vcons;
+ vcons->next = previous_cons->next;
+ }
+ previous_cons->next = vcons;
+ }
+ else
+ {
+ if (cons->vcons_list)
+ {
+ cons->vcons_list->prev = vcons;
+ vcons->next = cons->vcons_list;
+ }
+ cons->vcons_list = vcons;
+ }
+ mutex_unlock (&cons_list_lock);
+ *r_vcons = vcons;
+ return 0;
+}
+
+
+/* Release a reference to the virtual console VCONS. If this was the
+ last reference the virtual console is destroyed. */
+void
+vcons_release (vcons_t vcons)
+{
+ mutex_lock (&cons_list_lock);
+ if (--vcons->refcnt)
+ {
+ /* As we keep a reference for all input focus groups pointing to
+ the virtual console, and a reference for the active console,
+ we know that without references, this virtual console is
+ neither active nor used by any input group. */
+
+ (*vcons->cons->display_ops->destroy) (vcons->display_console);
+ /* XXX Destroy the rest of the state. */
+
+ if (vcons->prev)
+ vcons->prev->next = vcons->next;
+ if (vcons->next)
+ vcons->next->prev = vcons->prev;
+ vcons->cons->vcons_length--;
+ vcons->cons->refcnt--;
+ free (vcons);
+ }
+ mutex_unlock (&cons_list_lock);
+}
+
+
+/* Activate virtual console VCONS for WHO. WHO is a unique identifier
+ for the entity requesting the activation (which can be used by the
+ display driver to group several activation requests together). */
+void
+vcons_activate (vcons_t vcons, int who)
+{
+ mutex_lock (&cons_list_lock);
+ if (vcons->cons->vcons_active != vcons)
+ {
+ (*vcons->cons->display_ops->activate) (vcons->display_console, who);
+ vcons->cons->vcons_active->refcnt--;
+ vcons->refcnt++;
+ vcons->cons->vcons_active = vcons;
+ }
+ mutex_unlock (&cons_list_lock);
+}
+
+
+/* Resume the output on the virtual console VCONS. */
+void
+vcons_start_output (vcons_t vcons)
+{
+ mutex_lock (&vcons->output_lock);
+ if (vcons->output_stopped)
+ {
+ vcons->output_stopped = 0;
+ condition_broadcast (&vcons->output_resumed);
+ }
+ mutex_unlock (&vcons->output_lock);
+}
+
+
+/* Stop all output on the virtual console VCONS. */
+void
+vcons_stop_output (vcons_t vcons)
+{
+ mutex_lock (&vcons->output_lock);
+ vcons->output_stopped = 1;
+ mutex_unlock (&vcons->output_lock);
+}
+
+
+/* Return the number of pending output bytes for VCONS. */
+size_t
+vcons_pending_output (vcons_t vcons)
+{
+ int output_size;
+ mutex_lock (&vcons->output_lock);
+ output_size = vcons->output_size;
+ mutex_unlock (&vcons->output_lock);
+ return output_size;
+}
+
+
+/* Fush the input buffer, discarding all pending data. */
+void
+vcons_flush_input (vcons_t vcons)
+{
+ mutex_lock (&vcons->input_lock);
+ vcons->input_size = 0;
+ mutex_unlock (&vcons->input_lock);
+}
+
+
+/* Flush the output buffer, discarding all pending data. */
+void
+vcons_discard_output (vcons_t vcons)
+{
+ mutex_lock (&vcons->output_lock);
+ vcons->output_size = 0;
+ mutex_unlock (&vcons->output_lock);
+}
+
+
+/* Output DATALEN characters from the buffer DATA on the virtual
+ console VCONS. The DATA must be supplied in the system encoding
+ configured for VCONS. The function returns the amount of bytes
+ written (might be smaller than DATALEN) or -1 and the error number
+ in errno. If NONBLOCK is not zero, return with -1 and set errno
+ to EWOULDBLOCK if operation would block for a long time. */
+ssize_t
+vcons_output (vcons_t vcons, int nonblock, char *data, size_t datalen)
+{
+ error_t err;
+ char *output;
+ size_t output_size;
+ ssize_t amount;
+
+ error_t ensure_output_buffer_size (size_t new_size)
+ {
+ /* Must be a power of two. */
+#define OUTPUT_ALLOCSIZE 32
+
+ if (vcons->output_allocated < new_size)
+ {
+ char *new_output;
+ new_size = (new_size + OUTPUT_ALLOCSIZE - 1) & ~(OUTPUT_ALLOCSIZE - 1);
+ new_output = realloc (vcons->output, new_size);
+ if (!new_output)
+ return ENOMEM;
+ vcons->output = new_output;
+ vcons->output_allocated = new_size;
+ }
+ return 0;
+ }
+
+ mutex_lock (&vcons->output_lock);
+ while (vcons->output_stopped)
+ {
+ if (nonblock)
+ {
+ mutex_unlock (&vcons->output_lock);
+ errno = EWOULDBLOCK;
+ return -1;
+ }
+ if (hurd_condition_wait (&vcons->output_resumed, &vcons->output_lock))
+ {
+ mutex_unlock (&vcons->output_lock);
+ errno = EINTR;
+ return -1;
+ }
+ }
+
+ if (vcons->output_size)
+ {
+ err = ensure_output_buffer_size (vcons->output_size + datalen);
+ if (err)
+ {
+ mutex_unlock (&vcons->output_lock);
+ errno = ENOMEM;
+ return -1;
+ }
+ output = vcons->output;
+ output_size = vcons->output_size;
+ memcpy (output + output_size, data, datalen);
+ output_size += datalen;
+ }
+ else
+ {
+ output = data;
+ output_size = datalen;
+ }
+ amount = output_size;
+ err = (*vcons->cons->display_ops->output) (vcons->display_console,
+ &output, &output_size);
+ amount -= output_size;
+
+ if (err && !amount)
+ {
+ mutex_unlock (&vcons->output_lock);
+ errno = err;
+ return err;
+ }
+
+ /* XXX What should be done with invalid characters etc? */
+ if (output_size)
+ {
+ /* If we used the caller's buffer DATA, the remaining bytes
+ might not fit in our internal output buffer. In this case we
+ can reallocate the buffer in VCONS without needing to update
+ OUTPUT (as it points into DATA). */
+ err = ensure_output_buffer_size (output_size);
+ if (err)
+ {
+ mutex_unlock (&vcons->output_lock);
+ return err;
+ }
+ memmove (vcons->output, output, output_size);
+ }
+ vcons->output_size = output_size;
+ amount += output_size;
+
+ mutex_unlock (&vcons->output_lock);
+ return amount;
+}
+
+
+/* Add DATALEN bytes starting from DATA to the input queue in
+ VCONS. */
+error_t
+vcons_input (vcons_t vcons, char *data, size_t datalen)
+{
+ error_t err = 0;
+
+ error_t ensure_input_buffer_size (size_t new_size)
+ {
+ /* Must be a power of two. */
+#define INPUT_ALLOCSIZE 32
+
+ if (vcons->input_allocated < new_size)
+ {
+ char *new_input;
+ new_size = (new_size + INPUT_ALLOCSIZE - 1) & ~(INPUT_ALLOCSIZE - 1);
+ new_input = realloc (vcons->input, new_size);
+ if (!new_input)
+ return ENOMEM;
+ vcons->input = new_input;
+ vcons->input_allocated = new_size;
+ }
+ return 0;
+ }
+
+ mutex_lock (&vcons->input_lock);
+ err = ensure_input_buffer_size (vcons->input_size + datalen);
+ if (err)
+ {
+ mutex_unlock (&vcons->input_lock);
+ return err;
+ }
+ memcpy (vcons->input + vcons->input_size, data, datalen);
+ vcons->input_size += datalen;
+ mutex_unlock (&vcons->input_lock);
+ return 0;
+}
+
+
+/* Return the dimension of the virtual console VCONS in WINSIZE. */
+void
+vcons_getsize (vcons_t vcons, struct winsize *size)
+{
+ (*vcons->cons->display_ops->getsize) (vcons->display_console, size);
+}
+
+
+/* Set the owner of the virtual console VCONS to PID. The owner
+ receives the SIGWINCH signal when the terminal size changes. */
+error_t
+vcons_set_owner (vcons_t vcons, pid_t pid)
+{
+ mutex_lock (&vcons->lock);
+ vcons->has_owner = 1;
+ vcons->owner_id = pid;
+ mutex_unlock (&vcons->lock);
+ return 0;
+}
+
+
+/* Return the owner of the virtual console VCONS in PID. If there is
+ no owner, return ENOTTY. */
+error_t
+vcons_get_owner (vcons_t vcons, pid_t *pid)
+{
+ error_t err = 0;
+ mutex_lock (&vcons->lock);
+ if (!vcons->has_owner)
+ err = ENOTTY;
+ else
+ *pid = vcons->owner_id;
+ mutex_unlock (&vcons->lock);
+ return err;
+}