domingo, 16 de octubre de 2011

9p-srv: experimental devdraw for p9p

From the beginning, one of the goals of wsys was to replace all the system dependent code duplicated in several Plan 9 related projects. The unix version of wsys could already do this from Plan 9 (9vx or drawterm) and Inferno but, until now, there was no way to use wsys from p9p programs.

The problem with p9p is that it does not use common 9p devices. Instead, it uses a modified libdraw and the devdraw(1) program. There are versions of devdraw for x11 (x11-*.c) and several OS X systems (osx-*.c, cocoa-*.c). The file 9p-srv.c (included in the util/ directory of the wsys distribution) is an implementation of devdraw which makes use of 9pclient and wsys.

Compile 9p-srv.c and set the DEVDRAW env variable accordingly and wsys will take care of the windows of p9p applications too.

NOTE: 9p-srv is an experiment, even more than wsys is. It can fail and, anyway, it probably is not the best solution to the problem. It was an easy way to get the ball rolling, and it gives me more chances to test wsys.

devwsys is now wsys

Rio is a file server which provides virtual devices, but it is not called a device. Other file servers offer some other files which can be mounted on /dev, but they are not called devices either. P9p's devdraw is the exception, not the norm and, therefore, devwsys has been renamed to wsys.

For the moment, the code has not moved from its usual location: https://bitbucket.org/yiyus/devwsys-prev

There has been several improvements since the end of GSoC. Check the changelog.

domingo, 21 de agosto de 2011

Manual pages

man(1) pages have been added to the devwsys-prev repository. Html versions are available in the wiki of the project at bitbucket. The new manual pages are:

Wctl: a killer service

In Plan 9, rio(1) does not only manage windows. In fact, it is also a process manager. We create a new window to make a new process. When we want to finish it, we delete that window. Devwsys, on the other hand, is running on a host system, and therefore it cannot create or delete processes.

What we need to create processes is a service in the hosted Plan 9 system which mounts devwsys and runs the given command. This is what the wctl script does, announcing a file descriptor in /srv and reading new commands, in the format of rio's wctl service.

A more interesting problem is how to finish a process when that window is deleted. Rio reads a pid as part of the attach specifier. When the user asks to delete a window (with the mouse or a wctl message) a different thread sends a note to the process, waits a bit, kill it and, only then, deletes the window.

Devwsys cannot create or finish processes, so it needs a method to say it to wctl. The IPC mechanism used is, of course, 9P. The pid in the attach specifier is read by devwsys and stored. When the user wants to close a window (with a wctl message or a request from the X window manager), a read of the file /kill will return that pid. If we read the file before the window has been asked for deletion it will just block. That way, we can wait for a window to be deleted. If there is nobody blocked reading /kill, the window is immediately deleted.

The "killer" process has access to the kill file mounting the root of the devwsys file system, using the attach specifier "/". If this specifier is given to mount, a new window will not be created. Instead, we will have access to a file tree containing the kill file and the wsys and draw directories (where the draw directory does not contain a new file, because there is no associated window).

The first thing the wctl script does is mounting the root fs. Each time a pid is read from the kill file, killproc will send a kill note to the process, wait 0.2 seconds, and then will send kill to the ctl file of the process. Once the process finishes all the references to its window file system are lost, and the window is destroyed by devwsys (in theory, acme for example is not so simple).

martes, 16 de agosto de 2011

Libninep

When this project was started one of the first questions that had to be solved was how to serve the 9P protocol from unix. We already depend on some Inferno libraries and Inferno includes a library to write 9P servers, libstyx. This library looks like an obvious choice. However, libstyx has some limitations: it does not support out-of-order replies, does not give the option to select on extra fds (we need that for the connection with X) and its representation of file trees is somewhat static.

Libninep is a modified libstyx, which tries to solve these issues.

Most of the content of styx(10) and styxserver(10) still applies to libninep (after s/styx/ninep/g, of course). The most important differences are explained in the next paragraphs.

Main loop
The loop of a libstyx application has this form:
for(;;)
  styxwait(&s);
  styxproccess(&s);
}
After styxwait returns, styxproccess reads the next request, looks for the corresponding function in s->ops, runs it if found, and replies. With libninep we have more options. The main loop of devwsys looks like this:
for(;;)
  nineplisten(&s, xfd);
  ninepwait(&s);
  if(ninepready(&s, xfd)
    xevent();
  ninepdefault(&s);
  updaterefs();
  ninepreply(&s);
}
There are some new library functions here: nineplisten adds a fd to the fd set to select from and ninepready checks if it is ready. The styxprocess function has been split in two: ninepdefault and ninepreply. However, the key difference is ninepwait, which is doing more than styxwait.

While styxwait just waits for a fd to be ready and let styxprocess to do all the work, ninepwait will read the next 9P request when possible, setting s->curc and s->fcall. ninepdefault will process this request, using s->ops when available, and probably turning a Tmessage into a Rmessage. Only when ninepreply is called this request is actually replied. This way, we can use ninepdefault for common operations but, if that is not enough, we can also process 9P requests on our own, just filling the fields of s->fcall and calling ninepreply when we are done.

