summaryrefslogtreecommitdiff
path: root/console-client/current-vcs.c
diff options
context:
space:
mode:
Diffstat (limited to 'console-client/current-vcs.c')
-rw-r--r--console-client/current-vcs.c223
1 files changed, 223 insertions, 0 deletions
diff --git a/console-client/current-vcs.c b/console-client/current-vcs.c
new file mode 100644
index 00000000..3bfc1a75
--- /dev/null
+++ b/console-client/current-vcs.c
@@ -0,0 +1,223 @@
+/* current-vcs.c -- Add a "current vcs" symlink to the cons node.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Written by Samuel Thibault.
+
+ This program 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.
+
+ This program 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 <argp.h>
+#include <driver.h>
+#include <input.h>
+#include <stdio.h>
+#include <error.h>
+#include <sys/mman.h>
+
+#include "trans.h"
+
+
+/* We use the same infrastructure as the kbd and mouse repeaters. */
+
+/* The default name of the node of the repeater. */
+#define DEFAULT_REPEATER_NODE "vcs"
+
+/* The name of the repeater node. */
+static char *repeater_node;
+
+/* The repeater node. */
+static consnode_t vcs_node;
+
+
+/* Callbacks for vcs console node. */
+
+/* Reading the link to get current vcs, returns length of path (trailing \0
+ excluded). */
+static error_t
+vcs_readlink (struct iouser *user, struct node *np, char *buf)
+{
+ int cur;
+ error_t ret = 0;
+
+ ret = console_current_id (&cur);
+ if (!ret)
+ {
+ if (!buf)
+ ret = snprintf (NULL, 0, "%s/%d", cons_file, cur);
+ else
+ ret = sprintf (buf, "%s/%d", cons_file, cur);
+
+ if (ret < 0)
+ ret = errno;
+ }
+ return ret;
+}
+
+static error_t
+vcs_read (struct protid *user, char **data,
+ mach_msg_type_number_t * datalen, off_t offset,
+ mach_msg_type_number_t amount)
+{
+ int err;
+ int size;
+ char *buf;
+
+ if (amount > 0)
+ {
+ size = vcs_readlink (user->user, NULL, NULL);
+ if (size < 0)
+ return size;
+
+ buf = alloca (size);
+
+ err = vcs_readlink (user->user, NULL, buf);
+
+ if (err < 0)
+ return err;
+
+ if (offset + amount > size)
+ amount = size - offset;
+ if (amount < 0)
+ amount = 0;
+
+ if (*datalen < amount)
+ {
+ *data = mmap (0, amount, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ return ENOMEM;
+ }
+
+ memcpy (*data, buf + offset, amount);
+ *datalen = amount;
+ }
+ return 0;
+}
+
+/* Making a link to set current vcs.
+ Relative values perform relative switches. */
+static error_t
+vcs_mksymlink (struct iouser *user, struct node *np, char *name)
+{
+ char *c, *d;
+ int vt, delta = 0;
+
+ c = strrchr (name, '/');
+ if (!c)
+ c = name;
+ else
+ c++;
+ if (!*c)
+ return EINVAL;
+
+ if (*c == '-' || *c == '+')
+ delta = 1;
+
+ vt = strtol (c, &d, 10);
+ if (*d)
+ /* Bad number. */
+ return EINVAL;
+
+ if (!vt)
+ return 0;
+ return console_switch (delta ? 0 : vt, delta ? vt : 0);
+}
+
+
+static const char doc[] = "Current VCS Driver";
+
+static const struct argp_option options[] =
+ {
+ { "repeater", 'r', "NODE", OPTION_ARG_OPTIONAL,
+ "Set a current vcs translator on NODE (default: " DEFAULT_REPEATER_NODE ")"},
+ { 0 }
+ };
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ int *pos = (int *) state->input;
+
+ switch (key)
+ {
+ case 'r':
+ repeater_node = arg ? arg: DEFAULT_REPEATER_NODE;
+ break;
+
+ case ARGP_KEY_END:
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ *pos = state->next;
+ return 0;
+}
+
+static struct argp argp = {options, parse_opt, 0, doc};
+
+/* Initialize the current VCS driver. */
+static error_t
+vcs_repeat_init (void **handle, int no_exit, int argc, char *argv[], int *next)
+{
+ error_t err;
+ int pos = 1;
+
+ /* Parse the arguments. */
+ err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER | ARGP_NO_EXIT
+ | ARGP_SILENT, 0, &pos);
+ *next += pos - 1;
+
+ if (err && err != EINVAL)
+ return err;
+
+ return 0;
+}
+
+static error_t
+vcs_repeat_start (void *handle)
+{
+ error_t err;
+
+ err = console_create_consnode (repeater_node, &vcs_node);
+ if (err)
+ return err;
+
+ vcs_node->read = vcs_read;
+ vcs_node->write = NULL;
+ vcs_node->select = NULL;
+ vcs_node->open = NULL;
+ vcs_node->close = NULL;
+ vcs_node->demuxer = NULL;
+ vcs_node->readlink = vcs_readlink;
+ vcs_node->mksymlink = vcs_mksymlink;
+ console_register_consnode (vcs_node);
+
+ return 0;
+}
+
+static error_t
+vcs_repeat_fini (void *handle, int force)
+{
+ console_unregister_consnode (vcs_node);
+ console_destroy_consnode (vcs_node);
+ return 0;
+}
+
+
+struct driver_ops driver_vcs_repeat_ops =
+ {
+ vcs_repeat_init,
+ vcs_repeat_start,
+ vcs_repeat_fini
+ };
+