doing what weston-launch did, but in a saner way
weston-launch is already pretty sane :) Shameless plug: I Rewrote It In Rust (well, not protocol compatible with the original weston-launch, just the same idea.) My version uses Capsicum sandboxing on FreeBSD. The (privileged) parent has a socket to the child, a kqueue and a descriptor to /dev, that’s it. No way to open ~/.ssh/secret_key, it can only openat files under /dev.
[evdev] gets an ever increasing pile of “helper” libraries (libevdev, libudev, libinput, libratbag, libkilljoy, libreadabuttonpress, libvaluebetween0and1, …)
Okay, that’s not fair. udev is hotplug, and ratbag is configuring dank gaming LEDs on gaming mice. It makes perfect sense that the kernel reports simple events and userspace (libinput) interprets it in complex ways (touch gestures, calibration, etc.)
evdev is one of the few parts of Linux that don’t suck, and I’m very happy to have it on FreeBSD too.
weston-launch is already pretty sane :)
It’s the decoupling between -launch and the main process being a hard coded exec I have a beef with. Hurts when you have multiple versions installed etc. So yes, I’ve had futile weston debugging sessions boiling down to forgetting that weston-launch does this.
I took a peak at your plug - now my Rust is a bit non-existing, but to me it look like you can path traverse out of drm and input from those starts_with checks and collect sensitive data from the other device nodes in there? i.e. why xenocara went with a whitelist.
evdev is one of the few parts of Linux that don’t suck, and I’m very happy to have it on FreeBSD too
Have you actually seriously worked with it, as in the real thing and not a reasonably clean “we ported the structs and ioctls” and without someone wrapping udev+libevent+device fixup database?
The cracks doesn’t start to show until you work with input devices that aren’t your basic keyboard and mouse. They widen quickly and large enough to swallow you whole after that. The ‘big box of fun’ input devices that I try whenever I feel the need to lower my spirits cover everything from joysticks that randomly gets exposed as a keyboard and as a joystick depending on what else I have plugged into the bus, mice and keyboards that expose LEDs but then require separate custom hid packets for the changes to activate, etc.
To clarify my stance: evdev is much too simple for the devices it provides access for - lacking basics like filter controls, ambiguous and unreliable device identity (both collisions and failure to track the same device moving between connected ports), lost relationships (for devices that split up into 2+ separate subdevices). Just look at this small slice of nightmare fuel, a condensed version of what udev is doing.
Yeah, protecting other devices is not something I bothered to do yet. But it’ll be easy to just open the input and dri subdirectories. A bit unfortunate that the virtual terminals aren’t in a subdirectory, but opening the vt doesn’t take user input.
About evdev, well, Synaptics touchpad + TrackPoint and Windows Precision touchscreen work fine. I actually added Apple Magic Trackpad support to bthidd and that mostly works too. I also made a little daemon that generates events on a virtual device based on a user script that gets events from other devices.
We don’t have gamepad support yet… also we don’t have udev anything, only a stub libudev that uses devd for hotplug notifications.
Is there a real need to classify devices as mouse/keyboard/etc? Why not assume that any device is everything at once? The only meaningful distinction I can think of is touchpad vs touchscreen (both send absolute events)…
Most gains of knowing the separation is in the user interface and the performance optimization stages, where it can be incredibly noticeable. Since everything is dynamic / hotplug and non-deterministic it’s hard to know if the system is in a usable state or not if you don’t preserve type. Then there’s the threat model that comes with something like a rubber ducky…
Some generals: For keyboards you want to load/probe for translation tables/state machines, repeat-rate controls / recovery keybindings.
For mice you want dynamic sample rate controls dependent on the type of your input focus, and with most optical mice today, detect / discard jitter if at rest. The more you can early-discard or memory map instead of queue+copy the better. If you work with accessibility or want to reduce physical motion, pair it with an eye tracker (adding support for that right now).
For gamepads you really want them disabled at the lowest level until they are actually needed or they can murder your input stack. Something like the PS3 or PS4 controller exposes 30ish analog inputs, each at very high samplerates, even if they’re not used, they get sampled, read, forwarded, queued, copied, and much later discarded.
touchpad vs touchscreen (there’s a lot here) both devices classes can actually send both absolute and relative events, think of the Z axis or “pressure” (some touchscreens can even give you a rough 2.5D heightmap of what it “sees” while some hide the analysis in firmware). These behave/“feel” better if you align or resample them to the sample rate of the current output display, and there’s big gains if you hook up to CPU/GPU governors. Precision and calibration varies with room temperature and grounding / signal environment.
Yeah, protecting other devices is not something I bothered to do yet. But it’ll be easy to just open the input and dri subdirectories. A bit unfortunate that the virtual terminals aren’t in a subdirectory, but opening the vt doesn’t take user input.
I guess it’s not very portable, but in the past I’ve used devfs rulesets[1] in chroots or jails for isolating device nodes for certain daemons.
[1] https://www.freebsd.org/cgi/man.cgi?query=devfs.rules&sektion=5&n=1
Putting on my “very definitely not a scientist” hat:
$ time for i in `seq 1000`; do true; done
real 0m0.010s
user 0m0.004s
sys 0m0.004s
$ time for i in `seq 1000`; do /bin/true; done
real 0m1.280s
user 0m0.112s
sys 0m0.320s
$ time for i in `seq 1000`; do sh -c ''; done
real 0m1.552s
user 0m0.172s
sys 0m0.292s
$ lsb_release -d
Description: Debian GNU/Linux 8.10 (jessie)
Beware of constructs like this if you write production code and intend it to be portable.
$ time for i in `seq 1000`; do true; done
Syntax error: "do" unexpected
Also: true is not guaranteed to be a builtin, : is.
The “very definitely not a scientist” hat has a tag on the inside saying in big red letters “very definitely not for production use”. =) You would indeed need to subshell or something using /bin/time instead of bash’s builtin time, and I made no effort whatsoever to be portable (or to ensure caches were filled or the processor unburdened with background tasks).
As another possibly amusing case:
$ readlink /bin/sh
dash
$ time for i in `seq 1000`; do bash -c ''; done
real 0m2.488s
user 0m0.180s
sys 0m0.332s
Anybody want to try with zsh? ;)
Sure, i’ll do a few more for comparison on a SLOW arm box, that’ll make this all the more pronounced. :)
$ for x in ash bash mksh zsh; do
time (for i in `seq 1000`; do ${x} -c ''; done)
done
( for i in `seq 1000`; do; ${x} -c ''; done; ) 0.39s user 0.39s system 2% cpu 35.974 total
( for i in `seq 1000`; do; ${x} -c ''; done; ) 0.08s user 0.71s system 1% cpu 46.198 total
( for i in `seq 1000`; do; ${x} -c ''; done; ) 0.20s user 0.58s system 2% cpu 36.801 total
( for i in `seq 1000`; do; ${x} -c ''; done; ) 0.21s user 0.61s system 1% cpu 44.066 total
$ time (for i in `seq 1000`; do /bin/true; done)
( for i in `seq 1000`; do; /bin/true; done; ) 0.74s user 0.00s system 2% cpu 32.241 total
Lets compare that to a fully static minimal true (no ld.so shenanigans) note, this is a musl libc system not glibc:
$ echo 'int main(){}' > /tmp/true.c
$ make CC=clang CFLAGS=-Oz LDFLAGS=-static /tmp/true
clang -Oz -static /tmp/true.c -o /tmp/true
$ objdump -p /tmp/true | grep NEEDED
$ time (for i in `seq 1000`; do /tmp/true; done)
( for i in `seq 1000`; do; /tmp/true; done; ) 0.74s user 0.00s system 2% cpu 36.887 total
Well that was ostensibly worse, go figure. Lets strip it too to be sure.
$ strip /tmp/true
$ time (for i in `seq 1000`; do /tmp/true; done)
( for i in `seq 1000`; do; /tmp/true; done; ) 0.74s user 0.00s system 2% cpu 36.406 total
¯\_(ツ)_/¯
I would’ve guessed that minimal true would be the fastest of the bunch, but guess not, GO FIGURE. YMMV this is all not scientific at all.
POSIX shell, on the other hand, has many competing implementations on many different operating systems - all of which are compatible with each other because they conform to the standard.
That’s not really true. dash and bash are incompatible in some basic ways, which are not resolved by POSIX. I list two here:
http://www.oilshell.org/blog/2018/01/28.html#limit-to-posix
Example:
$ dash -c 'x="a b"; readonly y=$x; echo $y'
a
$ bash -c 'x="a b"; readonly y=$x; echo $y'
a b
The above is a very short POSIX shell script, but which one is right?
dash is actually the most “divergent” shell I’ve tested. bash, mksh, and busybox ash all tend to agree on the important stuff (although they implement different features, and wildly disagree on error cases). dash has basic disagreements like this.
Also, I mention that Debian doesn’t even limit itself to POSIX – they forked/developed dash to replace bash, but apparently couldn’t do what they wanted without some extensions like local, which I agree is essential.
BTW, this is from my user experience, not just writing Oil. I tried to switch to dash maybe 4 years ago, but gave up because it was so incompatible (and harder to use than bash).
The above is a very short POSIX shell script, but which one is right?
You could just put double quotes around the argument to readonly and be done with it:
dash -c 'x="a b"; readonly "y=$x"; echo $y'
a b
Yes, it’s inconsistent compared to normal assignment. Yes, it’s portable.
Having worked on code that needs to run on multiple platforms and having had to deal with shell scripts incompatible between e.g. zsh and bash with people on either side arguing that their shell is superior, I feel that the least common denominator (POSIX) is the way to go. Pragmatic side-steps can still be made if the result is that it doesn’t cause more problems than it solves. local tends to work across shells and does solve a real-world problem.
Sure, but my point is that “use POSIX”, and especially “read the POSIX spec”, is not great advice.
That’s not the only example; I could name at least a half dozen other divergences that occur in practice, which POSIX is silent on.
I’ve never heard anyone say “use POSIX, but make sure to quote your assignments, and … “.
There are very few people who learn programming languages by reading specifications. Usually it’s a matter of working with an existing codebase, googling and reading StackOverflow results, reading books, etc.
I have a whole book on POSIX shell, so I’m very familiar with this kind of advice. I just don’t think it’s useful. It’s just a big headache that very few people will follow. For example, echo vs. printf. 95%+ of shell scripts I see use echo, even though it’s not fully-specified and not portable.
YMMV. Following the advice in “Beginning Portable Shell Scripting” by Peter Seebach, the Open Group base spec1 and tools like ShellCheck has worked very well for me, deploying to Slackware with /bin/sh symlinked to bash (IIRC, or maybe it was a copied binary?), Debian with dash as /bin/sh, FreeBSD with /bin/sh being an Almquist shell (ash) descendant, and running the same scripts directly in bash and zsh as well.
I tend to stay away from csh, tcsh and I’ve never seen them being used as a /bin/sh substitute, though I would be interested in knowing if there are such systems.
How was the C function tested? With/without -O2 makes a difference, and using unsigned integers also makes a difference.
collatz_c, linux/amd64, gcc 6.3.0 without optimizations:
0000000000000000 <collatz_c>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 89 7d ec mov %edi,-0x14(%rbp)
7: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
e: eb 2e jmp 3e <collatz_c+0x3e>
10: 8b 45 ec mov -0x14(%rbp),%eax
13: 83 e0 01 and $0x1,%eax
16: 85 c0 test %eax,%eax
18: 75 11 jne 2b <collatz_c+0x2b>
1a: 8b 45 ec mov -0x14(%rbp),%eax
1d: 89 c2 mov %eax,%edx
1f: c1 ea 1f shr $0x1f,%edx
22: 01 d0 add %edx,%eax
24: d1 f8 sar %eax
26: 89 45 ec mov %eax,-0x14(%rbp)
29: eb 0f jmp 3a <collatz_c+0x3a>
2b: 8b 55 ec mov -0x14(%rbp),%edx
2e: 89 d0 mov %edx,%eax
30: 01 c0 add %eax,%eax
32: 01 d0 add %edx,%eax
34: 83 c0 01 add $0x1,%eax
37: 89 45 ec mov %eax,-0x14(%rbp)
3a: 83 45 fc 01 addl $0x1,-0x4(%rbp)
3e: 83 7d ec 01 cmpl $0x1,-0x14(%rbp)
42: 75 cc jne 10 <collatz_c+0x10>
44: 8b 45 fc mov -0x4(%rbp),%eax
47: 5d pop %rbp
48: c3 retq
collatz_c, linux/amd64, gcc 6.3.0, -O2:
0000000000000000 <collatz_c>:
0: 31 c0 xor %eax,%eax
2: 83 ff 01 cmp $0x1,%edi
5: 75 1a jne 21 <collatz_c+0x21>
7: eb 2c jmp 35 <collatz_c+0x35>
9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
10: 89 fa mov %edi,%edx
12: 83 c0 01 add $0x1,%eax
15: c1 ea 1f shr $0x1f,%edx
18: 01 d7 add %edx,%edi
1a: d1 ff sar %edi
1c: 83 ff 01 cmp $0x1,%edi
1f: 74 12 je 33 <collatz_c+0x33>
21: 40 f6 c7 01 test $0x1,%dil
25: 74 e9 je 10 <collatz_c+0x10>
27: 8d 7c 7f 01 lea 0x1(%rdi,%rdi,2),%edi
2b: 83 c0 01 add $0x1,%eax
2e: 83 ff 01 cmp $0x1,%edi
31: 75 ee jne 21 <collatz_c+0x21>
33: f3 c3 repz retq
35: f3 c3 repz retq
collatz_c, linux/amd64, gcc 6.3.0, -O2, s/int/unsigned int/:
0000000000000000 <collatz_c>:
0: 31 c0 xor %eax,%eax
2: 83 ff 01 cmp $0x1,%edi
5: 74 23 je 2a <collatz_c+0x2a>
7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
e: 00 00
10: 89 fa mov %edi,%edx
12: 8d 4c 7f 01 lea 0x1(%rdi,%rdi,2),%ecx
16: d1 ea shr %edx
18: 83 e7 01 and $0x1,%edi
1b: 0f 45 d1 cmovne %ecx,%edx
1e: 83 c0 01 add $0x1,%eax
21: 83 fa 01 cmp $0x1,%edx
24: 89 d7 mov %edx,%edi
26: 75 e8 jne 10 <collatz_c+0x10>
28: f3 c3 repz retq
2a: f3 c3 repz retq
On one side, it’s kinda cool because no ads and better potential for predictable income.
On the other hand, no, this is my CPU and the various tabs and apps I have open make it slow enough already.
The upside to this assumes a responsible site doing this intentionally and disclosing it.
Most of the cases here are compromised websites, so they have whatever ads they normally have and are mining crypto on someone else’s behalf. I’ve seen this happen quite a few times now while working with compromised customers (I work for a company that provides VPS services)
While I think the idea of replacing the internets ad based model with a crypto mining based one is interesting, that isn’t really the issue being discussed in the article.
It would be amusing if we started to have additional cores for offloading ad-mining. Or if people found a way to offload the mining to the Intel ME.
I use this daily. If you need a custom firmware image, Yocto is great and I havn’t really come across a good alternative. From a developer perspective though, I would rather have something less Linux specific, less Python and less bloated. I’ve had to increase the number of inotify watchers system wide multiple times (it’s now in the higher five figures). I’ve had to wait tens of seconds for commands that should complete in milliseconds (devtool build). I’ve had problems with packages not ending up in the final image even though they’re included in the dependency graph, or old artifacts of rebuilt packages ending up in the final image, and other silent failures which were really hard to track down due to the complexity of the tooling. I’ve had problems where bitbake tries to download internal packages from an external repo and otherwise leaking information.
This leads to a lack of trust in the tooling and a lot of developers hacking away at the build system to create their own tooling because the standard tooling is perceived as flawed, even if it works great 95% of the time.
exa is written in Rust, so it’s small, fast, and portable.
-rwxr-xr-x 1 root wheel 38K 28 Apr 20:31 /bin/ls
-rwxr-xr-x@ 1 curtis staff 1.3M 7 Jul 12:25 exa-macos-x86_64
?
Stripping it helps a bit… but not much though.
$ du -hs exa-macos-x86_64
1.3M exa-macos-x86_64
$ strip exa-macos-x86_64
$ du -hs exa-macos-x86_64
956K exa-macos-x86_64
More fun is what it links to:
$ otool -L /bin/ls
/bin/ls:
/usr/lib/libutil.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.60.2)
$ du -hs /usr/lib/libutil.dylib /usr/lib/libncurses.5.4.dylib /usr/lib/libSystem.B.dylib
28K /usr/lib/libutil.dylib
284K /usr/lib/libncurses.5.4.dylib
12K /usr/lib/libSystem.B.dylib
$ otool -L /tmp/exa-macos-x86_64
/tmp/exa-macos-x86_64:
/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 57740.60.18)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1349.8.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.8)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.60.2)
$ du -hs /usr/lib/libiconv.2.dylib /System/Library/Frameworks/Security.framework/Versions/A/Security /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation /usr/lib/libz.1.dylib /usr/lib/libSystem.B.dylib
1.6M /usr/lib/libiconv.2.dylib
9.3M /System/Library/Frameworks/Security.framework/Versions/A/Security
9.7M /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
96K /usr/lib/libz.1.dylib
12K /usr/lib/libSystem.B.dylib
To be fair, exa is a self-contained executable, while ls probably has a dependency to libc, which it loads dynamically. If Rust ever becomes very popular and its runtime is installed by default everywhere, its executables will also be a few KB only.
FWIW, linking ls from GNU coreutils statically with musl-libc on x86_64 gave me a 147K ELF with no shared object dependencies.
For that to be true Rust would have to have well defined and stable ABI. Which it doesn’t have right now.
Rust binaries actually do dynamically link to libc. Its standard library, which calls libc, is statically compiled into binaries.
I ran a separate /usr partition for a while. It’s an utter pain in the arse and I stopped doing it rather quickly. Tbh I only did it because I thought it would enable me to manage my diskspace better. It was also more complicated rather than easier.
/home, /pictures and / are the only partitions you need.
/, /home, /etc, /var, /tmp for me (on zfs).
/tmp, /var as memory disks.
/etc as ro, with possibility for rw separate from /.
/ as ro.
Why keep /var in memory instead of on-disk? Seems like you want things like webroots and log files to persist between boots.
I usually install webroots under datarootdir. Most of the time I don’t mind purging logfiles at reboot, though memory disks can be backed by a file if wanted/needed, see mdconfig(8). If I want my logs to be persistent, I usually log to a remote machine (with rw /var/log).
I like my systems to be as immutable as possible. I still remount when updating &c but the default is ro.
I need /var to persist in case I need any logfiles from previous boots, plus it can hang systemd during shutdown because it wants a place to log to. /etc is secured via some etc-git manager that pushes to my git server. Memory disks don’t count as diskspace or partitions to me.
Atleast, that’s my opinion.