Hacker News new | past | comments | ask | show | jobs | submit | gritzko's comments login

So, systemd is like "doing Linux services the Microsoft way"


Your remark may suggest that everything MS does is “wrong”. This is of course an extreme overstatement and all of their approaches should be evaluated on their own.


So far the argument for systemd seems to be "well it works so why are you complaining"


It's a declerative boot system, where units can declare their dependence on another unit/service, and they will be brought up in accord, with a potential for parallelization, proper error handling, proper logging, all while the user complexity is a trivial .ini-like file.

It also has a good integration with Linux features, like cgroups, allowing easy user-isolation, has user-services that allow proper resource management, and many more.


systemd is just much better for managing cross-cutting concerns. E.g. having the machine wake up from sleep for a timer and do a task (e.g. backup or updates) which delay sleep is trivial, portable and reliable with systemd and probably technically possible without it.


Sorry, I'm not deep enough into it to determine why systemd would be better. Perhaps it's not. I know it's less unix-y, but we shouldn't treat that as holy imho, it's a good guideline though, I try to follow the unix philosophy.


Just saw Lennart Poettering speak at Fosdem and perhaps it’s even more Unix-y then system V init :)


And all the system managers that followed UNIX philosophy failed in being as straight forward as systemd, you know, the whole point of that Unix philosophy is to be more straight forward


Here, "system managers" and "straight forward" are pretty loaded terms...


I'll elaborate on this if you like.

It works, and it works well. It does what it says on the tin. It brings together multiple disparate functionalities to a level that most end users need, while also having the ability to replace a lot of those functionalities (systemd-resolved, systemd-networkd, systemd-timesyncd as examples) with other services if that's what your deployment needs.

It replaces the old style of services being just a bunch of shell scripts that ran one after another. It provides a consistent interface to configure, start, stop, restart, enable, disable, and monitor those services. It allows dynamism by creating service templates, allowing you to launch six different variants of a service using the same base service configuration.

It also allows you to take advantage of a lot of Linux's security features, by implementing service-level restrictions on filesystems, network access, user IDs, PIDs, and so on. It even provides the ability to listen on a socket and pass connections to a process, meaning you could have a network-listening daemon that doesn't even actually have access to your network.

It provides cgroup integration so that you can limit the resources provided to an individual service so that you can prevent one process from causing issues with the rest of your hardware. It can even point a service at a separate root directory, essentially chrooting a service before it even starts.

It provides tooling so that individual services can interact with one another, and so that user-space tooling (either theirs or a custom d-bus client) can interact with services, and provides functionality for validating whether or not the user has permissions to do so. It also provides functionality to easily override configuration in override files, allowing you to make local changes without changing the files that were distributed, making it easy to debug whether it's your changes or not that are causing an issue, or to test changes without worrying about putting things back.

It eliminates the issues of having multiple different services, often either conflicting or overlapping, to provide the things it provides. Multiple separate configuration files, configuration syntaxes, and so on. It eliminates the need for distros to create config generation tooling to produce the right config files to tie each service together, and then write their own daemons or cronjobs to check each of them for changes.

And all of this functionality is, essentially, optional. I can do a hundred different things with a service file, but I don't have to; I can basically write a service file in ten lines of ini-compatible configuration, using (and being aware of) none of that functionality, and it works fine, or I can write a service file that locks a service into its own network, with no access to the existing filesystem, only the most basic entries in /dev, only the files it needs in /etc, limited by seccomp filters, using systemd's watchdog functionality to restart it if it hangs or gets overloaded, limiting it to specific CPU cores and changing CPU and IO weighting, limiting the number of processes that it can create, and even filtering which system calls it's allowed to use.

In short, it's a system that can be very easy to use and start working with, but provides more functionality and complexity than I think almost anyone realizes. It's the most capable and simplest way of handling system services, and the extra pieces just make it less complex to add functionality like network management, name lookups, boot management, and so on to your system if all you need is the basics.


it's a good argument


It comes down to a philosophy you choose.

I personally think that most of the OS decisions done by Microsoft are wrong and should be killed with fire but also I'm far from thinking bad about the people who made these decisions. Perhaps these decisions make sense from their standpoint given the information they had a time.

On the other hand on Linux I'd really wish for a centralized settings repository like Registry. But not from the systemd crowd, of course.


The registry is /etc

