diff options
Diffstat (limited to 'libcons/vcons-open.c')
-rw-r--r-- | libcons/vcons-open.c | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/libcons/vcons-open.c b/libcons/vcons-open.c new file mode 100644 index 00000000..8c34fc5e --- /dev/null +++ b/libcons/vcons-open.c @@ -0,0 +1,175 @@ +/* vcons-open.c - Open a virtual 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 <unistd.h> +#include <stdio.h> +#include <sys/mman.h> +#include <sys/fcntl.h> + +#include <hurd.h> +#include <mach.h> + +#include "cons.h" + +/* Open the virtual console for VCONS_ENTRY. CONS is locked. + Afterwards, R_VCONS will be locked. */ +error_t +cons_vcons_open (cons_t cons, vcons_list_t vcons_entry, vcons_t *r_vcons) +{ + error_t err = 0; + char *name; + file_t vconsp = MACH_PORT_NULL; + file_t file = MACH_PORT_NULL; + int fd = -1; + struct stat statbuf; + mach_port_t notify = MACH_PORT_NULL; + vcons_t vcons; + + if (asprintf (&name, "%u", vcons_entry->id) < 0) + return err; + + /* Set up the port we receive notification messages on. */ + err = ports_create_port (cons_port_class, cons_port_bucket, + sizeof (*vcons), &vcons); + if (err) + goto err; + vcons->notify.cons = NULL; + vcons->cons = cons; + vcons->vcons_entry = vcons_entry; + vcons->id = vcons_entry->id; + mutex_init (&vcons->lock); + vcons->input = -1; + vcons->display = MAP_FAILED; + vcons->scrolling = 0; + + /* Open the directory port of the virtual console. */ + vconsp = file_name_lookup_under (cons->dirport, name, + O_DIRECTORY | O_RDONLY, 0); + if (vconsp == MACH_PORT_NULL) + { + err = errno; + goto err; + } + + /* Within that directory, open the input node. */ + file = file_name_lookup_under (vconsp, "input", O_WRONLY /* | O_NONBLOCK */, 0); + if (file == MACH_PORT_NULL) + err = errno; + else + { + vcons->input = openport (file, O_WRONLY /* | O_NONBLOCK */); + if (vcons->input < 0) + err = errno; + else + /* openport() consumed the reference. */ + file = MACH_PORT_NULL; + } + if (err) + goto err; + + /* Within that directory, also open the display node. */ + file = file_name_lookup_under (vconsp, "display", O_RDONLY, 0); + if (file == MACH_PORT_NULL) + err = errno; + else + { + /* Acquire an additional reference for openport(). */ + err = mach_port_mod_refs (mach_task_self (), file, + MACH_PORT_RIGHT_SEND, +1); + if (err) + goto err; + fd = openport (file, O_RDONLY); + if (fd < 0) + err = errno; + } + if (err) + goto err; + + /* Map the whole file. */ + if (fstat (fd, &statbuf) < 0) + { + err = errno; + goto err; + } + vcons->display_size = statbuf.st_size; + vcons->display = mmap (0, vcons->display_size, PROT_READ, MAP_SHARED, fd, 0); + if (vcons->display == MAP_FAILED) + { + err = errno; + goto err; + } + + if (vcons->display->magic != CONS_MAGIC + || vcons->display->version >> CONS_VERSION_MAJ_SHIFT != 0) + { + err = EINVAL; + goto err; + } + vcons->state.screen.width = vcons->display->screen.width; + vcons->state.screen.height = vcons->display->screen.height; + vcons->state.screen.lines = vcons->display->screen.lines; + vcons->state.screen.matrix = (conchar_t *) + (((uint32_t *) vcons->display) + vcons->display->screen.matrix); + vcons->state.changes.length = vcons->display->changes.length; + vcons->state.changes.buffer = (cons_change_t *) + (((uint32_t *) vcons->display) + vcons->display->changes.buffer); + + /* Request notification messages. */ + notify = ports_get_right (vcons); + mach_port_set_qlimit (mach_task_self (), notify, 1); + + /* When this succeeds, we will immediately receive notification + messages for this virtual console. */ + mutex_lock (&vcons->lock); + err = file_notice_changes (file, notify, MACH_MSG_TYPE_MAKE_SEND); + if (!err) + { + *r_vcons = vcons; + goto out; + } + + err: + if (vcons->input >= 0) + { + close (vcons->input); + vcons->input = -1; + } + if (vcons->display != MAP_FAILED) + { + munmap (vcons->display, vcons->display_size); + vcons->display = MAP_FAILED; + } + if (notify) + { + mach_port_deallocate (mach_task_self (), notify); + ports_port_deref (vcons); + } + ports_destroy_right (vcons); + out: + if (fd > 0) + close (fd); + if (file != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), file); + if (vconsp != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), vconsp); + free (name); + return err; +} |