You can write C code such that its unit tested, fuzzed, statically analyzed and reviewed, and often embedded code has one or two specific jobs.
I would have loved to see Zig here, as it makes all of the above (testing, analysis, code clarity for review) easier, but its also not memory safe necessarily.
You could write it in D with @safe (which is SafeD or whatever), which is memory safe, but thats not a very popular language.
You could use Ada, but again, thats not as easy to find devs for as C is.
You could use Go or Ocaml, though go isn't really memory safe and you can easily cause data races in goroutines, and ocaml has a runtime afaik so thats out of the question for low memory devices.
You could use Rust, assuming it had existed back when they started it, and you would get memory safety, but also an entire kitchen sink of useless garbage (like C++). Youd also have to shell out to unsafe{} in a lot of places, unless you use a crate for it, which will then do unsafe{} for you.
So chances are, whatever you do, C is a pretty sane choice, or maybe C++ if you want RAII to at least make your resource management and lifetimes easy to handle.
I dont think you deserve to be downvoted, since this is an interesting discussion to have. However, I think it would have been helpful for the discussion if you had outlined which language youd suggest and why.
To Steelman your argument, I would say you think that C is unsafe to such a degree that even a for loop is UB most of the time (integer overflow as UB), no real way to check array bounds, no real way to catch off-by-one and similarly stupid simple errors, use after free, egc. and that the entire ecosystem relies on raw pointers and macros, and its a shitshow. I think your point would have been to suggest Rust, as it fixes all these issues, while bringing along a stronger type system and a better toolchain.
I'm not saying C is a wrong choice here, but this argument doesn't make much sense to me. All the other languages also come with "useless garbage" (some even with a collector for them). I don't see why you'd need that many unsafe blocks either.
I feel like Rust, as a language, is much better equipped for writing a daemon like this. I'll even go as far as to say that modern C++ (C++2x) is a better choice than C for this stuff with its superior memory and resource management tools.
Of course most of the OpenWRT development is done in C, so doing it in another language requires a very good reason. I wouldn't look at the concept of "a networked daemon exchanging messages between arbitrary services" and think C of all languages is a good fit unless every other developer on the team can only write C.
Zig would've been a nice way to meet halfway, but the language isn't finished yet.
Of course the point is moot because this particular project existed years before any of the modern alternatives or many of the C++ improvements were even available. Still, if they'd start a project like this again, I'd hope they'd pick a better language for it.
Binary size is sometimes a big deal. Two decades ago I investigated using C++ for an "embedded" linux ssh server, but decided the 30kB overhead was too large (target was a 4MB laptop). The server ended up being used in OpenWRT and other places, I'm curious if it would have happened if I'd gone with C++ instead.
Alas, there is little interest in/awareness of the middle ground between the no_std world and the "size doesn't matter" world in the Rust community right now. I've asked a bunch of people for pointers as to where to start working towards supporting those cases but I mainly got shrugs. There were attempts at more lightweight stdlibs and so on, but they all seem to have fizzled out.
I wonder how much of this is inherent in the language design. Rust heavily leans on monomorphization, so as a first approximation, you will always generate more code (before maybe optimizing that away again). Famously, the Swift people went to great lengths to avoid these problems: https://faultlore.com/blah/swift-abi/. But while running Swift on Linux is possible, this is even more niche.
The key was to turn off panic unwinding, turn on size optimization, and full LTO. I think I recall increasing the inlining threshold helped a bit.
I feel like Rust without std might as well be a different language. Almost all of the available mindshare, docs, libraries are dependant on it.
Sure, you can use it. But to me it just looks like technological poverty. Even compared to the C ecosystem.
There were sufficient crates to write a ssh server for a rp2040 (mostly out of curiosity) using what's available in no_std.
That repo has a few crates depending on each other - sunset is the toplevel ssh, sunset-embassy adds no_std async, then the async dir has std rust, with a commandline ssh client in the examples dir.
There's obviously a lot of need for improvement in Rust in regards to small systems, as people have pointed out. But there's also a lot more middle ground in Rust vs, say, C++: You can be no_std and still have Vector, for example, while in C++ the STL is an all-or-nothing thing.
It's a smaller community, and it will come down to individuals deciding it's important and making contributions.
It should be possible to use a shared library for std but it would come with the limitation that the bins must be compiled with exactly the same rustc as std.
edit: Also D in safe mode like mentioend above - not sure if it was around in 2010?
On some devices you have 32MB flash storage in total.
32 Megabytes, not gigabytes. This is not a typo.
This needs to fit the bootloader, the Linux kernel, the initrd, the rootfs containing all the user-space tools making OpenWRT an actual usable OS and whatever daemons you need to implement your particular network needs. Oh and you probably want the WebUI too.
In 32MBs. There are newspapers online which loads more than that just to show the front page!
There’s no room for 2MB HelloWorld type languages in this space.
Eg on https://ada.godbolt.org/ you can see the default program compiled to ~25 lines of assembler.
edit: actually it contains a call to a bounds check function that presumably is in the runtime library, i don't know how big that is.
A major point in this is that C also has a fairly big runtime library that should be factored into the comparison!
You can still build software for OpenWRT that requires the much bigger Glibc, but of course it will not work that well on devices with limited memory.
464.7K Oct 15 2022 /lib/libc.so
As seen on openwrt on armv7l system 20.0K Oct 13 2022 /usr/lib/libudev.so.1
8.0K Dec 15 2022 /sbin/udevtriggerThe original releases of OpenWRT used uClibc, which is nowhere near Glibc levels of bloat (Musl beats it on code quality and is used today, but didn’t exist back then). Also, yeah, you’re going to have a libc on a Linux system no matter what, so this is one of the rare cases where dynamic linking makes for a legitimate optimization.
But libc will be there anyways, unless you are rewriting absolutely all of userland.
You have to consider the surrounding ecosystem. Those interested in such languages are not necessarily those interested in contributing solutions to the problem space. Any project attempting to use such a language in OpenWrt would very likely not have survived until today.
In the embedded space, you use small effective languages like assembly and C. You either design for explicit memory use entirely, or you systematically test for memory usage and waste - especially if you're delivering industrial applications. The so-called "memory safe" systems tend to be very expensive for memory usage and space, and not worth the investment. Usually, anyway. This may change in the future but it's a bad change if it comes with additional power usage requirements and the environment also requires minimal power use.
I don't know if we now have a better alternative that is similar in speed, ram use and binary size.
If we do I'd would read a discussion on which one to use with interest.