If you want some more structure to it then you'll probably end up with the registry and whatever it is that people hate about it.

IMO it would be enough to put the /etc into git so one can rollback bad changes.


Don't registry get loaded on boot, making it heavier that just being /etc on the hard drive?, also yes, /etc can be view as some sort of registry, but the implementation is different and that's matter


Parts of it do get loaded and unloaded as needed from disk to memory and back to disk. It's certainly not all loaded into memory


It's more like doing Linux services the UNIX(TM) way since it's more similar to other UNIX service managers like SMF from Solaris or SRC from AIX in the integration -- NT's service manager requires an active event loop which responds to messages.

As an aside, the reason I don't like systemd is because it's inferior to its UNIX counterparts -- especially SMF -- for system management.


Maybe someone should port smf or launchd to Linux?


Given that it was based off of the design of OS X's launchd, I think it's probably more accurate to say it's "doing Linux services the Apple way".


Isn't MacOS a UNIX?


I work on a C dialect where everything is flattened. JSON and other trees in particular. Binary heaps are flat, merge sort and iterator heaps are absolutely great, can build LSM databases with that. Stacks, circular buffers, hash maps, etc, all flat. Templated output (PHP like) is done by a flat data structure.

https://github.com/gritzko/librdx/blob/master/abc/B.md

Apart from locality and lifetimes, these flat data structures improve composability. When every data structure is a flat buffer, you can mmap them or zip them or send them by the network, all by the same routine. They are uniform like bricks, in a sense.


I worked in a language where all datastructures were "flattened", could be trivially serialized to disk, and read in again. Called print and read. The language was called lisp. All flat, just parens.

Some of my compilers export the AST as lisp trees. Much smaller and more readable than json, and it can be executed. Uniform like bricks


> All flat, just parens.

So not flat then. Prefix is not postfix. Forth, and most concatenative languages, are much closer to actually bein, flat.

Lisp is trivial to flatten, but that's not the same thing.


Within my current project, I use a special C dialect. So, I have to write a lot of explanatory text for those who dare to use it. And even in solo projects, I have been in situations when I had to check my own docs to understand what's going on.

As a result, my project is effectively also a wiki:

https://replicated.wiki/abc/

https://github.com/gritzko/librdx/tree/master/abc

The idea is to put motivational and explanatory text into the parallel wiki, while all the API docs stay in the code the normal way. These are seriously different things.

The next step to unit tests all the code docs. Or, the other way around, to document tests to make them joy to read. That is the only way to solve doc rot.

Overall, I am trying to get as close to Literate programming as practically possible: https://en.wikipedia.org/wiki/Literate_programming

Because code is hypertext, IDE is a browser.


That is the top question, actually. Given all the billions at stake.


*infernonce


I recently coded a linear-probing hash map in C and I highly recommend you to use fuzz tests. These evolve naturally from unit tests: next step table unit tests, next step property test, next step fuzzing, all steps are incremental, hence easy.

Having a fuzz test was in-va-lu-ab-le. I caught several bugs right there. The delete function was particularly tricky.

I ended up with two fuzz tests as my hash table has a key feature: convergence. Having same contents, it would have exactly same bits in the buffer. In other words, it is independent of the insertion/deletion order. For this, I added another fuzz test. I would add a third one if I realize there is an important invariant I did not fuzz test. That is not much work, but so much useful!

https://github.com/gritzko/librdx/blob/master/abc/fuzz/HASH....

https://github.com/gritzko/librdx/blob/master/abc/fuzz/HASHd...

P.S. In your case, I recommend clang's libfuzzer with ASAN.

https://llvm.org/docs/LibFuzzer.html#fuzzer-usage


Here's another example of unit tests for hash tables (and RB-trees and skiplists and…)

https://github.com/FRRouting/frr/blob/master/tests/lib/test_...

