* What is it? Idempotent system configuration as code, written in glotawk. You write how your system should be configured; lacrum's machinery moves the actual configuration toward the goal. This is about the same idea as cfengine 2, or early Puppet. Example: #+begin_src lacrum (use-this-host) (add-line-to-file "/tmp/hosts" "ff02::1 ip6-allnodes" nil) (replace-line-in-file "/etc/ssh/sshd_config" "^PermitRootLogin\\>" "PermitRootLogin prohibit-password") (sysrc :set "ntpd_enable" "YES") #+end_src * What's it for? I envision defining jails, and hosts that host those jails, using this, like a Bastillefile but assuming less. * All right, how do I use it then? First build glotawk, in the parent directory. Then run ~make~ in this directory. It will build an executable file called ~lacrum~, which is in fact a copy of glotawk plus the additional functions defined in this directory. See below on the language understood by this interpreter. ** Enhancing and redistributing As detailed in the ../LICENSE file, this software is made available to you under the terms of the 2-clause BSD license. * Contributing For now, lacrum lives in the same Pijul repository as glotawk. ** Future goals Right now, the functions that do the work do it directly: by calling functions that check whether a change needs to be made, and then, if necessary, effecting the change. We could instead cause rules to be accumulated, analyzed for dependencies, then executed. Instead of doing the changes directly, We could compile a shell script that would find necessary facts and do necessary changes, without worrying about how to quote everything properly. We could determine how to back out each change made, and compose an undo script that would allow rolling back a set of changes quickly. In order to support the use of string usernames and group names, we execute the ~/usr/bin/getent~ of the target while building. This means the build host has to be able to run target executables, so we can't build an image for a target of a different architecture. Integrate better with OccamBSD. Support other operating systems. * More odd stuff https://j.agrue.info/, gemini://j.agrue.info * What do I write? The underlying Lisp is glotawk, so see its documentation for more details. But here we add: ** Rules These are how the intended configuration of the system is expressed. (If you are reading this file as plain text, using a program that doesn't understand Org markup syntax, you should know that the stars and slashes below serve to denote *bold* words and /italic/ words; they are not part of the Lisp syntax you should use.) (*replace-line-in-file* /file-path/ /detect-regex/ /replace-line/) Find any line in /file-path/ that matches /detect-regex/, and replace the whole line with /replace-line/. (Make sure /detect-regex/ will only match zero lines or one line, to avoid unintended changes.) (*add-line-to-file* /file-path/ /line/ /detect-regex/) In the file /file-path/, if there is no line matching /detect-regex/, add /line/ to the end of the file. (Make sure /detect-regex/ matches /line/, to maintain idempotence.) (*delete-lines-matching* /file-path/ /regex/) In the file /file-path/, delete any lines that match /regex/. (*file-exists* /file-path/ /owner/ /group/ /mode/) Make sure the plain file /file-path/ exists and has the given /owner/, /group/ and /mode/. /owner/ and /group/ may be numbers or names; /mode/ is an octal mode expressed as a string, for example ~"644"~. (*file-does-not-exist* /file-path/) Make sure plain file /file-path/ does not exist, by removing it if it does. (*dir-exists* /dir-path/ /owner/ /group/ /mode/) Make sure directory /dir-path/ exists and has the given /owner/, /group/ and /mode/. Compare *file-exists*, above. (*file-has-immediate-contents* /file-path/ /contents/) Make sure the plain file /file-path/ contains the exact /contents/, given as a string. Note Glotawk's string syntax: strings are text between a pair of unescaped double-quotes "like this". If the second double quote is not on the same line as the first, that's no problem. Unescaped newlines are just part of the string. Backslash escapes do what you might expect from awk strings (or C strings, which they imitate): a backslash immediately before a newline allows continuation of the string on the next line without inserting a newline into the string; ~\\~, ~\"~, ~\n~, ~\r~, in fact all the lettered escapes abfnrtv, act as indicated in printf(3). (*file-text-copied-from* /dest-file-path/ /src-file-path/) Make sure the plain file /dest-file-path/ has the same contents as the plain file /src-file-path/—this latter is reckoned relative to the current directory, but the former relative to the root. (*file-contents-from-m4-template* /dest/ /tmpl/ /vars/) Make sure the plain file /dest/ contains the filled-in ~m4~ template stored in the file /tmpl/, where m4 is run with ~-D~ (define) switches for each of the /vars/, which should be of the shape ~(("name1" "value1") ("name2" "value2")...)~. See m4(1). Make sure none of the values can contain any of the names. m4 is not my favorite templating language, but it's part of POSIX and of the FreeBSD base system. (*file-exists-with-entire-contents* /file-path/ /o/ /g/ /m/ /desc/ /contents/) This is *file-exists* plus *file-has-immediate-contents*. (*local-user-exists* /user-name/ /additional-groups/) Make sure the user /user-name/ exists and has a home directory, and is in /additional-groups/ (a list of strings). (*package-installed* /name/ /pkg-opts/) Make sure the package /name/ is installed. /pkg-opts/ is a list of strings, each of which will be handed as a command line argument to ~pkg~. (*sysrc* /op/ /name/ /value/) Set something in ~/etc/rc.conf~. /op/ must be one of the keywords ~:set~, ~:add~, or ~:remove~, corresponding with sysrc(8)'s ~=~, ~+=~, or ~-=~ operators. /name/ and /value/ are strings. * Colophon Michael Dexter made [[https://github.com/michaeldexter/occambsd][OccamBSD]]. A simulacrum is an image or representation of something. I built pared-down jail images with OccamBSD, but then wanted to configure them at build time, and deploy a new version by untarring it and restarting the jail, or some such. The es scripts that did that were named occulacrum. This now is simpler, and it's just a layer, a thin lacquer over glotawk, if you will.