What devwsys is doing is calling updaterefs after ninepdefault. This function will take care of updating the number of references to a window after an attach, a walk, or a clunk transaction. To do this with libstyx, we would have to give up on using its facilities to handle file trees and implement walks and dirreads ourselves.

Out of order replies
When reading event files (cons, mouse) the file server cannot reply immediately to a request, it has to wait until an event is received. This functionality is provided by two functions: ninepreplylater will put a request on hold and returns a pointer to a Pending struct, which can be passed to ninepcompleted to reply when we are done. After calling ninepreplylater, ninepreply will have no effect until ninepwait is called again.

It is the responsibility of the client to keep a queue of pending requests if desired. However, libninep will take care of marking them as flushed if a Tflush request is made.

File trees: binding
Libstyx includes some functions to help with the creation of file trees: styxadddir and styxaddfile. The advantage of using these functions is that using them we do not have to worry about implementing walks or directory reads, but they do not allow us to give several entry points to a given file. For example, devwsys has to give access to the files of a window in the wsys directory of every other window and on its root, and the clients of draw should be accessible by every window.

This problem is solved in Linux using links. The same problem is solved by Plan 9 (and Inferno) in a more elegant way by bind. So, libninep includes a ninepbind function. Its usage is quite simple: given the qid of two directories (pqid and qid) the files of qid will also be accessible from pqid.

In devwsys, the file system root only includes two directories: /wsys and /draw:
ninepadddir(s, Qroot, Qwsys, "wsys", 0555, eve);
ninepadddir(s, Qroot, Qdraw, "draw", 0555, eve);
When a new window is created (due to an attach request), files for that window are created inside the corresponding /wsys/n/ directory:
p = PATH(w->id, 0);
ninepadddir(s, Qwsys, p, name, 0555, eve);
ninepaddfile(s, p, p|Qcons, "cons", 0666, eve);
ninepaddfile(s, p, p|Qconsctl, "consctl", 0222, eve);
...
ninepadddir(s, p, p|Qwsys, "wsys", 0555, eve);
ninepadddir(s, p, p|Qdraw, "draw", 0555, eve);
ninepaddfile(s, p|Qdraw, p|Qnew, "new", 0666, eve);
Then, we bind the directories /wsys and /draw to the corresponding directories of the window:
ninepbind(s, p|Qwsys, Qwsys);
ninepbind(s, p|Qdraw, Qdraw); 
When a new client attachs to devwsys, wsysattach (which is server->ops->attach) will be called. This functions sets the qid to be returned to be the one corresponding to /wsys/n and the binding magic will take care automatically of walking and reading directories.
Note 1: rio's fs lets you to walk to /wsys/n/wsys, but no further. Devwsys, which uses libninep and bind directories, does not have this limit. You can walk as deep as you want into /wsys/n1/wsys/n2/wsys/n1/wsys/...
Note 2: The files of every draw client are added to /draw/n. Although /draw is bound to /wsys/n/draw we still have access to the /wsys/n/draw/new file which corresponds to the window.
Announce on unix addresses
You can only post your 9P server to a port using libstyx. Instead, libninep can also listen on unix!... addresses, as libixp does. A new function, ninepnamespace, gives the default namespace to use, maybe read from the environment variable NAMESPACE, else of the form /tmp/ns.$USER.$DISPLAY/ (as p9p and libixp do).

lunes, 18 de julio de 2011

Running devwsys

The most straightforward way to use devwsys is from 9vx. First, devwsys have to be launched:

user@unix$ devwsys
This will post a 9p server in /tmp/ns.user.:0/wsys (where user is the user name in the unix system and :0 is the current X display). Now, this 9p server can be mounted from 9vx to create new windows. An easy way to access devwsys is setting the wsys environment variable. Then, you will be able to open programs in new windows using window(1) with the -m flag:

term% wsys='#Z/tmp/ns.user.:0/wsys'

term% window -m acme
For a better experience the wctl rc script, included in the util directory of the devwsys distribution, can be used to provide a wctl service. Then, the -m option is not needed any more, new applications will use the namespace of the wctl process:

term% wctl=`{wctl}

post...

term% window acme
Similar commands will work from drawterm(8) too. Inferno, instead, is a bit different. Some files have different names, and cursors have a slight different format. To solve this issue, devwsys will deduce what cursor format is being used depending on what is written to the cursor file, while extra keyboard and pointer files are equivalent to cons and mouse. So, the only required step is to mount devwsys before launching wm(1):

; mount -Ab '#U*/tmp/ns.user.:0/wsys' /dev

; wm/wm
Have fun!

lunes, 4 de julio de 2011

Drawing cats


The main function of the drawing device is to draw the catclock window. This video shows how devwsys can be used for this fundamental task, and even to play sudokus, using 9vx on top of linux.

There are still many thing to be done: resizes are not working, programs segfault when the right mouse button is pressed, anything using fonts will fail and memory is being leaked all over the place. But we can draw cats, everything should get easier now.