Version 0.2 of Gophermoon did send all output from stdout
(standard out) and stderr (standard error) back to the socket. For
error messages, that was not ideal, as Lua stack traces do not honor the
Gopher protocol :)
In this version 0.3 I have added a logging channel for Gophermoon. Writing a log-file into the filesystem does not work, as Gophermoon runs inside an ephermeral container, and all changes to the file system are lost once the client request is done and the session terminates.
So logging is done via syslog. While it is possible to have a
syslog-daemon (or systemd-journald) running inside the container, that
would make the container more complex. My solution is to have the
systemd unit catch all data from the stderr file descriptor and send
that to syslog (and implicit to the systemd journal as well).
A small change in the Gophermoon systemd service unit
(/etc/systemd/system/gophermoon@.service):
[Unit] Description=GopherMoon Gopher Server in Lua [Service] ExecStart=/bin/systemd-nspawn -q -x --private-network -D /srv/gophermoon /bin/lua /bin/gophermoon StandardInput=socket StandardError=syslog
Standard input and standard output are still connected to the socket (from Systemd socket activation, see Part 1), but all data written into standard error will now end up in the Syslog/Journal.
Systemd needs to be made aware of the changes:
systemctl daemon-reload
The function write_log in Gophermoon is used to write a log entry:
--- write a log entry function write_log(severity,msg) io.stderr:write(severity .. ": " .. msg .. "\n") end
Example use from the source code:
[...]
write_log("INFO", "Selector is " .. selector)
[...]
if isReadable(filepath) then
send_textfile(filepath)
else
send_error("Selector [" .. selector .. "] not found")
write_log("ERROR","Selector not found")
end
[...]
The systemctl status command for the socket activation unit of
Gophermoon gives a count of connections (successful and
unsuccessful):
* gophermoon.socket - GopherMoon - Gopher-Server in Lua Loaded: loaded (/etc/systemd/system/gophermoon.socket; enabled; vendor preset: disabled) Active: active (listening) since Sa 2017年12月23日 22:54:44 CET; 2 days ago Listen: [::]:70 (Stream) Accepted: 209; Connected: 0 Dez 23 22:54:44 gopher.defaultroutes.de systemd[1]: Stopping GopherMoon - Gopher-Server in Lua. Dez 23 22:54:44 gopher.defaultroutes.de systemd[1]: Listening on GopherMoon - Gopher-Server in Lua. Dez 23 22:54:44 gopher.defaultroutes.de systemd[1]: Starting GopherMoon - Gopher-Server in Lua.
Systemd creates a new service unit for each new connection, the unit name contains
Here is a Lua-Error message and stack trace from stderr in the
Systemd journal:
# journalctl -u "gophermoon@*" Dez 26 19:21:28 gopher.defaultroutes.de systemd[1]: Started GopherMoon Gopher Server in Lua (185.22.143.172:49828). Dez 26 19:21:28 gopher.defaultroutes.de systemd[1]: Starting GopherMoon Gopher Server in Lua (185.22.143.172:49828)... Dez 26 19:21:29 gopher.defaultroutes.de systemd-nspawn[29973]: Info: Selector is /0/filedoesnotexists Dez 26 19:21:29 gopher.defaultroutes.de systemd-nspawn[29973]: /bin/lua: /bin/gophermoon:37: attempt to index a nil value (global 'f') Dez 26 19:21:29 gopher.defaultroutes.de systemd-nspawn[29973]: stack traceback: Dez 26 19:21:29 gopher.defaultroutes.de systemd-nspawn[29973]: /bin/gophermoon:37: in function 'isDir' Dez 26 19:21:29 gopher.defaultroutes.de systemd-nspawn[29973]: /bin/gophermoon:134: in main chunk Dez 26 19:21:29 gopher.defaultroutes.de systemd-nspawn[29973]: [C]: in ? Dez 26 19:21:29 gopher.defaultroutes.de systemd[1]: gophermoon@172-5.45.107.88:70-185.22.143.172:49828.service: main process exited, code=exited, status=1/FAILURE Dez 26 19:21:29 gopher.defaultroutes.de systemd[1]: Unit gophermoon@172-5.45.107.88:70-185.22.143.172:49828.service entered failed state. Dez 26 19:21:29 gopher.defaultroutes.de systemd[1]: gophermoon@172-5.45.107.88:70-185.22.143.172:49828.service failed.
A list of all Gophermoon service names and connection information.
# journalctl -o verbose -u "gophermoon@*" | grep "_SYSTEMD_UNIT" | tail _SYSTEMD_UNIT=gophermoon@199-5.45.107.88:70-212.111.43.252:60215.service _SYSTEMD_UNIT=gophermoon@200-5.45.107.88:70-212.111.43.252:47726.service _SYSTEMD_UNIT=gophermoon@201-5.45.107.88:70-212.111.43.252:18139.service _SYSTEMD_UNIT=gophermoon@202-5.45.107.88:70-212.111.43.252:14576.service _SYSTEMD_UNIT=gophermoon@203-5.45.107.88:70-212.111.43.252:62963.service _SYSTEMD_UNIT=gophermoon@204-5.45.107.88:70-212.111.43.252:21729.service _SYSTEMD_UNIT=gophermoon@205-5.45.107.88:70-185.22.143.172:50432.service _SYSTEMD_UNIT=gophermoon@206-5.45.107.88:70-185.22.143.172:50433.service _SYSTEMD_UNIT=gophermoon@207-5.45.107.88:70-185.22.143.172:50434.service _SYSTEMD_UNIT=gophermoon@208-5.45.107.88:70-185.22.143.172:50436.service
In a later blog post I will show how to parse and aggrgate the connection information for some nice statistics (available via Gopher protocol of course).
Git commit for today: https://github.com/cstrotm/gophermoon/commit/ead8b366c639034b34884b17fb5c4fb28211c012
the sourcecode is now on Github @ https://github.com/cstrotm/gophermoon
With every blog post, I will post the link to the commit associated with the blog post.
Today Gophermoon learned how to serve static text files. The program first looks at the selector send by the client. If the selector is empty, and uses the root directory.
--- read the selector from the client selector = io.read() --- remove CR (13/0ドルD) from input selector=selector:sub(1, -2) --- if an empty selector has been send, use the root if selector == '' then selector = "/" end --- make selector relative to the gopher root filepath = gopherroot .. selector
Next, if the selector points to a directory, it looks if there is a
file names gophermap in the direcory. A gophermap file is a
Gopher-Document describing the resources available in this directory
(as well as links outside the directory). Gophermoon uses the same
gophermap format as Gophernicus (see
https://github.com/prologic/gophernicus/blob/master/README.Gophermap)
Welcome to GopherMoon @ defaultroutes.de ---------------------------------------- 0Gophermoon 0.1 - a Gopher-Server in Lua -- Part 1 /gophermoon01.txt blog.defaultroutes.de 70 0Gophermoon 0.1 - a Gopher-Server in Lua -- Part 2 /gophermoon02.txt blog.defaultroutes.de 70 0Gophermoon 0.2 - a Gopher-Server in Lua -- Part 3 /gophermoon03.txt blog.defaultroutes.de 70 0Gophermoon Sourcecode /gophermoon blog.defaultroutes.de 70
Lines that do not contain a tabulator are formatted as "i" (Information) file type.
--- if the selector points to a directory,
--- do we have a "gophermap" file? If "yes",
--- send that file. Else, if the selector
--- points to a (text-) file, send it
--- if both are false, send an error message
if isDir(filepath) then
gophermap = filepath .. "/gophermap"
if isReadable(gophermap) then
send_gophermap(gophermap)
end
else
if isReadable(filepath) then
send_textfile(filepath)
else
send_info("Welcome to GopherMoon @ defaultroutes.de")
send_info("----------------------------------------")
send_info("Error: Selector not found")
send_end()
end
end
If the selector isn't a directory, the program checks the selector is a readable file, and if it is, it sends the file gopher-style (with CR/LF and terminating the file with ".").
--- send a Textfile Gopher-style function send_textfile(filepath) local f=io.open(filepath,"r") if f~=nil then for line in io.lines(filepath) do io.write(line .. "\r\n") end io.close(f) end send_end() end
The commit from today is https://github.com/cstrotm/gophermoon/commit/f300e28677059c9364151a89b36afe552d858700
I will not change the (minimal) Gopher server (this time), but will prepare the execution environment for Gophermoon: the result will be a container containing, only one executeable and one Lua-Script besides the Gopher content.
The container will be isolated from the network and the filesystem and process-space of the host machine.
So any coding errors I will create while expanding the Gophermoon-Server will have limited security impact on the host system (unless there is a security issue in the container code of the Linux-Kernel, which there have been in the past. Nothing is fully secure).
I'm testing on a RedHat EL 7 machine, but the same result should be possible on other modern Linux systems with a container manager (systemd-nspawn, docker, rkt, LXC …).
One feature that makes the Go programming language popular among users of Linux-Containers is the fact that Go produces static binaries. Static binaries have no code runtime dependencies, they are self contained and do not need to be installed, but can just copied around and "just work".
Lua is written in C, but we can create static binaries in C too.
It is recommended to compile the static Lua binary on an development machine, not on the production Gopher server.
For Red Hat based systems, we need to install few build tools and the static library files for the GNU-Libc:
yum install make gcc wget glibc-static
Next, we're downloading the Lua sourcecode from https://www.lua.org/download.html
mkdir ~/src cd ~/src wget https://www.lua.org/ftp/lua-5.3.4.tar.gz tar xfz lua-5.3.4.tar.gz cd lua-5.3.4/src
The option -static lets the gcc compiler and linker build a static
binary. Change the MYCFLAGS and MYLDFLAGS lines in Makefile:
[...] MYCFLAGS= -static MYLDFLAGS= -static [...]
Now we can build the new Lua interpreter. I'm building the posix
flavor, which is more generic than the Linux flavor and links less
external libraries:
make posix strip lua
The resulting lua binary should be a static ELF executable:
# file lua lua: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=0631bb0d50ad1209dbf55f2b1dd0eb60fc388df7, stripped # ls -lh lua -rwxr-xr-x. 1 root root 1,7M 23. Dez 23:04 lua
Next the container root directory is created and the static lua
interpreter together with the gophermoon script is placed into that
container directory:
mkdir -p /srv/gophermoon/bin cp lua /srv/gophermoon/bin/ mv /usr/local/sbin/gophermoon /srv/gophermoon/bin/
Without timezone information files within the container directory,
systemd-nspawn will complain about not being able to update the
timezone in the container
Timezone <your-host-timezone> does not exist in container, not updating container timezone.
So far, I found no better way than to copy the timezone information into the container root (only one file is enough if you know the timezone of your host machine):
mkdir -p /srv/gophermoon/usr/share/zoneinfo cp -a /usr/share/zoneinfo/* /srv/gophermoon/usr/share/zoneinfo/
A quick manual test starting the container from the commandline:
# /bin/systemd-nspawn -q --private-network -D /srv/gophermoon /bin/lua /bin/gophermoon iWelcome to GopherMoon @ defaultroutes.de defaultroutes.de 70 i---------------------------------------- defaultroutes.de 70 .
Here is the updated Systemd-Service-Unit (/etc/systemd/system/gophermoon@.service):
[Unit] Description=GopherMoon Gopher Server in La [Service] ExecStart=/bin/systemd-nspawn -q -x --private-network -D /srv/gophermoon /bin/lua /bin/gophermoon StandardInput=socket
The -x option creates an ephemeral container that is started from an
BTRFS snapshot each time a new gopher connection comes in. The
snapshot is destroyed as soon as the container terminates. An intruder
will not be able to store new files into the container system.
At last we reload the new unit into Systemd and restart the Socket-Activation:
systemctl daemon-reload systemctl restart gophermoon.socket
Now gophermoon runs in a (more) secure environment.
I'm writing a simple Gopher-Server in Lua. I want to play around with the Gopher Protocol, Systemd and Linux-Container and learn some Lua-Programming on the way.
Gopher is a document retrieval protocol that had been around the same time the world-wide-web was born, but it is much simpler that HTTP and HTML. It works with plain text and directories, and the protocol on the wire is ultra-simple.
The server will be named gophermoon.
Gopher (the protocol) is documented in RFC 1436 https://www.ietf.org/rfc/rfc1436.txt with some extension defined as Gopher+ in http://iubio.bio.indiana.edu/soft/util/gopher/Gopher+-spec.text.
In the beginning, I will use Systemd to do the network part of the protocol (listening on port 70). The Lua program will just read and write to standard-input and -output.
Here are the Service-Unit and the Socket-Activation-Unit for
Systemd. The Gophermoon Service is in
/etc/systemd/system/gophermoon@.service
[Unit] Description=GopherMoon Gopher Server in La [Service] ExecStart=-/bin/lua /usr/local/sbin/gophermoon StandardInput=socket
/etc/systemd/system/gophermoon.socket[Unit] Description=GopherMoon - Gopher-Server in Lua [Socket] ListenStream=70 Accept=yes [Install] WantedBy=sockets.target
Below is a very simple Lua-Script implementing a Hello-World Gopher-Service. It just emits the lines for a static welcome message.
A Gopher-Server waits for a connection and reads the path the client sends (ignored for now). Then it writes the Gopher-Menue out. Each Menu-Line has five fields. The first field is 1 character wide, the other fields are separated by the tabulator character (/t). Each line is terminated by a CRLF sequence.
A line with a single dot "." marks the end of the communication, server and client will close the connection.
--- Gopher Moon Version 0.1
--- a simple Gopher Server in Lua
--- with some help from Systemd socket activation
path = io.read()
io.write("iWelcome to GopherMoon @ defaultroutes.de\t\tblog.defaultroutes.de\t70\r\n")
io.write("i----------------------------------------\t\tblog.defaultroutes.de\t70\r\n")
io.write(".\r\n")
Systemd needs to know about the new unit-files, so we do a reload:
systemctl daemon-reload
Now we can enable and start the socket (no need to start/enable the service, as it will be started once a connection to Port 70 is made).
systemctl enable --now gophermoon.socket
The open socket on the Gopher-Port 70 now visible:
# lsof -i COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME systemd 1 root 40u IPv6 3989631 0t0 TCP *:gopher (LISTEN) [...]
Gopher is simple, it can be tested with the telnet command:
# telnet blog.defaultroutes.de 70 Trying 5.45.107.88... Connected to blog.defaultroutes.de. Escape character is '^]'. iWelcome to GopherMoon @ defaultroutes.de blog.defaultroutes.de 70 i---------------------------------------- blog.defaultroutes.de 70 . Connection closed by foreign host.
gophermoon-01-lynx.png
Figure 1: test with the lynx https://lynx.browser.org/
gophermoon-01-omniweb.png
Figure 2: test with OmniWeb https://www.omnigroup.com/more (MacOS X)
First we save the GUID (Globally Unique Identifier) Partition Table
(GPT), so that we can restore the same partition table on the target
machine. We use the commandline version of gdisk for this, named
sgdisk
sgdisk --backup=/srv/macos/parttable.gpt.sgdisk /dev/sda
The content of the partitions can be cloned using the partclone tool. A default MacOS X installation has three partitions:
Model: ATA APPLE HDD HTS547 (scsi) Disk /dev/sda: 976773168s Sector size (logical/physical): 512B/4096B Partition Table: gpt Number Start End Size File system Name Flags 1 40s 409639s 409600s fat32 EFI system partition boot 2 409640s 975503591s 975093952s hfs+ Customer 3 975503592s 976773127s 1269536s hfs+ Recovery HD
First we create backup images of all partitions:
partclone.vfat -I -c -s /dev/sda1 | gzip > /srv/macos/sda1.partclone.gz partclone.hfsplus -I -c -s /dev/sda2 | gzip > /srv/macos/sda2.partclone.gz partclone.hfsplus -I -c -s /dev/sda3 | gzip > /srv/macos/sda3.partclone.gz
To restore the saved MacOS X installation, boot another Mac using Linux (a Knoppix live Linux DVD will work).
Next we restore the partition table (GPT) using sgdisk (/dev/sda is
the target disk, all data on that disk will be erased, be warned!):
sgdisk --load-backup=/srv/macos/parttable.gpt.sgdisk /dev/sda partprobe
as an alternative, the partitions can be created using parted and the
GUID type codes set by sgdisk (important!):
sgdisk --zap /dev/sda parted -s /dev/sda mklabel gpt parted -s /dev/sda mkpart primary 40s 409639s parted -s /dev/sda name 1 "'EFI system partition'" parted -s /dev/sda set 1 boot on parted -s /dev/sda mkpart primary 409640s 975503591s parted -s /dev/sda name 2 "'MacOS X System'" parted -s /dev/sda mkpart primary 975503592s 976773127s parted -s /dev/sda name 3 "'Recovery HD'" sgdisk -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B /dev/sda sgdisk -t 2:48465300-0000-11AA-AA11-00306543ECAC /dev/sda sgdisk -t 3:48465300-0000-11AA-AA11-00306543ECAC /dev/sda partprobe
Restore the partition content:
zcat /srv/macos/sda1.partclone.gz | partclone.vfat -r -o /dev/sda1 zcat /srv/macos/sda2.partclone.gz | partclone.hfsplus -r -o /dev/sda2 zcat /srv/macos/sda3.partclone.gz | partclone.hfsplus -r -o /dev/sda3
If all went well, the new disk is ready to boot MacOS X.
For reference, here are the GUID details of a default MacOS X 10.8 MacBook Pro install:
root@(none):~# sgdisk -p /dev/sda Disk /dev/sda: 976773168 sectors, 465.8 GiB Logical sector size: 512 bytes Disk identifier (GUID): 497736B0-7EA0-4C45-AB5F-8841CD773D24 Partition table holds up to 128 entries First usable sector is 34, last usable sector is 976773134 Partitions will be aligned on 8-sector boundaries Total free space is 262157 sectors (128.0 MiB) root@(none):~# sgdisk -i1 /dev/sda Partition GUID code: C12A7328-F81F-11D2-BA4B-00A0C93EC93B (EFI System) Partition unique GUID: 6749C82F-02C9-4FF5-A889-F31A21726F8E First sector: 40 (at 20.0 KiB) Last sector: 409639 (at 200.0 MiB) Partition size: 409600 sectors (200.0 MiB) Attribute flags: 0000000000000000 Partition name: 'EFI system partition' root@(none):~# sgdisk -i2 /dev/sda Partition GUID code: 48465300-0000-11AA-AA11-00306543ECAC (Apple HFS/HFS+) Partition unique GUID: 60F7A3AA-69B3-4E59-87A0-3A47BB659255 First sector: 409640 (at 200.0 MiB) Last sector: 975241447 (at 465.0 GiB) Partition size: 974831808 sectors (464.8 GiB) Attribute flags: 0000000000000000 Partition name: 'MacOS X System' root@(none):~# sgdisk -i3 /dev/sda Partition GUID code: 48465300-0000-11AA-AA11-00306543ECAC (Apple HFS/HFS+) Partition unique GUID: FD007AA4-CF3A-42F6-BFC6-B3BC25521FC2 First sector: 975503592 (at 465.2 GiB) Last sector: 976773127 (at 465.8 GiB) Partition size: 1269536 sectors (619.9 MiB) Attribute flags: 0000000000000000 Partition name: 'Recovery HD'
#!/bin/sh
gforth -e "use ${1} : dump-blocks 0 do i list loop ; : blocks get-block-fid file-size drop d>s 1024 / ; blocks dump-blocks bye"
Save in a script file (dump-blocks), make it executable, and then you can use
# dump-blocks 6502f83.fb > 6502f83.fs]]>
I found the "Digger V8" dissassembler on Heenk Robbers webpage along with the AHCC C-Compiler. The Digger is able to disassemble both ColdFire and MC68000, and it can switch between the two CPU types with a simple switch. Finding instructions that cannot be executed on the ColdFire is now only a matter of switching the CPU type and watching the assembler code change.
The Digger package also contains a list of all M68000 assembler instructions, with information on the instructions available on each CPU type (MC68000, MC68020, MC68030, ColdFire …).
04923b6d-7a78-4d0f-b6b2-ff229b8a77da.jpg bc7a1b88-5039-4a04-8c68-9c3ceecada90.jpg
I made the table below as a quick look-up tool to find MC68000 instructions that need replacement on the ColdFire (this table is still work in progress, the column "workaround" will be filled while I work on the ColdFire porting project):
| Opcode | possible workaround | Description |
|---|---|---|
| ori -> ccr | OR immediate to CCR | |
| ori -> sr | OR immediate to Status Register | |
| andi -> ccr | AND immediate to CCR | |
| andi -> sr | AND immediate to Status Register | |
| eori -> ccr | EOR immediate to CCR | |
| eori -> sr | EOR immediate to Status Register | |
| movep | Move peripheral data | |
| trapv | Trap on Overflow | |
| rtr | Return and restore condition codes | |
| chk.w | Check register against bounds | |
| dbcc | Test condition, decrement and branch | |
| sbcd Dx,Dy | Subtract decimal with extend | |
| sbcd -(Ax),-(Ay) | Subtract decimal with extend | |
| suba.w | Subtract address | |
| subx -(Ax),-(Ay) | Subtract extended | |
| linea | LINE-A access | |
| abcd Dx,Dy | Add decimal with extend | |
| abcd -(Ax),-(Ay) | Exchange registers | |
| exg Dx,Dy | Exchange registers | |
| exg Ax,Ay | Exchange registers | |
| exg Dx,Ay | Exchange registers | |
| adda.w | Add address | |
| addx -(Ax),-(Ay) | Add extended | |
| roxl | Rotate left with extend | |
| roxr | Rotate right with extend | |
| rol | Rotate left | |
| ror | Rotate right | |
| asl | Arithmetic shift left | |
| asr | Arithmetic shift right | |
| lsl | Logical shift left | |
| lsr | Logical shoft right |
My FireBee Development setup
ceadfdef-d95e-400a-92ad-ee2d49b54a23.jpg
The FireBee is a modern incarnation of the Atari ST computer line, including Ethernet, USB, CF-Card and SD-Card interfaces. A modern computer. And it still runs Atari TOS programs. Or almost …
The ColdFire CPU is a somewhat CISC morphed to RISC system, where some "not so often used" CPU instructions have been removed to make the CPU core simpler but faster. The "not so often used" does refer to common compilers, mostly C compilers. It does not seem true for Forth. The Atari ST VolksForth refuses to work on the FireBee. It probably uses some M68K instructions not available on the FireBee.
That will be my first target on the FireBee system: getting VolksForth to run. After that is done, VolksForth ST needs a log due overhaul. The last version was from around 1988, before Atari TT and Falcon and all the new GEM API. The old version does not know about MiNT or EmuTOS. That has to change.
The FireBee board
9285a77b-5d2a-4b12-9da6-045271ec1095.jpg
I will report my progress here. The sourcecode can be found, along with the developer documentation in the repository over at http://fossil.forth-ev.de/volksforth.
Books to read …
5f7375e9-8181-4cb5-9581-2da3c5e60931.jpg
Dirk Brühl und Michael Kalus haben das CamelForth vom Brad Rodriguez für das TI Launchpad Board angepasst. Das 44ドルth Projekt hat eine eigene Webseite auf http://www.4e4th.eu. Auf der Webseite der Forth-Gesellschaft e.V. gibt es Neuigkeiten zum TI Launchpad und CamelForth (aka 44ドルth): http://www.forth-ev.de/index.php?topic=launchpad. Die Quellen und die aktuelle HEX-Download Datei findet sich im SVN Repository der Forth-Gesellschaft unter http://forth-ev.de/repos/CF430G2553/ in der Datei CF430G2553.a43.
65f30a67-b71c-4d87-91be-f78d6f7c83f5.jpg
Unter Linux kann diese HEX-Datei mit dem Flasher-Programm mspdebug auf das Launchpad geladen werden (Version 0.19 oder später):
$ sudo /usr/local/bin/mspdebug rf2500 "prog CF430G2553.a43"
Unter Linux meldet sich das TI Launchpad meist unter /dev/ttyACM0, jedoch kann es sein das bei modernen Linux Systemen Hintergrundprogramme (wie der ModemManager) sofort diese Schnittstelle belegen und versuchen das LaunchPad als Modem anzusprechen (Siehe Linux today: too much plug and play).
Das CamelForth auf dem TI Launchpad kann mit einem Terminal-Programm abgesprochen werden.
Anleitung zum Bauen des CamelForth aus den Quellen, und die Programmierung des TI Launchpad unter MacOS X oder Windows befinden sich im Wiki der Forth Gesellschaft unter http://www.forth-ev.de/wiki/doku.php/projects:4e4th:start.
Das CamelForth für das LaunchPad befindet sich derzeit (März 2012) in aktiver Entwicklung, es sind in den kommenden Wochen noch Verbesserungen und Änderungen zu erwarten.
Hilfe zum CamelForth auf dem Launchpad und zu Forth allgemein gibt es in deutscher Sprache im Usenet unter de.comp.lang.forth.
]]>Screen ist der Allrounder unter den Terminal-Programmen. Eigendlich mehr ein Fenster-Manager für die Kommandozeile. Keine Konfiguration, einfach loslegen. Um den vollen Funktionsumfang ausnutzen zu können unbedingt einen Blick in die Man-Pages werfen.
da94c492-d3de-4a80-8c61-b00c9975bdbd.png
$ screen /dev/ttyUSB1
Webseite: GNU screen
Bei Programmierprojekten ist Emacs die erste Wahl. Per "M-x serial-term" wird das eingebaute Terminalprogramm aufgerufen, Geräteschnittstelle und Baud-Rate einstellen, und flupps, ist die Verbindung da. Quelltexte kann man auch editieren.
c1d3ee58-b439-4938-a5b0-06e0892539d5.png
Webseite: http://www.gnu.org/software/emacs/
Da alt-ehrwürdige Kermit, läuft fast auf so vielen Plattformen wie Forth. Funktioniert immer noch, kommt mit Scripting-Funktionen. Gut für Automatisierungsaufgaben.
/home/cas$ kermit C-Kermit 9.0.301 OPEN SOURCE:, 11 Jul 2011, for Linux (64-bit) Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. Type ? or HELP for help. (/home/cas/) C-Kermit>set line /dev/ttyUSB1 (/home/cas/) C-Kermit>set serial 8n1 (/home/cas/) C-Kermit>set flow-control none (/home/cas/) C-Kermit>set carrier-watch off (/home/cas/) C-Kermit>connect Connecting to /dev/ttyUSB1, speed 9600 Escape character: Ctrl-\ (ASCII 28, FS): enabled Type the escape character followed by C to get back, or followed by ? to see other options. ---------------------------------------------------- > amforth 4.2 ATmega328P > ok > ok > ok > (Back at csmobile4.home.strotmann.de) ---------------------------------------------------- (/home/cas/) C-Kermit>quit Closing /dev/ttyUSB1...OK
Webseite: Kermit Projekt
Wir von vielen Anwendern benutzt, ist aber nicht meine erste Wahl.
358cc086-7d55-4fbc-b6aa-6366e623de52.png
a156cddd-254c-4c6b-ad32-36a06296b405.png
7e607b39-8d43-4acc-99b6-c9c0af71b7dc.png
Webseite: MiniCom
Einfach und simpel, ohne Ballast.
/home/cas$ cu -s 9600 -l /dev/ttyUSB1
98665169-3e88-4161-af67-9e1dddd33b79.png
Webseite: GTK Term
Putty kennen viele unter Windows. Aber es gibt auch einen Port für Linux/Unix, der funktioniert auch gut. Ideal für Programmierer, die zwischen den Welten wandern.
6d59ee61-e6b8-4b6e-9202-dc2cea2d59bf.png
4a7cf8df-f010-480f-97af-898dbce4774e.png
Webseite: Putty
Ich benutze einen avrisp MKII Programmer (ca. 30 Euro) um den Arduino neu zu programmieren.
a5642706-0905-4e4a-a0f6-a99423568437.jpg
Sowohl der Programmer als auch das Arduino-Board müssen über USB mit Strom versorgt werden.
Dieser Schritt muss pro Board nur einmal durchgeführt werden, es schaded aber nicht, wenn dieser Schritt bei einem Board ausgeführt wird, welches schon einmal für amForth vorbereitet war- Das amForth benötigt eine ganz bestimmte Konfiguration der "Fuses" auf dem Arduino-Board (siehe amForth Handbuch). Geschrieben werden die Fuses mit avrdude unter Linux:
sudo avrdude -q -c avrispmkII -P usb -p atmega328p -U efuse:w:0x05:m \ -U hfuse:w:0xD9:m -U lfuse:w:0xFF:m avrdude: AVR device initialized and ready to accept instructions avrdude: Device signature = 0x1e950f avrdude: reading input file "0x05" avrdude: writing efuse (1 bytes): avrdude: 1 bytes of efuse written avrdude: verifying efuse memory against 0x05: avrdude: load data efuse data from input file 0x05: avrdude: input file 0x05 contains 1 bytes avrdude: reading on-chip efuse data: avrdude: verifying ... avrdude: 1 bytes of efuse verified avrdude: reading input file "0xD9" avrdude: writing hfuse (1 bytes): avrdude: 1 bytes of hfuse written avrdude: verifying hfuse memory against 0xD9: avrdude: load data hfuse data from input file 0xD9: avrdude: input file 0xD9 contains 1 bytes avrdude: reading on-chip hfuse data: avrdude: verifying ... avrdude: 1 bytes of hfuse verified avrdude: reading input file "0xFF" avrdude: writing lfuse (1 bytes): avrdude: 1 bytes of lfuse written avrdude: verifying lfuse memory against 0xFF: avrdude: load data lfuse data from input file 0xFF: avrdude: input file 0xFF contains 1 bytes avrdude: reading on-chip lfuse data: avrdude: verifying ... avrdude: 1 bytes of lfuse verified avrdude: safemode: Fuses OK avrdude done. Thank you.
Sind die Fuses richtig gesetzt, kann nun das amForth auf den Arduino programmiert werden:
sudo avrdude -q -c avrispmkII -P usb -p atmega328p -e -U flash:w:duemilanove.hex:i \ -U eeprom:w:duemilanove.eep.hex:i avrdude: AVR device initialized and ready to accept instructions avrdude: Device signature = 0x1e950f avrdude: erasing chip avrdude: reading input file "duemilanove.hex" avrdude: writing flash (32300 bytes): avrdude: 32300 bytes of flash written avrdude: verifying flash memory against duemilanove.hex: avrdude: load data flash data from input file duemilanove.hex: avrdude: input file duemilanove.hex contains 32300 bytes avrdude: reading on-chip flash data: avrdude: verifying ... avrdude: 32300 bytes of flash verified avrdude: reading input file "duemilanove.eep.hex" avrdude: writing eeprom (64 bytes): avrdude: 64 bytes of eeprom written avrdude: verifying eeprom memory against duemilanove.eep.hex: avrdude: load data eeprom data from input file duemilanove.eep.hex: avrdude: input file duemilanove.eep.hex contains 64 bytes avrdude: reading on-chip eeprom data: avrdude: verifying ... avrdude: 64 bytes of eeprom verified avrdude: safemode: Fuses OK avrdude done. Thank you.
An dieser Stelle sollte schon der amForth Prompt erscheinen, wenn der Arduino mit einem Terminalprogramm über die USB-serielle Schnittstelle angesprochen wird.
Zum 'nackten' amForth wird nun noch einiges an Quellcode in den Arduino geladen. Hier benutze ich das "amforth-upload.py" Tool. Die Dateien sind:
Diese Dateien sind Bestandteil von amForth, befinden sich aber auch in dieser ZIP-Datei.
amforth-upload.py -t /dev/ttyUSB0 lib/misc.frt lib/bitnames.frt lib/ans94/marker.frt ../amforth/releases/4.2/core/devices/atmega328p/atmega328p.frt lsof: WARNING: can't stat() fuse.gvfs-fuse-daemon file system /home/cas/.gvfs Output information may be incomplete. \ some definitions that may be useful ok > : erase ok 0 fill ok; ok > : .( \ (s -- ) ok [char] ) word count type ok; immediate ok > \ dump free ressources ok > : .res ok base @ >r ok decimal ok ver ." running at " f_cpu 1000 um/mod . drop ." kHz " cr ok ." free FLASH cells " unused u. cr [...]
Der Arduino ist nun vorbereitet und kann für den amForth Workshop, oder für jedes andere amForth Projekt, benutzt werden. Viel Spass!
\ 2011年10月26日 EW \ 2011年11月02日 CS \ arduino duemilanove + danger shield \ morse code stuff (Potsdam/Augsburg/Oberhausen) \ make marker loads: \ lib/misc.frt \ lib/bitnames.frt \ lib/ans94/marker.frt \ ../../amforth/releases/4.2/core/devices/atmega328p/atmega328p.frt marker --base-- decimal PORTD 5 portpin: led1 PORTD 6 portpin: led2 PORTD 3 portpin: bz \ --- buzzer ------------------------------------------------- \ 2 ms T_period =^= 500 Hz \ Ton-Ausgabe : buzz ( cycles -- ) 0 ?do bz low 1ms bz high 1ms loop ; \ Warten ohne Ton : gap ( cycles -- ) 0 ?do bz high 1ms bz high 1ms loop ; \ LED fuer 3ms blinken lassen : blink ( cycles -- ) led1 high 0 ?do 1ms 1ms 1ms loop led1 low ; \ Umschalter zwischen Morse-Ton und Morse-LED Edefer transmit : piepser ['] buzz is transmit ; : blinker ['] blink is transmit ; decimal : kurz 50 transmit 50 gap ; \ Signal kurz : lang 150 transmit 50 gap ; \ Signal lang : Zend 100 gap ; \ Pause zwischen Zeichen : Wend 300 gap ; \ Pause zwischen Worten : init led1 pin_output led1 low led2 pin_output led2 low bz pin_output piepser ;
Die erste Implementierung ist sehr gradlinig mit einem Forth-Wort für jeden Morse-Buchstaben und einer grossen IF-THEN Konstruktion im Hauptteil:
\ 2011年10月26日 EW \ 2011年11月02日 CS \ arduino duemilanove + danger shield \ morse code stuff (Potsdam/Augsburg/Oberhausen) marker --morse-- \ je ein Forth-Wort pro Morse-Buchstabe : _A kurz lang Zend ; : _B lang kurz kurz kurz Zend ; : _C lang kurz lang kurz ; : _D lang kurz kurz Zend ; : _E kurz Zend ; : _F kurz kurz lang kurz Zend ; : _G lang lang kurz Zend ; : _H kurz kurz kurz kurz Zend ; : _I kurz kurz Zend ; : _J kurz lang lang lang Zend ; : _K lang kurz lang Zend ; : _L kurz lang kurz kurz Zend ; : _M lang lang Zend ; : _N lang kurz Zend ; : _O lang lang lang Zend ; : _P kurz lang lang kurz Zend ; : _Q lang lang kurz lang Zend ; : _R kurz lang kurz Zend ; : _S kurz kurz kurz Zend ; : _T lang Zend ; : _U kurz kurz lang Zend ; : _V kurz kurz kurz lang Zend ; : _W kurz lang lang Zend ; : _X lang kurz kurz lang Zend ; : _Y lang kurz lang lang Zend ; : _Z lang lang kurz kurz Zend ; \ Variable zum Speichern des original 'emit' variable o-emit \ neues Emit zum Ausgeben von Zeichen als Morse-Code : morseemit ( key -- ) dup o-emit @ execute ( original 'emit' aufrufen ) dup [char] a = if _A then dup [char] b = if _B then dup [char] c = if _C then dup [char] d = if _D then dup [char] e = if _E then dup [char] f = if _F then dup [char] g = if _G then dup [char] h = if _H then dup [char] i = if _I then dup [char] j = if _J then dup [char] k = if _K then dup [char] l = if _L then dup [char] m = if _M then dup [char] n = if _N then dup [char] o = if _O then dup [char] p = if _P then dup [char] q = if _Q then dup [char] r = if _R then dup [char] s = if _S then dup [char] t = if _T then dup [char] u = if _U then dup [char] v = if _V then dup [char] w = if _W then dup [char] x = if _X then dup [char] y = if _Y then dup [char] z = if _Z then dup bl = if Wend then ( Leerzeichen = Wortende ) drop Zend ( Zeichenende ) ; \ neues 'morseemit' in 'emit' eintragen : morse ['] emit defer@ o-emit ! ['] morseemit is emit ; \ original 'emit' wieder herstellen : endmorse o-emit @ is emit ;
In dieser Version des Morse-Programms nutzen wir immer noch Forth-Wörter für die Signale, aber ersetzten die Grosse IF-THEN Verzweigung durch eine Spring-Tabelle. Die Sprung-Tabelle enhält 256 Einträge für die 256 ASCII-Zeichen. In den Tabellen-Einträgen befindet sich die Adresse des jeweiligen Forth-Worts (Execution Token = xt), welches die Signale für den Morse-Code ausgibt. Ist ein Tabelleneintrag '0' so ist dieser Eintrag nicht gefüllt und es wird kein Morse-Code ausgegeben.
\ 2011年10月26日 EW \ 2011年11月02日 CS \ arduino duemilanove + danger shield \ morse code stuff (Potsdam/Augsburg/Oberhausen) \ 2nd version marker --morse-- : _A kurz lang Zend ; : _B lang kurz kurz kurz Zend ; : _C lang kurz lang kurz ; : _D lang kurz kurz Zend ; : _E kurz Zend ; : _F kurz kurz lang kurz Zend ; : _G lang lang kurz Zend ; : _H kurz kurz kurz kurz Zend ; : _I kurz kurz Zend ; : _J kurz lang lang lang Zend ; : _K lang kurz lang Zend ; : _L kurz lang kurz kurz Zend ; : _M lang lang Zend ; : _N lang kurz Zend ; : _O lang lang lang Zend ; : _P kurz lang lang kurz Zend ; : _Q lang lang kurz lang Zend ; : _R kurz lang kurz Zend ; : _S kurz kurz kurz Zend ; : _T lang Zend ; : _U kurz kurz lang Zend ; : _V kurz kurz kurz lang Zend ; : _W kurz lang lang Zend ; : _X lang kurz kurz lang Zend ; : _Y lang kurz lang lang Zend ; : _Z lang lang kurz kurz Zend ; \ erstelle Tabelle fuer execution token variable mtable 256 cells allot \ loesche Tabelle mtable 256 cells erase \ Hilfswort zum fuellen der Tabelle : >mtable ( xt c -- ) cells mtable + ! ; \ Tabelle zur kompilierzeit fuellen ' _A char a >mtable ' _B char b >mtable ' _C char c >mtable ' _D char d >mtable ' _E char e >mtable ' _F char f >mtable ' _G char g >mtable ' _H char h >mtable ' _I char i >mtable ' _J char j >mtable ' _K char k >mtable ' _L char l >mtable ' _M char m >mtable ' _N char n >mtable ' _O char o >mtable ' _P char p >mtable ' _Q char q >mtable ' _R char r >mtable ' _S char s >mtable ' _T char t >mtable ' _U char u >mtable ' _V char v >mtable ' _W char w >mtable ' _X char x >mtable ' _Y char y >mtable ' _Z char z >mtable ' Wend bl >mtable variable o-emit : morseemit ( key -- ) dup o-emit @ execute ( altes emit ausfuehren ) 255 and ( sicherstellen das wir nur 0-255 Werte bekommen ) cells mtable + @ ( execution token holen ) dup if execute ( wenn > 0 dann ausfuehren ) else drop then ( ansonsten wegwerfen ) ; : morse ['] emit defer@ o-emit ! ['] morseemit is emit ; : endmorse o-emit @ is emit ;
Morse-Code Signale als Forth-Wörter speichern ist einfach, verbraucht aber recht viel Flash-Speicher. Morse-Code Signale können in je einem Byte (8 bit) gespeichert werden. In der dritten Version des Morse-Programms werden bis zu fünf Morse-Signale in den oberen 5 Bits gespeichert ( 0 = kurz, 1 = lang ), die unteren 3 Bits speichern die Anzahl der Signale im Morse-Code. Hierdurch reduziert sich der Speicherverbrauch des Programms.
Die Tabelle mit dem gepackten Morse-Code wird vom Forth-Interpreter zur Kompilierzeit erstellt und verbraucht bei der Ausführung keine extra Rechenzeit:
\ 2011年10月26日 EW \ 2011年11月04日 CS \ arduino duemilanove + danger shield \ morse code stuff (Potsdam/Augsburg/Oberhausen) \ 3nd version marker --morse-- \ erstelle Tabelle fuer gepackte morse daten variable mtable 256 allot \ loesche Tabelle mtable 256 erase \ Hilfswort zum fuellen der Tabelle : >mtable ( gepackter-morsecode c -- ) mtable + c! ; \ Hilfswort zum packen vor morse code : pack ( #zeichen code -- pcode ) 3 lshift swap 7 and or ; : unpack ( pcode -- #zeichen code ) dup 7 and swap 3 rshift ; : binary 2 base ! ; \ Tabelle zur kompilierzeit fuellen binary 00010 decimal 2 pack char a >mtable binary 00001 decimal 4 pack char b >mtable binary 01010 decimal 4 pack char c >mtable binary 00001 decimal 3 pack char d >mtable binary 00000 decimal 1 pack char e >mtable binary 00100 decimal 4 pack char f >mtable binary 00011 decimal 3 pack char g >mtable binary 00000 decimal 4 pack char h >mtable binary 00000 decimal 2 pack char i >mtable binary 01110 decimal 4 pack char j >mtable binary 00101 decimal 3 pack char k >mtable binary 00010 decimal 4 pack char l >mtable binary 00011 decimal 2 pack char m >mtable binary 00001 decimal 2 pack char n >mtable binary 00111 decimal 3 pack char o >mtable binary 00110 decimal 4 pack char p >mtable binary 01101 decimal 4 pack char q >mtable binary 00010 decimal 3 pack char r >mtable binary 00000 decimal 3 pack char s >mtable binary 00001 decimal 1 pack char t >mtable binary 00001 decimal 3 pack char u >mtable binary 00001 decimal 4 pack char v >mtable binary 00011 decimal 3 pack char w >mtable binary 01001 decimal 4 pack char x >mtable binary 01011 decimal 4 pack char y >mtable binary 00011 decimal 4 pack char z >mtable variable o-emit : domorse ( code #signale -- ) 0 ?do ( Schleife Anzahl der Signale ) dup 1 and ( erstes bit maskieren ) if lang ( bit gesetzt = lang ) else kurz then ( sonst kurz ) 2/ ( bits nach rechts schieben ) loop drop Zend ( Zeichenende ) ; : morseemit ( key -- ) dup o-emit @ execute ( altes emit ausfuehren ) 255 and ( sicherstellen das wir nur 0-255 Werte bekommen ) dup bl = if ( leerzeichen? ) Wend ( Wortende zeit warten ) then mtable + c@ ( gepackten morse code holen ) unpack ( entpacken ) domorse ( signale ausgeben ) ; : morse ['] emit defer@ o-emit ! ['] morseemit is emit ; : endmorse o-emit @ is emit ;
Antworten auf diese Aufgaben können auf Mastodon gepostet werden. Bitte Geduld wenn ich nicht sofort antworte, ich kann leider nicht immer online sein.
Atari ST disk are formatted using a flavor of the FAT file system. Most of the time, it is possible to read this disks under Linux. The 'mtools' package contains tools to read and write FAT formatted floppy disks, without the need to rely on a file-system driver (the 'mtools' package is available in most Linux distributions. For Ubuntu Linux, execute "sudo apt-get install mtools" to install).
For reading old floppy disks, I recommended to use old style floppy disks, no modern USB-Floppy disks which can often only read standard PC formats. I've used a build-in 31⁄2" floppy drive inside a trusty old IBM ThinkPad T20 running Ubuntu 11.04.
Before reading the files from an Atari ST disk, I configure 'mtools' to skip the check for the floppy format. The FAT format on Atari ST disks is somewhat non-standard, which can trick the 'mtools':
Add to /etc/mtools.conf
MTOOLS_SKIP_CHECK=1
The mtools manual page documents:
MTOOLS_SKIP_CHECK: If this is set to 1, mtools skips most of its
sanity checks. This is needed to read some Atari disks which have been
made with the earlier ROMs, and which would not be recognized
otherwise.
Next, make sure that the floppy kernel driver module is loaded. It seems that modern Linux systems do not load the driver for floppy disk anymore:
# sudo lsmod | grep floppy # sudo modprobe floppy # sudo lsmod | grep floppy floppy 60032 0
Then I set the floppy drive parameters fixed for a 720kb floppy. The 'setfdprm' command is part of the 'fdutils' package:
# sudo setfdprm /dev/fd0 "720/720"
The command "mdir a:" should now show the directory content of the floppy disk. The floppy disk files can now be copies over to the Linux harddrive using 'mcopy':
# mkdir atari-st-floppy # cd atari-st-floppy # mcopy a:*.* .
Rich Text Format (RTF) is a standard format for text documents. Using tools like UnRTF, RTF documents can be easly converted into other formats. RTF can be read my most modern text-processing packages such like LibreOffice and AbiWord.
'wpls2rtf' is a small commandline tools that can translate 1st Word+ files into RTF documents. The original source can be found at ftp://ftp.funet.fi/pub/atari/util/wpls2rtf.zip. Using this tool, I was able to convert the old Atari ST documents into files that can be read and edited on modern systems.
The GIMP Script-Fu scheme snippet will take a file wildcard pattern ("*.JPG") and an rotate-type value (0 = 90° / 1 = 180° / 2 = 270°), level out the colors in the picture and save the changed image as a PNG file.
(define (batch-rotate pattern rotate-type) (let* ((filelist (cadr (file-glob pattern 1)))) (while (not (null? filelist)) (let* ((filename (car filelist)) (image (car (gimp-file-load RUN-NONINTERACTIVE filename filename))) (drawable (car (gimp-image-get-active-layer image)))) (set! filename (strbreakup filename ".")) (set! filename (butlast filename)) (set! filename (string-append (unbreakupstr filename ".") ".png")) (gimp-image-rotate image rotate-type) (gimp-levels-stretch drawable) (gimp-file-save RUN-NONINTERACTIVE image drawable filename filename) (gimp-image-delete image)) (set! filelist (cdr filelist)))))
The scheme function above can be stored into the users GIMP scripting
directory (~/.gimp-2.6/scripts/) as "rotate-and-level.scm".
The shell script below can be used to start the batch rotate process on a directory of JPEG pictures.
#!/bin/sh
# file rotate.sh
gimp -i -b "(batch-rotate \"${1}\" ${2})" -b '(gimp-quit 0)'
On a Unix shell, the glob file pattern needs to be quoted so that the shell will not expand it:
# ./rotate.sh "*.JPG" 0]]>
Two commands can spring clean the MacPorts installation.
sudo port clean --all installed
will run "make clean" on all installed ports, removing the temporary object code generated during compilation.
sudo port -f uninstall inactive
will remove 'inactive' ports, mainly older versions of applications that have been replaced by a more recent version. Running these two commands can free up some gigabyte of space on a harddisk (depending on the amount of MacPort applications installed).
]]>
The script presented here will read a firewall configuration from
/etc/ip6fw.conf and will apply the IPv6 firewall rules to the MacOS
X firewall.
#!/bin/sh
ip6fwconf="/etc/ip6fw.conf"
ip6fw=`which ip6fw`
logcmd="logger -p local0.info -t IP6FW"
if [ -z "$ip6fw" ]; then
$logcmd "no ip6fw binary found in path"
fi
$ip6fw -fq flush
while read line; do
# remove comments and normalize to lowercase
line=`echo $line | sed '/^ *#/d;s/#.*//' | tr "[:upper:]" "[:lower:]"`
if [[ $line == *logging=* ]]; then
value=${line#*=}
if [[ $value == 'yes' ]]; then
$logcmd "logging on"
sysctl -w 'net.inet6.ip6.fw.verbose=2'
else
$logcmd "logging off"
sysctl -w 'net.inet6.ip6.fw.verbose=0'
fi
fi
action=`echo $line | awk '{ print 2ドル }'`
rulenum=`echo $line | awk '{ print 1ドル }'`
rule=`echo $line | awk '{ for (i = 3; i <= NF; i++) { printf "%s ",$i } }'`
case $action in
allow | deny | unreach )
$logcmd -s "add $rulenum $action $rule"
$ip6fw add $rulenum $action $rule
;;
"")
;;
*) $logcmd "unsupported action $action"
;;
esac
done < $ip6fwconf
The default configuration for the MacOS X firewall is
# MacOS X 10.x ip6fw configuration logging=yes # loopback 10000 allow ipv6 from any to any via lo0 # Duplicate Address detection (DAD) 20000 allow ipv6-icmp from any to FF02::/16 # Neighborhood discovery (ND) 20010 allow ipv6-icmp from FE80::/10 to FE80::/10 20015 allow ipv6-icmp from FE80::/10 to FF02::/16 20016 allow ipv6-icmp from any to any icmptype 135,136 # ICMPv6 from the network 20020 allow ipv6-icmp from any to any in icmptype 1,2,3,4,128,129 # Router Advertisements 20050 allow ipv6-icmp from any to any icmptype 134 20055 allow ipv6-icmp from any to any icmptype 133 # allow established TCP connections 20100 allow tcp from any to any established # NTP multicast 20210 allow udp from any to ff02::1010 123 via en0 # mDNS (Rendezvous/Bonjour) 20220 allow udp from any to ff02::fb 5353 via en0 # allow all outgoing IPv6 connections 20230 allow ipv6 from any to any out # allow DNS in and out 20240 allow udp from any to any 53 out 20241 allow udp from any 53 to any in # Secure Shell 20500 allow tcp from any to any 22 # Web-Server #20510 allow tcp from any to any 80 # Postfix SMTP #20520 allow tcp from any to any 25 # IPP / CUPS 20530 allow tcp from any to any 631 # Apple Filing Protocol 20540 allow tcp from any to any 548 20541 allow tcp from any to any 427 # block all not otherwise allowed and log 65534 unreach admin log ipv6 from any to any
To execute this script everytime MacOS X boots, a LaunchDaemon
configuration is installed in /Library/LaunchDaemons:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Disabled</key> <false/> <key>Label</key> <string>de.strotmann.ip6fw</string> <key>OnDemand</key> <false/> <key>ProgramArguments</key> <array> <string>/usr/local/sbin/ip6fw-load</string> </array> <key>LaunchOnlyOnce</key> <true/> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <false/> <key>ServiceDescription</key> <string>loads ip6fw firewall rules from /etc/ip6fw.conf</string> </dict> </plist>
The installer package will install the script into
/usr/local/sbin/ip6fw-load, a default configuration file into
/etc/ip6fw.conf/default and if no file /etc/ip6fw.conf exists, it
will create this file containing the default configuration. It will
then load the LaunchDaemon configuration with
launchctl load -w /Library/LaunchDaemons/de.strotmann.ip6fw.plist
MacOS X 10.5/10.6 installer package
With sudo ip6fw list on the terminal commandline we can check that
all the rules are loaded and active:
# sudo ip6fw list Password: 10000 allow ipv6 from any to any via lo0 20000 allow ipv6-icmp from any to ff02::/16 20010 allow ipv6-icmp from fe80::/10 to fe80::/10 20015 allow ipv6-icmp from fe80::/10 to ff02::/16 20016 allow ipv6-icmp from any to any icmptype 135,136 20020 allow ipv6-icmp from any to any in icmptype 1,2,3,4,128,129 20050 allow ipv6-icmp from any to any icmptype 134 20055 allow ipv6-icmp from any to any icmptype 133 20100 allow tcp from any to any established 20210 allow udp from any to ff02::1010 123 via en0 20220 allow udp from any to ff02::fb 5353 via en0 20230 allow ipv6 from any to any out 20240 allow udp from any to any 53 out 20241 allow udp from any 53 to any in 20500 allow tcp from any to any 22 20530 allow tcp from any to any 631 20540 allow tcp from any to any 548 20541 allow tcp from any to any 427 65534 unreach admin log ipv6 from any to any 65535 allow ipv6 from any to any]]>
# sudo ip6fw add 20020 allow ipv6-icmp from any to any in icmptype 1,2,3,4,128,129 20020 allow ipv6-icmp from any to any in icmptype 1,2,3,4
The command still worked on MacOS X 10.5. It turned out that Apple has
compiled ip6fw for x86_64, a 64bit target.
# file /sbin/ip6fw /sbin/ip6fw: Mach-O universal binary with 3 architectures /sbin/ip6fw (for architecture x86_64): Mach-O 64-bit executable x86_64 /sbin/ip6fw (for architecture i386): Mach-O executable i386 /sbin/ip6fw (for architecture ppc7400): Mach-O executable ppc
The code, forked from the FreeBSD project in 2001 (which got the original code from the KAME project), is not 64bit clean.
But there is an easy solution. As the file contains in an Universal
binary the code for i386 (besides ppc and x86_64), it is possible
to strip away the 64bit code (and the ppc code not needed on Intel
MacOS X) to get a working copy of ip6fw:
# sudo ditto --rsrc --arch i386 /sbin/ip6fw /sbin/ip6fw.i386 # sudo mv /sbin/ip6fw.i386 /sbin/ip6fw
Now it is possible to enter ICMPv6 filter rules for ICMPv6 types above 127:
# sudo ip6fw add 20020 allow ipv6-icmp from any to any in icmptype 1,2,3,4,128,129 20020 allow ipv6-icmp from any to any in icmptype 1,2,3,4,128,129]]>
On my IPv6 router, I have the router advertisement daemon 'radvd' running:
blackbox# more /etc/radvd.conf
interface eth0
{
AdvOtherConfigFlag on;
AdvManagedFlag off;
AdvSendAdvert on;
RDNSS 2001:db8:2b6::1 {
AdvRDNSSLifetime 84600;
};
prefix 2001:db8:2b6::/64
{
AdvOnLink on;
AdvAutonomous on;
};
};
The Line 'RDNSS 2001:db8:2b6::1' configures the advertisement daemon to send the DNS Servers IPv6 address '2001:db8:2b6::1' with a lifetime of 24 hours (86400 seconds).
When the 'rdnssd' service is then started on a MacOS X client, it will receive the DNS server information and write it into the file '/usr/local/var/run/rdnssd/resolv.conf' (this file location can be changed with the commandline option '–resolv-file' (however 'rdnssd' should never write directly into '/etc/resolv.conf', as this will overwrite any data received via DHCP). The content of this file after receiving the DNS information is
more /usr/local/var/run/rdnssd/resolv.conf nameserver 2001:db8:2b6::1
Whenever the 'rdnssd' daemon updates its 'resolv.conf' file, it can start a script (the script location can be configured with '–merge-hook merge-hook', the default location is '/usr/local/etc/rdnssd/merge-hook' ('rdnssd' comes with an example script for Linux, but this will not work on MacOS X).
simple 'rdnssd' script hook:
#! /bin/sh
#
# resolv.conf merge hook for rdnssd
# MacOS X 10.6 version
# Carsten Strotmann, March 2011
# partly based on a scipt by Stephan Oeste
set -e
system_resolv="/etc/resolv.conf"
rdnssd_resolv="/usr/local/var/run/rdnssd/resolv.conf"
# make sure that each IP only appears once
cat $system_resolv $rdnssd_resolv | sort | uniq > /tmp/resolv.conf.tmp
# get the IP addresses of all DNS servers
ip=`cat /tmp/resolv.conf.tmp | grep "nameserver" | awk '{printf "%s ", 2ドル}'`
# get the local domain
domain=`cat /tmp/resolv.conf.tmp | grep "domain" | awk '{printf "%s ", 2ドル}'`
rm /tmp/resolv.conf.tmp
PSID=$( (scutil | grep PrimaryService | sed -e 's/.*PrimaryService : //')<< EOF
open
get State:/Network/Global/IPv4
d.show
quit
EOF
)
scutil << EOF
open
d.init
d.add ServerAddresses * $ip
d.add DomainName $domain
set State:/Network/Service/$PSID/DNS
quit
EOF
This is the LaunchDaemon description for the 'rdnssd' service:
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" > <plist version='1.0'> <dict> <key>Label</key><string>net.linkfanel.ndisc6</string> <key>ServiceDescription</key> <string>Recursive DNS Solicitation Daemon</string> <key>ProgramArguments</key> <array> <string>/usr/local/sbin/rdnssd</string> <string>-f</string> <string>-H/usr/local/etc/rdnssd/merge-hook</string> <string>-r/usr/local/var/run/rdnssd/resolv.conf</string> </array> <key>WorkingDirectory</key> <string>/</string> <key>Debug</key><false/> <key>Disabled</key><false/> <key>OnDemand</key><false/> <key>RunAtLoad</key><true/> </dict> </plist>
To start the 'rdnssd' service, copy the above plist into
/Library/LaunchDaemons/net.linkfanel.ndisc6.rdnssd.plist and
reconfigure launchd with sudo launchctl load -w /Library/LaunchDaemons/net.linkfanel.ndisc6.rdnssd.plist.
The 'rdnssd' source compiled without issues, but when started, it stopped immediatly with a segfault:
# sudo /usr/local/sbin/rdnssd -f [1] 1243 segmentation fault sudo /usr/local/sbin/rdnssd -f
On Linux I'm used to strace (and Solaris has truss) to trace the sysemcalls of a process, hoping that would point me to the error. MacOS X 10.5/10.6 has several very useful tools build on top of DTrace. Apple Technical Note 2124 lists a good part of these tools.
A run of the process using dtrace only showed that the process died after forking, with no additional information. errinfo also didn't show any suspicious syscalls failing. But the use of opensnoop (traces the files opened by a process) showed that acutally MacOS X is writing a crash dump for the failing process with additional information:
# opensnoop | grep rdnssd 0 1446 rdnssd 3 /dev/urandom 0 1446 rdnssd 3 /dev/dtracehelper 0 1446 rdnssd 3 /dev/urandom 0 1446 rdnssd 3 /dev/urandom 0 1446 rdnssd 3 /usr/share/locale/en_US.UTF-8/LC_CTYPE 0 1448 rdnssd -1 /etc/sysinfo.conf 0 1447 taskgated 4 /usr/local/sbin/rdnssd 0 1446 rdnssd 3 /usr/local/var/run/rdnssd.pid 0 1449 ReportCrash 5 /usr/local/sbin/rdnssd 0 1449 ReportCrash 5 /Developer/source/ipv6/ndisc6-1.0.1/rdnssd/rdnssd.o 0 1449 ReportCrash 5 /Developer/source/ipv6/ndisc6-1.0.1/rdnssd/icmp.o 0 1449 ReportCrash 5 /Developer/source/ipv6/ndisc6-1.0.1/rdnssd/rdnssd.o 0 1449 ReportCrash 5 /Developer/source/ipv6/ndisc6-1.0.1/rdnssd 0 1449 ReportCrash 5 /Developer/source/ipv6/ndisc6-1.0.1/rdnssd/icmp.o 0 1449 ReportCrash -1 /Developer/source/ipv6/ndisc6-1.0.1/rdnssd/../compat/libcompat.a(ppoll.o) 0 1449 ReportCrash 5 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618_localhost.crash 0 1449 ReportCrash -1 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618_localhost.crash 0 1449 ReportCrash 6 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618-1_localhost.crash 0 1449 ReportCrash 5 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 0 1449 ReportCrash 5 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 0 1449 ReportCrash 5 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 0 1449 ReportCrash 5 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 89 1450 mdworker 6 /usr/local/var/run/rdnssd.pid 89 1450 mdworker 6 /usr/local/var/run/rdnssd.pid 0 34 mds 57 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618-1_localhost.crash 89 34 mds -1 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618-1_localhost.crash 0 34 mds 57 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618_localhost.crash 89 34 mds -1 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618_localhost.crash 89 1450 mdworker -1 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 0 34 mds 7 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 89 34 mds -1 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist
A look into the crash report file revealed the information I was looking for:
more /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618_localhost.crash Process: rdnssd [1446] Path: /usr/local/sbin/rdnssd Identifier: rdnssd Version: ??? (???) Code Type: X86-64 (Native) Parent Process: zsh [1161] Date/Time: 2011年03月10日 19:06:17.998 +0100 OS Version: Mac OS X 10.6.6 (10J567) Report Version: 6 Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000008 Crashed Thread: 0 Dispatch queue: com.apple.main-thread Thread 0 Crashed: Dispatch queue: com.apple.main-thread 0 rdnssd 0x00000001000023d2 ppoll + 66 1 rdnssd 0x0000000100001fc2 main + 2418 (rdnssd.c:377) 2 rdnssd 0x00000001000010a4 start + 52 Thread 0 crashed with X86 Thread State (64-bit): rax: 0x0000000000000000 rbx: 0x00000000ffffffff rcx: 0x00007fff80b1ea02 rdx: 0x0000000000000000 rdi: 0x0000000000000003 rsi: 0x00007fff5fbffa4c rbp: 0x00007fff5fbff910 rsp: 0x00007fff5fbff8d0 r8: 0x00007fff70125f0c r9: 0x0000000000000000 r10: 0x00007fff5fbffa4c r11: 0x0000000000000202 r12: 0x0000000000000000 r13: 0x0000000000000001 r14: 0x00007fff5fbffa30 r15: 0x00007fff5fbff8dc rip: 0x00000001000023d2 rfl: 0x0000000000010246 cr2: 0x0000000000000008 Binary Images: 0x100000000 - 0x100002ff7 +rdnssd ??? (???) /usr/local/sbin/rdnssd 0x7fff5fc00000 - 0x7fff5fc3bdef dyld 132.1 (???) /usr/lib/dyld 0x7fff80abc000 - 0x7fff80c7dfff libSystem.B.dylib 125.2.1 (compatibility 1.0.0) <71E6D4C9-F945-6EC2-998C-D61AD590DAB6> /usr/lib/libSystem.B.dylib 0x7fff82f2c000 - 0x7fff82f30ff7 libmathCommon.A.dylib 315.0.0 (compatibility 1.0.0) <95718673-FEEE-B6ED-B127-BCDBDB60D4E5> /usr/lib/system/libmathCommon.A.dylib 0x7fffffe00000 - 0x7fffffe01fff libSystem.B.dylib ??? (???) <71E6D4C9-F945-6EC2-998C-D61AD590DAB6> /usr/lib/libSystem.B.dylib
The culprit is in the sourcefile rdnssd.c, line 377, in the function
call 'ppoll'. The reason was a bad memory access (EXC_BAD_ACCESS).
This should be all information needed to find and fix the issue.
Stay tuned…
]]>screenshot_0307170513.png
Below are packages for the TinyCore Linux System, compiled from the 11 October 2009 release sourcecode from the JASSPA Website.
There are three different packages:
Yesterday, I was looking for a 3 1⁄2" floppy disk for an Atari ST. I found one disk without label, and before formatting it, I tool a look what's stored on it. To my surprise, I found some of the first digitized pictures (from me) made on Hobbytronic 1992 (a computer fair in Dortmund) where ABBUC had a booth opposite a company doing scanner and digitizer cameras. So below we have the ABBUC Hobbytronic booth crew of 1992:
The theme of the exhibition has been "In space, no one can hear you scream", and for the RTX 2000 machines there was materials collected about NASA, AMSAT and ESA space missions that make use of the RTX 2000 CPU. Both RTX2000 Boards (a "kleiner Muck" and a "Wiesel 2000") were shown working, connected via RS232 to an Amstrad NC100 that was used as a terminal and Forth Editor.
Especially the VME Bus on the "kleiner Muck" got attention, and there will be a follow up project to connect the Muck with an Atari TT (which also has an VME Bus internally).
To be able to judge the processing speed of the RTX2000 compared with a regular CPU, Stefan "Beetle" Niestegge from the Atari TT exhibition helped running a simple benchmark (Primes) on both boards and on an Atari TT. The Atari TT was running BigForth ST on MINT on an Motorola 68030 with 30 Mhz.
As a result we found that the "Muck" with an 6Mhz RTX2000 is about the same processing speed (when it comes to Forth) as the 30Mhz Atari TT. Interestingly the 10Mhz "Wiesel" was about two times slower than the "Muck" and the "TT", which is probably due to some errors in our benchmarking (If someone has worked with the "Wiesel" board and has an idea what can cause this slowdown, please leave a comment).
The VCFe was again a very enjoyable weekend. Unfortunately the weather in Munich was too good, so there were not too much visitors finding their way into the VCFe Exhibition grounds.
The topic for next years VCFe will be "Online Communication". If you have nostalgia for UUCP, Mailboxing and acoustic couplers, please join next year!
]]>Learning Groovy last year and using it with Grails this year was great fun.
The cause is that Pragmatic Programmers have just release a beta book about Clojure, while there Scala book will arrive later this year.
While learning Clojure, my plan is also to give Emacs another try as my IDE of choice. So far, my flirt with Emacs always did not last very long, for various reasons.
But because Clojure and Emacs seems to be a nice fit, I will give Emacs another try.
Below some links (mainly for myself to remember):
]]>Apple MacOS X Timemachine is a nice piece of software. However it does not compress the data, and it only works on MacOS X. I was looking to a similar solution that works also on other Unix Systems (as well as MacOS X) and does transparent compression of the data. The idea is to have multiple backups on one disk, each backup showing the state of the source hard disk or directory at the time of the backup, browsable by the normal file system tools, without storing any data duplicated. I found a solution using the ZFS file system and 'rsync' (rsync is pre-installed on most Unixish operating systems).
My tutorial is for MacOS X, but it can be adapted to any of the Systems that support the ZFS file system
I used the steps from the ZFS on MacOS-Forge site to create a ZFS pool on an external USB drive: Finding the disk:
# diskutil list . . . /dev/disk2 #: type name size identifier 0: Apple\_partition\_scheme *9.4 GB disk2 1: Apple\_partition\_map 31.5 KB disk2s1 2: Apple\_HFS FW 9.2 GB disk2s3
writing a GPT label on the external disk (be sure to not format your 'main' disk here!):
# diskutil partitiondisk /dev/disk2 GPTFormat ZFS %noformat% 100% Started partitioning on disk disk2 Creating partition map [ +0%..10%..20%..30%..40%..50%..60%..70%..80%..90%..100% ] Finished partitioning on disk disk2 /dev/disk2 #: type name size identifier 0: GUID\_partition\_scheme *9.4 GB disk2 1: EFI 200.0 MB disk2s1 2: ZFS 9.0 GB disk2s2
creating a ZFS pool on the disk called 'macbook-backup':
# zpool create macbook-backup /dev/disk2s2
enable compression on the new pool and disable ATIME:
# zfs set compression=on macbook-backup # zfs set atime=off macbook-backup
the hard drive is now prepared.
now I create the first full backup, which I call the 'base' backup. For this I create a new file system called 'base' in the ZFS pool 'macbook-backup':
# zfs create macbook-backup/base
next I copy all files from my backup-source directory (or the whole source disk) to the backup:
# rsync -avh --progress --delete /Users/myuser /Volumes/macbook-backup/base/
depending on the size of the data to backup, this will take a while.
Once the backup is finished, we can access all files under
/Volumes/macbook-backup/base. With the ZFS command I can check the
compression-ratio of our backup:
# zfs get all macbook-backup/base NAME PROPERTY VALUE SOURCE macbook-backup/base type filesystem - macbook-backup/base creation Wed Jan 31 9:08 2007 - macbook-backup/base used 2.21G - macbook-backup/base available 1.76G - macbook-backup/base referenced 2.21G - macbook-backup/base compressratio 1.38x - macbook-backup/base mounted yes - macbook-backup/base quota none default macbook-backup/base reservation none default macbook-backup/base recordsize 128K default macbook-backup/base mountpoint /Volumes/macbook-backup/base default macbook-backup/base sharenfs off default macbook-backup/base shareiscsi off default macbook-backup/base checksum on default macbook-backup/base compression on local macbook-backup/base atime off local macbook-backup/base devices on default macbook-backup/base exec on default macbook-backup/base setuid on default macbook-backup/base readonly off default macbook-backup/base zoned off default macbook-backup/base snapdir hidden default macbook-backup/base aclmode groupmask default macbook-backup/base aclinherit secure default macbook-backup/base canmount on default macbook-backup/base xattr on default
Now, a few weeks later, I want to make a new, incremental backup. So I create a new snapshot of the base file system and then clone that snapshot into a new file system:
zfs snapshot macbook-backup/base@20080804 zfs clone macbook-backup/base@20080804 macbook-backup/20080804
The directory for my new, incremental backup will be
/Volumes/macbook-backup/20080804. So far the new file system does
not use any space on the hard drive. Now I do the new backup with
'rsync':
# rsync -avh --progress --delete /Users/myuser /Volumes/macbook-backup/20080804/
The new backup will only take as much new space on the backup hard
drive as there were changes compared to the base backup. But still I
am able to browse through the file system at
/Volumes/macbook-backup/20080804 and see all files that were
available at the date of the 2nd backup.
Any subsequent snapshots for more backups will be done from the 20080804 file system.
Binärdateien für SUN Solaris Unix Version 7 (SunOS 5.7) für Intel Prozessoren (nicht SPARC). Es ist die original MicroEmacs 4.00 Version und die JASSPA MicroEmacs 99 Version verfügbar. Unbedingt die benötigten Resourcedateien von der jeweiligen Homepage besorgen.
Dies ist die 32 Bit Version des MicroEmacs 4.00 Texteditor. Ich habe die Quelldateien der 3.12 EMX/GCC Version von Ralf Seidel (siehe Datei readme.os2) mit dem Quellcode 4.00 von Daniel Lawrence zusammengeführt. Die verwendete EMX Version ist 0.9d, es wird also die EMX Runtimeumgebung emxrt.zip ( http://hobbes.nmsu.edu oder http://www.leo.org) benötigt.
Ich habe einige Änderungen an dem Quellcode in os2npm.c durchgeführt um auf eine erweiterte Bildschirmauflösung von 50x132 Zeichen (XGA) schalten zu können. Diese Auflöung wird durch den Befehl "set $sres XGA" in der emacs.rc eingestellt. Die Bildschirmauflösung kann auch durch den OS/2 Befehl "mode con1 coYY,XX" vor dem Start von MicroEmacs eingestellt werden. XX ist die Anzahl der Spalten, YY ist die Anzahl der Zeilen.
Einige Änderungen in den Quelldateien file.c und german.h verbessern die Unterstützung der deutschen Sprache.
Bei Fragen oder Anregungen zur OS/2 Version schreibt mir eine Mail via Internet-Mail: carsten@strotmann.de.
Binaries for SUN Solaris Unix Version 7 (SunOS 5.7) / Intel (not SPARC). The original MicroEmacs 4.00 version and the JASSPA MicroEmacs 99 versions are available. Please don't forget to download the resourcefiles from the distribution-homepages!
This is a 32 Bit version of MicroEmacs 4.00. I used the MicroEmacs 3.12 emx/gcc port of Ralf Seidel (see file readme.os2) and merged it with the 4.00 Source from Daniel Lawrence. The emx version used is 0.9d, so you need emxrt.zip ( http://hobbes.nmsu.edu or http://www.leo.org).
I changed the Source os2npm.c to use a extended screen resolution XGA 50x132. You can set this resolution with "set $sres XGA" in the emacs.rc file. You can also change the screen resolution before starting MicroEmacs with the OS/2 command "mode con1 coYY,XX, where YY are the rows and XX are the columns.
I also changed file.c and german.h for german language support.
If you have questions about this port, feel free to contact me via Internet-Mail: carsten@strotmann.de.