(this file is #include'd from another, with some #define set up:)

https://github.com/FRRouting/frr/blob/master/tests/lib/test_...

The "tribe" of C developers with distaste for the preprocessor will absolutely hate how this is written. But it works and caught real issues. It doesn't use an external fuzzing tool but rather a deterministic PRNG (see prng_* calls) to get a good mix of call combinations. Coverage analysis was done to make sure it reaches everywhere.

(disclaimer: I wrote this. And it did catch bugs. Also it really should have a lot of comments.)


If you are in a tribe not hating on pp-macros, you might find this approach for testing/debugging data structures (or even the surrounding pp-based "CTL"-like C templating idea or abstract ways of dealing with the BST zoo) interesting:

    https://github.com/c-blake/bst/blob/main/test/bst-interact.c
Essentially, you just write a very simple (some might say trivial) interactive command-shell that can include an auto-checking upon edit of (in that case tree) invariants keyed-off an environment variable. The only missing piece is some little tool to generate random inserts/deletes/etc.

If the micro-shell has a pretty printer then it is easy to "replay" any failed series of edits with some "p" commands before & after invariant failures.


This is incredibly funny to me, because that's (almost) exactly how some of our other tests work :D

(Almost: we have a full-featured command shell and just use that for testing)

Shell bindings: https://github.com/FRRouting/frr/blob/master/tests/ospf6d/te...

Input: https://github.com/FRRouting/frr/blob/master/tests/ospf6d/te...

Expected output: https://github.com/FRRouting/frr/blob/master/tests/ospf6d/te...

Absolutely agree this is a great option to have for some kinds of tests.


One reason to have a razor thin custom command shell (perhaps obeying similar conventions across data structures..) is that the parsing for such can be so fast/consistent that you can also use it for benchmarking/perf regression testing with a workload generator. You might also find & record "anomalous" workloads that way or write a little "translator" (some might say "compiler") from a log to such a workload or etc.

I have done this for data structures even as fast as integer-keyed hash tables (though in that hyper-fast case you might need to try to measure & subtract off parser-loop/IO dispatch overhead and/or need statistical testing for "actually even different at all", perhaps along the lines of https://github.com/c-blake/bu/blob/main/doc/tim.md).


fuzz testing vs HN-popular-post testing, go!


The author describes the case of overlapping concurrent splices. It is a known funky corner case, yes. If we speak of editing program code, the rabbit hole is deeper as we ideally expect a valid program as a result of a merge. There was a project at JetBrains trying to solve this problem through AST-based merge. After delving into the rabbit hole much much deeper, the guys decided it is not worth it. This is what I was told.


Append-only is a very convenient corner case for skiplists to optimize. I call it "skiplogs" because it looks really different from the general case. An average node can be 3 bytes, for example.


Interesting. My students used Language Server Protocol data to make syntax-aware diffs. Very promising project. Unfortunately, everyone moved on. I am currently looking for ways to revitalize it.

https://github.com/shishyando/tokenized-myers-diff


I have the experience of writing parsers (lexers) in Ragel, using Go, Java C++, and C. I must say, once you have some boilerplate generator in place, raw C is as good as the Rust code the author describes. Maybe even better because simplicity. For example, this is the most of code necessary to have a JSON parser: https://github.com/gritzko/librdx/blob/master/JSON.lex

In fact, that eBNF only produces the lexer. The parser part is not that impressive either, 120 LoC and quite repetitive https://github.com/gritzko/librdx/blob/master/JSON.c

So, I believe, a parser infrastructure evolves till it only needs eBNF to make a parser. That is the saturation point.


That repetitivness can be seen as a downside, not a virtue. And I feel that Rust's ADTs make working with the resulting syntax tree much easier.

Though I agree that a little code generation and/or macro magic can make C significantly more workable.


I love love love ragel.

Won't the code here:

https://github.com/gritzko/librdx/blob/master/JSON.lex

accept "[" as valid json?

   delimiter = OpenObject | CloseObject | OpenArray | CloseArray | Comma | Colon;
   primitive = Number | String | Literal;
   JSON = ws* ( primitive? ( ws* delimiter ws* primitive? )* ) ws*;
   Root = JSON;
(pick zero of everything in JSON except one delimiter...)

I usually begin with the RFCs:

https://datatracker.ietf.org/doc/html/rfc4627#autoid-3

I'm not sure one can implement JSON with ragel... I believe ragel can only handle regular languages and JSON is context free.


That is a lexer, so yes, it accepts almost any sequence of valid tokens. Pure Ragel only parses regular languages, but there are ways.


C/Ragel bug was the cause of Cloudbleed, and the reason why Cloudflare switched to Rust.

https://en.wikipedia.org/wiki/Cloudbleed


I think I know the author of the bug :) But I use a C dialect that has bounds-checking. Abstractions leak anyway (see Meltdown/Spectre). https://meltdownattack.com/


Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: