diff options
Diffstat (limited to 'sites/pmikkelsen.com/plan9')
-rw-r--r-- | sites/pmikkelsen.com/plan9/basic_9p_server.md | 58 | ||||
-rw-r--r-- | sites/pmikkelsen.com/plan9/discord.md | 83 | ||||
-rw-r--r-- | sites/pmikkelsen.com/plan9/dns.md | 41 | ||||
-rw-r--r-- | sites/pmikkelsen.com/plan9/fonts.md | 29 | ||||
-rw-r--r-- | sites/pmikkelsen.com/plan9/lets_encrypt.md | 59 | ||||
-rw-r--r-- | sites/pmikkelsen.com/plan9/mounting-9p-over-drawterm.md | 58 | ||||
-rw-r--r-- | sites/pmikkelsen.com/plan9/network_booting.md | 30 | ||||
-rw-r--r-- | sites/pmikkelsen.com/plan9/trellofs.md | 347 | ||||
-rw-r--r-- | sites/pmikkelsen.com/plan9/using_irc.md | 48 |
9 files changed, 753 insertions, 0 deletions
diff --git a/sites/pmikkelsen.com/plan9/basic_9p_server.md b/sites/pmikkelsen.com/plan9/basic_9p_server.md new file mode 100644 index 0000000..7c1c514 --- /dev/null +++ b/sites/pmikkelsen.com/plan9/basic_9p_server.md @@ -0,0 +1,58 @@ +## Writing a basic 9P server + +On plan 9 the entire system is build around the idea of namespaces +and that _"everything is a file"_. For this reason it is very easy to write +a new 9P fileserver in C since all the boring tasks are implemented in +libraries. This note describes a minimal program which serves a folder to +`/mnt/hello9p` containing a single synthetic file with the contents "Hello from 9P!". + +## The code + + #include <u.h> + #include <libc.h> + #include <fcall.h> + #include <thread.h> + #include <9p.h> + + void + fsread(Req *r) + { + readstr(r, "Hello from 9P!\n"); + respond(r, nil); + } + + Srv fs = { + .read = fsread, + }; + + void + main(void) + { + Tree *tree; + + tree = alloctree(nil, nil, DMDIR|0555, nil); + fs.tree = tree; + createfile(tree->root, "hello", nil, 0555, nil); + + postmountsrv(&fs, nil, "/mnt/hello9p", MREPL | MCREATE); + } + +## Explanation + +The global variable `fs` is a structure which contains function pointers +to all the 9P handlers, but since I only plan on reading from the file, +only the `read` field is set. The fsread function calls two helper functions +from the 9p(2) library which will create a response with the given string as +the file contents. + +In `main` I start by allocating a new file tree, since this 9P server deals with +a fileserver that has a tree structure, and therefore I don't have to worry +about how directories are handled for example. A file is added with `createfile` +to the root of the tree. + +The call to `postmountsrv` will mount the 9P server under `/mnt/hello9p`. + +## Thats it + +This is not very complicated, but see the manpages at 9p(2) and 9pfile(2) +and intro(5) for more information about the libraries and 9P itself. diff --git a/sites/pmikkelsen.com/plan9/discord.md b/sites/pmikkelsen.com/plan9/discord.md new file mode 100644 index 0000000..8a9815f --- /dev/null +++ b/sites/pmikkelsen.com/plan9/discord.md @@ -0,0 +1,83 @@ +# Chatting on discord from 9front + +Sometimes you work with people who doesn't want to use the same communication +tools as you do. A sad example is when your team members wants to use discord, +and not something simpler like IRC. Oh well, gotta deal with it. + +This is the reason I wrote a "discord bot" (ugh) which can run on your 9front +system and allow you to chat on discord anyways. + +## Example gif + +The example below just opens two chat windows with different channels in each. +While it works both in acme and in the terminal, I like having everything +together inside acme. + +[![An animated gif showing discord chats from 9front][1]][1] + +## The code + +The project is made of a few scripts and a go program. The only thing that +actually communicates with discord is the go program, and the scripts just +makes it easier to send and recieve on the correct channels. The code is +available [here](https://git.sr.ht/~pmikkelsen/discordfront), and there is +a precompiled version of the go program included (compiled for 9front amd64). + +## Usage + +To use the service, you must first create a bot on discord and get the +access token. I personally found this step much more confusing then the +actual coding itself, which either tells you something about my google +skills, or about discord.. Oh, and then you must invite the bot to your +discord server.. + +Next, the server part of the bot must be started: + + `discordsrv YOURTOKEN` + +This will post a pipe in /srv/discordfront, where messages can be send +and recieved from, but you should not read from it directly. The +`discordsrv` script does this for you as well, and it takes each line +and dumps it in a nice readable format into +`$home/lib/discord/logs/$serverName/$channelName` +where the server name is the name of a discord server (or guild?), and +the channel name is, well, the name of the channel. + +Now that the server is started, you can either run `discordacme` which +opens an acme with the file `$home/lib/discord/channels` open. This file +is not handled by any of the scripts, so it is just a handy text file to +keep shortcuts to various channels (see the gif for an example). +Inside `discordacme`, the `openChat` command will open a new chat window, +using the simpler `discord` script. + +The `discord` script simply runs `tail -f` on the given channel's logfile +while it reads messages from standard input and sends them to +/srv/discordfront at the same time. + +## Using remotely + +Since this program does not fetch previous messages, it might be a good +idea to run the server part `discordsrv` on a machine that is always online, +so that all messages gets logged. Since this is plan9, it is almost trivial +to still use the client on your local machine, just by using `rimport` to +import the relevant file trees from the server. + +Here is what I do (in a seperate script which is run at startup): + + #!/bin/rc + + rfork + rimport -ac -p $serverhost /srv + rimport -c -p $serverhost /usr/glenda/lib/discord + discordacme + +I don't even notice it is not running locally. + +## Notes + +* Yes, the names of the scripts suck. +* Yes, there is a lot of missing functionality. +* No, this has not been tested very much, it just seems to do the job +well enough for now. + +[1]: /images/discordgif.gif diff --git a/sites/pmikkelsen.com/plan9/dns.md b/sites/pmikkelsen.com/plan9/dns.md new file mode 100644 index 0000000..04bd7b5 --- /dev/null +++ b/sites/pmikkelsen.com/plan9/dns.md @@ -0,0 +1,41 @@ +# Using 9front as an authoritative DNS server + +This note describes the steps I took to make the 9front server +at pmikkelsen.com the authoritative dns server for itself. + +First, I logged into my domain name registrar and changed the dns servers for +my domain to `ns1.pmikkelsen.com` and `ns2.pmikkelsen.com` (It would not let me have +just one, but I choose to live dangerously and point both of those domains at +the same server). + +To let the world know _where_ `ns1` and `ns2` can be found, +I added their ip on the registrar's website (since my own dns server cannot serve +them for good reasons). Anyways, this might not be the same for you since you +may not use the same provider. + +## Starting the dns server in "serve mode" + +First I added 1 line to the `/cfg/$sysname/cpurc` file to enable the dns server. + + ndb/dns -s + +After that I added a few lines in `/lib/ndb/local` to setup all the dns records I needed: + + dom=pmikkelsen.com soa= + ip=80.240.16.196 + mx=vps1.pmikkelsen.com pref=1 + txtrr="v=spf1 a mx ip:80.240.16.196 ~all" + + dom=p9auth.pmikkelsen.com soa= + ip=80.240.16.196 + + dom=vps1.pmikkelsen.com soa= + ip=80.240.16.196 + + dom=_dmarc.pmikkelsen.com soa= + txtrr="v=DMARC1; p=none" + +With this done, I now have A records, mx records and txt records in place, so my website, +mail and rcpu works as expected. + +Bye.
\ No newline at end of file diff --git a/sites/pmikkelsen.com/plan9/fonts.md b/sites/pmikkelsen.com/plan9/fonts.md new file mode 100644 index 0000000..6cd6780 --- /dev/null +++ b/sites/pmikkelsen.com/plan9/fonts.md @@ -0,0 +1,29 @@ +# Fonts on 9front + +By default 9front uses a font called VGA but its too small for my eyes. + +The fonts I use (dejavu) look like this[![an image showing the fonts][1]][1] and are attached below: + +[proportional font](/_files/djv.tar) + +[monospace font](/_files/djvmono.tar) + +They can be recreated by having them installed on linux and doing the following from +linux with plan9port installed. + + fontsrv -m /tmp/fonts + cp -r /tmp/fonts/DejaVuSomeThing/14a/ /tmp/djv + +You can try a different font or font size if you want. + +Then connect to 9front via drawterm and run: + + mkdir /lib/font/bit/djv + dircp /mnt/term/tmp/djv /lib/font/bit/djv + +Finally change your `lib/profile` to use `/lib/font/bit/djv/font` :) + +Note that 9front includes truetypefs which allows you to use `.ttf` files +directly, but I find the results are better looking this way. + +[1]: /images/djvfonts.png
\ No newline at end of file diff --git a/sites/pmikkelsen.com/plan9/lets_encrypt.md b/sites/pmikkelsen.com/plan9/lets_encrypt.md new file mode 100644 index 0000000..f0b1c07 --- /dev/null +++ b/sites/pmikkelsen.com/plan9/lets_encrypt.md @@ -0,0 +1,59 @@ +## How I get tls certificates for 9front + +First of all, I use linux and drawterm for this for now, but +I would like to be able to do it all from 9front at some point. + +## Generate the certificate + +Install certbot on linux and run the following command + + certbot certonly --manual -d pmikkelsen.com -d vps1.pmikkelsen.com + +and do the challenges, they should be easy. + +## Importing the cert and private key + +Start drawterm and login as the hostowner. After this, the filesystem of the linux +system is available at `/mnt/term`. Run the following: + + cd /sys/lib/tls/ + cp /mnt/term/etc/letsencrypt/live/pmikkelsen.com/privkey.pem ./ + cp /mnt/term/etc/letsencrypt/live/pmikkelsen.com/fullchain.pem ./cert + +Now the private key must be converted to one that can be loaded into factotum + + auth/pemdecode 'PRIVATE KEY' privkey.pem | auth/asn12rsa -t 'service=tls role=client' > key + rm privkey.pem + chmod 400 key + +Add the following to `/cfg/$sysname/cpurc` to load the private key on boot. + + cat /sys/lib/tls/key >> /mnt/factotum/ctl + +Done. + +## SMTP over TLS + +I have the following in `/bin/service.auth/tcp25` + + #!/bin/rc + + user=`{cat /dev/user} + exec upas/smtpd -c /sys/lib/tls/cert -n $3 + +Notice I had to put it in the `/bin/service.auth` folder so that it could find the private key. + +## Https with rc-httpd + +I have the following in `/bin/service.auth/tcp443` + + #!/bin/rc + + exec tlssrv -c /sys/lib/tls/cert -l /sys/log/https /bin/service/tcp80 $* + +Again, in the `/bin/service.auth` folder. It simply wraps the plain http service +in a tls wrapper which looks like this for me + + #!/bin/rc + PLAN9=/ + exec /rc/bin/rc-httpd/rc-httpd >>[2]/sys/log/www diff --git a/sites/pmikkelsen.com/plan9/mounting-9p-over-drawterm.md b/sites/pmikkelsen.com/plan9/mounting-9p-over-drawterm.md new file mode 100644 index 0000000..414b9f2 --- /dev/null +++ b/sites/pmikkelsen.com/plan9/mounting-9p-over-drawterm.md @@ -0,0 +1,58 @@ +# Mounting a 9P connection over drawterm + +I sometimes use drawterm on linux to connect to my 9front server. +While it is possible to access the host system's files under `/mnt/term`, there is no builtin way to access the remote system's file under linux. +Now, why would anybody want to do this? +In my case, I often want to write some code under 9front, but for languages which aren't supported such as prolog in this case, so there are three options as I see it: + +* Store the files on the host machine, and access them under `/mnt/term`. +* Store the files on the server and somehow mount the server's filesystem on the host. +* Store the files on a third machine that both the host and server can access. + +Option number two seems best for me, so I asked around and it seems like the best tool to mount a 9P connection on linux is [9pfs](https://github.com/bunny351/9pfs). + +## How it works + +Before I could mount the connection, I had to serve it somehow. Normally I already serve 9P directly from my server's filesystem, but that requires some +authentication that 9pfs does not support, so I had to serve it without authentication. +Serving directly to the internet without authentication is of course pretty dumb since everyone can then access my files, so thanks to a hint from hiro, I figured +out that it is actually possible to use the host's network stack on the server by binding `/mnt/term/net` over `/net`. + +I'll just show the final script below and explain it afterwards: + + #!/bin/rc + + rfork n + bind /mnt/term/net /net + aux/listen1 -t tcp!*!12335 /bin/exportfs -r / & + os mkdir -p /tmp/drawterm + os 9pfs localhost -p 12335 /tmp/drawterm + +So the first thing that happens is that I bind the host's network in, and from that point on, every network connection in this namespace actually goes out +from the host instead of from the server! + +Then `exportfs` is started and it is serving the `/` directory over 9P at port 12335. +The `os` command runs a command on the host, so it just creates the folder that the system will be mounted to, and then uses `9pfs` to actually mount it. +The nice thing here is that `9pfs` just connects to localhost. + +## Using it + +As I said in the beginning, I did this to be able to edit files on the 9front server, and run compilers/interpreters on linux. +The `os` command goes a long way, but the following script makes it even easier (I have this installed as `linux`): + + #!/bin/rc + + dir=/tmp/drawterm/`{pwd} + os -d $dir $* + +This means I can just go into any directory on the server, type `linux ghci` and I get a haskell repl running in the correct directory, as seen in the gif below. + +[![An animated gif showing ghci loading a file on the 9front server][1]][1] + + +## Final notes + +The bind net trick still blows my mind a bit, since it is so trivial, yet so powerful. It is also fun to think about how one would do this +on other systems than plan9, which I can't even imagine. + +[1]: /images/linuxreverse.gif diff --git a/sites/pmikkelsen.com/plan9/network_booting.md b/sites/pmikkelsen.com/plan9/network_booting.md new file mode 100644 index 0000000..3bb51b9 --- /dev/null +++ b/sites/pmikkelsen.com/plan9/network_booting.md @@ -0,0 +1,30 @@ +## Network booting into the server at pmikkelsen.com + +Sometimes it is nice to be able to connect directly to a remote server +from my laptop and have the same root filesystem available. +This is possible by typing `tls` at the boot prompt and then +typing `pmikkelsen.com` for the fs and auth server. + +## Speed + +On my internet connection some things can be very slow +(such as compiling the entire system), because of fs access now happens +over the network. The easy fix is to just start a cpu connection to the server +where the fs access can happen fast since it is on the same machine. This is +done by typing + + rcpu -h pmikkelsen.com + +Of course the server has a pretty slow cpu.. + +## Server settings + +Some steps are needed to make this work: + +1. The user must be added to the fileserver and auth server. +2. The fileserver must be listening for remote connections. +3. The correct ndb entries must be set (in the beginning I forgot `fs` and I could +only connect via tcp, not tls) + +More information about all those steps can be found in section 7 of [the 9front FQA](http://fqa.9front.org/fqa7.html). + diff --git a/sites/pmikkelsen.com/plan9/trellofs.md b/sites/pmikkelsen.com/plan9/trellofs.md new file mode 100644 index 0000000..42e2cfd --- /dev/null +++ b/sites/pmikkelsen.com/plan9/trellofs.md @@ -0,0 +1,347 @@ +## A filesystem for viewing trello + +Since my last note [here](http://pmikkelsen.com/plan9/basic_9p_server) +was very simple, here comes +a small program (around 250 lines of C), which makes it +possible to view trello as a read-only filesystem. + +## Usage example + +First set the `trellokey` and `trellotoken` environment +variables to the API key and token from the trello website. + + term% trellokey=1298791723987937123..... + term% trellotoken=923748237497ab798798999999..... + +Then `webfs` must be started so the program can make +requests to the trello REST api. + + term% webfs + +Then `trellofs` itself must be started. It mounts the filesystem under `/mnt/trello`. + + term% trellofs + +If I now go and see which boards I have, I get the following + + term% cd /mnt/trello + term% ls + Beaglebone_Black + Fly + Fysik_Rapport + Guld_kobber + Ideer_til_2._produkt + Lektier + P0 + P1 + P2 + Projekt_Materialer + Spil + Welcome_Board + +The names of the folders are almost like the original board names, except +that that some characters have been replaced by `_`. Lets look at `P1` + + term% cd P1 + term% ls + Backlog + Done + Estimat + In-progress + Info + Master.C_Top_Down_Ting + Sprint_1 + Test + Test_sprint_1 + Todo + +Todo must have something worth looking at + + term% ls + Færdig_rapport_aflevering + Korrekturlæsning + +Those two items are the actual trello "cards", which I think of as tasks. The first +one means "Final report hand in", so let's have a look at that. + + term% cd Færdig_rapport_aflevering + term% ls + description + duedate + url + +There are always three files inside each card folder, but since the information in them +is not mandatory on trello, some of them will be empty. + + term% cat description + + term% cat duedate + 2019-12-18T13:00:00.000Z + term% cat url + https://trello.com/c/u23ZcNAm/9-f%C3%A6rdig-rapport-aflevering + +## Limitations + +The key and token are right now in environment variables, which is somewhat +awkward. Also I would like to be able to actually create and move cards directly +from the filesystem just by creating files, but that is not something I will implement +right now. + +## The code + +The code for the `trellofs` program is listed below. + + #include <u.h> + #include <libc.h> + #include <fcall.h> + #include <thread.h> + #include <9p.h> + #include <json.h> + + #define MAX_RESPONSE_SIZE (1024 * 1024 * 8) + + char *key; + char *token; + + void + fsread(Req *r) + { + char *str; + + if (r->fid->file->aux == nil) { + readstr(r, ""); + } else { + str = malloc(strlen(r->fid->file->aux) + 2); + sprint(str, "%s\n", r->fid->file->aux); + readstr(r, str); + } + respond(r, nil); + } + + Srv fs = { + .read = fsread, + }; + + JSON * + trelloget(char *endpoint, char *params) + { + int ctlfd, bodyfd; + int n; + int err; + char *buf; + JSON *res; + + res = nil; + bodyfd = -1; + + buf = malloc(MAX_RESPONSE_SIZE + 1); + if(buf == nil) { + perror("malloc"); + return nil; + } + + ctlfd = open("/mnt/web/clone", ORDWR); + if(ctlfd < 0) { + perror("open"); + goto fail; + } + + if(read(ctlfd, buf, 32) < 0) { + perror("read"); + goto fail; + } + + n = atoi(buf); + + sprint(buf, "url https://api.trello.com/%s?key=%s&token=%s", endpoint, key, token); + if(params) + sprint(buf + strlen(buf), "\&%s\n", params); + else + sprint(buf + strlen(buf), "\n"); + + err = write(ctlfd, buf, strlen(buf)); + if(err < 0){ + perror("write"); + goto fail; + } + + sprint(buf, "/mnt/web/%d/body", n); + bodyfd = open(buf, OREAD); + if(bodyfd < 0){ + perror("open"); + goto fail; + } + + err = readn(bodyfd, buf, MAX_RESPONSE_SIZE); + if (err < 0) { + perror("read"); + goto fail; + } + + res = jsonparse(buf); + + fail: + close(ctlfd); + close(bodyfd); + free(buf); + + return res; + } + + char * + escapename(char *str) + { + char *new; + int i, len; + + len = strlen(str); + + new = malloc(len + 1); + for(i = 0; i <= len; i++){ + switch(str[i]){ + case '/': + case ' ': + case ',': + case ':': + case '"': + case '\'': + new[i] = '_'; + break; + default: + new[i] = str[i]; + } + } + return new; + } + + void + addcard(File *dir, JSON *card) + { + JSON *element; + char *filename; + File *carddir; + char *description; + char *url; + char *duedate; + + element = jsonbyname(card, "name"); + filename = escapename(element->s); + carddir = createfile(dir, filename, nil, DMDIR|0555, nil); + if(carddir == nil){ + perror("createfile"); + return; + } else { + element = jsonbyname(card, "desc"); + description = strdup(element->s); + element = jsonbyname(card, "url"); + url = strdup(element->s); + element = jsonbyname(card, "due"); + if (element->t == JSONString) + duedate = strdup(element->s); + else + duedate = nil; + createfile(carddir, "description", nil, 0444, description); + createfile(carddir, "url", nil, 0444, url); + createfile(carddir, "duedate", nil, 0444, duedate); + } + free(filename); + } + + void + addlist(File *dir, JSON *list) + { + JSON *element; + char *filename; + JSON *cards; + JSONEl *card; + char cardsEndpoint[128]; + File *listdir; + + element = jsonbyname(list, "name"); + + filename = escapename(element->s); + listdir = createfile(dir, filename, nil, DMDIR|0555, nil); + if(listdir == nil){ + perror("createfile"); + return; + } else { + element = jsonbyname(list, "id"); + sprint(cardsEndpoint, "1/lists/%s/cards", element->s); + cards = trelloget(cardsEndpoint, "fields=desc,name,url,due"); + if(cards == nil) + return; + + for(card = cards->first; card != nil; card = card->next){ + addcard(listdir, card->val); + } + } + jsonfree(cards); + free(filename); + } + + void + addboard(File *root, JSON *board) + { + JSON *element; + JSONEl *list; + char *filename; + File *boarddir; + + element = jsonbyname(board, "name"); + filename = escapename(element->s); + boarddir = createfile(root, filename, nil, DMDIR|0555, nil); + if(boarddir == nil) { + perror("createfile"); + return; + } + + element = jsonbyname(board, "lists"); + for(list = element->first; list != nil; list = list->next){ + addlist(boarddir, list->val); + } + + free(filename); + } + + void + trelloinit(File *root) + { + JSON *result; + JSONEl *board; + + result = trelloget("1/members/me/boards", "fields=name,lists&lists=open"); + + for(board = result->first; board != nil; board = board->next){ + addboard(root, board->val); + } + jsonfree(result); + } + + void + main(void) + { + JSONfmtinstall(); + + key = getenv("trellokey"); + token = getenv("trellotoken"); + + Tree *tree; + + tree = alloctree(nil, nil, DMDIR|0555, nil); + + fs.tree = tree; + trelloinit(tree->root); + + postmountsrv(&fs, nil, "/mnt/trello", MREPL | MCREATE); + } + +The program can be compiled by hand or by using the following `mkfile`. + + BIN=/usr/glenda/bin/amd64 + + TARG=trellofs + + OFILES=\ + main.$O\ + + </sys/src/cmd/mkone diff --git a/sites/pmikkelsen.com/plan9/using_irc.md b/sites/pmikkelsen.com/plan9/using_irc.md new file mode 100644 index 0000000..0c3622c --- /dev/null +++ b/sites/pmikkelsen.com/plan9/using_irc.md @@ -0,0 +1,48 @@ +_Last updated: 2020-07-30_ + +# Using IRC on 9front + +While 9front comes with an irc client called `ircrc`, it can be quite annoying that it does not +keep a persistent connection. + +A different program `irc7` provides a server part which keeps a connection open to an irc server, +and a client part which allows for connecting to the server. This works great since it makes it possible to +get all the messages on a channel without being online all the time. + +## Setup + +Download the irc7 code via `hg`: + + hg clone https://code.9front.org/hg/irc7/ + +Build and install + + cd irc7 + mk install + +## Usage + +First the server program `ircsrv` must be started, so I would run + + ircsrv -e -p VerySecretPassword pmikkelsen net!irc.freenode.net!7000 + +to connect as `pmikkelsen` to freenode over tls and with password for my nickname. +After this, it is possible to connect to different channels by opening new rio windows +and running + + irc -t '#cat-v' -b + +to connect to the cat-v channel. The `-b` option without any extra arguments +prints the entire conversation since the ircsrv was started, +so it might not always be desirable. + +To see all the messages sent directly to me, I would run + + irc -t MSGS + +and maybe start a conversation with user 'mousehater' by running + + irc -t mousehater + +I run this on a remote cpu server that is rarely powered off, and then I just connect via +drawterm from linux to chat, or via rcpu from other 9front machines. |