Hello, there is a strange bug in puts(3) affecting /hurd/init (or /hurd/startup if you live on the edge). It is also sometimes affecting printf(3), as it is replaced by gcc with a call to puts under certain conditions (e.g. with a format string ending in '\n' without any conversion specifiers).
Here is a screenshot of /hurd/startup patched with the patch below: [... gnumach stuff...] task loaded: exec /hurd/exec start ext2fs: Hurd server bootstrap: ext2fs[device:hd0s1] exec startup proc auth. INIT: version 2.88 booting Using makefile-style concurrent boot in runlevel S. [...] Note the '.' after "auth", followed by a newline. The extra newline is printed by our runsystem script, as a workaround for this bug. This is what the shutdown is supposed to look like: [...] Deactivating swap...swapoff: /dev/hd0s5: 177152k swap space done. Unmounting local filesystems...done. mount: cannot remount /: Device or resource busy Will now halt. startup: notifying pfinet of halt...done startup: notifying tmpfs none of halt...done startup: notifying tmpfs none of halt...done startup: notifying tmpfs none of halt...done startup: notifying ext2fs device:hd0s1 of halt...done startup: halting Mach (flags 0x8)... In contrast, here is a shutdown with the unpatched /hurd/init: root@debian:~# halt-hurd init: notifying pfinet of shutdown...init: notifying tmpfs none of shutdown...init: notifying tmpfs none of shutdown...init: notifying tmpfs none of shutdown...init: notifying ext2fs device:hd0s1 of shutdown...init: halting Mach (flags 0x8)... Note that contrary to my comment in the patch below it isn't only swallowing the newline, but the whole string (i.e. ".\n" and "done\n"). I've no idea whats wrong, or why it is only affecting /hurd/startup. Maybe it's the way /hurd/startup setups stdout. Here's the code: /* Fetch a port to the bootstrap filesystem, the host priv and master device ports, and the console. */ if (task_get_bootstrap_port (mach_task_self (), &bootport) || fsys_getpriv (bootport, &host_priv, &device_master, &fstask) || device_open (device_master, D_WRITE, "console", &consdev)) crash_mach (); [...] stderr = stdout = mach_open_devstream (consdev, "w"); stdin = mach_open_devstream (consdev, "r"); if (stdout == NULL || stdin == NULL) crash_mach (); setbuf (stdout, NULL); In contrast, here is what libdiskfs does: err = get_privileged_ports (NULL, &dev); assert_perror (err); err = device_open (dev, D_READ|D_WRITE, "console", &cons); mach_port_deallocate (mach_task_self (), dev); assert_perror (err); stdin = mach_open_devstream (cons, "r"); stdout = stderr = mach_open_devstream (cons, "w"); So, startup calls setbuf while libdiskfs does not. I once removed that call, it didn't affect the bug. Also, startup only passes D_WRITE to device_open, while libdiskfs uses D_READ|D_WRITE. This is probably a bug, but I would expect it do affect reading from stdin (if anything), not writing to stdout. Thoughts? Justus diff --git a/startup/startup.c b/startup/startup.c index ff58270..e1f07a2 100644 --- a/startup/startup.c +++ b/startup/startup.c @@ -1663,3 +1663,10 @@ S_fsys_forward (mach_port_t server, mach_port_t requestor, { return EOPNOTSUPP; } + +/* XXX: puts is broken, it doesn't print the newline. */ +int +puts (const char *s) +{ + return printf ("%s%c", s, '\n') == 0? EOF: 1; +}