Hi, as discussed before, it is desirable to have a server-client model for the console:
VISUAL DISPLAY I \ / VISUAL DISPLAY II | | TERM ON VIRTUAl CONSOLE I ... > CONSOLE SERVER < TERM ON VIRTUAL CONSOLE II INPUT DEVICE I | | ... INPUT DEVICE II | | ... / \ The console server provides an abstract hardware console in the local encoding to the term server on the one hand, and on the other hand it provides two interfaces for user interaction: 1. The display interface, which is basically a map'able file which contains the text buffer and attribute (color, blinking, etc) matrix with the possibility for the user to receive asynchronous update information on the content. This also includes bells and gimmicks like this. 2. The input interface, which allows a kbd driver, mouse gestures, voice recognition software etc to input keyboard and similar events into the console server. This side of the interface will be UCS-4/UTF-8 based only. It is expected that usually, both parts are combined into a single program. This program also handles virtual console switching etc. See past discussion for details on what features this design makes possible. Please see also past discussion about what difficulties are in the design of the asynchronous update interface :) I gave a huge list, and did not make progress on that part yet. I guess experimentation will help a lot. (BTW, the Linux braille terminal driver polls the screen every so and so often, because there are no file change notifications, we should be able to do better than that). What I attach to this mail is a file that incorporates some the virtual console abstraction in console/console.c in the Hurd source tree, and new parts which provide a netfs based filesystem that looks like this: console: drwxrwx--- 2 root root 0 2. Jun 04:27 1/ drwxrwx--- 2 root root 0 2. Jun 04:27 3/ drwxrwx--- 2 root root 0 2. Jun 04:27 4/ 1: crw-rw---- 1 root root 0, 0 2. Jun 04:27 console -rw-rw---- 1 root root 2000 2. Jun 04:27 display 3: crw-rw---- 1 root root 0, 0 2. Jun 04:27 console -rw-rw---- 1 root root 2000 2. Jun 04:27 display 4: crw-rw---- 1 root root 0, 0 2. Jun 04:27 console -rw-rw---- 1 root root 2000 2. Jun 04:27 display The idea is that here are three virtual consoles provided by the server, with the IDs 1, 3 and 4. The "console" node in each directory provided by the server is for term, and the "display" node is for the display/input driver (better names are welcome). 2000 is 80x25 and the default size of a screen, this is just an example. I am inclined to give all virtual consoles for one console the same size and local encoding, but this is not terribly important. Nothing of this is set in stone, in particular the interface between the console server and the display driver. However, I think it might be a good idea to start with a simplicistic interface based on existing RPCs, and work from there. I think this is better than putting a lot of work into getting the existing code base with the old, monolithic design to make it functional. I have found a good description of xkb, btw, and will go for that. It just needs some time, and it might be better to write a clean console server, and a not-so-clean display and input driver so oskit-mach becomes usable, and then go from there. Anyway, the point of this mail is to put the netfs code into public. This is the first netfs based filesystem I wrote, and I took some of the code from hostmux (found a bug in there, too). The internals are like this: Virtual consoles are created on the fly when looking up nodes in root. This is unproblematic, as only available consoles are listed in netfs_get_dirents, and usually programs will work with that. Probably some parts of the virtual console should be allocated lazily, so you don't hit the full cost just to look up a node you don't want. A virtual console structure is also the place where created nodes are looked up. This safes the inode hash table. For this to work, each node has to keep a reference to the virtual console, and the virtual console have to back reference to the node (but without acquiring a reference to it). netfs_node_norefs removes the back reference before releasing the ref to the virtual console. Locking is a bit peculiar, but not really difficult, as the virtual console structure itself is fine-graded. All this is exactly how hostmux stores and references the node and hostname structures. The code is tested and works as far as it is there. It has one serious limitation, and that is the lookup of .., which currently assumes a flat hierarchy (only one level of subdirectories). It doesn't integrate all too much with the virtual console code, either, some comments show where this can happen. As I said, the point here is to get the filesystem part going, rather than the text buffer and input queue details (which I have already written in the console/* code and just need to reuse and put some nice abstractions in there). Any comments welcome, of course! Thanks, Marcus /* console -- a console server Copyright (C) 1997, 1999, 2002 Free Software Foundation, Inc. Written by Miles Bader and Marcus Brinkmann. 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 <hurd/netfs.h> #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <unistd.h> #include <argp.h> #include <error.h> #include <string.h> #include <fcntl.h> #include <dirent.h> #include <sys/mman.h> #include <sys/stat.h> #include <cthreads.h> #include <mcheck.h> #include <version.h> #include "console.h" const char *argp_program_version = STANDARD_HURD_VERSION (console); char *netfs_server_name = "console"; char *netfs_server_version = HURD_VERSION; int netfs_maxsymlinks = 16; /* Arbitrary. */ struct vcons { /* Protected by cons->lock. */ vcons_t next; vcons_t prev; /* We acquire one reference per netnode. */ int refcnt; /* The following members remain constant over the lifetime of the object and don't need to be locked. */ int id; char *name; cons_t cons; 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; /* Nodes in the filesystem referring to this virtual console. */ struct node *dir_node; struct node *cons_node; struct node *disp_node; /* 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 { /* The lock protects the console, all virtual consoles contained in it and the reference counters. It also locks the configuration. */ struct mutex lock; vcons_t vcons_list; /* The encoding. */ const char *encoding; struct node *node; mach_port_t underlying; /* A template for the stat information of all nodes. */ struct stat stat_template; } mycons; cons_t cons = &mycons; /* 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) { vcons_t previous_vcons = 0; vcons_t vcons; if (!id && !create) return EINVAL; mutex_lock (&cons->lock); if (id) { if (cons->vcons_list && cons->vcons_list->id <= id) { previous_vcons = cons->vcons_list; while (previous_vcons->next && previous_vcons->next->id <= id) previous_vcons = previous_vcons->next; if (previous_vcons->id == id) { previous_vcons->refcnt++; mutex_unlock (&cons->lock); *r_vcons = previous_vcons; return 0; } } else if (!create) { mutex_unlock (&cons->lock); return ESRCH; } } else { id = 1; if (cons->vcons_list && cons->vcons_list->id == 1) { previous_vcons = cons->vcons_list; while (previous_vcons && previous_vcons->id == id) { id++; previous_vcons = previous_vcons->next; } } } vcons = calloc (1, sizeof (struct vcons)); if (!vcons) { mutex_unlock (&vcons->cons->lock); return ENOMEM; } vcons->cons = cons; vcons->refcnt = 1; vcons->id = id; asprintf (&vcons->name, "%i", id); mutex_init (&vcons->lock); mutex_init (&vcons->output_lock); condition_init (&vcons->output_resumed); /* Insert the virtual console into the doubly linked list. */ if (previous_vcons) { vcons->prev = previous_vcons; if (previous_vcons->next) { previous_vcons->next->prev = vcons; vcons->next = previous_vcons->next; } previous_vcons->next = vcons; } else { if (cons->vcons_list) { cons->vcons_list->prev = vcons; vcons->next = cons->vcons_list; } cons->vcons_list = vcons; } mutex_unlock (&cons->lock); *r_vcons = vcons; return 0; } /* Acquire an additional reference to the virtual console VCONS. */ void vcons_ref (vcons_t vcons) { cons_t cons = vcons->cons; mutex_lock (&cons->lock); vcons->refcnt++; mutex_unlock (&cons->lock); } /* 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) { cons_t cons = vcons->cons; mutex_lock (&cons->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. */ /* XXX Destroy the state. */ free (vcons->name); if (vcons->prev) vcons->prev->next = vcons->next; if (vcons->next) vcons->next->prev = vcons->prev; if (!vcons->prev && !vcons->next) vcons->cons->vcons_list = NULL; free (vcons); } mutex_unlock (&cons->lock); } typedef enum { VCONS_NODE_DIR = 0, VCONS_NODE_CONSOLE, VCONS_NODE_DISPLAY } vcons_node_type; /* Make a new virtual node. Always consumes the ports. */ static error_t new_node (struct node **np, vcons_t vcons, vcons_node_type type) { struct netnode *nn = calloc (1, sizeof *nn); if (nn == 0) return ENOMEM; nn->vcons = vcons; *np = netfs_make_node (nn); if (*np == 0) { free (nn); return ENOMEM; } (*np)->nn_stat = cons->stat_template; switch (type) { case VCONS_NODE_DIR: (*np)->nn_stat.st_ino = vcons->id << 2; (*np)->nn_stat.st_mode |= S_IFDIR; (*np)->nn_stat.st_size = 0; break; case VCONS_NODE_CONSOLE: (*np)->nn_stat.st_ino = (vcons->id << 2) + 1; (*np)->nn_stat.st_mode |= S_IFCHR; /* Don't set nn_translated! */ (*np)->nn_stat.st_size = 0; break; case VCONS_NODE_DISPLAY: (*np)->nn_stat.st_ino = (vcons->id << 2) + 2; (*np)->nn_stat.st_mode |= S_IFREG; (*np)->nn_stat.st_size = 2000; /* XXX */ break; } /* If the underlying node isn't a directory, propagate read permission to execute permission since we need that for lookups. */ if (! S_ISDIR (cons->stat_template.st_mode) && S_ISDIR ((*np)->nn_stat.st_mode)) { if (cons->stat_template.st_mode & S_IRUSR) (*np)->nn_stat.st_mode |= S_IXUSR; if (cons->stat_template.st_mode & S_IRGRP) (*np)->nn_stat.st_mode |= S_IXGRP; if (cons->stat_template.st_mode & S_IROTH) (*np)->nn_stat.st_mode |= S_IXOTH; } fshelp_touch (&(*np)->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME, console_maptime); return 0; } /* Node management. */ /* Node NP has no more references; free all its associated storage. */ void netfs_node_norefs (struct node *np) { vcons_t vcons = np->nn->vcons; /* The root node does never go away. */ assert (!np->nn->cons && np->nn->vcons); /* Find the back reference to ourself in the virtual console structure, and delete it. */ mutex_lock (&vcons->lock); if (np == vcons->dir_node) vcons->dir_node = 0; else if (np == vcons->cons_node) vcons->cons_node = 0; else { assert (np == vcons->disp_node); vcons->disp_node = 0; } mutex_unlock (&vcons->lock); /* Release our reference. */ vcons_release (vcons); free (np->nn); free (np); } /* Attempt to create a file named NAME in DIR for USER with MODE. Set *NODE to the new node upon return. On any error, clear *NODE. *NODE should be locked on success; no matter what, unlock DIR before returning. */ error_t netfs_attempt_create_file (struct iouser *user, struct node *dir, char *name, mode_t mode, struct node **np) { /* We create virtual consoles dynamically on the fly, so there is no need for an explicit create operation. */ *np = 0; mutex_unlock (&dir->lock); return EOPNOTSUPP; } /* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero if we just created this node. Return an error if we should not permit the open to complete because of a permission restriction. */ error_t netfs_check_open_permissions (struct iouser *user, struct node *node, int flags, int newnode) { error_t err = 0; if (flags & O_READ) err = fshelp_access (&node->nn_stat, S_IREAD, user); if (!err && (flags & O_WRITE)) err = fshelp_access (&node->nn_stat, S_IWRITE, user); if (!err && (flags & O_EXEC)) err = fshelp_access (&node->nn_stat, S_IEXEC, user); return err; } /* This should attempt a utimes call for the user specified by CRED on node NODE, to change the atime to ATIME and the mtime to MTIME. */ error_t netfs_attempt_utimes (struct iouser *cred, struct node *node, struct timespec *atime, struct timespec *mtime) { error_t err = fshelp_isowner (&node->nn_stat, cred); int flags = TOUCH_CTIME; if (! err) { if (mtime) { node->nn_stat.st_mtime = mtime->tv_sec; node->nn_stat.st_mtime_usec = mtime->tv_nsec / 1000; } else flags |= TOUCH_MTIME; if (atime) { node->nn_stat.st_atime = atime->tv_sec; node->nn_stat.st_atime_usec = atime->tv_nsec / 1000; } else flags |= TOUCH_ATIME; fshelp_touch (&node->nn_stat, flags, console_maptime); } return err; } /* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC) in *TYPES for file NODE and user CRED. */ error_t netfs_report_access (struct iouser *cred, struct node *node, int *types) { *types = 0; if (fshelp_access (&node->nn_stat, S_IREAD, cred) == 0) *types |= O_READ; if (fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0) *types |= O_WRITE; if (fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0) *types |= O_EXEC; return 0; } /* Make sure that NP->nn_stat is filled with the most current information. CRED identifies the user responsible for the operation. NP is locked. */ error_t netfs_validate_stat (struct node *np, struct iouser *cred) { /* We are always uptodate. */ return 0; } /* This should sync the file NODE completely to disk, for the user CRED. If WAIT is set, return only after sync is completely finished. */ error_t netfs_attempt_sync (struct iouser *cred, struct node *np, int wait) { return 0; } /* Directory management. */ /* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If the name was not found, then return ENOENT. On any error, clear *NODE. (*NODE, if found, should be locked, this call should unlock DIR no matter what.) */ error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, char *name, struct node **node) { error_t err; *node = 0; err = fshelp_access (&dir->nn_stat, S_IEXEC, user); if (err) goto out; if (strcmp (name, ".") == 0) { /* Current directory -- just add an additional reference to DIR and return it. */ netfs_nref (dir); *node = dir; goto out; } if (strcmp (name, "..") == 0) { /* Parent directory -- if this is the root directory, return EAGAIN. Otherwise return the root node, because we know that our hierarchy is only one level deep. */ if (dir->nn->cons) err = EAGAIN; else { netfs_nref (netfs_root_node); *node = netfs_root_node; } goto out; } if (dir->nn->cons) { /* This is the root directory. Look up the desired virtual console, creating it on the fly if necessary. */ vcons_t vcons; int release_vcons = 0; char *tail = NULL; int id; errno = 0; id = strtol (name, &tail, 0); if ((tail && *tail) || errno) { err = ENOENT; goto out; } err = vcons_lookup (dir->nn->cons, id, 1, &vcons); if (err == ESRCH || err == EINVAL) err = ENOENT; if (err) goto out; mutex_lock (&vcons->lock); if (vcons->dir_node) { /* We already have a directory node for this virtual console. Use that, acquire a reference for it, and drop our extra reference to the virtual console. */ *node = vcons->dir_node; netfs_nref (*node); release_vcons = 1; } else { /* Create a new directory node, connsuming the reference to the virtual console. */ err = new_node (node, vcons, VCONS_NODE_DIR); if (!err) vcons->dir_node = *node; else release_vcons = 1; } mutex_unlock (&vcons->lock); if (release_vcons) vcons_release (vcons); } else { /* This is a virtual console directory node. */ vcons_t vcons = dir->nn->vcons; int ref_vcons = 0; assert (dir == vcons->dir_node); if (!strcmp (name, "console")) { mutex_lock (&vcons->lock); if (vcons->cons_node) { *node = vcons->cons_node; netfs_nref (*node); } else { err = new_node (node, vcons, VCONS_NODE_CONSOLE); if (!err) { vcons->cons_node = *node; ref_vcons = 1; } } mutex_unlock (&vcons->lock); } else if (!strcmp (name, "display")) { mutex_lock (&vcons->lock); if (vcons->disp_node) { *node = vcons->disp_node; netfs_nref (*node); } else { err = new_node (node, vcons, VCONS_NODE_DISPLAY); if (!err) { vcons->disp_node = *node; ref_vcons = 1; } } mutex_unlock (&vcons->lock); } else err = ENOENT; if (ref_vcons) vcons_ref (vcons); } if (!err) fshelp_touch (&dir->nn_stat, TOUCH_ATIME, console_maptime); out: mutex_unlock (&dir->lock); if (err) *node = 0; else mutex_lock (&(*node)->lock); return err; } /* Returned directory entries are aligned to blocks this many bytes long. Must be a power of two. */ #define DIRENT_ALIGN 4 #define DIRENT_NAME_OFFS offsetof (struct dirent, d_name) /* Length is structure before the name + the name + '\0', all padded to a four-byte alignment. */ #define DIRENT_LEN(name_len) \ ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \ & ~(DIRENT_ALIGN - 1)) /* Implement the netfs_get_dirents callback as described in <hurd/netfs.h>. */ error_t netfs_get_dirents (struct iouser *cred, struct node *dir, int first_entry, int num_entries, char **data, mach_msg_type_number_t *data_len, vm_size_t max_data_len, int *data_entries) { error_t err; int count = 0; size_t size = 0; /* Total size of our return block. */ struct vcons *first_vcons, *vcons; /* Add the length of a directory entry for NAME to SIZE and return true, unless it would overflow MAX_DATA_LEN or NUM_ENTRIES, in which case return false. */ int bump_size (const char *name) { if (num_entries == -1 || count < num_entries) { size_t new_size = size + DIRENT_LEN (strlen (name)); if (max_data_len > 0 && new_size > max_data_len) return 0; size = new_size; count++; return 1; } else return 0; } if (!dir->nn->cons && !(dir == dir->nn->vcons->dir_node)) return ENOTDIR; if (dir->nn->cons) { mutex_lock (&dir->nn->cons->lock); /* Find the first entry. */ for (first_vcons = dir->nn->cons->vcons_list, count = 2; first_vcons && first_entry > count; first_vcons = first_vcons->next) count++; count = 0; } /* Make space for the `.' and `..' entries. */ if (first_entry == 0) bump_size ("."); if (first_entry <= 1) bump_size (".."); if (dir->nn->cons) { /* See how much space we need for the result. */ for (vcons = first_vcons; vcons; vcons = vcons->next) #if 0 if (vcons->dir_node /* XXX see above */ && !bump_size (vcons->name)) #else if (!bump_size (vcons->name)) #endif break; } else { if (first_entry <= 2) bump_size ("console"); if (first_entry <= 3) bump_size ("display"); } /* Allocate it. */ *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); err = ((void *) *data == (void *) -1) ? errno : 0; if (! err) /* Copy out the result. */ { char *p = *data; int add_dir_entry (const char *name, ino_t fileno, int type) { if (num_entries == -1 || count < num_entries) { struct dirent hdr; size_t name_len = strlen (name); size_t sz = DIRENT_LEN (name_len); if (sz > size) return 0; else size -= sz; hdr.d_fileno = fileno; hdr.d_reclen = sz; hdr.d_type = type; hdr.d_namlen = name_len; memcpy (p, &hdr, DIRENT_NAME_OFFS); strcpy (p + DIRENT_NAME_OFFS, name); p += sz; count++; return 1; } else return 0; } *data_len = size; *data_entries = count; count = 0; if (dir->nn->cons) { /* Add `.' and `..' entries. */ if (first_entry == 0) add_dir_entry (".", 2, DT_DIR); if (first_entry <= 1) add_dir_entry ("..", 2, DT_DIR); /* Fill in the real directory entries. */ for (vcons = first_vcons; vcons; vcons = vcons->next) if ( #if 0 /* XXX see above */ vcons->dir_node && #endif !add_dir_entry (vcons->name, vcons->id << 2, DT_DIR)) break; mutex_unlock (&dir->nn->cons->lock); } else { /* Add `.' and `..' entries. */ if (first_entry == 0) add_dir_entry (".", dir->nn_stat.st_ino, DT_DIR); if (first_entry <= 1) add_dir_entry ("..", 2, DT_DIR); if (first_entry <= 2) add_dir_entry ("console", (vcons->id << 2) + 1, DT_REG); if (first_entry <= 3) add_dir_entry ("display", (vcons->id << 2) + 2, DT_REG); } } fshelp_touch (&dir->nn_stat, TOUCH_ATIME, console_maptime); return err; } /* This should sync the entire remote filesystem. If WAIT is set, return only after sync is completely finished. */ error_t netfs_attempt_syncfs (struct iouser *cred, int wait) { return 0; } /* This should attempt a chmod call for the user specified by CRED on node NODE, to change the owner to UID and the group to GID. */ error_t netfs_attempt_chown (struct iouser *cred, struct node *node, uid_t uid, uid_t gid) { cons_t cons = node->nn->cons; vcons_t vcons; error_t err; if (!cons) return EOPNOTSUPP; err = file_chown (cons->underlying, uid, gid); if (err) return err; /* Change NODE's owner. */ node->nn_stat.st_uid = uid; node->nn_stat.st_gid = gid; mutex_lock (&cons->lock); cons->stat_template.st_uid = uid; cons->stat_template.st_gid = gid; /* Change the owner of each leaf node. */ for (vcons = cons->vcons_list; vcons; vcons = vcons->next) { if (vcons->dir_node) { vcons->dir_node->nn_stat.st_uid = uid; vcons->dir_node->nn_stat.st_gid = gid; } if (vcons->cons_node) { vcons->cons_node->nn_stat.st_uid = uid; vcons->cons_node->nn_stat.st_gid = gid; } if (vcons->disp_node) { vcons->disp_node->nn_stat.st_uid = uid; vcons->disp_node->nn_stat.st_gid = gid; } } mutex_unlock (&cons->lock); fshelp_touch (&node->nn_stat, TOUCH_CTIME, console_maptime); return err; } /* This should attempt a chauthor call for the user specified by CRED on node NODE, to change the author to AUTHOR. */ error_t netfs_attempt_chauthor (struct iouser *cred, struct node *node, uid_t author) { cons_t cons = node->nn->cons; vcons_t vcons; error_t err; if (!cons) return EOPNOTSUPP; err = file_chauthor (cons->underlying, author); if (err) return err; /* Change NODE's owner. */ node->nn_stat.st_author = author; mutex_lock (&cons->lock); cons->stat_template.st_author = author; /* Change the owner of each leaf node. */ for (vcons = cons->vcons_list; vcons; vcons = vcons->next) { if (vcons->dir_node) vcons->dir_node->nn_stat.st_author = author; if (vcons->cons_node) vcons->cons_node->nn_stat.st_author = author; if (vcons->disp_node) vcons->disp_node->nn_stat.st_author = author; } mutex_unlock (&cons->lock); fshelp_touch (&node->nn_stat, TOUCH_CTIME, console_maptime); return err; } /* This should attempt a chmod call for the user specified by CRED on node NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning of chmod, this function is also used to attempt to change files into other types. If such a transition is attempted which is impossible, then return EOPNOTSUPP. */ error_t netfs_attempt_chmod (struct iouser *cred, struct node *node, mode_t mode) { error_t err; mode &= ~S_ITRANS; if ((mode & S_IFMT) == 0) mode |= (node->nn_stat.st_mode & S_IFMT); if (!node->nn->cons || ((mode & S_IFMT) != (node->nn_stat.st_mode & S_IFMT))) return EOPNOTSUPP; err = file_chmod (node->nn->cons->underlying, mode & ~S_IFMT); if (err) return err; node->nn_stat.st_mode = mode; fshelp_touch (&node->nn_stat, TOUCH_CTIME, console_maptime); return err; } /* The user must define this function. Attempt to turn locked node NP (user CRED) into a symlink with target NAME. */ error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *np, char *name) { return EOPNOTSUPP; } error_t netfs_attempt_mkdev (struct iouser *cred, struct node *np, mode_t type, dev_t indexes) { return EOPNOTSUPP; } error_t netfs_attempt_chflags (struct iouser *cred, struct node *np, int flags) { return EOPNOTSUPP; } error_t netfs_attempt_set_size (struct iouser *cred, struct node *np, off_t size) { return EOPNOTSUPP; } error_t netfs_attempt_statfs (struct iouser *cred, struct node *np, struct statfs *st) { return EOPNOTSUPP; } error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir, char *name, mode_t mode) { return EOPNOTSUPP; } error_t netfs_attempt_unlink (struct iouser *user, struct node *dir, char *name) { return EOPNOTSUPP; } error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir, char *fromname, struct node *todir, char *toname, int excl) { return EOPNOTSUPP; } error_t netfs_attempt_rmdir (struct iouser *user, struct node *dir, char *name) { return EOPNOTSUPP; } error_t netfs_attempt_link (struct iouser *user, struct node *dir, struct node *file, char *name, int excl) { return EOPNOTSUPP; } error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir, mode_t mode, struct node **np) { return EOPNOTSUPP; } error_t netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf) { return EOPNOTSUPP; } error_t netfs_attempt_read (struct iouser *cred, struct node *np, off_t offset, size_t *len, void *data) { vcons_t vcons = np->nn->vcons; if (!vcons || np == vcons->dir_node) return EOPNOTSUPP; if (np == vcons->cons_node) *len = 0; /* Pass input queue content to caller. */ else { assert (np == vcons->disp_node); *len = 0; /* Pass display content to caller. */ } return 0; } error_t netfs_attempt_write (struct iouser *cred, struct node *np, off_t offset, size_t *len, void *data) { vcons_t vcons = np->nn->vcons; if (!vcons || np == vcons->dir_node) return EOPNOTSUPP; if (np == vcons->cons_node) *len = 0; /* Write input to text matrix. */ else { assert (np == vcons->disp_node); *len = 0; /* Put input into input queue. */ } return 0; } int main (int argc, char **argv) { error_t err; mach_port_t bootstrap; struct stat ul_stat; struct netnode root_nn = { cons: cons, vcons: 0 }; mtrace(); struct argp argp = { NULL, NULL, NULL, "A translator that provides virtual console displays." }; /* Parse our command line arguments (all none of them). */ argp_parse (&argp, argc, argv, 0, 0, 0); task_get_bootstrap_port (mach_task_self (), &bootstrap); netfs_init (); /* Create the root node (some attributes initialized below). */ netfs_root_node = netfs_make_node (&root_nn); if (! netfs_root_node) error (5, ENOMEM, "Cannot create root node"); err = maptime_map (0, 0, &console_maptime); if (err) error (6, err, "Cannot map time"); mutex_init (&cons->lock); cons->node = netfs_root_node; cons->underlying = netfs_startup (bootstrap, O_READ); if (cons->underlying == MACH_PORT_NULL) error (5, err, "Cannot get underlying node"); err = io_stat (cons->underlying, &ul_stat); if (err) error (6, err, "Cannot stat underlying node"); /* CONS.stat_template contains some fields that are inherited by all nodes we create. */ cons->stat_template.st_uid = ul_stat.st_uid; cons->stat_template.st_gid = ul_stat.st_gid; cons->stat_template.st_author = ul_stat.st_author; cons->stat_template.st_mode = (ul_stat.st_mode & ~S_IFMT & ~S_ITRANS); cons->stat_template.st_fsid = getpid (); cons->stat_template.st_nlink = 1; cons->stat_template.st_fstype = FSTYPE_MISC; /* Initialize the root node's stat information. */ netfs_root_node->nn_stat = cons->stat_template; netfs_root_node->nn_stat.st_ino = 2; netfs_root_node->nn_stat.st_mode |= S_IFDIR; netfs_root_node->nn_translated = 0; /* If the underlying node isn't a directory, propagate read permission to execute permission since we need that for lookups. */ if (! S_ISDIR (ul_stat.st_mode)) { if (ul_stat.st_mode & S_IRUSR) netfs_root_node->nn_stat.st_mode |= S_IXUSR; if (ul_stat.st_mode & S_IRGRP) netfs_root_node->nn_stat.st_mode |= S_IXGRP; if (ul_stat.st_mode & S_IROTH) netfs_root_node->nn_stat.st_mode |= S_IXOTH; } fshelp_touch (&netfs_root_node->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME, console_maptime); netfs_server_loop (); /* Never returns. */ /*NOTREACHED*/ return 0; } -- `Rhubarb is no Egyptian god.' Debian http://www.debian.org [EMAIL PROTECTED] Marcus Brinkmann GNU http://www.gnu.org [EMAIL PROTECTED] [EMAIL PROTECTED] http://www.marcus-brinkmann.de _______________________________________________ Bug-hurd mailing list [EMAIL PROTECTED] http://mail.gnu.org/mailman/listinfo/bug-hurd