From 8a78337ea4dcb0b154bd1c754218ca01c8f9015d Mon Sep 17 00:00:00 2001
From: Paul Buetow
Back to the main site
+
diff --git a/about/index.html b/about/index.html
index 3bedd4b8..03f42412 100644
--- a/about/index.html
+++ b/about/index.html
@@ -2,12 +2,17 @@
+
Back to the main site
+
diff --git a/about/novels.html b/about/novels.html
index 31a7d114..17d69f2e 100644
--- a/about/novels.html
+++ b/about/novels.html
@@ -2,12 +2,17 @@
+
Go back
+
diff --git a/about/resources.html b/about/resources.html
index 3843558c..a1f14616 100644
--- a/about/resources.html
+++ b/about/resources.html
@@ -2,12 +2,17 @@
+
-
Technical references
I didn't read them from the beginning to the end, but I am using them to look up things. The books are in random order:
-
Self-development and soft-skills books
In random order:
+
Here are notes of mine for some of the books
@@ -165,31 +170,31 @@
Some of these were in-person with exams; others were online learning lectures only. In random order:
+
Technical guides
These are not whole books, but guides (smaller or larger) which I found very useful. in random order:
-
Podcasts
@@ -198,59 +203,59 @@
In random order:
-
Podcasts I liked
I liked them but am not listening to them anymore. The podcasts have either "finished" (no more episodes) or I stopped listening to them due to time constraints or a shift in my interests.
+
Newsletters I like
This is a mix of tech and non-tech newsletters I am subscribed to. In random order:
-
Magazines I like(d)
This is a mix of tech I like(d). I may not be a current subscriber, but now and then, I buy an issue. In random order:
-
@@ -282,11 +287,12 @@
Go back
+
diff --git a/about/showcase.html b/about/showcase.html
index ac803651..70813549 100644
--- a/about/showcase.html
+++ b/about/showcase.html
@@ -2,12 +2,17 @@
+
View on GitHub
+
diff --git a/code.ttf b/code.ttf
index 7c8e65b8..1d0a4011 100644
Binary files a/code.ttf and b/code.ttf differ
diff --git a/gemfeed/2008-06-26-perl-poetry.html b/gemfeed/2008-06-26-perl-poetry.html
index d54b7210..ce173057 100644
--- a/gemfeed/2008-06-26-perl-poetry.html
+++ b/gemfeed/2008-06-26-perl-poetry.html
@@ -2,12 +2,17 @@
+
#!/usr/bin/perl
+
#!/usr/bin/perl
-# (C) 2006 by Paul C. Buetow
+# (C) 2006 by Paul C. Buetow
-goto library for study $math;
-BEGIN { s/earching/ books/
-and read $them, $at, $the } library:
+goto library for study $math;
+BEGIN { s/earching/ books/
+and read $them, $at, $the } library:
-our $topics, cos and tan,
-require strict; import { of, tied $patience };
+our $topics, cos and tan,
+require strict; import { of, tied $patience };
-do { int'egrate'; sub trade; };
-do { exp'onentize' and abs'olutize' };
-study and study and study and study;
+do { int'egrate'; sub trade; };
+do { exp'onentize' and abs'olutize' };
+study and study and study and study;
-foreach $topic ({of, math}) {
-you, m/ay /go, to, limits }
+foreach $topic ({of, math}) {
+you, m/ay /go, to, limits }
-do { not qw/erk / unless $success
-and m/ove /o;$n and study };
+do { not qw/erk / unless $success
+and m/ove /o;$n and study };
-do { int'egrate'; sub trade; };
-do { exp'onentize' and abs'olutize' };
-study and study and study and study;
+do { int'egrate'; sub trade; };
+do { exp'onentize' and abs'olutize' };
+study and study and study and study;
-grep /all/, exp'onents' and cos'inuses';
-/seek results/ for @all, log'4rithms';
+grep /all/, exp'onents' and cos'inuses';
+/seek results/ for @all, log'4rithms';
-'you' =~ m/ay /go, not home
-unless each %book ne#ars
-$completion;
+'you' =~ m/ay /go, not home
+unless each %book ne#ars
+$completion;
-do { int'egrate'; sub trade; };
-do { exp'onentize' and abs'olutize' };
+do { int'egrate'; sub trade; };
+do { exp'onentize' and abs'olutize' };
-#at
-home: //ig,'nore', time and sleep $very =~ s/tr/on/g;
-__END__
+#at
+home: //ig,'nore', time and sleep $very =~ s/tr/on/g;
+__END__
@@ -106,47 +111,47 @@ __END__
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
-#!/usr/bin/perl
+
#!/usr/bin/perl
-# (C) 2006 by Paul C. Buetow
+# (C) 2006 by Paul C. Buetow
-Christmas:{time;#!!!
+Christmas:{time;#!!!
-Children: do tell $wishes;
+Children: do tell $wishes;
-Santa: for $each (@children) {
-BEGIN { read $each, $their, wishes and study them; use Memoize#ing
+Santa: for $each (@children) {
+BEGIN { read $each, $their, wishes and study them; use Memoize#ing
-} use constant gift, 'wrapping';
-package Gifts; pack $each, gift and bless $each and goto deliver
-or do import if not local $available,!!! HO, HO, HO;
+} use constant gift, 'wrapping';
+package Gifts; pack $each, gift and bless $each and goto deliver
+or do import if not local $available,!!! HO, HO, HO;
-redo Santa, pipe $gifts, to_childs;
-redo Santa and do return if last one, is, delivered;
+redo Santa, pipe $gifts, to_childs;
+redo Santa and do return if last one, is, delivered;
-deliver: gift and require diagnostics if our $gifts ,not break;
-do{ use NEXT; time; tied $gifts} if broken and dump the, broken, ones;
-The_children: sleep and wait for (each %gift) and try { to => untie $gifts };
+deliver: gift and require diagnostics if our $gifts ,not break;
+do{ use NEXT; time; tied $gifts} if broken and dump the, broken, ones;
+The_children: sleep and wait for (each %gift) and try { to => untie $gifts };
-redo Santa, pipe $gifts, to_childs;
-redo Santa and do return if last one, is, delivered;
+redo Santa, pipe $gifts, to_childs;
+redo Santa and do return if last one, is, delivered;
-The_christmas_tree: formline s/ /childrens/, $gifts;
-alarm and warn if not exists $Christmas{ tree}, @t, $ENV{HOME};
-write <<EMail
- to the parents to buy a new christmas tree!!!!111
- and send the
-EMail
-;wait and redo deliver until defined local $tree;
+The_christmas_tree: formline s/ /childrens/, $gifts;
+alarm and warn if not exists $Christmas{ tree}, @t, $ENV{HOME};
+write <<EMail
+ to the parents to buy a new christmas tree!!!!111
+ and send the
+EMail
+;wait and redo deliver until defined local $tree;
-redo Santa, pipe $gifts, to_childs;
-redo Santa and do return if last one, is, delivered ;}
+redo Santa, pipe $gifts, to_childs;
+redo Santa and do return if last one, is, delivered ;}
-END {} our $mission and do sleep until next Christmas ;}
+END {} our $mission and do sleep until next Christmas ;}
-__END__
+__END__
-This is perl, v5.8.8 built for i386-freebsd-64int
+This is perl, v5.8.8 built for i386-freebsd-64int
shopping.pl
@@ -155,35 +160,35 @@ This is perl, v5.8.8 built #!/usr/bin/perl
+#!/usr/bin/perl
-# (C) 2007 by Paul C. Buetow
+# (C) 2007 by Paul C. Buetow
-BEGIN{} goto mall for $shopping;
+BEGIN{} goto mall for $shopping;
-m/y/; mall: seek$s, cool products(), { to => $sell };
-for $their (@business) { to:; earn:; a:; lot:; of:; money: }
+m/y/; mall: seek$s, cool products(), { to => $sell };
+for $their (@business) { to:; earn:; a:; lot:; of:; money: }
-do not goto home and exit mall if exists $new{product};
-foreach $of (q(uality rich products)){} package products;
+do not goto home and exit mall if exists $new{product};
+foreach $of (q(uality rich products)){} package products;
-our $news; do tell cool products() and do{ sub#tract
-cool{ $products and shift @the, @bad, @ones;
+our $news; do tell cool products() and do{ sub#tract
+cool{ $products and shift @the, @bad, @ones;
-do bless [q(uality)], $products
-and return not undef $stuff if not (local $available) }};
+do bless [q(uality)], $products
+and return not undef $stuff if not (local $available) }};
-do { study and study and study for cool products() }
-and do { seek $all, cool products(), { to => $buy } };
+do { study and study and study for cool products() }
+and do { seek $all, cool products(), { to => $buy } };
-do { write $them, $down } and do { order: foreach (@case) { package s } };
-goto home if not exists $more{money} or die q(uerying) ;for( @money){};
+do { write $them, $down } and do { order: foreach (@case) { package s } };
+goto home if not exists $more{money} or die q(uerying) ;for( @money){};
-at:;home: do { END{} and:; rest:; a:; bit: exit $shopping }
-and sleep until unpack$ing, cool products();
+at:;home: do { END{} and:; rest:; a:; bit: exit $shopping }
+and sleep until unpack$ing, cool products();
-__END__
-This is perl, v5.8.8 built for i386-freebsd-64int
+__END__
+This is perl, v5.8.8 built for i386-freebsd-64int
More...
@@ -203,11 +208,12 @@ This is perl, v5.8.8 built
- Generated with Gemtexter 3.0.1-develop |
- served by OpenBSD/relayd(8)+httpd(8) |
- Site Mirrors
-
- Webring: previous | shring | next
+ Generated with Gemtexter 3.0.1-develop |
+ served by OpenBSD/relayd(8)+httpd(8) |
+ Site Mirrors
+
+ Webring: previous | shring | next
datatype ’a multi - = EMPTY - | ELEM of ’a - | UNION of ’a multi * ’a multi +datatype ’a multi + = EMPTY + | ELEM of ’a + | UNION of ’a multi * ’a multi
Haskell:
@@ -52,11 +57,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -data (Eq a) => Multi a - = Empty - | Elem a - | Union (Multi a) (Multi a) - deriving Show +data (Eq a) => Multi a + = Empty + | Elem a + | Union (Multi a) (Multi a) + deriving Show
Processing a multi
@@ -67,12 +72,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -fun number (EMPTY) _ = 0 - | number (ELEM x) w = if x = w then 1 else 0 - | number (UNION (x,y)) w = (number x w) + (number y w) -fun test_number w = number (UNION (EMPTY, \ - UNION (ELEM 4, UNION (ELEM 6, \ - UNION (UNION (ELEM 4, ELEM 4), EMPTY))))) w +fun number (EMPTY) _ = 0 + | number (ELEM x) w = if x = w then 1 else 0 + | number (UNION (x,y)) w = (number x w) + (number y w) +fun test_number w = number (UNION (EMPTY, \ + UNION (ELEM 4, UNION (ELEM 6, \ + UNION (UNION (ELEM 4, ELEM 4), EMPTY))))) w
Haskell:
@@ -81,11 +86,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -number Empty _ = 0 -number (Elem x) w = if x == w then 1 else 0 -test_number w = number (Union Empty \ - (Union (Elem 4) (Union (Elem 6) \ - (Union (Union (Elem 4) (Elem 4)) Empty)))) w +number Empty _ = 0 +number (Elem x) w = if x == w then 1 else 0 +test_number w = number (Union Empty \ + (Union (Elem 4) (Union (Elem 6) \ + (Union (Union (Elem 4) (Elem 4)) Empty)))) w
Simplify function
@@ -96,19 +101,19 @@ test_number w = number (Union -fun simplify (UNION (x,y)) = - let fun is_empty (EMPTY) = true | is_empty _ = false - val x’ = simplify x - val y’ = simplify y - in if (is_empty x’) andalso (is_empty y’) - then EMPTY - else if (is_empty x’) - then y’ - else if (is_empty y’) - then x’ - else UNION (x’, y’) - end - | simplify x = x +-r SUGAR_FREE=yes -declare -r I_NEED_THE_BUZZ=no +fun simplify (UNION (x,y)) = + let fun is_empty (EMPTY) = true | is_empty _ = false + val x’ = simplify x + val y’ = simplify y + in if (is_empty x’) andalso (is_empty y’) + then EMPTY + else if (is_empty x’) + then y’ + else if (is_empty y’) + then x’ + else UNION (x’, y’) + end + | simplify x = x
Haskell:
@@ -117,17 +122,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -simplify (Union x y) - | (isEmpty x’) && (isEmpty y’) = Empty - | isEmpty x’ = y’ - | isEmpty y’ = x’ - | otherwise = Union x’ y’ - where - isEmpty Empty = True - isEmpty _ = False - x’ = simplify x - y’ = simplify y -simplify x = x +simplify (Union x y) + | (isEmpty x’) && (isEmpty y’) = Empty + | isEmpty x’ = y’ + | isEmpty y’ = x’ + | otherwise = Union x’ y’ + where + isEmpty Empty = True + isEmpty _ = False + x’ = simplify x + y’ = simplify y +simplify x = x
Delete all
@@ -138,12 +143,12 @@ simplify x = x by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -fun delete_all m w = - let fun delete_all’ (ELEM x) = if x = w then EMPTY else ELEM x - | delete_all’ (UNION (x,y)) = UNION (delete_all’ x, delete_all’ y) - | delete_all’ x = x - in simplify (delete_all’ m) - end +fun delete_all m w = + let fun delete_all’ (ELEM x) = if x = w then EMPTY else ELEM x + | delete_all’ (UNION (x,y)) = UNION (delete_all’ x, delete_all’ y) + | delete_all’ x = x + in simplify (delete_all’ m) + end
Haskell:
@@ -152,11 +157,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -delete_all m w = simplify (delete_all’ m) - where - delete_all’ (Elem x) = if x == w then Empty else Elem x - delete_all’ (Union x y) = Union (delete_all’ x) (delete_all’ y) - delete_all’ x = x +delete_all m w = simplify (delete_all’ m) + where + delete_all’ (Elem x) = if x == w then Empty else Elem x + delete_all’ (Union x y) = Union (delete_all’ x) (delete_all’ y) + delete_all’ x = x
Delete one
@@ -167,21 +172,21 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -fun delete_one m w = - let fun delete_one’ (UNION (x,y)) = - let val (x’, deleted) = delete_one’ x - in if deleted - then (UNION (x’, y), deleted) - else let val (y’, deleted) = delete_one’ y - in (UNION (x, y’), deleted) - end - end - | delete_one’ (ELEM x) = - if x = w then (EMPTY, true) else (ELEM x, false) - | delete_one’ x = (x, false) - val (m’, _) = delete_one’ m - in simplify m’ - end +fun delete_one m w = + let fun delete_one’ (UNION (x,y)) = + let val (x’, deleted) = delete_one’ x + in if deleted + then (UNION (x’, y), deleted) + else let val (y’, deleted) = delete_one’ y + in (UNION (x, y’), deleted) + end + end + | delete_one’ (ELEM x) = + if x = w then (EMPTY, true) else (ELEM x, false) + | delete_one’ x = (x, false) + val (m’, _) = delete_one’ m + in simplify m’ + end
Haskell:
@@ -190,19 +195,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -delete_one m w = do - let (m’, _) = delete_one’ m - simplify m’ - where - delete_one’ (Union x y) = - let (x’, deleted) = delete_one’ x - in if deleted - then (Union x’ y, deleted) - else let (y’, deleted) = delete_one’ y - in (Union x y’, deleted) - delete_one’ (Elem x) = - if x == w then (Empty, True) else (Elem x, False) - delete_one’ x = (x, False) +delete_one m w = do + let (m’, _) = delete_one’ m + simplify m’ + where + delete_one’ (Union x y) = + let (x’, deleted) = delete_one’ x + in if deleted + then (Union x’ y, deleted) + else let (y’, deleted) = delete_one’ y + in (Union x y’, deleted) + delete_one’ (Elem x) = + if x == w then (Empty, True) else (Elem x, False) + delete_one’ x = (x, False)
Higher-order functions
@@ -227,11 +232,12 @@ my_filter f l = foldr (make_filter_fn f) [] l
Back to the main site
+ diff --git a/gemfeed/2010-05-07-lazy-evaluation-with-standard-ml.html b/gemfeed/2010-05-07-lazy-evaluation-with-standard-ml.html index 641d8c0c..0565803b 100644 --- a/gemfeed/2010-05-07-lazy-evaluation-with-standard-ml.html +++ b/gemfeed/2010-05-07-lazy-evaluation-with-standard-ml.html @@ -2,12 +2,17 @@ +Lazy Evaluation with Standard ML - + + + + + @@ -113,11 +118,12 @@ first 10 nat_pairs_not_null
Back to the main site
+ diff --git a/gemfeed/2010-05-09-the-fype-programming-language.html b/gemfeed/2010-05-09-the-fype-programming-language.html index 372c9d53..746e9322 100644 --- a/gemfeed/2010-05-09-the-fype-programming-language.html +++ b/gemfeed/2010-05-09-the-fype-programming-language.html @@ -2,12 +2,17 @@ +The Fype Programming Language - + + + + + @@ -69,12 +74,12 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -typedef struct { - Tupel *p_tupel_argv; // Contains command line options - List *p_list_token; // Initial list of token - Hash *p_hash_syms; // Symbol table - char *c_basename; -} Fype; +typedef struct { + Tupel *p_tupel_argv; // Contains command line options + List *p_list_token; // Initial list of token + Hash *p_hash_syms; // Symbol table + char *c_basename; +} Fype;
And here is a snippet from the primary Fype "class implementation":
@@ -83,53 +88,53 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -Fype* -fype_new() { - Fype *p_fype = malloc(sizeof(Fype)); +Fype* +fype_new() { + Fype *p_fype = malloc(sizeof(Fype)); - p_fype->p_hash_syms = hash_new(512); - p_fype->p_list_token = list_new(); - p_fype->p_tupel_argv = tupel_new(); - p_fype->c_basename = NULL; + p_fype->p_hash_syms = hash_new(512); + p_fype->p_list_token = list_new(); + p_fype->p_tupel_argv = tupel_new(); + p_fype->c_basename = NULL; - garbage_init(); + garbage_init(); - return (p_fype); -} + return (p_fype); +} -void -fype_delete(Fype *p_fype) { - argv_tupel_delete(p_fype->p_tupel_argv); +void +fype_delete(Fype *p_fype) { + argv_tupel_delete(p_fype->p_tupel_argv); - hash_iterate(p_fype->p_hash_syms, symbol_cleanup_hash_syms_cb); - hash_delete(p_fype->p_hash_syms); + hash_iterate(p_fype->p_hash_syms, symbol_cleanup_hash_syms_cb); + hash_delete(p_fype->p_hash_syms); - list_iterate(p_fype->p_list_token, token_ref_down_cb); - list_delete(p_fype->p_list_token); + list_iterate(p_fype->p_list_token, token_ref_down_cb); + list_delete(p_fype->p_list_token); - if (p_fype->c_basename) - free(p_fype->c_basename); + if (p_fype->c_basename) + free(p_fype->c_basename); - garbage_destroy(); -} + garbage_destroy(); +} -int -fype_run(int i_argc, char **pc_argv) { - Fype *p_fype = fype_new(); +int +fype_run(int i_argc, char **pc_argv) { + Fype *p_fype = fype_new(); - // argv: Maintains command line options - argv_run(p_fype, i_argc, pc_argv); + // argv: Maintains command line options + argv_run(p_fype, i_argc, pc_argv); - // scanner: Creates a list of token - scanner_run(p_fype); + // scanner: Creates a list of token + scanner_run(p_fype); - // interpret: Interpret the list of token - interpret_run(p_fype); + // interpret: Interpret the list of token + interpret_run(p_fype); - fype_delete(p_fype); + fype_delete(p_fype); - return (0); -} + return (0); +}
Data types
@@ -558,11 +563,12 @@ BB
Back to the main site
+ diff --git a/gemfeed/2011-05-07-perl-daemon-service-framework.html b/gemfeed/2011-05-07-perl-daemon-service-framework.html index d68f12ca..071bf51b 100644 --- a/gemfeed/2011-05-07-perl-daemon-service-framework.html +++ b/gemfeed/2011-05-07-perl-daemon-service-framework.html @@ -2,12 +2,17 @@ +Perl Daemon (Service Framework) - + + + + + @@ -58,14 +63,14 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Starting - ./bin/perldaemon start (or shortcut ./control start) +# Starting + ./bin/perldaemon start (or shortcut ./control start) -# Stopping - ./bin/perldaemon stop (or shortcut ./control stop) +# Stopping + ./bin/perldaemon stop (or shortcut ./control stop) -# Alternatively: Starting in foreground -./bin/perldaemon start daemon.daemonize=no (or shortcut ./control foreground) +# Alternatively: Starting in foreground +./bin/perldaemon start daemon.daemonize=no (or shortcut ./control foreground)
To stop a daemon from running in foreground mode, "Ctrl+C" must be hit. To see more available startup options run "./control" without any argument.
@@ -78,30 +83,30 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -pb@titania:~/svn/utils/perldaemon/trunk$ ./control keys -# Path to the logfile -daemon.logfile=./log/perldaemon.log +pb@titania:~/svn/utils/perldaemon/trunk$ ./control keys +# Path to the logfile +daemon.logfile=./log/perldaemon.log -# The amount of seconds until the next event look takes place -daemon.loopinterval=1 +# The amount of seconds until the next event look takes place +daemon.loopinterval=1 -# Path to the modules dir -daemon.modules.dir=./lib/PerlDaemonModules +# Path to the modules dir +daemon.modules.dir=./lib/PerlDaemonModules -# Specifies either the daemon should run in daemon or foreground mode -daemon.daemonize=yes +# Specifies either the daemon should run in daemon or foreground mode +daemon.daemonize=yes -# Path to the pidfile -daemon.pidfile=./run/perldaemon.pid +# Path to the pidfile +daemon.pidfile=./run/perldaemon.pid -# Each module should run every run interval seconds -daemon.modules.runinterval=3 +# Each module should run every run interval seconds +daemon.modules.runinterval=3 -# Path to the alive file (is touched every loop interval seconds, usable for monitoring) -daemon.alivefile=./run/perldaemon.alive +# Path to the alive file (is touched every loop interval seconds, usable for monitoring) +daemon.alivefile=./run/perldaemon.alive -# Specifies the working directory -daemon.wd=./ +# Specifies the working directory +daemon.wd=./
Example
@@ -112,17 +117,17 @@ daemon.wd=./ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -$ ./control keys | grep daemon.loopinterval -daemon.loopinterval=1 -$ ./control keys daemon.loopinterval=10 | grep daemon.loopinterval -daemon.loopinterval=10 -$ ./control start daemon.loopinterval=10; sleep 10; tail -n 2 log/perldaemon.log -Starting daemon now... -Mon Jun 13 11:29:27 2011 (PID 2838): Triggering PerlDaemonModules::ExampleModule -(last triggered before 10.002106s; carry: 7.002106s; wanted interval: 3s) -Mon Jun 13 11:29:27 2011 (PID 2838): ExampleModule Test 2 -$ ./control stop -Stopping daemon now... +$ ./control keys | grep daemon.loopinterval +daemon.loopinterval=1 +$ ./control keys daemon.loopinterval=10 | grep daemon.loopinterval +daemon.loopinterval=10 +$ ./control start daemon.loopinterval=10; sleep 10; tail -n 2 log/perldaemon.log +Starting daemon now... +Mon Jun 13 11:29:27 2011 (PID 2838): Triggering PerlDaemonModules::ExampleModule +(last triggered before 10.002106s; carry: 7.002106s; wanted interval: 3s) +Mon Jun 13 11:29:27 2011 (PID 2838): ExampleModule Test 2 +$ ./control stop +Stopping daemon now...
If you want to change that property forever, either edit perldaemon.conf or do this:
@@ -131,7 +136,7 @@ Stopping daemon now... by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -$ ./control keys daemon.loopinterval=10 > new.conf; mv new.conf conf/perldaemon.conf +$ ./control keys daemon.loopinterval=10 > new.conf; mv new.conf conf/perldaemon.conf
HiRes event loop
@@ -148,35 +153,35 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -package PerlDaemonModules::ExampleModule; +package PerlDaemonModules::ExampleModule; -use strict; -use warnings; +use strict; +use warnings; -sub new ($$$) { - my ($class, $conf) = @_; +sub new ($$$) { + my ($class, $conf) = @_; - my $self = bless { conf => $conf }, $class; + my $self = bless { conf => $conf }, $class; - # Store some private module stuff - $self->{counter} = 0; + # Store some private module stuff + $self->{counter} = 0; - return $self; -} + return $self; +} -# Runs periodically in a loop (set interval in perldaemon.conf) -sub do ($) { - my $self = shift; - my $conf = $self->{conf}; - my $logger = $conf->{logger}; +# Runs periodically in a loop (set interval in perldaemon.conf) +sub do ($) { + my $self = shift; + my $conf = $self->{conf}; + my $logger = $conf->{logger}; - # Calculate some private module stuff - my $count = ++$self->{counter}; + # Calculate some private module stuff + my $count = ++$self->{counter}; - $logger->logmsg("ExampleModule Test $count"); -} + $logger->logmsg("ExampleModule Test $count"); +} -1; +1;
Your own module
@@ -187,11 +192,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -cd ./lib/PerlDaemonModules/ - cp ExampleModule.pm YourModule.pm - vi YourModule.pm - cd - - ./bin/perldaemon restart (or shortcurt ./control restart) +cd ./lib/PerlDaemonModules/ + cp ExampleModule.pm YourModule.pm + vi YourModule.pm + cd - + ./bin/perldaemon restart (or shortcurt ./control restart)
Now watch ./log/perldaemon.log closely. It is a good practice to test your modules in 'foreground mode' (see above how to do that).
@@ -215,11 +220,12 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
+ diff --git a/gemfeed/2014-03-24-the-fibonacci.pl.c-polyglot.html b/gemfeed/2014-03-24-the-fibonacci.pl.c-polyglot.html index 2e6e75fd..5bbedc7a 100644 --- a/gemfeed/2014-03-24-the-fibonacci.pl.c-polyglot.html +++ b/gemfeed/2014-03-24-the-fibonacci.pl.c-polyglot.html @@ -2,12 +2,17 @@ +The fibonacci.pl.raku.c Polyglot - + + + + + @@ -27,47 +32,47 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#include <stdio.h> +#include <stdio.h> -#define $arg function_argument -#define my int -#define sub int -#define BEGIN int main(void) +#define $arg function_argument +#define my int +#define sub int +#define BEGIN int main(void) -my $arg; +my $arg; -sub hello() { - printf("Hello, welcome to the Fibonacci Numbers!\n"); - printf("This program is all, valid C and C++ and Perl and Raku code!\n"); - printf("It calculates all fibonacci numbers from 0 to 9!\n\n"); - return 0; -} +sub hello() { + printf("Hello, welcome to the Fibonacci Numbers!\n"); + printf("This program is all, valid C and C++ and Perl and Raku code!\n"); + printf("It calculates all fibonacci numbers from 0 to 9!\n\n"); + return 0; +} -sub fibonacci() { - my $n = $arg; +sub fibonacci() { + my $n = $arg; - if ($n < 2) { - return $n; - } + if ($n < 2) { + return $n; + } - $arg = $n - 1; - my $fib1 = fibonacci(); - $arg = $n - 2; - my $fib2 = fibonacci(); + $arg = $n - 1; + my $fib1 = fibonacci(); + $arg = $n - 2; + my $fib2 = fibonacci(); - return $fib1 + $fib2; -} + return $fib1 + $fib2; +} -BEGIN { - hello(); - my $i = 0; +BEGIN { + hello(); + my $i = 0; - while ($i <= 10) { - $arg = $i; - printf("fib(%d) = %d\n", $i, fibonacci()); - $i++; - } -} + while ($i <= 10) { + $arg = $i; + printf("fib(%d) = %d\n", $i, fibonacci()); + $i++; + } +}
You can find the full source code at GitHub:
@@ -80,41 +85,41 @@ BEGIN { by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% gcc fibonacci.pl.raku.c -o fibonacci -% ./fibonacci -Hello, welcome to the Fibonacci Numbers! -This program is all, valid C and C++ and Perl and Raku code! -It calculates all fibonacci numbers from 0 to 9! +% gcc fibonacci.pl.raku.c -o fibonacci +% ./fibonacci +Hello, welcome to the Fibonacci Numbers! +This program is all, valid C and C++ and Perl and Raku code! +It calculates all fibonacci numbers from 0 to 9! -fib(0) = 0 -fib(1) = 1 -fib(2) = 1 -fib(3) = 2 -fib(4) = 3 -fib(5) = 5 -fib(6) = 8 -fib(7) = 13 -fib(8) = 21 -fib(9) = 34 -fib(10) = 55 +fib(0) = 0 +fib(1) = 1 +fib(2) = 1 +fib(3) = 2 +fib(4) = 3 +fib(5) = 5 +fib(6) = 8 +fib(7) = 13 +fib(8) = 21 +fib(9) = 34 +fib(10) = 55 -% g++ fibonacci.pl.raku.c -o fibonacci -% ./fibonacci -Hello, welcome to the Fibonacci Numbers! -This program is all, valid C and C++ and Perl and Raku code! -It calculates all fibonacci numbers from 0 to 9! +% g++ fibonacci.pl.raku.c -o fibonacci +% ./fibonacci +Hello, welcome to the Fibonacci Numbers! +This program is all, valid C and C++ and Perl and Raku code! +It calculates all fibonacci numbers from 0 to 9! -fib(0) = 0 -fib(1) = 1 -fib(2) = 1 -fib(3) = 2 -fib(4) = 3 -fib(5) = 5 -fib(6) = 8 -fib(7) = 13 -fib(8) = 21 -fib(9) = 34 -fib(10) = 55 +fib(0) = 0 +fib(1) = 1 +fib(2) = 1 +fib(3) = 2 +fib(4) = 3 +fib(5) = 5 +fib(6) = 8 +fib(7) = 13 +fib(8) = 21 +fib(9) = 34 +fib(10) = 55
Let's run it with Perl and Raku
@@ -123,39 +128,39 @@ fib(10) = 55 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% perl fibonacci.pl.raku.c -Hello, welcome to the Fibonacci Numbers! -This program is all, valid C and C++ and Perl and Raku code! -It calculates all fibonacci numbers from 0 to 9! +% perl fibonacci.pl.raku.c +Hello, welcome to the Fibonacci Numbers! +This program is all, valid C and C++ and Perl and Raku code! +It calculates all fibonacci numbers from 0 to 9! -fib(0) = 0 -fib(1) = 1 -fib(2) = 1 -fib(3) = 2 -fib(4) = 3 -fib(5) = 5 -fib(6) = 8 -fib(7) = 13 -fib(8) = 21 -fib(9) = 34 -fib(10) = 55 +fib(0) = 0 +fib(1) = 1 +fib(2) = 1 +fib(3) = 2 +fib(4) = 3 +fib(5) = 5 +fib(6) = 8 +fib(7) = 13 +fib(8) = 21 +fib(9) = 34 +fib(10) = 55 -% raku fibonacci.pl.raku.c -Hello, welcome to the Fibonacci Numbers! -This program is all, valid C and C++ and Perl and Raku code! -It calculates all fibonacci numbers from 0 to 9! +% raku fibonacci.pl.raku.c +Hello, welcome to the Fibonacci Numbers! +This program is all, valid C and C++ and Perl and Raku code! +It calculates all fibonacci numbers from 0 to 9! -fib(0) = 0 -fib(1) = 1 -fib(2) = 1 -fib(3) = 2 -fib(4) = 3 -fib(5) = 5 -fib(6) = 8 -fib(7) = 13 -fib(8) = 21 -fib(9) = 34 -fib(10) = 55 +fib(0) = 0 +fib(1) = 1 +fib(2) = 1 +fib(3) = 2 +fib(4) = 3 +fib(5) = 5 +fib(6) = 8 +fib(7) = 13 +fib(8) = 21 +fib(9) = 34 +fib(10) = 55
It's entertaining to play with :-).
@@ -164,11 +169,12 @@ fib(10) = 55
Back to the main site
+ diff --git a/gemfeed/2015-12-05-run-debian-on-your-phone-with-debroid.html b/gemfeed/2015-12-05-run-debian-on-your-phone-with-debroid.html index 9d537d43..75b4cb59 100644 --- a/gemfeed/2015-12-05-run-debian-on-your-phone-with-debroid.html +++ b/gemfeed/2015-12-05-run-debian-on-your-phone-with-debroid.html @@ -2,12 +2,17 @@ +Run Debian on your phone with Debroid - + + + + + @@ -56,24 +61,24 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -sudo dnf install debootstrap -# 5g -dd if=/dev/zero of=jessie.img bs=$[ 1024 * 1024 ] \ - count=$[ 1024 * 5 ] +sudo dnf install debootstrap +# 5g +dd if=/dev/zero of=jessie.img bs=$[ 1024 * 1024 ] \ + count=$[ 1024 * 5 ] -# Show used loop devices -sudo losetup -f -# Store the next free one to $loop -loop=loopN -sudo losetup /dev/$loop jessie.img +# Show used loop devices +sudo losetup -f +# Store the next free one to $loop +loop=loopN +sudo losetup /dev/$loop jessie.img -mkdir jessie -sudo mkfs.ext4 /dev/$loop -sudo mount /dev/$loop jessie -sudo debootstrap --foreign --variant=minbase \ - --arch armel jessie jessie/ \ - http://http.debian.net/debian -sudo umount jessie +mkdir jessie +sudo mkfs.ext4 /dev/$loop +sudo mount /dev/$loop jessie +sudo debootstrap --foreign --variant=minbase \ + --arch armel jessie jessie/ \ + http://http.debian.net/debian +sudo umount jessie
Copy Debian image to the phone
@@ -84,42 +89,42 @@ sudo umount jessie by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -adb root && adb wait-for-device && adb shell -mkdir -p /storage/sdcard1/Linux/jessie -exit +adb root && adb wait-for-device && adb shell +mkdir -p /storage/sdcard1/Linux/jessie +exit -# Sparse image problem, may be too big for copying otherwise -gzip jessie.img -# Copy over -adb push jessie.img.gz /storage/sdcard1/Linux/jessie.img.gz -adb shell -cd /storage/sdcard1/Linux -gunzip jessie.img.gz +# Sparse image problem, may be too big for copying otherwise +gzip jessie.img +# Copy over +adb push jessie.img.gz /storage/sdcard1/Linux/jessie.img.gz +adb shell +cd /storage/sdcard1/Linux +gunzip jessie.img.gz -# Show used loop devices -losetup -f -# Store the next free one to $loop -loop=loopN +# Show used loop devices +losetup -f +# Store the next free one to $loop +loop=loopN -# Use the next free one (replace the loop number) -losetup /dev/block/$loop $(pwd)/jessie.img -mount -t ext4 /dev/block/$loop $(pwd)/jessie +# Use the next free one (replace the loop number) +losetup /dev/block/$loop $(pwd)/jessie.img +mount -t ext4 /dev/block/$loop $(pwd)/jessie -# Bind-Mound proc, dev, sys` -busybox mount --bind /proc $(pwd)/jessie/proc -busybox mount --bind /dev $(pwd)/jessie/dev -busybox mount --bind /dev/pts $(pwd)/jessie/dev/pts -busybox mount --bind /sys $(pwd)/jessie/sys +# Bind-Mound proc, dev, sys` +busybox mount --bind /proc $(pwd)/jessie/proc +busybox mount --bind /dev $(pwd)/jessie/dev +busybox mount --bind /dev/pts $(pwd)/jessie/dev/pts +busybox mount --bind /sys $(pwd)/jessie/sys -# Bind-Mound the rest of Android -mkdir -p $(pwd)/jessie/storage/sdcard{0,1} -busybox mount --bind /storage/emulated \ - $(pwd)/jessie/storage/sdcard0 -busybox mount --bind /storage/sdcard1 \ - $(pwd)/jessie/storage/sdcard1 +# Bind-Mound the rest of Android +mkdir -p $(pwd)/jessie/storage/sdcard{0,1} +busybox mount --bind /storage/emulated \ + $(pwd)/jessie/storage/sdcard0 +busybox mount --bind /storage/sdcard1 \ + $(pwd)/jessie/storage/sdcard1 -# Check mounts -mount | grep jessie +# Check mounts +mount | grep jessie
Second debootstrap stage
@@ -130,11 +135,11 @@ mount | grep jessie by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -chroot $(pwd)/jessie /bin/bash -l -export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin -/debootstrap/debootstrap --second-stage -exit # Leave chroot -exit # Leave adb shell +chroot $(pwd)/jessie /bin/bash -l +export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin +/debootstrap/debootstrap --second-stage +exit # Leave chroot +exit # Leave adb shell
Setup of various scripts
@@ -145,36 +150,36 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Install script jessie.sh -adb push storage/sdcard1/Linux/jessie.sh /storage/sdcard/Linux/jessie.sh -adb shell -cd /storage/sdcard1/Linux -sh jessie.sh enter +# Install script jessie.sh +adb push storage/sdcard1/Linux/jessie.sh /storage/sdcard/Linux/jessie.sh +adb shell +cd /storage/sdcard1/Linux +sh jessie.sh enter -# Bashrc -cat <<END >~/.bashrc -export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH -export EDITOR=vim -hostname $(cat /etc/hostname) -END +# Bashrc +cat <<END >~/.bashrc +export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH +export EDITOR=vim +hostname $(cat /etc/hostname) +END -# Fixing an error message while loading the profile -sed -i s#id#/usr/bin/id# /etc/profile +# Fixing an error message while loading the profile +sed -i s#id#/usr/bin/id# /etc/profile -# Setting the hostname -echo phobos > /etc/hostname -echo 127.0.0.1 phobos > /etc/hosts -hostname phobos +# Setting the hostname +echo phobos > /etc/hostname +echo 127.0.0.1 phobos > /etc/hosts +hostname phobos -# Apt-sources -cat <<END > sources.list -deb http://ftp.uk.debian.org/debian/ jessie main contrib non-free -deb-src http://ftp.uk.debian.org/debian/ jessie main contrib non-free -END -apt-get update -apt-get upgrade -apt-get dist-upgrade -exit # Exit chroot +# Apt-sources +cat <<END > sources.list +deb http://ftp.uk.debian.org/debian/ jessie main contrib non-free +deb-src http://ftp.uk.debian.org/debian/ jessie main contrib non-free +END +apt-get update +apt-get upgrade +apt-get dist-upgrade +exit # Exit chroot
Entering Debroid and enable a service
@@ -185,19 +190,19 @@ apt-get dist-upgrade by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -sh jessie.sh enter +sh jessie.sh enter -# Setup example serice uptimed -apt-get install uptimed -cat <<END > /etc/rc.debroid -export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH -service uptimed status &>/dev/null || service uptimed start -exit 0 -END +# Setup example serice uptimed +apt-get install uptimed +cat <<END > /etc/rc.debroid +export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH +service uptimed status &>/dev/null || service uptimed start +exit 0 +END -chmod 0755 /etc/rc.debroid -exit # Exit chroot -exit # Exit adb shell +chmod 0755 /etc/rc.debroid +exit # Exit chroot +exit # Exit adb shell
Include to Android startup:
@@ -208,10 +213,10 @@ chmod 0755 /etc/rc.debroid by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -adb push data/local/userinit.sh /data/local/userinit.sh -adb shell -chmod +x /data/local/userinit.sh -exit +adb push data/local/userinit.sh /data/local/userinit.sh +adb shell +chmod +x /data/local/userinit.sh +exit
Reboot & test! Enjoy!
@@ -220,11 +225,12 @@ chmod +x /data/local/userinit.sh
Back to the main site
+ diff --git a/gemfeed/2016-04-03-offsite-backup-with-zfs.html b/gemfeed/2016-04-03-offsite-backup-with-zfs.html index 39f6caf9..ef42d4a1 100644 --- a/gemfeed/2016-04-03-offsite-backup-with-zfs.html +++ b/gemfeed/2016-04-03-offsite-backup-with-zfs.html @@ -2,12 +2,17 @@ +Offsite backup with ZFS - + + + + + @@ -57,11 +62,12 @@
Back to the main site
+ diff --git a/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.html b/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.html index e26c354d..3a16acdd 100644 --- a/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.html +++ b/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.html @@ -2,12 +2,17 @@ +Jails and ZFS with Puppet on FreeBSD - + + + + + @@ -429,11 +434,12 @@ Notice: Finished catalog run in 206.09 seconds
Back to the main site
+ diff --git a/gemfeed/2016-04-16-offsite-backup-with-zfs-part2.html b/gemfeed/2016-04-16-offsite-backup-with-zfs-part2.html index 3aa08168..edd35e4e 100644 --- a/gemfeed/2016-04-16-offsite-backup-with-zfs-part2.html +++ b/gemfeed/2016-04-16-offsite-backup-with-zfs-part2.html @@ -2,12 +2,17 @@ +Offsite backup with ZFS (Part 2) - + + + + + @@ -43,11 +48,12 @@
Back to the main site
+ diff --git a/gemfeed/2016-05-22-spinning-up-my-own-authoritative-dns-servers.html b/gemfeed/2016-05-22-spinning-up-my-own-authoritative-dns-servers.html index 3b428a68..c20fe9d6 100644 --- a/gemfeed/2016-05-22-spinning-up-my-own-authoritative-dns-servers.html +++ b/gemfeed/2016-05-22-spinning-up-my-own-authoritative-dns-servers.html @@ -2,12 +2,17 @@ +Spinning up my own authoritative DNS servers - + + + + + @@ -261,11 +266,12 @@ apply Service "dig6" {
Back to the main site
+ diff --git a/gemfeed/2016-11-20-object-oriented-programming-with-ansi-c.html b/gemfeed/2016-11-20-object-oriented-programming-with-ansi-c.html index bedf6692..6071ea51 100644 --- a/gemfeed/2016-11-20-object-oriented-programming-with-ansi-c.html +++ b/gemfeed/2016-11-20-object-oriented-programming-with-ansi-c.html @@ -2,12 +2,17 @@ +Object oriented programming with ANSI C - + + + + + @@ -34,37 +39,37 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#include <stdio.h> +#include <stdio.h> -typedef struct { - double (*calculate)(const double, const double); - char *name; -} something_s; +typedef struct { + double (*calculate)(const double, const double); + char *name; +} something_s; -double multiplication(const double a, const double b) { - return a * b; -} +double multiplication(const double a, const double b) { + return a * b; +} -double division(const double a, const double b) { - return a / b; -} +double division(const double a, const double b) { + return a / b; +} -int main(void) { - something_s mult = (something_s) { - .calculate = multiplication, - .name = "Multiplication" - }; +int main(void) { + something_s mult = (something_s) { + .calculate = multiplication, + .name = "Multiplication" + }; - something_s div = (something_s) { - .calculate = division, - .name = "Division" - }; + something_s div = (something_s) { + .calculate = division, + .name = "Division" + }; - const double a = 3, b = 2; + const double a = 3, b = 2; - printf("%s(%f, %f) => %f\n", mult.name, a, b, mult.calculate(a,b)); - printf("%s(%f, %f) => %f\n", div.name, a, b, div.calculate(a,b)); -} + printf("%s(%f, %f) => %f\n", mult.name, a, b, mult.calculate(a,b)); + printf("%s(%f, %f) => %f\n", div.name, a, b, div.calculate(a,b)); +}
As you can see, you can call the function (pointed by the function pointer) with the same syntax as in C++ or Java:
@@ -73,8 +78,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -printf("%s(%f, %f) => %f\n", mult.name, a, b, mult.calculate(a,b)); -printf("%s(%f, %f) => %f\n", div.name, a, b, div.calculate(a,b)); +printf("%s(%f, %f) => %f\n", mult.name, a, b, mult.calculate(a,b)); +printf("%s(%f, %f) => %f\n", div.name, a, b, div.calculate(a,b));
However, that's just syntactic sugar for:
@@ -83,8 +88,8 @@ printf("%s(%f, %f) => %f\n by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -printf("%s(%f, %f) => %f\n", mult.name, a, b, (*mult.calculate)(a,b)); -printf("%s(%f, %f) => %f\n", div.name, a, b, (*div.calculate)(a,b)); +printf("%s(%f, %f) => %f\n", mult.name, a, b, (*mult.calculate)(a,b)); +printf("%s(%f, %f) => %f\n", div.name, a, b, (*div.calculate)(a,b));
Output:
@@ -93,10 +98,10 @@ printf("%s(%f, %f) => %f\n by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -pbuetow ~/git/blog/source [38268]% gcc oop-c-example.c -o oop-c-example -pbuetow ~/git/blog/source [38269]% ./oop-c-example -Multiplication(3.000000, 2.000000) => 6.000000 -Division(3.000000, 2.000000) => 1.500000 +pbuetow ~/git/blog/source [38268]% gcc oop-c-example.c -o oop-c-example +pbuetow ~/git/blog/source [38269]% ./oop-c-example +Multiplication(3.000000, 2.000000) => 6.000000 +Division(3.000000, 2.000000) => 1.500000
Not complicated at all, but nice to know and helps to make the code easier to read!
@@ -109,7 +114,7 @@ Division(3.000000, 2.000000 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -mult.calculate(mult,a,b)); +mult.calculate(mult,a,b));
Real object oriented programming with C
@@ -130,11 +135,12 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
+ diff --git a/gemfeed/2018-06-01-realistic-load-testing-with-ioriot-for-linux.html b/gemfeed/2018-06-01-realistic-load-testing-with-ioriot-for-linux.html index c4079734..a6083bb6 100644 --- a/gemfeed/2018-06-01-realistic-load-testing-with-ioriot-for-linux.html +++ b/gemfeed/2018-06-01-realistic-load-testing-with-ioriot-for-linux.html @@ -2,12 +2,17 @@ +Realistic load testing with I/O Riot for Linux - + + + + + @@ -218,11 +223,12 @@ Total time: 1213.00s
Back to the main site
+ diff --git a/gemfeed/2021-04-22-dtail-the-distributed-log-tail-program.html b/gemfeed/2021-04-22-dtail-the-distributed-log-tail-program.html index 8c32a7d5..32c2889f 100644 --- a/gemfeed/2021-04-22-dtail-the-distributed-log-tail-program.html +++ b/gemfeed/2021-04-22-dtail-the-distributed-log-tail-program.html @@ -2,12 +2,17 @@ +DTail - The distributed log tail program - + + + + + @@ -144,11 +149,12 @@ dtail –servers serverlist.txt –files ‘/var/log/*.log’ –regex ‘(?i:er
Back to the main site
+ diff --git a/gemfeed/2021-04-24-welcome-to-the-geminispace.html b/gemfeed/2021-04-24-welcome-to-the-geminispace.html index 7f60b5db..b610fecc 100644 --- a/gemfeed/2021-04-24-welcome-to-the-geminispace.html +++ b/gemfeed/2021-04-24-welcome-to-the-geminispace.html @@ -2,12 +2,17 @@ +Welcome to the Geminispace - + + + + + @@ -117,11 +122,12 @@
Back to the main site
+ diff --git a/gemfeed/2021-05-16-personal-bash-coding-style-guide.html b/gemfeed/2021-05-16-personal-bash-coding-style-guide.html index 3a832d5b..2026ef35 100644 --- a/gemfeed/2021-05-16-personal-bash-coding-style-guide.html +++ b/gemfeed/2021-05-16-personal-bash-coding-style-guide.html @@ -2,12 +2,17 @@ +Personal Bash coding style guide - + + + + + @@ -66,7 +71,7 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/bin/bash +#!/bin/bash
... as the shebang line, but that does not work on all Unix and Unix-like operating systems (e.g., the *BSDs don't have Bash installed to /bin/bash). Better is:
@@ -75,7 +80,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/env bash +#!/usr/bin/env bash
Two space soft-tabs indentation
@@ -96,14 +101,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# All fits on one line -command1 | command2 +# All fits on one line +command1 | command2 -# Long commands -command1 \ - | command2 \ - | command3 \ - | command4 +# Long commands +command1 \ + | command2 \ + | command3 \ + | command4
I think there is a better way like the following, which is less noisy. The pipe | already indicates the Bash that another command is expected, thus making the explicit line breaks with \ obsolete:
@@ -112,11 +117,11 @@ command1 \ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Long commands -command1 | - command2 | - command3 | - command4 +# Long commands +command1 | + command2 | + command3 | + command4
Update: It's 2023 now, and I have changed my mind. I think Google's way is the better one. It may be a bit more to type, but the leading | are a nice eye catcher, so you know immediately what is going on!
@@ -129,11 +134,11 @@ command1 | by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -greet () { - local -r greeting="${1}" - local -r name="${2}" - echo "${greeting} ${name}!" -} +greet () { + local -r greeting="${1}" + local -r name="${2}" + echo "${greeting} ${name}!" +}
In this particular example, I agree that you should quote them as you don't know the input (are there, for example, whitespace characters?). But if you are sure that you are only using simple bare words, then I think that the code looks much cleaner when you do this instead:
@@ -142,11 +147,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -say_hello_to_paul () { - local -r greeting=Hello - local -r name=Paul - echo "$greeting $name!" -} +say_hello_to_paul () { + local -r greeting=Hello + local -r name=Paul + echo "$greeting $name!" +}
You see, I also omitted the curly braces { } around the variables. I only use the curly braces around variables when it makes the code either easier/clearer to read or if it is necessary to use them:
@@ -155,9 +160,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -declare FOO=bar -# Curly braces around FOO are necessary -echo "foo${FOO}baz" +declare FOO=bar +# Curly braces around FOO are necessary +echo "foo${FOO}baz"
A few more words on always quoting the variables: For the sake of consistency (and for making ShellCheck happy), I am not against quoting everything I encounter. I also think that the larger the Bash script becomes, the more critical it becomes always to quote variables. That's because it will be more likely that you might not remember that some of the functions don't work on values with spaces in them, for example. It's just that I won't quote everything in every small script I write.
@@ -170,13 +175,13 @@ echo "foo${FOO}baz" by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Prefer this: -addition=$(( X + Y )) -substitution="${string/#foo/bar}" +# Prefer this: +addition=$(( X + Y )) +substitution="${string/#foo/bar}" -# Instead of this: -addition="$(expr "${X}" + "${Y}")" -substitution="$(echo "${string}" | sed -e 's/^foo/bar/')" +# Instead of this: +addition="$(expr "${X}" + "${Y}")" +substitution="$(echo "${string}" | sed -e 's/^foo/bar/')"
I can't entirely agree here. The external commands (especially sed) are much more sophisticated and powerful than the built-in Bash versions. Sed can do much more than the Bash can ever do by itself when it comes to text manipulation (the name "sed" stands for streaming editor, after all).
@@ -197,20 +202,20 @@ substitution="$(echo "${string}declaredeclare -r SUGAR_FREE=yes +declare -r I_NEED_THE_BUZZ=no -buy_soda () { - local -r sugar_free=$1 +buy_soda () { + local -r sugar_free=$1 - if [[ $sugar_free == yes ]]; then - echo 'Diet Dr. Pepper' - else - echo 'Pepsi Coke' - fi -} + if [[ $sugar_free == yes ]]; then + echo 'Diet Dr. Pepper' + else + echo 'Pepsi Coke' + fi +} -buy_soda $I_NEED_THE_BUZZ +buy_soda $I_NEED_THE_BUZZ
Non-evil alternative to variable assignments via eval
@@ -221,12 +226,12 @@ buy_soda $I_NEED_THE_BUZZ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# What does this set? -# Did it succeed? In part or whole? -eval $(set_my_variables) +# What does this set? +# Did it succeed? In part or whole? +eval $(set_my_variables) -# What happens if one of the returned values has a space in it? -variable="$(eval some_function)" +# What happens if one of the returned values has a space in it? +variable="$(eval some_function)"
However, if I want to read variables from another file, I don't have to use eval here. I only have to source the file:
@@ -265,39 +270,39 @@ Hello paul, it is Sat 15 May 19:21:12 BST 2021 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -filter_lines () { - echo 'Start filtering lines in a fancy way!' >&2 - grep ... | sed .... -} +filter_lines () { + echo 'Start filtering lines in a fancy way!' >&2 + grep ... | sed .... +} -process_lines () { - echo 'Start processing line by line!' >&2 - while read -r line; do - ... do something and produce a result... - echo "$result" - done -} +process_lines () { + echo 'Start processing line by line!' >&2 + while read -r line; do + ... do something and produce a result... + echo "$result" + done +} -# Do some post-processing of the data -postprocess_lines () { - echo 'Start removing duplicates!' >&2 - sort -u -} +# Do some post-processing of the data +postprocess_lines () { + echo 'Start removing duplicates!' >&2 + sort -u +} -genreate_report () { - echo 'My boss wants to have a report!' >&2 - tee outfile.txt - wc -l outfile.txt -} +genreate_report () { + echo 'My boss wants to have a report!' >&2 + tee outfile.txt + wc -l outfile.txt +} -main () { - filter_lines | - process_lines | - postprocess_lines | - generate_report -} +main () { + filter_lines | + process_lines | + postprocess_lines | + generate_report +} -main +main
The stdout is always passed as a pipe to the next following stage. The stderr is used for info logging.
@@ -312,13 +317,13 @@ main by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -some_function () { - local -r param_foo="$1"; shift - local -r param_baz="$1"; shift - local -r param_bay="$1"; shift +some_function () { + local -r param_foo="$1"; shift + local -r param_baz="$1"; shift + local -r param_bay="$1"; shift - # ... -} + # ... +}
Want to add a param_baz? Just do this:
@@ -327,14 +332,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -some_function () { - local -r param_foo="$1"; shift - local -r param_bar="$1"; shift - local -r param_baz="$1"; shift - local -r param_bay="$1"; shift +some_function () { + local -r param_foo="$1"; shift + local -r param_bar="$1"; shift + local -r param_baz="$1"; shift + local -r param_bay="$1"; shift - # ... -} + # ... +}
Want to remove param_foo? Nothing easier than that:
@@ -343,13 +348,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -some_function () { - local -r param_bar="$1"; shift - local -r param_baz="$1"; shift - local -r param_bay="$1"; shift - - # ... -} +some_function () { + local -r param_bar="$1"; shift + local -r param_baz="$1"; shift + local -r param_bay="$1"; shift + + # ... +}
As you can see, I didn't need to change any other assignments within the function. Of course, you would also need to change the function argument lists at every occasion where the function is invoked - you would do that within the same refactoring session.
@@ -362,9 +367,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -set -e -grep -q foo <<< bar -echo Jo +set -e +grep -q foo <<< bar +echo Jo
Here 'Jo' will never be printed out as the grep didn't find any match. It's unrealistic for most scripts to run in paranoid mode purely, so there must be a way to add exceptions. Critical Bash scripts of mine tend to look like this:
@@ -373,26 +378,26 @@ echo Jo by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/env bash +#!/usr/bin/env bash -set -e +set -e -some_function () { - # .. some critical code - # ... +some_function () { + # .. some critical code + # ... - set +e - # Grep might fail, but that's OK now - grep .... - local -i ec=$? - set -e + set +e + # Grep might fail, but that's OK now + grep .... + local -i ec=$? + set -e - # .. critical code continues ... - if [[ $ec -ne 0 ]]; then - : # ... - fi - # ... -} + # .. critical code continues ... + if [[ $ec -ne 0 ]]; then + : # ... + fi + # ... +}
Learned
@@ -407,10 +412,10 @@ some_function () { by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -if [[ "${my_var}" > 3 ]]; then - # True for 4, false for 22. - do_something -fi +if [[ "${my_var}" > 3 ]]; then + # True for 4, false for 22. + do_something +fi
... but it is probably an unintended lexicographical comparison. A correct way would be:
@@ -419,9 +424,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -if (( my_var > 3 )); then - do_something -fi +if (( my_var > 3 )); then + do_something +fi
or
@@ -430,9 +435,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -if [[ "${my_var}" -gt 3 ]]; then - do_something -fi +if [[ "${my_var}" -gt 3 ]]; then + do_something +fi
PIPESTATUS
@@ -445,10 +450,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -tar -cf - ./* | ( cd "${dir}" && tar -xf - ) -if (( PIPESTATUS[0] != 0 || PIPESTATUS[1] != 0 )); then - echo "Unable to tar files to ${dir}" >&2 -fi +tar -cf - ./* | ( cd "${dir}" && tar -xf - ) +if (( PIPESTATUS[0] != 0 || PIPESTATUS[1] != 0 )); then + echo "Unable to tar files to ${dir}" >&2 +fi
However, as PIPESTATUS will be overwritten as soon as you do any other command, if you need to act differently on errors based on where it happened in the pipe, you'll need to assign PIPESTATUS to another variable immediately after running the command (don't forget that [ is a command and will wipe out PIPESTATUS).
@@ -457,14 +462,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -tar -cf - ./* | ( cd "${DIR}" && tar -xf - ) -return_codes=( "${PIPESTATUS[@]}" ) -if (( return_codes[0] != 0 )); then - do_something -fi -if (( return_codes[1] != 0 )); then - do_something_else -fi +tar -cf - ./* | ( cd "${DIR}" && tar -xf - ) +return_codes=( "${PIPESTATUS[@]}" ) +if (( return_codes[0] != 0 )); then + do_something +fi +if (( return_codes[1] != 0 )); then + do_something_else +fi
Use common sense and BE CONSISTENT.
@@ -495,11 +500,12 @@ return_codes=( "${PIPESTATUS[@]}" )
Back to the main site
+ diff --git a/gemfeed/2021-06-05-gemtexter-one-bash-script-to-rule-it-all.html b/gemfeed/2021-06-05-gemtexter-one-bash-script-to-rule-it-all.html index 926335cd..ec797afe 100644 --- a/gemfeed/2021-06-05-gemtexter-one-bash-script-to-rule-it-all.html +++ b/gemfeed/2021-06-05-gemtexter-one-bash-script-to-rule-it-all.html @@ -2,12 +2,17 @@ +Gemtexter - One Bash script to rule it all - + + + + + @@ -114,18 +119,18 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul in uranus in gemtexter on 🌱 main -❯ wc -l gemtexter lib/* - 117 gemtexter - 59 lib/assert.source.sh - 128 lib/atomfeed.source.sh - 64 lib/gemfeed.source.sh - 161 lib/generate.source.sh - 50 lib/git.source.sh - 162 lib/html.source.sh - 30 lib/log.source.sh - 63 lib/md.source.sh - 834 total +paul in uranus in gemtexter on 🌱 main +❯ wc -l gemtexter lib/* + 117 gemtexter + 59 lib/assert.source.sh + 128 lib/atomfeed.source.sh + 64 lib/gemfeed.source.sh + 161 lib/generate.source.sh + 50 lib/git.source.sh + 162 lib/html.source.sh + 30 lib/log.source.sh + 63 lib/md.source.sh + 834 total
This way, the script could grow far beyond 1000 lines of code and still be maintainable. With more features, execution speed may slowly become a problem, though. I already notice that Gemtexter doesn't produce results instantly but requires few seconds of runtime already. That's not a problem yet, though.
@@ -160,9 +165,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -gemtext='=> http://example.org Description of the link' -assert::equals "$(generate::make_link html "$gemtext")" \ - '<a class="textlink" href="http://example.org">Description of the link</a><br />' +gemtext='=> http://example.org Description of the link' +assert::equals "$(generate::make_link html "$gemtext")" \ + '<a class="textlink" href="http://example.org">Description of the link</a><br />'
Markdown unit test example
@@ -171,9 +176,9 @@ assert::equals "$(generate::make_link html "$gemtex by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -gemtext='=> http://example.org Description of the link' -assert::equals "$(generate::make_link md "$gemtext")" \ - '[Description of the link](http://example.org) ' +gemtext='=> http://example.org Description of the link' +assert::equals "$(generate::make_link md "$gemtext")" \ + '[Description of the link](http://example.org) '
Handcrafted HTML styles
@@ -222,11 +227,12 @@ assert::equals "$(generate::make_link md "$gemtext<
Back to the main site
+ diff --git a/gemfeed/2021-07-04-the-well-grounded-rubyist.html b/gemfeed/2021-07-04-the-well-grounded-rubyist.html index 1c572a06..a61ac5e6 100644 --- a/gemfeed/2021-07-04-the-well-grounded-rubyist.html +++ b/gemfeed/2021-07-04-the-well-grounded-rubyist.html @@ -2,12 +2,17 @@ +The Well-Grounded Rubyist - + + + + + @@ -141,11 +146,12 @@ Hello World
Back to the main site
+ diff --git a/gemfeed/2021-08-01-on-being-pedantic-about-open-source.html b/gemfeed/2021-08-01-on-being-pedantic-about-open-source.html index fe745a70..c8b2daa1 100644 --- a/gemfeed/2021-08-01-on-being-pedantic-about-open-source.html +++ b/gemfeed/2021-08-01-on-being-pedantic-about-open-source.html @@ -2,12 +2,17 @@ +On being Pedantic about Open-Source - + + + + + @@ -151,11 +156,12 @@
Back to the main site
+ diff --git a/gemfeed/2021-09-12-keep-it-simple-and-stupid.html b/gemfeed/2021-09-12-keep-it-simple-and-stupid.html index a11eea9a..4363766f 100644 --- a/gemfeed/2021-09-12-keep-it-simple-and-stupid.html +++ b/gemfeed/2021-09-12-keep-it-simple-and-stupid.html @@ -2,12 +2,17 @@ +Keep it simple and stupid - + + + + + @@ -138,11 +143,12 @@
Back to the main site
+ diff --git a/gemfeed/2021-10-22-defensive-devops.html b/gemfeed/2021-10-22-defensive-devops.html index e9689033..f7d6fc04 100644 --- a/gemfeed/2021-10-22-defensive-devops.html +++ b/gemfeed/2021-10-22-defensive-devops.html @@ -2,12 +2,17 @@ +Defensive DevOps - + + + + + @@ -131,11 +136,12 @@
Back to the main site
+ diff --git a/gemfeed/2021-11-29-bash-golf-part-1.html b/gemfeed/2021-11-29-bash-golf-part-1.html index 22c38b78..5991843d 100644 --- a/gemfeed/2021-11-29-bash-golf-part-1.html +++ b/gemfeed/2021-11-29-bash-golf-part-1.html @@ -2,12 +2,17 @@ +Bash Golf Part 1 - + + + + + @@ -504,11 +509,12 @@ bash: line 1: 1/10.0 : syntax error: invalid arithmetic operator (error token is
Back to the main site
+ diff --git a/gemfeed/2021-12-26-how-to-stay-sane-as-a-devops-person.html b/gemfeed/2021-12-26-how-to-stay-sane-as-a-devops-person.html index 05f4bad7..e5be6a73 100644 --- a/gemfeed/2021-12-26-how-to-stay-sane-as-a-devops-person.html +++ b/gemfeed/2021-12-26-how-to-stay-sane-as-a-devops-person.html @@ -2,12 +2,17 @@ +How to stay sane as a DevOps person - + + + + + @@ -160,11 +165,12 @@
Back to the main site
+ diff --git a/gemfeed/2022-01-01-bash-golf-part-2.html b/gemfeed/2022-01-01-bash-golf-part-2.html index b8c77be3..43dca349 100644 --- a/gemfeed/2022-01-01-bash-golf-part-2.html +++ b/gemfeed/2022-01-01-bash-golf-part-2.html @@ -2,12 +2,17 @@ +Bash Golf Part 2 - + + + + + @@ -523,11 +528,12 @@ PAUL:X:1000:1000:PAUL BUETOW:/HOME/PAUL:/BIN/BASH
Back to the main site
+ diff --git a/gemfeed/2022-01-23-welcome-to-the-foo.zone.html b/gemfeed/2022-01-23-welcome-to-the-foo.zone.html index 3bc42ed3..1cb15a4f 100644 --- a/gemfeed/2022-01-23-welcome-to-the-foo.zone.html +++ b/gemfeed/2022-01-23-welcome-to-the-foo.zone.html @@ -2,12 +2,17 @@ +Welcome to the foo.zone - + + + + + @@ -62,11 +67,12 @@
Back to the main site
+ diff --git a/gemfeed/2022-02-04-computer-operating-systems-i-use.html b/gemfeed/2022-02-04-computer-operating-systems-i-use.html index 666658e7..1bb1455b 100644 --- a/gemfeed/2022-02-04-computer-operating-systems-i-use.html +++ b/gemfeed/2022-02-04-computer-operating-systems-i-use.html @@ -2,12 +2,17 @@ +Computer operating systems I use(d) - + + + + + @@ -276,11 +281,12 @@ GNU/kFreeBSD rhea.buetow.org 8.0-RELEASE-p5 FreeBSD 8.0-RELEASE-p5 #2: Sat Nov 2
Back to the main site
+ diff --git a/gemfeed/2022-03-06-the-release-of-dtail-4.0.0.html b/gemfeed/2022-03-06-the-release-of-dtail-4.0.0.html index aadc7f23..78e92e89 100644 --- a/gemfeed/2022-03-06-the-release-of-dtail-4.0.0.html +++ b/gemfeed/2022-03-06-the-release-of-dtail-4.0.0.html @@ -2,12 +2,17 @@ +The release of DTail 4.0.0 - + + + + + @@ -336,11 +341,12 @@ exec /usr/local/bin/dtailhealth --server localhost:2222
Back to the main site
+ diff --git a/gemfeed/2022-04-10-creative-universe.html b/gemfeed/2022-04-10-creative-universe.html index d4a24614..bff0a98c 100644 --- a/gemfeed/2022-04-10-creative-universe.html +++ b/gemfeed/2022-04-10-creative-universe.html @@ -2,12 +2,17 @@ +Creative universe - + + + + + @@ -168,11 +173,12 @@ learn () {
Back to the main site
+ diff --git a/gemfeed/2022-05-27-perl-is-still-a-great-choice.html b/gemfeed/2022-05-27-perl-is-still-a-great-choice.html index a549863e..064511ec 100644 --- a/gemfeed/2022-05-27-perl-is-still-a-great-choice.html +++ b/gemfeed/2022-05-27-perl-is-still-a-great-choice.html @@ -2,12 +2,17 @@ +Perl is still a great choice - + + + + + @@ -174,11 +179,12 @@
Back to the main site
+ diff --git a/gemfeed/2022-06-15-sweating-the-small-stuff.html b/gemfeed/2022-06-15-sweating-the-small-stuff.html index cdb98bf7..73241ee4 100644 --- a/gemfeed/2022-06-15-sweating-the-small-stuff.html +++ b/gemfeed/2022-06-15-sweating-the-small-stuff.html @@ -2,12 +2,17 @@ +Sweating the small stuff - Tiny projects of mine - + + + + + @@ -367,11 +372,12 @@ v = 008 [v = p*c*(s != c ? 2 : 1)] Total logical CPUs
Back to the main site
+ diff --git a/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.html b/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.html index a97963ee..02a74a54 100644 --- a/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.html +++ b/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.html @@ -2,12 +2,17 @@ +Let's Encrypt with OpenBSD and Rex - + + + + + @@ -708,11 +713,12 @@ rex commons
Back to the main site
+ diff --git a/gemfeed/2022-08-27-gemtexter-1.1.0-lets-gemtext-again.html b/gemfeed/2022-08-27-gemtexter-1.1.0-lets-gemtext-again.html index ff1c5ec4..e12729ea 100644 --- a/gemfeed/2022-08-27-gemtexter-1.1.0-lets-gemtext-again.html +++ b/gemfeed/2022-08-27-gemtexter-1.1.0-lets-gemtext-again.html @@ -2,12 +2,17 @@ +Gemtexter 1.1.0 - Let's Gemtext again - + + + + + @@ -57,23 +62,23 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -check_dependencies () { - # At least, Bash 5 is required - local -i required_version=5 - IFS=. read -ra version <<< "$BASH_VERSION" - if [ "${version[0]}" -lt $required_version ]; then - log ERROR "ERROR, \"bash\" must be at least at major version $required_version!" - exit 2 - fi +check_dependencies () { + # At least, Bash 5 is required + local -i required_version=5 + IFS=. read -ra version <<< "$BASH_VERSION" + if [ "${version[0]}" -lt $required_version ]; then + log ERROR "ERROR, \"bash\" must be at least at major version $required_version!" + exit 2 + fi - # These must be the GNU versions of the commands - for tool in $DATE $SED $GREP; do - if ! $tool --version | grep -q GNU; then - log ERROR "ERROR, \"$tool\" command is not the GNU version, please install!" - exit 2 - fi - done -} + # These must be the GNU versions of the commands + for tool in $DATE $SED $GREP; do + if ! $tool --version | grep -q GNU; then + log ERROR "ERROR, \"$tool\" command is not the GNU version, please install!" + exit 2 + fi + done +}
Especially macOS users didn't read the README carefully enough to install GNU Grep, GNU Sed and GNU Date before using Gemtexter.
@@ -94,7 +99,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -./gemtexter --generate '.*hello.*' +./gemtexter --generate '.*hello.*'
Revamped git support
@@ -131,11 +136,12 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
+ diff --git a/gemfeed/2022-09-30-after-a-bad-nights-sleep.html b/gemfeed/2022-09-30-after-a-bad-nights-sleep.html index 8c6cb3cb..4af8753b 100644 --- a/gemfeed/2022-09-30-after-a-bad-nights-sleep.html +++ b/gemfeed/2022-09-30-after-a-bad-nights-sleep.html @@ -2,12 +2,17 @@ +After a bad night's sleep - + + + + + @@ -131,11 +136,12 @@ jgs (________\ \
Back to the main site
+ diff --git a/gemfeed/2022-10-30-installing-dtail-on-openbsd.html b/gemfeed/2022-10-30-installing-dtail-on-openbsd.html index a5aaeefe..b40e883e 100644 --- a/gemfeed/2022-10-30-installing-dtail-on-openbsd.html +++ b/gemfeed/2022-10-30-installing-dtail-on-openbsd.html @@ -2,12 +2,17 @@ +Installing DTail on OpenBSD - + + + + + @@ -378,11 +383,12 @@ REMOTE|fishfinger|100|7|fstab|093f510ec5c0f512.h /usr/local ffs rw,wxallowed,nod
Back to the main site
+ diff --git a/gemfeed/2022-11-24-i-tried-emacs-but-i-switched-back-to-neovim.html b/gemfeed/2022-11-24-i-tried-emacs-but-i-switched-back-to-neovim.html index 978f6839..870252ff 100644 --- a/gemfeed/2022-11-24-i-tried-emacs-but-i-switched-back-to-neovim.html +++ b/gemfeed/2022-11-24-i-tried-emacs-but-i-switched-back-to-neovim.html @@ -2,12 +2,17 @@ +I tried (Doom) Emacs, but I switched back to (Neo)Vim - + + + + + @@ -94,10 +99,10 @@ Art by \ \_! / __! by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -" Clipboard -vnoremap ,y !pbcopy<CR>ugv -vnoremap ,i !pbpaste<CR> -nmap ,i !wpbpaste<CR> +" Clipboard +vnoremap ,y !pbcopy<CR>ugv +vnoremap ,i !pbpaste<CR> +nmap ,i !wpbpaste<CR>
That's only a very few lines and does precisely what I want. It's quick and dirty but get's the job done! If VimScript becomes too cumbersome, I can use Lua for NeoVim scripting.
@@ -142,11 +147,12 @@ nmap ,i !wpbpaste<CR>
Back to the main site
+ diff --git a/gemfeed/2022-12-24-ultrarelearning-java-my-takeaways.html b/gemfeed/2022-12-24-ultrarelearning-java-my-takeaways.html index fdb7d474..6f3dd3de 100644 --- a/gemfeed/2022-12-24-ultrarelearning-java-my-takeaways.html +++ b/gemfeed/2022-12-24-ultrarelearning-java-my-takeaways.html @@ -2,12 +2,17 @@ +(Re)learning Java - My takeaways - + + + + + @@ -137,11 +142,12 @@
Back to the main site
+ diff --git a/gemfeed/2023-01-23-why-grapheneos-rox.html b/gemfeed/2023-01-23-why-grapheneos-rox.html index 4b326b94..22fcd6ba 100644 --- a/gemfeed/2023-01-23-why-grapheneos-rox.html +++ b/gemfeed/2023-01-23-why-grapheneos-rox.html @@ -2,12 +2,17 @@ +Why GrapheneOS rox - + + + + + @@ -167,11 +172,12 @@ Art by Joan Stark
Back to the main site
+ diff --git a/gemfeed/2023-02-26-how-to-shut-down-after-work.html b/gemfeed/2023-02-26-how-to-shut-down-after-work.html index 0b0b8854..b5c7c07c 100644 --- a/gemfeed/2023-02-26-how-to-shut-down-after-work.html +++ b/gemfeed/2023-02-26-how-to-shut-down-after-work.html @@ -2,12 +2,17 @@ +How to shut down after work - + + + + + @@ -103,11 +108,12 @@
Back to the main site
+ diff --git a/gemfeed/2023-03-16-the-pragmatic-programmer-book-notes.html b/gemfeed/2023-03-16-the-pragmatic-programmer-book-notes.html index 418ee711..051625f9 100644 --- a/gemfeed/2023-03-16-the-pragmatic-programmer-book-notes.html +++ b/gemfeed/2023-03-16-the-pragmatic-programmer-book-notes.html @@ -2,12 +2,17 @@ +'The Pragmatic Programmer' book notes - + + + + + @@ -112,11 +117,12 @@
Back to the main site
+ diff --git a/gemfeed/2023-03-25-gemtexter-2.0.0-lets-gemtext-again-2.html b/gemfeed/2023-03-25-gemtexter-2.0.0-lets-gemtext-again-2.html index 57582782..223ceeb8 100644 --- a/gemfeed/2023-03-25-gemtexter-2.0.0-lets-gemtext-again-2.html +++ b/gemfeed/2023-03-25-gemtexter-2.0.0-lets-gemtext-again-2.html @@ -2,12 +2,17 @@ +Gemtexter 2.0.0 - Let's Gemtext again² - + + + + + @@ -123,8 +128,8 @@ Blablabla... by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -declare -xr PRE_GENERATE_HOOK=./pre_generate_hook.sh -declare -xr POST_PUBLISH_HOOK=./post_publish_hook.sh +declare -xr PRE_GENERATE_HOOK=./pre_generate_hook.sh +declare -xr POST_PUBLISH_HOOK=./post_publish_hook.sh
Use of safer Bash options
@@ -141,10 +146,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% cat gemfeed/2023-02-26-title-here.gmi -# Title here +% cat gemfeed/2023-02-26-title-here.gmi +# Title here -The remaining content of the Gemtext file... +The remaining content of the Gemtext file...
Gemtexter will add a line starting with > Published at ... now. Any subsequent Atom feed generation will then use that date.
@@ -153,12 +158,12 @@ The remaining content of the Gemtext file... by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% cat gemfeed/2023-02-26-title-here.gmi -# Title here +% cat gemfeed/2023-02-26-title-here.gmi +# Title here -> Published at 2023-02-26T21:43:51+01:00 +> Published at 2023-02-26T21:43:51+01:00 -The remaining content of the Gemtext file... +The remaining content of the Gemtext file...
XMLLint support
@@ -182,11 +187,12 @@ The remaining content of the Gemtext file...
Back to the main site
+ diff --git a/gemfeed/2023-04-01-never-split-the-difference-book-notes.html b/gemfeed/2023-04-01-never-split-the-difference-book-notes.html index ffe52af6..f09a56b9 100644 --- a/gemfeed/2023-04-01-never-split-the-difference-book-notes.html +++ b/gemfeed/2023-04-01-never-split-the-difference-book-notes.html @@ -2,12 +2,17 @@ +'Never split the difference' book notes - + + + + + @@ -175,11 +180,12 @@
Back to the main site
+ diff --git a/gemfeed/2023-05-01-unveiling-guprecords:-uptime-records-with-raku.html b/gemfeed/2023-05-01-unveiling-guprecords:-uptime-records-with-raku.html index bedad8bf..a90f5edc 100644 --- a/gemfeed/2023-05-01-unveiling-guprecords:-uptime-records-with-raku.html +++ b/gemfeed/2023-05-01-unveiling-guprecords:-uptime-records-with-raku.html @@ -2,12 +2,17 @@ +Unveiling `guprecords.raku`: Global Uptime Records with Raku - + + + + + @@ -81,7 +86,7 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -$ raku guprecords.raku --stats=dir=$HOME/git/uprecords/stats --all +$ raku guprecords.raku --stats=dir=$HOME/git/uprecords/stats --all
This command will generate a comprehensive uptime report from the collected statistics, making it easy to review and enjoy the data.
@@ -193,11 +198,12 @@ no1 in 455 days, 18:52:44 | at Sun Jul 21 07:37:51 2024
Back to the main site
+ diff --git a/gemfeed/2023-05-06-the-obstacle-is-the-way-book-notes.html b/gemfeed/2023-05-06-the-obstacle-is-the-way-book-notes.html index d6355fc1..8a2c8be2 100644 --- a/gemfeed/2023-05-06-the-obstacle-is-the-way-book-notes.html +++ b/gemfeed/2023-05-06-the-obstacle-is-the-way-book-notes.html @@ -2,12 +2,17 @@ +'The Obstacle is the Way' book notes - + + + + + @@ -128,11 +133,12 @@
Back to the main site
+ diff --git a/gemfeed/2023-06-01-kiss-server-monitoring-with-gogios.html b/gemfeed/2023-06-01-kiss-server-monitoring-with-gogios.html index b4b61489..2a7d7603 100644 --- a/gemfeed/2023-06-01-kiss-server-monitoring-with-gogios.html +++ b/gemfeed/2023-06-01-kiss-server-monitoring-with-gogios.html @@ -2,12 +2,17 @@ +KISS server monitoring with Gogios - + + + + + @@ -117,11 +122,11 @@ Have a nice day! by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -git clone https://codeberg.org/snonux/gogios.git -cd gogios -go build -o gogios cmd/gogios/main.go -doas cp gogios /usr/local/bin/gogios -doas chmod 755 /usr/local/bin/gogios +git clone https://codeberg.org/snonux/gogios.git +cd gogios +go build -o gogios cmd/gogios/main.go +doas cp gogios /usr/local/bin/gogios +doas chmod 755 /usr/local/bin/gogios
You can use cross-compilation if you want to compile Gogios for OpenBSD on a Linux system without installing the Go compiler on OpenBSD. Follow these steps:
@@ -130,9 +135,9 @@ doas chmod 755 /usr/local/bin/gogios by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -export GOOS=openbsd -export GOARCH=amd64 -go build -o gogios cmd/gogios/main.go +export GOOS=openbsd +export GOARCH=amd64 +go build -o gogios cmd/gogios/main.go
On your OpenBSD system, copy the binary to /usr/local/bin/gogios and set the correct permissions as described in the previous section. All steps described here you could automate with your configuration management system of choice. I use Rexify, the friendly configuration management system, to automate the installation, but that is out of the scope of this document.
@@ -147,11 +152,11 @@ go build -o gogios cmd/gogios/main.go by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -doas adduser -group _gogios -batch _gogios -doas usermod -d /var/run/gogios _gogios -doas mkdir -p /var/run/gogios -doas chown _gogios:_gogios /var/run/gogios -doas chmod 750 /var/run/gogios +doas adduser -group _gogios -batch _gogios +doas usermod -d /var/run/gogios _gogios +doas mkdir -p /var/run/gogios +doas chown _gogios:_gogios /var/run/gogios +doas chmod 750 /var/run/gogios
Please note that creating a user and group might differ depending on your operating system. For other operating systems, consult their documentation for creating system users and groups.
@@ -164,8 +169,8 @@ doas chmod 750 /var/run/gogios by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -doas pkg_add monitoring-plugins -doas pkg_add nrpe # If you want to execute checks remotely via NRPE. +doas pkg_add monitoring-plugins +doas pkg_add nrpe # If you want to execute checks remotely via NRPE.
Once the installation is complete, you can find the monitoring plugins in the /usr/local/libexec/nagios directory, which then can be configured to be used in gogios.json.
@@ -192,41 +197,41 @@ echo 'This is a test email from OpenBSD.' | mail -s 'Test Email' by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -{ - "EmailTo": "paul@dev.buetow.org", - "EmailFrom": "gogios@buetow.org", - "CheckTimeoutS": 10, - "CheckConcurrency": 2, - "StateDir": "/var/run/gogios", - "Checks": { - "Check ICMP4 www.foo.zone": { - "Plugin": "/usr/local/libexec/nagios/check_ping", - "Args": [ "-H", "www.foo.zone", "-4", "-w", "50,10%", "-c", "100,15%" ], - "Retries": 3, - "RetryInterval": 10 - }, - "Check ICMP6 www.foo.zone": { - "Plugin": "/usr/local/libexec/nagios/check_ping", - "Args": [ "-H", "www.foo.zone", "-6", "-w", "50,10%", "-c", "100,15%" ], - "Retries": 3, - "RetryInterval": 10 - }, - "www.foo.zone HTTP IPv4": { - "Plugin": "/usr/local/libexec/nagios/check_http", - "Args": ["www.foo.zone", "-4"], - "DependsOn": ["Check ICMP4 www.foo.zone"] - }, - "www.foo.zone HTTP IPv6": { - "Plugin": "/usr/local/libexec/nagios/check_http", - "Args": ["www.foo.zone", "-6"], - "DependsOn": ["Check ICMP6 www.foo.zone"] - } - "Check NRPE Disk Usage foo.zone": { - "Plugin": "/usr/local/libexec/nagios/check_nrpe", - "Args": ["-H", "foo.zone", "-c", "check_disk", "-p", "5666", "-4"] - } - } -} +{ + "EmailTo": "paul@dev.buetow.org", + "EmailFrom": "gogios@buetow.org", + "CheckTimeoutS": 10, + "CheckConcurrency": 2, + "StateDir": "/var/run/gogios", + "Checks": { + "Check ICMP4 www.foo.zone": { + "Plugin": "/usr/local/libexec/nagios/check_ping", + "Args": [ "-H", "www.foo.zone", "-4", "-w", "50,10%", "-c", "100,15%" ], + "Retries": 3, + "RetryInterval": 10 + }, + "Check ICMP6 www.foo.zone": { + "Plugin": "/usr/local/libexec/nagios/check_ping", + "Args": [ "-H", "www.foo.zone", "-6", "-w", "50,10%", "-c", "100,15%" ], + "Retries": 3, + "RetryInterval": 10 + }, + "www.foo.zone HTTP IPv4": { + "Plugin": "/usr/local/libexec/nagios/check_http", + "Args": ["www.foo.zone", "-4"], + "DependsOn": ["Check ICMP4 www.foo.zone"] + }, + "www.foo.zone HTTP IPv6": { + "Plugin": "/usr/local/libexec/nagios/check_http", + "Args": ["www.foo.zone", "-6"], + "DependsOn": ["Check ICMP6 www.foo.zone"] + } + "Check NRPE Disk Usage foo.zone": { + "Plugin": "/usr/local/libexec/nagios/check_nrpe", + "Args": ["-H", "foo.zone", "-c", "check_disk", "-p", "5666", "-4"] + } + } +}
doas -u _gogios /usr/local/bin/gogios -cfg /etc/gogios.json +doas -u _gogios /usr/local/bin/gogios -cfg /etc/gogios.json
To run Gogios via CRON on OpenBSD as the gogios user and check all services once per minute, follow these steps:
@@ -297,11 +302,12 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
+ diff --git a/gemfeed/2023-07-17-career-guide-and-soft-skills-book-notes.html b/gemfeed/2023-07-17-career-guide-and-soft-skills-book-notes.html index b0a6fbec..e08eee69 100644 --- a/gemfeed/2023-07-17-career-guide-and-soft-skills-book-notes.html +++ b/gemfeed/2023-07-17-career-guide-and-soft-skills-book-notes.html @@ -2,12 +2,17 @@ +'Software Developers Career Guide and Soft Skills' book notes - + + + + + @@ -360,11 +365,12 @@
Back to the main site
+ diff --git a/gemfeed/2023-07-21-gemtexter-2.1.0-lets-gemtext-again-3.html b/gemfeed/2023-07-21-gemtexter-2.1.0-lets-gemtext-again-3.html index 7bcae7ca..4cc32a19 100644 --- a/gemfeed/2023-07-21-gemtexter-2.1.0-lets-gemtext-again-3.html +++ b/gemfeed/2023-07-21-gemtexter-2.1.0-lets-gemtext-again-3.html @@ -2,12 +2,17 @@ +Gemtexter 2.1.0 - Let's Gemtext again³ - + + + + + @@ -72,9 +77,9 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -if [ -n "$foo" ]; then - echo "$foo" -fi +if [ -n "$foo" ]; then + echo "$foo" +fi
Please run source-highlight --lang-list for a list of all supported languages.
@@ -103,7 +108,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -declare -xr MASTODON_URI='https://fosstodon.org/@snonux' +declare -xr MASTODON_URI='https://fosstodon.org/@snonux'
and add the following into your index.gmi:
@@ -118,7 +123,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -<a href='https://fosstodon.org/@snonux' rel='me'>Me at Mastodon</a> +<a href='https://fosstodon.org/@snonux' rel='me'>Me at Mastodon</a>
More
@@ -138,11 +143,12 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
+ diff --git a/gemfeed/2023-08-18-site-reliability-engineering-part-1.html b/gemfeed/2023-08-18-site-reliability-engineering-part-1.html index 716dcfd5..d729fd97 100644 --- a/gemfeed/2023-08-18-site-reliability-engineering-part-1.html +++ b/gemfeed/2023-08-18-site-reliability-engineering-part-1.html @@ -2,12 +2,17 @@ +Site Reliability Engineering - Part 1: SRE and Organizational Culture - + + + + + @@ -73,11 +78,12 @@ DC on fire:
Back to the main site
+ diff --git a/gemfeed/2023-09-25-dtail-usage-examples.html b/gemfeed/2023-09-25-dtail-usage-examples.html index 6723a770..16c130d6 100644 --- a/gemfeed/2023-09-25-dtail-usage-examples.html +++ b/gemfeed/2023-09-25-dtail-usage-examples.html @@ -2,12 +2,17 @@ +DTail usage examples - + + + + + @@ -70,7 +75,7 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dtail --servers serverlist.txt --grep INFO --files "/var/log/dserver/*.log" +% dtail --servers serverlist.txt --grep INFO --files "/var/log/dserver/*.log"
Hint: you can also provide a comma separated server list, e.g.: servers server1.example.org,server2.example.org:PORT,...
@@ -83,7 +88,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dtail --servers serverlist.txt --grep INFO "/var/log/dserver/*.log" +% dtail --servers serverlist.txt --grep INFO "/var/log/dserver/*.log"
Aggregating logs
@@ -96,10 +101,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dtail --servers serverlist.txt \ - --files '/var/log/dserver/*.log' \ - --query 'from STATS select sum($goroutines),sum($cgocalls), - last($time),max(lifetimeConnections)' +% dtail --servers serverlist.txt \ + --files '/var/log/dserver/*.log' \ + --query 'from STATS select sum($goroutines),sum($cgocalls), + last($time),max(lifetimeConnections)'
Beware: For map-reduce queries to work, you have to ensure that DTail supports your log format. Check out the documentaiton of the DTail query language and the DTail log formats on the DTail homepage for more information.
@@ -112,10 +117,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dtail --servers serverlist.txt \ - --files '/var/log/dserver/*.log' \ - 'from STATS select sum($goroutines),sum($cgocalls), - last($time),max(lifetimeConnections)' +% dtail --servers serverlist.txt \ + --files '/var/log/dserver/*.log' \ + 'from STATS select sum($goroutines),sum($cgocalls), + last($time),max(lifetimeConnections)'
Here is another example:
@@ -124,10 +129,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dtail --servers serverlist.txt \ - --files '/var/log/dserver/*.log' \ - --query 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg, - lifetimeConnections group by $hostname order by max($cgocalls)' +% dtail --servers serverlist.txt \ + --files '/var/log/dserver/*.log' \ + --query 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg, + lifetimeConnections group by $hostname order by max($cgocalls)'
@@ -138,9 +143,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dtail --servers serverlist.txt \ - --files '/var/log/dserver/*.log' \ - --query 'from STATS select ... outfile append result.csv' +% dtail --servers serverlist.txt \ + --files '/var/log/dserver/*.log' \ + --query 'from STATS select ... outfile append result.csv'
How to use dcat
@@ -153,7 +158,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dcat --servers serverlist.txt --files /etc/hostname +% dcat --servers serverlist.txt --files /etc/hostname
@@ -164,7 +169,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dcat --servers serverlist.txt /etc/hostname +% dcat --servers serverlist.txt /etc/hostname
How to use dgrep
@@ -175,9 +180,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dgrep --servers server1.example.org:2223 \ - --files /etc/passwd \ - --regex nologin +% dgrep --servers server1.example.org:2223 \ + --files /etc/passwd \ + --regex nologin
Generally, dgrep is also a very useful way to search historic application logs for certain content.
@@ -194,10 +199,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dmap --servers serverlist.txt \ - --files '/var/log/dserver/*.log' \ - --query 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg, - lifetimeConnections group by $hostname order by max($cgocalls)' +% dmap --servers serverlist.txt \ + --files '/var/log/dserver/*.log' \ + --query 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg, + lifetimeConnections group by $hostname order by max($cgocalls)'
Remember: For that to work, you have to make sure that DTail supports your log format. You can either use the ones already defined in internal/mapr/logformat or add an extension to support a custom log format. The example here works out of the box though, as DTail understands its own log format already.
@@ -220,9 +225,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dmap --files /var/log/dserver/dserver.log - --query 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg, - lifetimeConnections group by $hostname order by max($cgocalls)' +% dmap --files /var/log/dserver/dserver.log + --query 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg, + lifetimeConnections group by $hostname order by max($cgocalls)'
As a shorthand version the following command can be used:
@@ -231,9 +236,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dmap 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg, - lifetimeConnections group by $hostname order by max($cgocalls)' \ - /var/log/dsever/dserver.log +% dmap 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg, + lifetimeConnections group by $hostname order by max($cgocalls)' \ + /var/log/dsever/dserver.log
You can also use a file input pipe as follows:
@@ -242,9 +247,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% cat /var/log/dserver/dserver.log | \ - dmap 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg, - lifetimeConnections group by $hostname order by max($cgocalls)' +% cat /var/log/dserver/dserver.log | \ + dmap 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg, + lifetimeConnections group by $hostname order by max($cgocalls)'
Aggregating CSV files
@@ -255,16 +260,16 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% cat example.csv -name,lastname,age,profession -Michael,Jordan,40,Basketball player -Michael,Jackson,100,Singer -Albert,Einstein,200,Physician -% dmap --query 'select lastname,name where age > 40 logformat csv outfile result.csv' example.csv -% cat result.csv -lastname,name -Jackson,Michael -Einstein,Albert +% cat example.csv +name,lastname,age,profession +Michael,Jordan,40,Basketball player +Michael,Jackson,100,Singer +Albert,Einstein,200,Physician +% dmap --query 'select lastname,name where age > 40 logformat csv outfile result.csv' example.csv +% cat result.csv +lastname,name +Jackson,Michael +Einstein,Albert
DMap can also be used to query and aggregate CSV files from remote servers.
@@ -277,44 +282,44 @@ Einstein,Albert by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% dtail /var/log/dserver/dserver.log +% dtail /var/log/dserver/dserver.log
-% dtail --logLevel trace /var/log/dserver/dserver.log +% dtail --logLevel trace /var/log/dserver/dserver.log
-% dcat /etc/passwd +% dcat /etc/passwd
-% dcat --plain /etc/passwd > /etc/test -# Should show no differences. -diff /etc/test /etc/passwd +% dcat --plain /etc/passwd > /etc/test +# Should show no differences. +diff /etc/test /etc/passwd
-% dgrep --regex ERROR --files /var/log/dserver/dsever.log +% dgrep --regex ERROR --files /var/log/dserver/dsever.log
-% dgrep --before 10 --after 10 --max 10 --grep ERROR /var/log/dserver/dsever.log +% dgrep --before 10 --after 10 --max 10 --grep ERROR /var/log/dserver/dsever.log
Use --help for more available options. Or go to the DTail page for more information! Hope you find DTail useful!
@@ -334,11 +339,12 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
+ diff --git a/gemfeed/2023-10-29-kiss-static-web-photo-albums-with-photoalbum.sh.html b/gemfeed/2023-10-29-kiss-static-web-photo-albums-with-photoalbum.sh.html index b67d90e4..1faf9285 100644 --- a/gemfeed/2023-10-29-kiss-static-web-photo-albums-with-photoalbum.sh.html +++ b/gemfeed/2023-10-29-kiss-static-web-photo-albums-with-photoalbum.sh.html @@ -2,12 +2,17 @@ +KISS static web photo albums with `photoalbum.sh` - + + + + + @@ -130,42 +135,42 @@ photoalbum makemake by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% photoalbum makemake -You may now customize ./photoalbumrc and run make +% photoalbum makemake +You may now customize ./photoalbumrc and run make -% cat Makefile -all: - photoalbum generate photoalbumrc -clean: - photoalbum clean photoalbumrc +% cat Makefile +all: + photoalbum generate photoalbumrc +clean: + photoalbum clean photoalbumrc -% cat photoalbumrc -# The title of the photoalbum -TITLE='A simple Photoalbum' +% cat photoalbumrc +# The title of the photoalbum +TITLE='A simple Photoalbum' -# Thumbnail height geometry -THUMBHEIGHT=300 -# Normal geometry height (when viewing photo). Uncomment, to keep original size. -HEIGHT=1200 -# Max previews per page. -MAXPREVIEWS=40 -# Randomly shuffle all previews. -# SHUFFLE=yes +# Thumbnail height geometry +THUMBHEIGHT=300 +# Normal geometry height (when viewing photo). Uncomment, to keep original size. +HEIGHT=1200 +# Max previews per page. +MAXPREVIEWS=40 +# Randomly shuffle all previews. +# SHUFFLE=yes -# Diverse directories, need to be full paths, not relative! -INCOMING_DIR=$(pwd)/incoming -DIST_DIR=$(pwd)/dist -TEMPLATE_DIR=/usr/share/photoalbum/templates/default -#TEMPLATE_DIR=/usr/share/photoalbum/templates/minimal +# Diverse directories, need to be full paths, not relative! +INCOMING_DIR=$(pwd)/incoming +DIST_DIR=$(pwd)/dist +TEMPLATE_DIR=/usr/share/photoalbum/templates/default +#TEMPLATE_DIR=/usr/share/photoalbum/templates/minimal -# Includes a .tar of the incoming dir in the dist, can be yes or no -TARBALL_INCLUDE=yes -TARBALL_SUFFIX=.tar -TAR_OPTS='-c' +# Includes a .tar of the incoming dir in the dist, can be yes or no +TARBALL_INCLUDE=yes +TARBALL_SUFFIX=.tar +TAR_OPTS='-c' -# Some debugging options -#set -e -#set -x +# Some debugging options +#set -e +#set -x
In the case for irregular.ninja, I changed the defaults to the following:
@@ -174,38 +179,38 @@ TAR_OPTS='-c' by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> ---- photoalbumrc 2023-10-29 21:42:00.894202045 +0200 -+++ photoalbumrc.new 2023-06-04 10:40:08.030994440 +0300 -@@ -1,23 +1,24 @@ - # The title of the photoalbum --TITLE='A simple Photoalbum' -+TITLE='Irregular.Ninja' +--- photoalbumrc 2023-10-29 21:42:00.894202045 +0200 ++++ photoalbumrc.new 2023-06-04 10:40:08.030994440 +0300 +@@ -1,23 +1,24 @@ + # The title of the photoalbum +-TITLE='A simple Photoalbum' ++TITLE='Irregular.Ninja' - # Thumbnail height geometry --THUMBHEIGHT=300 -+THUMBHEIGHT=400 - # Normal geometry height (when viewing photo). Uncomment, to keep original size. --HEIGHT=1200 -+HEIGHT=1800 - # Max previews per page. - MAXPREVIEWS=40 --# Randomly shuffle all previews. --# SHUFFLE=yes -+# Randomly shuffle -+SHUFFLE=yes + # Thumbnail height geometry +-THUMBHEIGHT=300 ++THUMBHEIGHT=400 + # Normal geometry height (when viewing photo). Uncomment, to keep original size. +-HEIGHT=1200 ++HEIGHT=1800 + # Max previews per page. + MAXPREVIEWS=40 +-# Randomly shuffle all previews. +-# SHUFFLE=yes ++# Randomly shuffle ++SHUFFLE=yes - # Diverse directories, need to be full paths, not relative! --INCOMING_DIR=$(pwd)/incoming -+INCOMING_DIR=~/Nextcloud/Photos/irregular.ninja - DIST_DIR=$(pwd)/dist - TEMPLATE_DIR=/usr/share/photoalbum/templates/default - #TEMPLATE_DIR=/usr/share/photoalbum/templates/minimal + # Diverse directories, need to be full paths, not relative! +-INCOMING_DIR=$(pwd)/incoming ++INCOMING_DIR=~/Nextcloud/Photos/irregular.ninja + DIST_DIR=$(pwd)/dist + TEMPLATE_DIR=/usr/share/photoalbum/templates/default + #TEMPLATE_DIR=/usr/share/photoalbum/templates/minimal - # Includes a .tar of the incoming dir in the dist, can be yes or no --TARBALL_INCLUDE=yes -+TARBALL_INCLUDE=no - TARBALL_SUFFIX=.tar - TAR_OPTS='-c' + # Includes a .tar of the incoming dir in the dist, can be yes or no +-TARBALL_INCLUDE=yes ++TARBALL_INCLUDE=no + TARBALL_SUFFIX=.tar + TAR_OPTS='-c'
So I changed the album title, adjusted some image and thumbnail dimensions, and I want all images to be randomly shuffled every time the album is generated! I also have all my photos in my Nextcloud Photo directory and don't want to copy them to the local incoming directory. Also, a tarball containing the whole album as a download isn't provided.
@@ -306,11 +311,12 @@ blurs html index.html photos thumbs
Back to the main site
+ diff --git a/gemfeed/2023-11-11-mind-management-book-notes.html b/gemfeed/2023-11-11-mind-management-book-notes.html index 19d30776..f90dbff7 100644 --- a/gemfeed/2023-11-11-mind-management-book-notes.html +++ b/gemfeed/2023-11-11-mind-management-book-notes.html @@ -2,12 +2,17 @@ +'Mind Management' book notes - + + + + + @@ -145,11 +150,12 @@
Back to the main site
+ diff --git a/gemfeed/2023-11-19-site-reliability-engineering-part-2.html b/gemfeed/2023-11-19-site-reliability-engineering-part-2.html index 6cfd8a2c..8689808a 100644 --- a/gemfeed/2023-11-19-site-reliability-engineering-part-2.html +++ b/gemfeed/2023-11-19-site-reliability-engineering-part-2.html @@ -2,12 +2,17 @@ +Site Reliability Engineering - Part 2: Operational Balance - + + + + + @@ -63,11 +68,12 @@
Back to the main site
+ diff --git a/gemfeed/2023-12-10-bash-golf-part-3.html b/gemfeed/2023-12-10-bash-golf-part-3.html index 0b848821..538b7a9e 100644 --- a/gemfeed/2023-12-10-bash-golf-part-3.html +++ b/gemfeed/2023-12-10-bash-golf-part-3.html @@ -2,12 +2,17 @@ +Bash Golf Part 3 - + + + + + @@ -56,24 +61,24 @@ jgs^^^^^^^`^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/env bash +#!/usr/bin/env bash -log () { - local -r level="$1"; shift - local -r message="$1"; shift - local -i pid="$$" +log () { + local -r level="$1"; shift + local -r message="$1"; shift + local -i pid="$$" - local -r callee=${FUNCNAME[1]} - local -r stamp=$(date +%Y%m%d-%H%M%S) + local -r callee=${FUNCNAME[1]} + local -r stamp=$(date +%Y%m%d-%H%M%S) - echo "$level|$stamp|$pid|$callee|$message" >&2 -} + echo "$level|$stamp|$pid|$callee|$message" >&2 +} -at_home_friday_evening () { - log INFO 'One Peperoni Pizza, please' -} +at_home_friday_evening () { + log INFO 'One Peperoni Pizza, please' +} -at_home_friday_evening +at_home_friday_evening
The output is as follows:
@@ -82,8 +87,8 @@ at_home_friday_evening by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -❯ ./logexample.sh -INFO|20231210-082732|123002|at_home_friday_evening|One Peperoni Pizza, please +❯ ./logexample.sh +INFO|20231210-082732|123002|at_home_friday_evening|One Peperoni Pizza, please
:(){ :|:& };:
@@ -117,18 +122,18 @@ INFO|20231210-082732|< by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/env bash - -outer() { - inner() { - echo 'Intel inside!' - } - inner -} - -inner -outer -inner +#!/usr/bin/env bash + +outer() { + inner() { + echo 'Intel inside!' + } + inner +} + +inner +outer +inner
And let's execute it:
@@ -146,26 +151,26 @@ Intel inside! by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/env bash - -outer1() { - inner() { - echo 'Intel inside!' - } - inner -} - -outer2() { - inner() { - echo 'Wintel inside!' - } - inner -} - -outer1 -inner -outer2 -inner +#!/usr/bin/env bash + +outer1() { + inner() { + echo 'Intel inside!' + } + inner +} + +outer2() { + inner() { + echo 'Wintel inside!' + } + inner +} + +outer1 +inner +outer2 +inner
And let's run it:
@@ -186,14 +191,14 @@ Wintel inside! by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/env bash +#!/usr/bin/env bash -some_expensive_operations() { - echo "Doing expensive operations with '$1' from pid $$" -} +some_expensive_operations() { + echo "Doing expensive operations with '$1' from pid $$" +} -for i in {0..9}; do echo $i; done \ - | xargs -P10 -I{} bash -c 'some_expensive_operations "{}"' +for i in {0..9}; do echo $i; done \ + | xargs -P10 -I{} bash -c 'some_expensive_operations "{}"'
We try here to run ten parallel processes; each of them should run the some_expensive_operations function with a different argument. The arguments are provided to xargs through STDIN one per line. When executed, we get this:
@@ -218,15 +223,15 @@ bash: line 1: some_expensive_operations: command not found by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/env bash +#!/usr/bin/env bash -some_expensive_operations() { - echo "Doing expensive operations with '$1' from pid $$" -} -export -f some_expensive_operations +some_expensive_operations() { + echo "Doing expensive operations with '$1' from pid $$" +} +export -f some_expensive_operations -for i in {0..9}; do echo $i; done \ - | xargs -P10 -I{} bash -c 'some_expensive_operations "{}"' +for i in {0..9}; do echo $i; done \ + | xargs -P10 -I{} bash -c 'some_expensive_operations "{}"'
When we run this now, we get:
@@ -251,19 +256,19 @@ Doing expensive operations with '9' from pid 132840 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/env bash +#!/usr/bin/env bash -some_other_function() { - echo "$1" -} +some_other_function() { + echo "$1" +} -some_expensive_operations() { - some_other_function "Doing expensive operations with '$1' from pid $$" -} -export -f some_expensive_operations +some_expensive_operations() { + some_other_function "Doing expensive operations with '$1' from pid $$" +} +export -f some_expensive_operations -for i in {0..9}; do echo $i; done \ - | xargs -P10 -I{} bash -c 'some_expensive_operations "{}"' +for i in {0..9}; do echo $i; done \ + | xargs -P10 -I{} bash -c 'some_expensive_operations "{}"'
... because some_other_function isn't exported! You will also need to add an export -f some_other_function!
@@ -276,22 +281,22 @@ some_expensive_operations() { by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/env bash - -foo() { - local foo=bar # Declare local/dynamic variable - bar - echo "$foo" -} - -bar() { - echo "$foo" - foo=baz -} - -foo=foo # Declare global variable -foo # Call function foo -echo "$foo" +#!/usr/bin/env bash + +foo() { + local foo=bar # Declare local/dynamic variable + bar + echo "$foo" +} + +bar() { + echo "$foo" + foo=baz +} + +foo=foo # Declare global variable +foo # Call function foo +echo "$foo"
Let's pause a minute. What do you think the output would be?
@@ -316,34 +321,34 @@ foo by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/env bash +#!/usr/bin/env bash -declare -r foo=foo -declare -r bar=bar +declare -r foo=foo +declare -r bar=bar -if [ "$foo" = foo ]; then - if [ "$bar" = bar ]; then - echo ok1 - fi -fi +if [ "$foo" = foo ]; then + if [ "$bar" = bar ]; then + echo ok1 + fi +fi -if [ "$foo" = foo ] && [ "$bar" == bar ]; then - echo ok2a -fi +if [ "$foo" = foo ] && [ "$bar" == bar ]; then + echo ok2a +fi -[ "$foo" = foo ] && [ "$bar" == bar ] && echo ok2b +[ "$foo" = foo ] && [ "$bar" == bar ] && echo ok2b -if [[ "$foo" = foo && "$bar" == bar ]]; then - echo ok3a -fi +if [[ "$foo" = foo && "$bar" == bar ]]; then + echo ok3a +fi - [[ "$foo" = foo && "$bar" == bar ]] && echo ok3b + [[ "$foo" = foo && "$bar" == bar ]] && echo ok3b -if test "$foo" = foo && test "$bar" = bar; then - echo ok4a -fi +if test "$foo" = foo && test "$bar" = bar; then + echo ok4a +fi -test "$foo" = foo && test "$bar" = bar && echo ok4b +test "$foo" = foo && test "$bar" = bar && echo ok4b
The output we get is:
@@ -367,18 +372,18 @@ ok4b by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/env bash +#!/usr/bin/env bash -# Single line comment +# Single line comment -# These are two single line -# comments one after another +# These are two single line +# comments one after another -: <<COMMENT -This is another way a -multi line comment -could be written! -COMMENT +: <<COMMENT +This is another way a +multi line comment +could be written! +COMMENT
I will not demonstrate the execution of this script, as it won't print anything! It's obviously not the most pretty way of commenting on your code, but it could sometimes be handy!
@@ -391,11 +396,11 @@ COMMENT by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/env bash +#!/usr/bin/env bash -echo foo -echo echo baz >> $0 -echo bar +echo foo +echo echo baz >> $0 +echo bar
When it is run, it will do:
@@ -429,11 +434,12 @@ echo baz
Back to the main site
+ diff --git a/gemfeed/2024-01-09-site-reliability-engineering-part-3.html b/gemfeed/2024-01-09-site-reliability-engineering-part-3.html index 6df79f17..eca5cc8e 100644 --- a/gemfeed/2024-01-09-site-reliability-engineering-part-3.html +++ b/gemfeed/2024-01-09-site-reliability-engineering-part-3.html @@ -2,12 +2,17 @@ +Site Reliability Engineering - Part 3: On-Call Culture - + + + + + @@ -77,11 +82,12 @@
Back to the main site
+ diff --git a/gemfeed/2024-01-13-one-reason-why-i-love-openbsd.html b/gemfeed/2024-01-13-one-reason-why-i-love-openbsd.html index 0f201cc5..fefdad45 100644 --- a/gemfeed/2024-01-13-one-reason-why-i-love-openbsd.html +++ b/gemfeed/2024-01-13-one-reason-why-i-love-openbsd.html @@ -2,12 +2,17 @@ +One reason why I love OpenBSD - + + + + + @@ -39,8 +44,8 @@ SSFISHKISSFISHKISSFISHKISSFISHKIS SFIS by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -$ doas installboot sd0 # Update the bootloader (not for every upgrade required) -$ doas sysupgrade # Update all binaries (including Kernel) +$ doas installboot sd0 # Update the bootloader (not for every upgrade required) +$ doas sysupgrade # Update all binaries (including Kernel)
sysupgrade downloaded and upgraded to the next release and rebooted the system. After the reboot, I run:
@@ -51,9 +56,9 @@ $ doas sysupgrade # Update all binaries (including Kerne by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -$ doas sysmerge # Update system configuration files -$ doas pkg_add -u # Update all packages -$ doas reboot # Just in case, reboot one more time +$ doas sysmerge # Update system configuration files +$ doas pkg_add -u # Update all packages +$ doas reboot # Just in case, reboot one more time
That's it! Took me around 5 minutes in total! No issues, only these few comands, only 5 minutes! It just works! No problems, no conflicts, no tons (actually none) config file merge conflicts.
@@ -88,11 +93,12 @@ $ doas reboot # Just in case, reboot one more time Back to the main site
+ diff --git a/gemfeed/2024-02-04-from-babylon5.buetow.org-to-.cloud.html b/gemfeed/2024-02-04-from-babylon5.buetow.org-to-.cloud.html index fc4904ab..ee690ab4 100644 --- a/gemfeed/2024-02-04-from-babylon5.buetow.org-to-.cloud.html +++ b/gemfeed/2024-02-04-from-babylon5.buetow.org-to-.cloud.html @@ -2,12 +2,17 @@ +From `babylon5.buetow.org` to `*.buetow.cloud` - + + + + + @@ -197,11 +202,12 @@
Back to the main site
+ diff --git a/gemfeed/2024-03-03-a-fine-fyne-android-app-for-quickly-logging-ideas-programmed-in-golang.html b/gemfeed/2024-03-03-a-fine-fyne-android-app-for-quickly-logging-ideas-programmed-in-golang.html index 29e335bc..cd658d33 100644 --- a/gemfeed/2024-03-03-a-fine-fyne-android-app-for-quickly-logging-ideas-programmed-in-golang.html +++ b/gemfeed/2024-03-03-a-fine-fyne-android-app-for-quickly-logging-ideas-programmed-in-golang.html @@ -2,12 +2,17 @@ +A fine Fyne Android app for quickly logging ideas programmed in Go - + + + + + @@ -74,11 +79,12 @@
Back to the main site
+ diff --git a/gemfeed/2024-04-01-KISS-high-availability-with-OpenBSD.html b/gemfeed/2024-04-01-KISS-high-availability-with-OpenBSD.html index ed9f17dd..0eeff480 100644 --- a/gemfeed/2024-04-01-KISS-high-availability-with-OpenBSD.html +++ b/gemfeed/2024-04-01-KISS-high-availability-with-OpenBSD.html @@ -2,12 +2,17 @@ +KISS high-availability with OpenBSD - + + + + + @@ -98,38 +103,38 @@ _____|_:_:_| (o)-(o) |_:_:_|--'`-. ,--. ksh under-water (((\'/ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/bin/ksh +#!/bin/ksh -ZONES_DIR=/var/nsd/zones/master/ -DEFAULT_MASTER=fishfinger.buetow.org -DEFAULT_STANDBY=blowfish.buetow.org +ZONES_DIR=/var/nsd/zones/master/ +DEFAULT_MASTER=fishfinger.buetow.org +DEFAULT_STANDBY=blowfish.buetow.org -determine_master_and_standby () { - local master=$DEFAULT_MASTER - local standby=$DEFAULT_STANDBY +determine_master_and_standby () { + local master=$DEFAULT_MASTER + local standby=$DEFAULT_STANDBY - . - . - . - - local -i health_ok=1 - if ! ftp -4 -o - https://$master/index.txt | grep -q "Welcome to $master"; then - echo "https://$master/index.txt IPv4 health check failed" - health_ok=0 - elif ! ftp -6 -o - https://$master/index.txt | grep -q "Welcome to $master"; then - echo "https://$master/index.txt IPv6 health check failed" - health_ok=0 - fi - if [ $health_ok -eq 0 ]; then - local tmp=$master - master=$standby - standby=$tmp - fi + . + . + . + + local -i health_ok=1 + if ! ftp -4 -o - https://$master/index.txt | grep -q "Welcome to $master"; then + echo "https://$master/index.txt IPv4 health check failed" + health_ok=0 + elif ! ftp -6 -o - https://$master/index.txt | grep -q "Welcome to $master"; then + echo "https://$master/index.txt IPv6 health check failed" + health_ok=0 + fi + if [ $health_ok -eq 0 ]; then + local tmp=$master + master=$standby + standby=$tmp + fi - . - . - . -} + . + . + . +}
The failover scripts looks for the ; Enable failover string in the DNS zone files and swaps the A and AAAA records of the DNS entries accordingly:
@@ -138,42 +143,42 @@ determine_master_and_standby () { by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -fishfinger$ grep failover /var/nsd/zones/master/foo.zone.zone - 300 IN A 46.23.94.99 ; Enable failover - 300 IN AAAA 2a03:6000:6f67:624::99 ; Enable failover -www 300 IN A 46.23.94.99 ; Enable failover -www 300 IN AAAA 2a03:6000:6f67:624::99 ; Enable failover -standby 300 IN A 23.88.35.144 ; Enable failover -standby 300 IN AAAA 2a01:4f8:c17:20f1::42 ; Enable failover +fishfinger$ grep failover /var/nsd/zones/master/foo.zone.zone + 300 IN A 46.23.94.99 ; Enable failover + 300 IN AAAA 2a03:6000:6f67:624::99 ; Enable failover +www 300 IN A 46.23.94.99 ; Enable failover +www 300 IN AAAA 2a03:6000:6f67:624::99 ; Enable failover +standby 300 IN A 23.88.35.144 ; Enable failover +standby 300 IN AAAA 2a01:4f8:c17:20f1::42 ; Enable failover
-transform () { - sed -E ' - /IN A .*; Enable failover/ { - /^standby/! { - s/^(.*) 300 IN A (.*) ; (.*)/\1 300 IN A '$(cat /var/nsd/run/master_a)' ; \3/; - } - /^standby/ { - s/^(.*) 300 IN A (.*) ; (.*)/\1 300 IN A '$(cat /var/nsd/run/standby_a)' ; \3/; - } - } - /IN AAAA .*; Enable failover/ { - /^standby/! { - s/^(.*) 300 IN AAAA (.*) ; (.*)/\1 300 IN AAAA '$(cat /var/nsd/run/master_aaaa)' ; \3/; - } - /^standby/ { - s/^(.*) 300 IN AAAA (.*) ; (.*)/\1 300 IN AAAA '$(cat /var/nsd/run/standby_aaaa)' ; \3/; - } - } - / ; serial/ { - s/^( +) ([0-9]+) .*; (.*)/\1 '$(date +%s)' ; \3/; - } - ' -} +transform () { + sed -E ' + /IN A .*; Enable failover/ { + /^standby/! { + s/^(.*) 300 IN A (.*) ; (.*)/\1 300 IN A '$(cat /var/nsd/run/master_a)' ; \3/; + } + /^standby/ { + s/^(.*) 300 IN A (.*) ; (.*)/\1 300 IN A '$(cat /var/nsd/run/standby_a)' ; \3/; + } + } + /IN AAAA .*; Enable failover/ { + /^standby/! { + s/^(.*) 300 IN AAAA (.*) ; (.*)/\1 300 IN AAAA '$(cat /var/nsd/run/master_aaaa)' ; \3/; + } + /^standby/ { + s/^(.*) 300 IN AAAA (.*) ; (.*)/\1 300 IN AAAA '$(cat /var/nsd/run/standby_aaaa)' ; \3/; + } + } + / ; serial/ { + s/^( +) ([0-9]+) .*; (.*)/\1 '$(date +%s)' ; \3/; + } + ' +}
After the failover, the script reloads nsd and performs a sanity check to see if DNS still works. If not, a rollback will be performed:
@@ -182,48 +187,48 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#! Race condition !# - -if [ -f $zone_file.bak ]; then - mv $zone_file.bak $zone_file -fi +#! Race condition !# + +if [ -f $zone_file.bak ]; then + mv $zone_file.bak $zone_file +fi -cat $zone_file | transform > $zone_file.new.tmp +cat $zone_file | transform > $zone_file.new.tmp -grep -v ' ; serial' $zone_file.new.tmp > $zone_file.new.noserial.tmp -grep -v ' ; serial' $zone_file > $zone_file.old.noserial.tmp +grep -v ' ; serial' $zone_file.new.tmp > $zone_file.new.noserial.tmp +grep -v ' ; serial' $zone_file > $zone_file.old.noserial.tmp -echo "Has zone $zone_file changed?" -if diff -u $zone_file.old.noserial.tmp $zone_file.new.noserial.tmp; then - echo "The zone $zone_file hasn't changed" - rm $zone_file.*.tmp - return 0 -fi +echo "Has zone $zone_file changed?" +if diff -u $zone_file.old.noserial.tmp $zone_file.new.noserial.tmp; then + echo "The zone $zone_file hasn't changed" + rm $zone_file.*.tmp + return 0 +fi -cp $zone_file $zone_file.bak -mv $zone_file.new.tmp $zone_file -rm $zone_file.*.tmp -echo "Reloading nsd" -nsd-control reload +cp $zone_file $zone_file.bak +mv $zone_file.new.tmp $zone_file +rm $zone_file.*.tmp +echo "Reloading nsd" +nsd-control reload -if ! zone_is_ok $zone; then - echo "Rolling back $zone_file changes" - cp $zone_file $zone_file.invalid - mv $zone_file.bak $zone_file - echo "Reloading nsd" - nsd-control reload - zone_is_ok $zone - return 3 -fi +if ! zone_is_ok $zone; then + echo "Rolling back $zone_file changes" + cp $zone_file $zone_file.invalid + mv $zone_file.bak $zone_file + echo "Reloading nsd" + nsd-control reload + zone_is_ok $zone + return 3 +fi -for cleanup in invalid bak; do - if [ -f $zone_file.$cleanup ]; then - rm $zone_file.$cleanup - fi -done +for cleanup in invalid bak; do + if [ -f $zone_file.$cleanup ]; then + rm $zone_file.$cleanup + fi +done -echo "Failover of zone $zone to $MASTER completed" -return 1 +echo "Failover of zone $zone to $MASTER completed" +return 1
A non-zero return code (here, 3 when a rollback and 1 when a DNS failover was performed) will cause CRON to send an E-Mail with the whole script output.
@@ -280,13 +285,13 @@ echo "Failover of zone $zone to $MASTER completed" by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Weekly auto-failover for Let's Encrypt automation -local -i -r week_of_the_year=$(date +%U) -if [ $(( week_of_the_year % 2 )) -eq 0 ]; then - local tmp=$master - master=$standby - standby=$tmp -fi +# Weekly auto-failover for Let's Encrypt automation +local -i -r week_of_the_year=$(date +%U) +if [ $(( week_of_the_year % 2 )) -eq 0 ]; then + local tmp=$master + master=$standby + standby=$tmp +fi
This way, a DNS failover is performed weekly so that the ACME automation can update the Let's Encrypt certificates (for master and standby) before they expire on each VM.
@@ -349,11 +354,12 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
+ diff --git a/gemfeed/2024-05-01-slow-productivity-book-notes.html b/gemfeed/2024-05-01-slow-productivity-book-notes.html index 8ab1182a..c7641815 100644 --- a/gemfeed/2024-05-01-slow-productivity-book-notes.html +++ b/gemfeed/2024-05-01-slow-productivity-book-notes.html @@ -2,12 +2,17 @@ +'Slow Productivity' book notes - + + + + + @@ -171,11 +176,12 @@
Back to the main site
+ diff --git a/gemfeed/2024-05-03-projects-i-currently-dont-have-time-for.html b/gemfeed/2024-05-03-projects-i-currently-dont-have-time-for.html index 7889e2b9..31974467 100644 --- a/gemfeed/2024-05-03-projects-i-currently-dont-have-time-for.html +++ b/gemfeed/2024-05-03-projects-i-currently-dont-have-time-for.html @@ -2,12 +2,17 @@ +Projects I currently don't have time for - + + + + + @@ -220,12 +225,12 @@ Art by Laura Brown by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -Cluster :UK, :uk01 do - Customer.C1A1.segments.volumes.each do |volume| - puts volume.usage_stats - volume.move_off! if volume.over_subscribed? - end -end +Cluster :UK, :uk01 do + Customer.C1A1.segments.volumes.each do |volume| + puts volume.usage_stats + volume.move_off! if volume.over_subscribed? + end +end
I am abandoning this project because my workplace has stopped the annual pet project competition, and I have other more important projects to work on at the moment.
@@ -327,11 +332,12 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
+ diff --git a/gemfeed/2024-06-23-terminal-multiplexing-with-tmux.html b/gemfeed/2024-06-23-terminal-multiplexing-with-tmux.html index a64fd737..f6e99302 100644 --- a/gemfeed/2024-06-23-terminal-multiplexing-with-tmux.html +++ b/gemfeed/2024-06-23-terminal-multiplexing-with-tmux.html @@ -2,12 +2,17 @@ +Terminal multiplexing with `tmux` - Z-Shell edition - + + + + + @@ -90,13 +95,13 @@ jgs `-=========-`() by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -alias tm=tmux -alias tl='tmux list-sessions' -alias tn=tmux::new -alias ta=tmux::attach -alias tx=tmux::remote -alias ts=tmux::search -alias tssh=tmux::cluster_ssh +alias tm=tmux +alias tl='tmux list-sessions' +alias tn=tmux::new +alias ta=tmux::attach +alias tx=tmux::remote +alias ts=tmux::search +alias tssh=tmux::cluster_ssh
Note all tmux::...; those are custom shell functions doing certain things, and they aren't part of the Tmux distribution. But let's run through every alias one by one.
@@ -111,23 +116,23 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Create new session and if already exists attach to it -tmux::new () { - readonly session=$1 - local date=date - if where gdate &>/dev/null; then - date=gdate - fi +# Create new session and if already exists attach to it +tmux::new () { + readonly session=$1 + local date=date + if where gdate &>/dev/null; then + date=gdate + fi - tmux::cleanup_default - if [ -z "$session" ]; then - tmux::new T$($date +%s) - else - tmux new-session -d -s $session - tmux -2 attach-session -t $session || tmux -2 switch-client -t $session - fi -} -alias tn=tmux::new + tmux::cleanup_default + if [ -z "$session" ]; then + tmux::new T$($date +%s) + else + tmux new-session -d -s $session + tmux -2 attach-session -t $session || tmux -2 switch-client -t $session + fi +} +alias tn=tmux::new
There is a lot going on here. Let's have a detailed look at what it is doing. As a note, the function relies on GNU Date, so MacOS is looking for the gdate commands to be available. Otherwise, it will fall back to date. You need to install GNU Date for Mac, as it isn't installed by default there. As I use Fedora Linux on my personal Laptop and a MacBook for work, I have to make it work for both.
@@ -142,14 +147,14 @@ tmux::new () { by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -tmux::cleanup_default () { - local s - tmux list-sessions | grep '^T.*: ' | grep -F -v attached | - cut -d: -f1 | while read -r s; do - echo "Killing $s" - tmux kill-session -t "$s" - done -} +tmux::cleanup_default () { + local s + tmux list-sessions | grep '^T.*: ' | grep -F -v attached | + cut -d: -f1 | while read -r s; do + echo "Killing $s" + tmux kill-session -t "$s" + done +}
The cleanup function kills all open Tmux sessions that haven't been renamed properly yet—but only if they aren't attached (e.g., don't run in the foreground in any terminal). Cleaning them up automatically keeps my Tmux sessions as neat and tidy as possible.
@@ -166,16 +171,16 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -tmux::attach () { - readonly session=$1 +tmux::attach () { + readonly session=$1 - if [ -z "$session" ]; then - tmux attach-session || tmux::new - else - tmux attach-session -t $session || tmux::new $session - fi -} -alias ta=tmux::attach + if [ -z "$session" ]; then + tmux attach-session || tmux::new + else + tmux attach-session -t $session || tmux::new $session + fi +} +alias ta=tmux::attach
If no session is specified (as the argument of the function), it will try to attach to the first open session. If no Tmux server is running, it will create a new one with tmux::new. Otherwise, with a session name given as the argument, it will attach to it. If unsuccessful (e.g., the session doesn't exist), it will be created and attached to.
@@ -188,12 +193,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -tmux::remote () { - readonly server=$1 - tmux new -s $server "ssh -t $server 'tmux attach-session || tmux'" || \ - tmux attach-session -d -t $server -} -alias tr=tmux::remote +tmux::remote () { + readonly server=$1 + tmux new -s $server "ssh -t $server 'tmux attach-session || tmux'" || \ + tmux attach-session -d -t $server +} +alias tr=tmux::remote
Change of the Tmux prefix for better nesting
@@ -216,15 +221,15 @@ set-option -g prefix C-g by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -tmux::search () { - local -r session=$(tmux list-sessions | fzf | cut -d: -f1) - if [ -z "$TMUX" ]; then - tmux attach-session -t $session - else - tmux switch -t $session - fi -} -alias ts=tmux::search +tmux::search () { + local -r session=$(tmux list-sessions | fzf | cut -d: -f1) + if [ -z "$TMUX" ]; then + tmux attach-session -t $session + else + tmux switch -t $session + fi +} +alias ts=tmux::search
All it does is list all currently open sessions in fzf, where one of them can be searched and selected through fuzzy find, and then either switch (if already inside a session) to the other session or attach to the other session (if not yet in Tmux).
@@ -251,15 +256,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -tmux::cluster_ssh () { - if [ -f "$1" ]; then - tmux::tssh_from_file $1 - return - fi +tmux::cluster_ssh () { + if [ -f "$1" ]; then + tmux::tssh_from_file $1 + return + fi - tmux::tssh_from_argument $@ -} -alias tssh=tmux::cluster_ssh + tmux::tssh_from_argument $@ +} +alias tssh=tmux::cluster_ssh
This function is just a wrapper around the more complex tmux::tssh_from_file and tmux::tssh_from_argument functions, as you have learned already. Most of the magic happens there.
@@ -272,23 +277,23 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -tmux::tssh_from_argument () { - local -r session=$1; shift - local first_server=$1; shift +tmux::tssh_from_argument () { + local -r session=$1; shift + local first_server=$1; shift - tmux new-session -d -s $session "ssh -t $first_server" - if ! tmux list-session | grep "^$session:"; then - echo "Could not create session $session" - return 2 - fi + tmux new-session -d -s $session "ssh -t $first_server" + if ! tmux list-session | grep "^$session:"; then + echo "Could not create session $session" + return 2 + fi - for server in "${@[@]}"; do - tmux split-window -t $session "tmux select-layout tiled; ssh -t $server" - done + for server in "${@[@]}"; do + tmux split-window -t $session "tmux select-layout tiled; ssh -t $server" + done - tmux setw -t $session synchronize-panes on - tmux -2 attach-session -t $session | tmux -2 switch-client -t $session -} + tmux setw -t $session synchronize-panes on + tmux -2 attach-session -t $session | tmux -2 switch-client -t $session +}
It expects at least two arguments. The first argument is the session name to create for the clustered SSH session. All other arguments are server hostnames or FQDNs to which to connect. The first one is used to make the initial session. All remaining ones are added to that session with tmux split-window -t $session.... At the end, we enable synchronized panes by default, so whenever you type, the commands will be sent to every SSH connection, thus allowing the neat ClusterSSH feature to run commands on multiple servers simultaneously. Once done, we attach (or switch, if already in Tmux) to it.
@@ -308,12 +313,12 @@ bind-key P setw synchronize-panes on by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -tmux::tssh_from_file () { - local -r serverlist=$1; shift - local -r session=$(basename $serverlist | cut -d. -f1) +tmux::tssh_from_file () { + local -r serverlist=$1; shift + local -r session=$(basename $serverlist | cut -d. -f1) - tmux::tssh_from_argument $session $(awk '{ print $1} ' $serverlist | sed 's/.lan./.lan/g') -} + tmux::tssh_from_argument $session $(awk '{ print $1} ' $serverlist | sed 's/.lan./.lan/g') +}
tssh examples
@@ -444,11 +449,12 @@ bind-key r source-file ~/.config/tmux/tmux.conf \; display-message "tmux.conf re
Back to the main site
+ diff --git a/gemfeed/2024-07-05-random-weird-things.html b/gemfeed/2024-07-05-random-weird-things.html index 9645d261..b5d99539 100644 --- a/gemfeed/2024-07-05-random-weird-things.html +++ b/gemfeed/2024-07-05-random-weird-things.html @@ -2,12 +2,17 @@ +Random Weird Things - Part Ⅰ - + + + + + @@ -56,52 +61,52 @@ WHOA!! ( o.o ) by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -❯ traceroute -m 60 bad.horse -traceroute to bad.horse (162.252.205.157), 60 hops max, 60 byte packets - 1 _gateway (192.168.1.1) 5.237 ms 5.264 ms 6.009 ms - 2 77-85-0-2.ip.btc-net.bg (77.85.0.2) 8.753 ms 7.112 ms 8.336 ms - 3 212-39-69-103.ip.btc-net.bg (212.39.69.103) 9.434 ms 9.268 ms 9.986 ms - 4 * * * - 5 xe-1-2-0.mpr1.fra4.de.above.net (80.81.194.26) 39.812 ms 39.030 ms 39.772 ms - 6 * ae12.cs1.fra6.de.eth.zayo.com (64.125.26.172) 123.576 ms * - 7 * * * - 8 * * * - 9 ae10.cr1.lhr15.uk.eth.zayo.com (64.125.29.17) 119.097 ms 119.478 ms 120.767 ms -10 ae2.cr1.lhr11.uk.zip.zayo.com (64.125.24.140) 120.398 ms 121.147 ms 120.948 ms -11 * * * -12 ae25.mpr1.yyz1.ca.zip.zayo.com (64.125.23.117) 145.072 ms * 181.773 ms -13 ae5.mpr1.tor3.ca.zip.zayo.com (64.125.23.118) 168.239 ms 168.158 ms 168.137 ms -14 64.124.217.237.IDIA-265104-ZYO.zip.zayo.com (64.124.217.237) 168.026 ms 167.999 ms 165.451 ms -15 * * * -16 t00.toroc1.on.ca.sn11.net (162.252.204.2) 131.598 ms 131.308 ms 131.482 ms -17 bad.horse (162.252.205.130) 131.430 ms 145.914 ms 130.514 ms -18 bad.horse (162.252.205.131) 136.634 ms 145.295 ms 135.631 ms -19 bad.horse (162.252.205.132) 139.158 ms 148.363 ms 138.934 ms -20 bad.horse (162.252.205.133) 145.395 ms 148.054 ms 147.140 ms -21 he.rides.across.the.nation (162.252.205.134) 149.687 ms 147.731 ms 150.135 ms -22 the.thoroughbred.of.sin (162.252.205.135) 156.644 ms 155.155 ms 156.447 ms -23 he.got.the.application (162.252.205.136) 161.187 ms 162.318 ms 162.674 ms -24 that.you.just.sent.in (162.252.205.137) 166.763 ms 166.675 ms 164.243 ms -25 it.needs.evaluation (162.252.205.138) 172.073 ms 171.919 ms 171.390 ms -26 so.let.the.games.begin (162.252.205.139) 175.386 ms 174.180 ms 175.965 ms -27 a.heinous.crime (162.252.205.140) 180.857 ms 180.766 ms 180.192 ms -28 a.show.of.force (162.252.205.141) 187.942 ms 186.669 ms 186.986 ms -29 a.murder.would.be.nice.of.course (162.252.205.142) 191.349 ms 191.939 ms 190.740 ms -30 bad.horse (162.252.205.143) 195.425 ms 195.716 ms 196.186 ms -31 bad.horse (162.252.205.144) 199.238 ms 200.620 ms 200.318 ms -32 bad.horse (162.252.205.145) 207.554 ms 206.729 ms 205.201 ms -33 he-s.bad (162.252.205.146) 211.087 ms 211.649 ms 211.712 ms -34 the.evil.league.of.evil (162.252.205.147) 212.657 ms 216.777 ms 216.589 ms -35 is.watching.so.beware (162.252.205.148) 220.911 ms 220.326 ms 221.961 ms -36 the.grade.that.you.receive (162.252.205.149) 225.384 ms 225.696 ms 225.640 ms -37 will.be.your.last.we.swear (162.252.205.150) 232.312 ms 230.989 ms 230.919 ms -38 so.make.the.bad.horse.gleeful (162.252.205.151) 235.761 ms 235.291 ms 235.585 ms -39 or.he-ll.make.you.his.mare (162.252.205.152) 241.350 ms 239.407 ms 238.394 ms -40 o_o (162.252.205.153) 246.154 ms 247.650 ms 247.110 ms -41 you-re.saddled.up (162.252.205.154) 250.925 ms 250.401 ms 250.619 ms -42 there-s.no.recourse (162.252.205.155) 256.071 ms 251.154 ms 255.340 ms -43 it-s.hi-ho.silver (162.252.205.156) 260.152 ms 261.775 ms 261.544 ms -44 signed.bad.horse (162.252.205.157) 262.430 ms 261.410 ms 261.365 ms +❯ traceroute -m 60 bad.horse +traceroute to bad.horse (162.252.205.157), 60 hops max, 60 byte packets + 1 _gateway (192.168.1.1) 5.237 ms 5.264 ms 6.009 ms + 2 77-85-0-2.ip.btc-net.bg (77.85.0.2) 8.753 ms 7.112 ms 8.336 ms + 3 212-39-69-103.ip.btc-net.bg (212.39.69.103) 9.434 ms 9.268 ms 9.986 ms + 4 * * * + 5 xe-1-2-0.mpr1.fra4.de.above.net (80.81.194.26) 39.812 ms 39.030 ms 39.772 ms + 6 * ae12.cs1.fra6.de.eth.zayo.com (64.125.26.172) 123.576 ms * + 7 * * * + 8 * * * + 9 ae10.cr1.lhr15.uk.eth.zayo.com (64.125.29.17) 119.097 ms 119.478 ms 120.767 ms +10 ae2.cr1.lhr11.uk.zip.zayo.com (64.125.24.140) 120.398 ms 121.147 ms 120.948 ms +11 * * * +12 ae25.mpr1.yyz1.ca.zip.zayo.com (64.125.23.117) 145.072 ms * 181.773 ms +13 ae5.mpr1.tor3.ca.zip.zayo.com (64.125.23.118) 168.239 ms 168.158 ms 168.137 ms +14 64.124.217.237.IDIA-265104-ZYO.zip.zayo.com (64.124.217.237) 168.026 ms 167.999 ms 165.451 ms +15 * * * +16 t00.toroc1.on.ca.sn11.net (162.252.204.2) 131.598 ms 131.308 ms 131.482 ms +17 bad.horse (162.252.205.130) 131.430 ms 145.914 ms 130.514 ms +18 bad.horse (162.252.205.131) 136.634 ms 145.295 ms 135.631 ms +19 bad.horse (162.252.205.132) 139.158 ms 148.363 ms 138.934 ms +20 bad.horse (162.252.205.133) 145.395 ms 148.054 ms 147.140 ms +21 he.rides.across.the.nation (162.252.205.134) 149.687 ms 147.731 ms 150.135 ms +22 the.thoroughbred.of.sin (162.252.205.135) 156.644 ms 155.155 ms 156.447 ms +23 he.got.the.application (162.252.205.136) 161.187 ms 162.318 ms 162.674 ms +24 that.you.just.sent.in (162.252.205.137) 166.763 ms 166.675 ms 164.243 ms +25 it.needs.evaluation (162.252.205.138) 172.073 ms 171.919 ms 171.390 ms +26 so.let.the.games.begin (162.252.205.139) 175.386 ms 174.180 ms 175.965 ms +27 a.heinous.crime (162.252.205.140) 180.857 ms 180.766 ms 180.192 ms +28 a.show.of.force (162.252.205.141) 187.942 ms 186.669 ms 186.986 ms +29 a.murder.would.be.nice.of.course (162.252.205.142) 191.349 ms 191.939 ms 190.740 ms +30 bad.horse (162.252.205.143) 195.425 ms 195.716 ms 196.186 ms +31 bad.horse (162.252.205.144) 199.238 ms 200.620 ms 200.318 ms +32 bad.horse (162.252.205.145) 207.554 ms 206.729 ms 205.201 ms +33 he-s.bad (162.252.205.146) 211.087 ms 211.649 ms 211.712 ms +34 the.evil.league.of.evil (162.252.205.147) 212.657 ms 216.777 ms 216.589 ms +35 is.watching.so.beware (162.252.205.148) 220.911 ms 220.326 ms 221.961 ms +36 the.grade.that.you.receive (162.252.205.149) 225.384 ms 225.696 ms 225.640 ms +37 will.be.your.last.we.swear (162.252.205.150) 232.312 ms 230.989 ms 230.919 ms +38 so.make.the.bad.horse.gleeful (162.252.205.151) 235.761 ms 235.291 ms 235.585 ms +39 or.he-ll.make.you.his.mare (162.252.205.152) 241.350 ms 239.407 ms 238.394 ms +40 o_o (162.252.205.153) 246.154 ms 247.650 ms 247.110 ms +41 you-re.saddled.up (162.252.205.154) 250.925 ms 250.401 ms 250.619 ms +42 there-s.no.recourse (162.252.205.155) 256.071 ms 251.154 ms 255.340 ms +43 it-s.hi-ho.silver (162.252.205.156) 260.152 ms 261.775 ms 261.544 ms +44 signed.bad.horse (162.252.205.157) 262.430 ms 261.410 ms 261.365 ms
2. ASCII cinema
@@ -129,20 +134,20 @@ traceroute to bad.horse (162.252.#include <stdio.h> +#include <stdio.h> -int main(void) { - int array[5] = { 1, 2, 3, 4, 5 }; +int main(void) { + int array[5] = { 1, 2, 3, 4, 5 }; - for (int i = 0; i < 5; i++) - printf("%d\n", array[i]); + for (int i = 0; i < 5; i++) + printf("%d\n", array[i]); - for (int i = 0; i < 5; i++) - printf("%d\n", i[array]); + for (int i = 0; i < 5; i++) + printf("%d\n", i[array]); - for (int i = 0; i < 5; i++) - printf("%d\n", *(i + array)); -} + for (int i = 0; i < 5; i++) + printf("%d\n", *(i + array)); +}
5. Variables with prefix $
@@ -153,20 +158,20 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#include <stdio.h> +#include <stdio.h> -int main(void) { - int $array[5] = { 1, 2, 3, 4, 5 }; +int main(void) { + int $array[5] = { 1, 2, 3, 4, 5 }; - for (int $i = 0; $i < 5; $i++) - printf("%d\n", $array[$i]); + for (int $i = 0; $i < 5; $i++) + printf("%d\n", $array[$i]); - for (int $i = 0; $i < 5; $i++) - printf("%d\n", $i[$array]); + for (int $i = 0; $i < 5; $i++) + printf("%d\n", $i[$array]); - for (int $i = 0; $i < 5; $i++) - printf("%d\n", *($i + $array)); -} + for (int $i = 0; $i < 5; $i++) + printf("%d\n", *($i + $array)); +}
6. Object oriented shell scripts using ksh
@@ -177,40 +182,40 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/usr/bin/ksh93 - -typeset -T Point_t=( - integer -h 'x coordinate' x=0 - integer -h 'y coordinate' y=0 - typeset -h 'point color' color="red" +#!/usr/bin/ksh93 + +typeset -T Point_t=( + integer -h 'x coordinate' x=0 + integer -h 'y coordinate' y=0 + typeset -h 'point color' color="red" - function getcolor { - print -r ${_.color} - } + function getcolor { + print -r ${_.color} + } - function setcolor { - _.color=$1 - } + function setcolor { + _.color=$1 + } - setxy() { - _.x=$1; _.y=$2 - } + setxy() { + _.x=$1; _.y=$2 + } - getxy() { - print -r "(${_.x},${_.y})" - } -) - -Point_t point - -echo "Initial coordinates are (${point.x},${point.y}). Color is ${point.color}" - -point.setxy 5 6 -point.setcolor blue - -echo "New coordinates are ${point.getxy}. Color is ${point.getcolor}" - -exit 0 + getxy() { + print -r "(${_.x},${_.y})" + } +) + +Point_t point + +echo "Initial coordinates are (${point.x},${point.y}). Color is ${point.color}" + +point.setxy 5 6 +point.setcolor blue + +echo "New coordinates are ${point.getxy}. Color is ${point.getcolor}" + +exit 0
Using types to create object oriented Korn shell 93 scripts
@@ -223,18 +228,18 @@ echo "New coordinates are ${point.getxy}. Color is ${point by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -package main +package main -import "fmt" +import "fmt" -func main() { - var i int - f := func() *int { - return &i - } - *f()++ - fmt.Println(i) -} +func main() { + var i int + f := func() *int { + return &i + } + *f()++ + fmt.Println(i) +}
Go playground
@@ -396,11 +401,12 @@ r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\
Back to the main site
+ diff --git a/gemfeed/2024-07-07-the-stoic-challenge-book-notes.html b/gemfeed/2024-07-07-the-stoic-challenge-book-notes.html index a13e9450..cb5610e6 100644 --- a/gemfeed/2024-07-07-the-stoic-challenge-book-notes.html +++ b/gemfeed/2024-07-07-the-stoic-challenge-book-notes.html @@ -2,12 +2,17 @@ +'The Stoic Challenge' book notes - + + + + + @@ -87,11 +92,12 @@
Back to the main site
+ diff --git a/gemfeed/2024-08-05-typing-127.1-words-per-minute.html b/gemfeed/2024-08-05-typing-127.1-words-per-minute.html index 669d37a4..bcd7a1b0 100644 --- a/gemfeed/2024-08-05-typing-127.1-words-per-minute.html +++ b/gemfeed/2024-08-05-typing-127.1-words-per-minute.html @@ -2,12 +2,17 @@ +Typing `127.1` words per minute (`>100wpm average`) - + + + + + @@ -248,11 +253,12 @@
Back to the main site
+ diff --git a/gemfeed/2024-09-07-projects-i-support.html b/gemfeed/2024-09-07-projects-i-support.html index 01cf18b0..33247351 100644 --- a/gemfeed/2024-09-07-projects-i-support.html +++ b/gemfeed/2024-09-07-projects-i-support.html @@ -2,12 +2,17 @@ +Projects I financially support - + + + + + @@ -119,11 +124,12 @@
Back to the main site
+ diff --git a/gemfeed/2024-09-07-site-reliability-engineering-part-4.html b/gemfeed/2024-09-07-site-reliability-engineering-part-4.html index a3980380..e3ce4bb7 100644 --- a/gemfeed/2024-09-07-site-reliability-engineering-part-4.html +++ b/gemfeed/2024-09-07-site-reliability-engineering-part-4.html @@ -2,12 +2,17 @@ +Site Reliability Engineering - Part 4: Onboarding for On-Call Engineers - + + + + + @@ -92,11 +97,12 @@ jgs \\`_..---.Y.---.._`//
Back to the main site
+ diff --git a/gemfeed/2024-10-02-gemtexter-3.0.0-lets-gemtext-again-4.html b/gemfeed/2024-10-02-gemtexter-3.0.0-lets-gemtext-again-4.html index 6056850c..c59a65e2 100644 --- a/gemfeed/2024-10-02-gemtexter-3.0.0-lets-gemtext-again-4.html +++ b/gemfeed/2024-10-02-gemtexter-3.0.0-lets-gemtext-again-4.html @@ -2,12 +2,17 @@ +Gemtexter 3.0.0 - Let's Gemtext again⁴ - + + + + + @@ -76,7 +81,7 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -declare -xr HTML_THEME_DIR=./extras/html/themes/simple +declare -xr HTML_THEME_DIR=./extras/html/themes/simple
To customize the theme or create your own, simply copy the theme directory and modify it as needed. This makes it also much easier to switch between layouts.
@@ -102,11 +107,12 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
+ diff --git a/gemfeed/2024-10-24-staff-engineer-book-notes.html b/gemfeed/2024-10-24-staff-engineer-book-notes.html index 94037125..c952e5bf 100644 --- a/gemfeed/2024-10-24-staff-engineer-book-notes.html +++ b/gemfeed/2024-10-24-staff-engineer-book-notes.html @@ -2,12 +2,17 @@ +'Staff Engineer' book notes - + + + + + @@ -152,11 +157,12 @@
Back to the main site
+ diff --git a/gemfeed/2024-11-17-f3s-kubernetes-with-freebsd-part-1.html b/gemfeed/2024-11-17-f3s-kubernetes-with-freebsd-part-1.html index 9fe54ab8..999f8f3b 100644 --- a/gemfeed/2024-11-17-f3s-kubernetes-with-freebsd-part-1.html +++ b/gemfeed/2024-11-17-f3s-kubernetes-with-freebsd-part-1.html @@ -2,12 +2,17 @@ +f3s: Kubernetes with FreeBSD - Part 1: Setting the stage - + + + + + @@ -198,11 +203,12 @@
Back to the main site
+ diff --git a/gemfeed/2024-12-03-f3s-kubernetes-with-freebsd-part-2.html b/gemfeed/2024-12-03-f3s-kubernetes-with-freebsd-part-2.html index 5578fbf1..ab955a5c 100644 --- a/gemfeed/2024-12-03-f3s-kubernetes-with-freebsd-part-2.html +++ b/gemfeed/2024-12-03-f3s-kubernetes-with-freebsd-part-2.html @@ -2,12 +2,17 @@ +f3s: Kubernetes with FreeBSD - Part 2: Hardware and base installation - + + + + + @@ -132,9 +137,9 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[paul@earth]~/Downloads% sudo dd \ - if=FreeBSD-14.1-RELEASE-amd64-bootonly.iso \ - of=/dev/sda conv=sync +[paul@earth]~/Downloads% sudo dd \ + if=FreeBSD-14.1-RELEASE-amd64-bootonly.iso \ + of=/dev/sda conv=sync
Next, I plugged the Beelinks (one after another) into my monitor via HDMI (the resolution of the FreeBSD text console seems strangely stretched, as I am using the LG Dual Up monitor), connected Ethernet, an external USB keyboard, and the FreeBSD USB stick, and booted the devices up. With F7, I entered the boot menu and selected the USB stick for the FreeBSD installation.
@@ -160,9 +165,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -root@f0:~ # freebsd-update fetch -root@f0:~ # freebsd-update install -root@f0:~ # freebsd-update reboot +root@f0:~ # freebsd-update fetch +root@f0:~ # freebsd-update install +root@f0:~ # freebsd-update reboot
I also added the following entries for the three FreeBSD boxes to the /etc/hosts file:
@@ -171,11 +176,11 @@ root@f0:~ # freebsd-update reboot by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -root@f0:~ # cat <<END >>/etc/hosts -192.168.1.130 f0 f0.lan f0.lan.buetow.org -192.168.1.131 f1 f1.lan f1.lan.buetow.org -192.168.1.132 f2 f2.lan f2.lan.buetow.org -END +root@f0:~ # cat <<END >>/etc/hosts +192.168.1.130 f0 f0.lan f0.lan.buetow.org +192.168.1.131 f1 f1.lan f1.lan.buetow.org +192.168.1.132 f2 f2.lan f2.lan.buetow.org +END
You might wonder why bother using the hosts file? Why not use DNS properly? The reason is simplicity. I don't manage 100 hosts, only a few here and there. Having an OpenWRT router in my home, I could also configure everything there, but maybe I'll do that later. For now, keep it simple and straightforward.
@@ -188,7 +193,7 @@ END by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -root@f0:~ # pkg install helix doas zfs-periodic uptimed +root@f0:~ # pkg install helix doas zfs-periodic uptimed
Helix editor
@@ -205,7 +210,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -root@f0:~ # cp /usr/local/etc/doas.conf.sample /usr/local/etc/doas.conf +root@f0:~ # cp /usr/local/etc/doas.conf.sample /usr/local/etc/doas.conf
https://man.openbsd.org/doas
@@ -218,17 +223,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -root@f0:~ # cat <<END >>/etc/periodic.conf -daily_zfs_snapshot_enable="YES" -daily_zfs_snapshot_pools="zroot" -daily_zfs_snapshot_keep="7" -weekly_zfs_snapshot_enable="YES" -weekly_zfs_snapshot_pools="zroot" -weekly_zfs_snapshot_keep="5" -monthly_zfs_snapshot_enable="YES" -monthly_zfs_snapshot_pools="zroot" -monthly_zfs_snapshot_keep="6" -END +root@f0:~ # cat <<END >>/etc/periodic.conf +daily_zfs_snapshot_enable="YES" +daily_zfs_snapshot_pools="zroot" +daily_zfs_snapshot_keep="7" +weekly_zfs_snapshot_enable="YES" +weekly_zfs_snapshot_pools="zroot" +weekly_zfs_snapshot_keep="5" +monthly_zfs_snapshot_enable="YES" +monthly_zfs_snapshot_pools="zroot" +monthly_zfs_snapshot_keep="6" +END
https://github.com/ross/zfs-periodic
@@ -243,9 +248,9 @@ END by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -root@f0:~ # cp /usr/local/mimecast/etc/uptimed.conf-dist \ - /usr/local/mimecast/etc/uptimed.conf -root@f0:~ # hx /usr/local/mimecast/etc/uptimed.conf +root@f0:~ # cp /usr/local/mimecast/etc/uptimed.conf-dist \ + /usr/local/mimecast/etc/uptimed.conf +root@f0:~ # hx /usr/local/mimecast/etc/uptimed.conf
In the Helix editor session, I changed LOG_MAXIMUM_ENTRIES to 0 to keep all uptime entries forever and not cut off at 50 (the default config). After that, I enabled and started uptimed:
@@ -254,8 +259,8 @@ root@f0:~ # hx /usr/local/mimecast/etc/uptimed.conf -root@f0:~ # service uptimed enable -root@f0:~ # service uptimed start +root@f0:~ # service uptimed enable +root@f0:~ # service uptimed start
To check the current uptime stats, I can now run uprecords:
@@ -264,15 +269,15 @@ root@f0:~ # service uptimed start by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -root@f0:~ # uprecords - # Uptime | System Boot up -----------------------------+--------------------------------------------------- --> 1 0 days, 00:07:34 | FreeBSD 14.1-RELEASE Mon Dec 2 12:21:44 2024 -----------------------------+--------------------------------------------------- -NewRec 0 days, 00:07:33 | since Mon Dec 2 12:21:44 2024 - up 0 days, 00:07:34 | since Mon Dec 2 12:21:44 2024 - down 0 days, 00:00:00 | since Mon Dec 2 12:21:44 2024 - %up 100.000 | since Mon Dec 2 12:21:44 2024 +root@f0:~ # uprecords + # Uptime | System Boot up +----------------------------+--------------------------------------------------- +-> 1 0 days, 00:07:34 | FreeBSD 14.1-RELEASE Mon Dec 2 12:21:44 2024 +----------------------------+--------------------------------------------------- +NewRec 0 days, 00:07:33 | since Mon Dec 2 12:21:44 2024 + up 0 days, 00:07:34 | since Mon Dec 2 12:21:44 2024 + down 0 days, 00:00:00 | since Mon Dec 2 12:21:44 2024 + %up 100.000 | since Mon Dec 2 12:21:44 2024
This is how I track the uptimes for all of my host:
@@ -290,17 +295,17 @@ NewRec 0 days, 00: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % ifconfig re0 -re0: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500 - options=8209b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGIC,LINKSTATE> - ether e8:ff:1e:d7:1c:ac - inet 192.168.1.130 netmask 0xffffff00 broadcast 192.168.1.255 - inet6 fe80::eaff:1eff:fed7:1cac%re0 prefixlen 64 scopeid 0x1 - inet6 fd22:c702:acb7:0:eaff:1eff:fed7:1cac prefixlen 64 detached autoconf - inet6 2a01:5a8:304:1d5c:eaff:1eff:fed7:1cac prefixlen 64 autoconf pltime 10800 vltime 14400 - media: Ethernet autoselect (1000baseT <full-duplex>) - status: active - nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL> +paul@f0:~ % ifconfig re0 +re0: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500 + options=8209b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGIC,LINKSTATE> + ether e8:ff:1e:d7:1c:ac + inet 192.168.1.130 netmask 0xffffff00 broadcast 192.168.1.255 + inet6 fe80::eaff:1eff:fed7:1cac%re0 prefixlen 64 scopeid 0x1 + inet6 fd22:c702:acb7:0:eaff:1eff:fed7:1cac prefixlen 64 detached autoconf + inet6 2a01:5a8:304:1d5c:eaff:1eff:fed7:1cac prefixlen 64 autoconf pltime 10800 vltime 14400 + media: Ethernet autoselect (1000baseT <full-duplex>) + status: active + nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL>
RAM
@@ -311,8 +316,8 @@ re0: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX, by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % sysctl hw.physmem -hw.physmem: 16902905856 +paul@f0:~ % sysctl hw.physmem +hw.physmem: 16902905856
@@ -324,11 +329,11 @@ hw.physmem: 16902905856 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % sysctl dev.cpu | grep freq: -dev.cpu.3.freq: 705 -dev.cpu.2.freq: 705 -dev.cpu.1.freq: 604 -dev.cpu.0.freq: 604 +paul@f0:~ % sysctl dev.cpu | grep freq: +dev.cpu.3.freq: 705 +dev.cpu.2.freq: 705 +dev.cpu.1.freq: 604 +dev.cpu.0.freq: 604
CPU throttling
@@ -339,14 +344,14 @@ dev.cpu.0.freq: 604 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas pkg install ubench -paul@f0:~ % rehash # For tcsh to find the newly installed command -paul@f0:~ % ubench & -paul@f0:~ % sysctl dev.cpu | grep freq: -dev.cpu.3.freq: 2922 -dev.cpu.2.freq: 2922 -dev.cpu.1.freq: 2923 -dev.cpu.0.freq: 2922 +paul@f0:~ % doas pkg install ubench +paul@f0:~ % rehash # For tcsh to find the newly installed command +paul@f0:~ % ubench & +paul@f0:~ % sysctl dev.cpu | grep freq: +dev.cpu.3.freq: 2922 +dev.cpu.2.freq: 2922 +dev.cpu.1.freq: 2923 +dev.cpu.0.freq: 2922
Idle, all three Beelinks plus the switch consumed 26.2W. But with ubench stressing all the CPUs, it went up to 38.8W.
@@ -369,7 +374,7 @@ dev.cpu.0.freq: 2922 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[paul@earth]~% sudo dnf install -y wol +[paul@earth]~% sudo dnf install -y wol
Next, I created a simple script (~/bin/wol-f3s) to wake and shutdown the machines:
@@ -378,68 +383,68 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -#!/bin/bash -# Wake-on-LAN and shutdown script for f3s cluster (f0, f1, f2) +#!/bin/bash +# Wake-on-LAN and shutdown script for f3s cluster (f0, f1, f2) -# MAC addresses -F0_MAC="e8:ff:1e:d7:1c:ac" # f0 (192.168.1.130) -F1_MAC="e8:ff:1e:d7:1e:44" # f1 (192.168.1.131) -F2_MAC="e8:ff:1e:d7:1c:a0" # f2 (192.168.1.132) +# MAC addresses +F0_MAC="e8:ff:1e:d7:1c:ac" # f0 (192.168.1.130) +F1_MAC="e8:ff:1e:d7:1e:44" # f1 (192.168.1.131) +F2_MAC="e8:ff:1e:d7:1c:a0" # f2 (192.168.1.132) -# IP addresses -F0_IP="192.168.1.130" -F1_IP="192.168.1.131" -F2_IP="192.168.1.132" +# IP addresses +F0_IP="192.168.1.130" +F1_IP="192.168.1.131" +F2_IP="192.168.1.132" -# SSH user -SSH_USER="paul" +# SSH user +SSH_USER="paul" -# Broadcast address for your LAN -BROADCAST="192.168.1.255" +# Broadcast address for your LAN +BROADCAST="192.168.1.255" -wake() { - local name=$1 - local mac=$2 - echo "Sending WoL packet to $name ($mac)..." - wol -i "$BROADCAST" "$mac" -} +wake() { + local name=$1 + local mac=$2 + echo "Sending WoL packet to $name ($mac)..." + wol -i "$BROADCAST" "$mac" +} -shutdown_host() { - local name=$1 - local ip=$2 - echo "Shutting down $name ($ip)..." - ssh -o ConnectTimeout=5 "$SSH_USER@$ip" "doas poweroff" 2>/dev/null && \ - echo " ✓ Shutdown command sent to $name" || \ - echo " ✗ Failed to reach $name (already down?)" -} +shutdown_host() { + local name=$1 + local ip=$2 + echo "Shutting down $name ($ip)..." + ssh -o ConnectTimeout=5 "$SSH_USER@$ip" "doas poweroff" 2>/dev/null && \ + echo " ✓ Shutdown command sent to $name" || \ + echo " ✗ Failed to reach $name (already down?)" +} -ACTION="${1:-all}" +ACTION="${1:-all}" -case "$ACTION" in - f0) wake "f0" "$F0_MAC" ;; - f1) wake "f1" "$F1_MAC" ;; - f2) wake "f2" "$F2_MAC" ;; - all|"") - wake "f0" "$F0_MAC" - wake "f1" "$F1_MAC" - wake "f2" "$F2_MAC" - ;; - shutdown|poweroff|down) - shutdown_host "f0" "$F0_IP" - shutdown_host "f1" "$F1_IP" - shutdown_host "f2" "$F2_IP" - echo "" - echo "✓ Shutdown commands sent to all machines." - exit 0 - ;; - *) - echo "Usage: $0 [f0|f1|f2|all|shutdown]" - exit 1 - ;; -esac +case "$ACTION" in + f0) wake "f0" "$F0_MAC" ;; + f1) wake "f1" "$F1_MAC" ;; + f2) wake "f2" "$F2_MAC" ;; + all|"") + wake "f0" "$F0_MAC" + wake "f1" "$F1_MAC" + wake "f2" "$F2_MAC" + ;; + shutdown|poweroff|down) + shutdown_host "f0" "$F0_IP" + shutdown_host "f1" "$F1_IP" + shutdown_host "f2" "$F2_IP" + echo "" + echo "✓ Shutdown commands sent to all machines." + exit 0 + ;; + *) + echo "Usage: $0 [f0|f1|f2|all|shutdown]" + exit 1 + ;; +esac -echo "" -echo "✓ WoL packets sent. Machines should boot in a few seconds." +echo "" +echo "✓ WoL packets sent. Machines should boot in a few seconds."
After making the script executable with chmod +x ~/bin/wol-f3s, I can now control the machines with simple commands:
@@ -448,9 +453,9 @@ echo "✓ WoL packets sent. Machines should boot in a few by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[paul@earth]~% wol-f3s # Wake all three -[paul@earth]~% wol-f3s f0 # Wake only f0 -[paul@earth]~% wol-f3s shutdown # Shutdown all three via SSH +[paul@earth]~% wol-f3s # Wake all three +[paul@earth]~% wol-f3s f0 # Wake only f0 +[paul@earth]~% wol-f3s shutdown # Shutdown all three via SSH
Testing WoL and Shutdown
@@ -461,15 +466,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[paul@earth]~% wol-f3s shutdown -Shutting down f0 (192.168.1.130)... - ✓ Shutdown command sent to f0 -Shutting down f1 (192.168.1.131)... - ✓ Shutdown command sent to f1 -Shutting down f2 (192.168.1.132)... - ✓ Shutdown command sent to f2 +[paul@earth]~% wol-f3s shutdown +Shutting down f0 (192.168.1.130)... + ✓ Shutdown command sent to f0 +Shutting down f1 (192.168.1.131)... + ✓ Shutdown command sent to f1 +Shutting down f2 (192.168.1.132)... + ✓ Shutdown command sent to f2 -✓ Shutdown commands sent to all machines. +✓ Shutdown commands sent to all machines.
After waiting for them to fully power down (about 1 minute), I sent the WoL magic packets:
@@ -478,15 +483,15 @@ Shutting down f2 (192.168.1.1 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[paul@earth]~% wol-f3s -Sending WoL packet to f0 (e8:ff:1e:d7:1c:ac)... -Waking up e8:ff:1e:d7:1c:ac... -Sending WoL packet to f1 (e8:ff:1e:d7:1e:44)... -Waking up e8:ff:1e:d7:1e:44... -Sending WoL packet to f2 (e8:ff:1e:d7:1c:a0)... -Waking up e8:ff:1e:d7:1c:a0... +[paul@earth]~% wol-f3s +Sending WoL packet to f0 (e8:ff:1e:d7:1c:ac)... +Waking up e8:ff:1e:d7:1c:ac... +Sending WoL packet to f1 (e8:ff:1e:d7:1e:44)... +Waking up e8:ff:1e:d7:1e:44... +Sending WoL packet to f2 (e8:ff:1e:d7:1c:a0)... +Waking up e8:ff:1e:d7:1c:a0... -✓ WoL packets sent. Machines should boot in a few seconds. +✓ WoL packets sent. Machines should boot in a few seconds.
Within 30-50 seconds, all three machines successfully booted up and became accessible via SSH!
@@ -507,7 +512,7 @@ Waking up e8:ff:1e:d7:1c:a0... by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[paul@earth]~% wol-f3s shutdown +[paul@earth]~% wol-f3s shutdown
And all three machines will shut down cleanly. The next time I need them, a simple wol-f3s command wakes them all back up. This combination makes the cluster very energy-efficient while maintaining quick access when needed.
@@ -555,11 +560,12 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
+ diff --git a/gemfeed/2024-12-15-random-helix-themes.html b/gemfeed/2024-12-15-random-helix-themes.html index 3b399487..0ed69e26 100644 --- a/gemfeed/2024-12-15-random-helix-themes.html +++ b/gemfeed/2024-12-15-random-helix-themes.html @@ -2,12 +2,17 @@ +Random Helix Themes - + + + + + @@ -25,33 +30,33 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -export EDITOR=hx -export VISUAL=$EDITOR -export GIT_EDITOR=$EDITOR -export HELIX_CONFIG_DIR=$HOME/.config/helix +export EDITOR=hx +export VISUAL=$EDITOR +export GIT_EDITOR=$EDITOR +export HELIX_CONFIG_DIR=$HOME/.config/helix -editor::helix::random_theme () { - # May add more theme search paths based on OS. This one is - # for Fedora Linux, but there is also MacOS, etc. - local -r theme_dir=/usr/share/helix/runtime/themes - if [ ! -d $theme_dir ]; then - echo "Helix theme dir $theme_dir doesnt exist" - return 1 - fi +editor::helix::random_theme () { + # May add more theme search paths based on OS. This one is + # for Fedora Linux, but there is also MacOS, etc. + local -r theme_dir=/usr/share/helix/runtime/themes + if [ ! -d $theme_dir ]; then + echo "Helix theme dir $theme_dir doesnt exist" + return 1 + fi - local -r config_file=$HELIX_CONFIG_DIR/config.toml - local -r random_theme="$(basename "$(ls $theme_dir \ - | grep -v random.toml | grep .toml | sort -R \ - | head -n 1)" | cut -d. -f1)" + local -r config_file=$HELIX_CONFIG_DIR/config.toml + local -r random_theme="$(basename "$(ls $theme_dir \ + | grep -v random.toml | grep .toml | sort -R \ + | head -n 1)" | cut -d. -f1)" - sed "/^theme =/ { s/.*/theme = \"$random_theme\"/; }" \ - $config_file > $config_file.tmp && - mv $config_file.tmp $config_file -} + sed "/^theme =/ { s/.*/theme = \"$random_theme\"/; }" \ + $config_file > $config_file.tmp && + mv $config_file.tmp $config_file +} -if [ -f $HELIX_CONFIG_DIR/config.toml ]; then - editor::helix::random_theme -fi +if [ -f $HELIX_CONFIG_DIR/config.toml ]; then + editor::helix::random_theme +fi
So every time I open a new terminal or shell, editor::helix::random_theme gets called, which randomly selects a theme from all installed ones and updates the helix config accordingly.
@@ -60,16 +65,16 @@ editor::helix::random_theme () { by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[paul@earth] ~ % editor::helix::random_theme -[paul@earth] ~ % head -n 1 ~/.config/helix/config.toml -theme = "jellybeans" -[paul@earth] ~ % editor::helix::random_theme -[paul@earth] ~ % head -n 1 ~/.config/helix/config.toml -theme = "rose_pine" -[paul@earth] ~ % editor::helix::random_theme -[paul@earth] ~ % head -n 1 ~/.config/helix/config.toml -theme = "noctis" -[paul@earth] ~ % +[paul@earth] ~ % editor::helix::random_theme +[paul@earth] ~ % head -n 1 ~/.config/helix/config.toml +theme = "jellybeans" +[paul@earth] ~ % editor::helix::random_theme +[paul@earth] ~ % head -n 1 ~/.config/helix/config.toml +theme = "rose_pine" +[paul@earth] ~ % editor::helix::random_theme +[paul@earth] ~ % head -n 1 ~/.config/helix/config.toml +theme = "noctis" +[paul@earth] ~ %
A better version
@@ -80,44 +85,45 @@ theme = "noctis" by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -export EDITOR=hx -export VISUAL=$EDITOR -export GIT_EDITOR=$EDITOR -export HELIX_CONFIG_DIR=$HOME/.config/helix +export EDITOR=hx +export VISUAL=$EDITOR +export GIT_EDITOR=$EDITOR +export HELIX_CONFIG_DIR=$HOME/.config/helix -editor::helix::theme::get_random () { - for dir in $(hx --health \ - | awk '/^Runtime directories/ { print $3 }' | tr ';' ' '); do - if [ -d $dir/themes ]; then - ls $dir/themes - fi - done | grep -F .toml | sort -R | head -n 1 | cut -d. -f1 -} +editor::helix::theme::get_random () { + for dir in $(hx --health \ + | awk '/^Runtime directories/ { print $3 }' | tr ';' ' '); do + if [ -d $dir/themes ]; then + ls $dir/themes + fi + done | grep -F .toml | sort -R | head -n 1 | cut -d. -f1 +} -editor::helix::theme::set () { - local -r theme="$1"; shift +editor::helix::theme::set () { + local -r theme="$1"; shift - local -r config_file=$HELIX_CONFIG_DIR/config.toml + local -r config_file=$HELIX_CONFIG_DIR/config.toml - sed "/^theme =/ { s/.*/theme = \"$theme\"/; }" \ - $config_file > $config_file.tmp && - mv $config_file.tmp $config_file -} + sed "/^theme =/ { s/.*/theme = \"$theme\"/; }" \ + $config_file > $config_file.tmp && + mv $config_file.tmp $config_file +} -if [ -f $HELIX_CONFIG_DIR/config.toml ]; then - editor::helix::theme::set $(editor::helix::theme::get_random) -fi +if [ -f $HELIX_CONFIG_DIR/config.toml ]; then + editor::helix::theme::set $(editor::helix::theme::get_random) +fi
I hope you had some fun. E-Mail your comments to paul@nospam.buetow.org :-)
Back to the main site
+ diff --git a/gemfeed/2025-01-01-posts-from-october-to-december-2024.html b/gemfeed/2025-01-01-posts-from-october-to-december-2024.html index 5c474849..b9f8b677 100644 --- a/gemfeed/2025-01-01-posts-from-october-to-december-2024.html +++ b/gemfeed/2025-01-01-posts-from-october-to-december-2024.html @@ -2,12 +2,17 @@ +Posts from October to December 2024 - + + + + + @@ -349,11 +354,12 @@
Back to the main site
+ diff --git a/gemfeed/2025-01-15-working-with-an-sre-interview.html b/gemfeed/2025-01-15-working-with-an-sre-interview.html index 4658e48b..a7abd60e 100644 --- a/gemfeed/2025-01-15-working-with-an-sre-interview.html +++ b/gemfeed/2025-01-15-working-with-an-sre-interview.html @@ -2,12 +2,17 @@ +Working with an SRE Interview - + + + + + @@ -193,11 +198,12 @@
Back to the main site
+ diff --git a/gemfeed/2025-02-01-f3s-kubernetes-with-freebsd-part-3.html b/gemfeed/2025-02-01-f3s-kubernetes-with-freebsd-part-3.html index 30252c8d..d9ff5126 100644 --- a/gemfeed/2025-02-01-f3s-kubernetes-with-freebsd-part-3.html +++ b/gemfeed/2025-02-01-f3s-kubernetes-with-freebsd-part-3.html @@ -2,12 +2,17 @@ +f3s: Kubernetes with FreeBSD - Part 3: Protecting from power cuts - + + + + + @@ -62,11 +67,11 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0: ~ % doas freebsd-update fetch -paul@f0: ~ % doas freebsd-update install -paul@f0: ~ % doas freebsd-update -r 14.2-RELEASE upgrade -paul@f0: ~ % doas freebsd-update install -paul@f0: ~ % doas shutdown -r now +paul@f0: ~ % doas freebsd-update fetch +paul@f0: ~ % doas freebsd-update install +paul@f0: ~ % doas freebsd-update -r 14.2-RELEASE upgrade +paul@f0: ~ % doas freebsd-update install +paul@f0: ~ % doas shutdown -r now
And after rebooting, I ran:
@@ -75,10 +80,10 @@ paul@f0: ~ % doas shutdown -r now by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0: ~ % doas freebsd-update install -paul@f0: ~ % doas pkg update -paul@f0: ~ % doas pkg upgrade -paul@f0: ~ % doas shutdown -r now +paul@f0: ~ % doas freebsd-update install +paul@f0: ~ % doas pkg update +paul@f0: ~ % doas pkg upgrade +paul@f0: ~ % doas shutdown -r now
And after another reboot, I was on 14.2:
@@ -87,9 +92,9 @@ paul@f0: ~ % doas shutdown -r now by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % uname -a -FreeBSD f0.lan.buetow.org 14.2-RELEASE FreeBSD 14.2-RELEASE - releng/14.2-n269506-c8918d6c7412 GENERIC amd64 +paul@f0:~ % uname -a +FreeBSD f0.lan.buetow.org 14.2-RELEASE FreeBSD 14.2-RELEASE + releng/14.2-n269506-c8918d6c7412 GENERIC amd64
And, of course, I ran this on all 3 nodes!
@@ -135,8 +140,8 @@ FreeBSD f0.lan.buetow.org 14.2-RELEASE FreeBSD- paul@f0: ~ % doas dmesg | grep UPS -ugen0.2: <American Power Conversion Back-UPS BX750MI> at usbus0 +paul@f0: ~ % doas dmesg | grep UPS +ugen0.2: <American Power Conversion Back-UPS BX750MI> at usbus0
apcupsd Installation
@@ -147,7 +152,7 @@ ugen0.2: <American Power Conversion Back-UPS BX7 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0: ~ % doas install apcupsd +paul@f0: ~ % doas install apcupsd
I have made the following modifications to the configuration file so that the UPS can be used via the USB interface:
@@ -156,29 +161,29 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:/usr/local/etc/apcupsd % diff -u apcupsd.conf.sample apcupsd.conf ---- apcupsd.conf.sample 2024-11-01 16:40:42.000000000 +0200 -+++ apcupsd.conf 2024-12-03 10:58:24.009501000 +0200 -@@ -31,7 +31,7 @@ - # 940-1524C, 940-0024G, 940-0095A, 940-0095B, - # 940-0095C, 940-0625A, M-04-02-2000 - # --UPSCABLE smart -+UPSCABLE usb +paul@f0:/usr/local/etc/apcupsd % diff -u apcupsd.conf.sample apcupsd.conf +--- apcupsd.conf.sample 2024-11-01 16:40:42.000000000 +0200 ++++ apcupsd.conf 2024-12-03 10:58:24.009501000 +0200 +@@ -31,7 +31,7 @@ + # 940-1524C, 940-0024G, 940-0095A, 940-0095B, + # 940-0095C, 940-0625A, M-04-02-2000 + # +-UPSCABLE smart ++UPSCABLE usb - # To get apcupsd to work, in addition to defining the cable - # above, you must also define a UPSTYPE, which corresponds to -@@ -88,8 +88,10 @@ - # that apcupsd binds to that particular unit - # (helpful if you have more than one USB UPS). - # --UPSTYPE apcsmart --DEVICE /dev/usv -+UPSTYPE usb -+DEVICE + # To get apcupsd to work, in addition to defining the cable + # above, you must also define a UPSTYPE, which corresponds to +@@ -88,8 +88,10 @@ + # that apcupsd binds to that particular unit + # (helpful if you have more than one USB UPS). + # +-UPSTYPE apcsmart +-DEVICE /dev/usv ++UPSTYPE usb ++DEVICE - # POLLTIME <int> - # Interval (in seconds) at which apcupsd polls the UPS for status. This + # POLLTIME <int> + # Interval (in seconds) at which apcupsd polls the UPS for status. This
I left the remaining settings as the default ones; for example, the following are of main interest:
@@ -201,10 +206,10 @@ MINUTES 3 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:/usr/local/etc/apcupsd % doas sysrc apcupsd_enable=YES -apcupsd_enable: -> YES -paul@f0:/usr/local/etc/apcupsd % doas service apcupsd start -Starting apcupsd. +paul@f0:/usr/local/etc/apcupsd % doas sysrc apcupsd_enable=YES +apcupsd_enable: -> YES +paul@f0:/usr/local/etc/apcupsd % doas service apcupsd start +Starting apcupsd.
UPS Connectivity Test
@@ -215,43 +220,43 @@ Starting apcupsd. by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % apcaccess -APC : 001,035,0857 -DATE : 2025-01-26 14:43:27 +0200 -HOSTNAME : f0.lan.buetow.org -VERSION : 3.14.14 (31 May 2016) freebsd -UPSNAME : f0.lan.buetow.org -CABLE : USB Cable -DRIVER : USB UPS Driver -UPSMODE : Stand Alone -STARTTIME: 2025-01-26 14:43:25 +0200 -MODEL : Back-UPS BX750MI -STATUS : ONLINE -LINEV : 230.0 Volts -LOADPCT : 4.0 Percent -BCHARGE : 100.0 Percent -TIMELEFT : 65.3 Minutes -MBATTCHG : 5 Percent -MINTIMEL : 3 Minutes -MAXTIME : 0 Seconds -SENSE : Medium -LOTRANS : 145.0 Volts -HITRANS : 295.0 Volts -ALARMDEL : No alarm -BATTV : 13.6 Volts -LASTXFER : Automatic or explicit self test -NUMXFERS : 0 -TONBATT : 0 Seconds -CUMONBATT: 0 Seconds -XOFFBATT : N/A -SELFTEST : NG -STATFLAG : 0x05000008 -SERIALNO : 9B2414A03599 -BATTDATE : 2001-01-01 -NOMINV : 230 Volts -NOMBATTV : 12.0 Volts -NOMPOWER : 410 Watts -END APC : 2025-01-26 14:44:06 +0200 +paul@f0:~ % apcaccess +APC : 001,035,0857 +DATE : 2025-01-26 14:43:27 +0200 +HOSTNAME : f0.lan.buetow.org +VERSION : 3.14.14 (31 May 2016) freebsd +UPSNAME : f0.lan.buetow.org +CABLE : USB Cable +DRIVER : USB UPS Driver +UPSMODE : Stand Alone +STARTTIME: 2025-01-26 14:43:25 +0200 +MODEL : Back-UPS BX750MI +STATUS : ONLINE +LINEV : 230.0 Volts +LOADPCT : 4.0 Percent +BCHARGE : 100.0 Percent +TIMELEFT : 65.3 Minutes +MBATTCHG : 5 Percent +MINTIMEL : 3 Minutes +MAXTIME : 0 Seconds +SENSE : Medium +LOTRANS : 145.0 Volts +HITRANS : 295.0 Volts +ALARMDEL : No alarm +BATTV : 13.6 Volts +LASTXFER : Automatic or explicit self test +NUMXFERS : 0 +TONBATT : 0 Seconds +CUMONBATT: 0 Seconds +XOFFBATT : N/A +SELFTEST : NG +STATFLAG : 0x05000008 +SERIALNO : 9B2414A03599 +BATTDATE : 2001-01-01 +NOMINV : 230 Volts +NOMBATTV : 12.0 Volts +NOMPOWER : 410 Watts +END APC : 2025-01-26 14:44:06 +0200
APC Info on Partner Nodes:
@@ -270,10 +275,10 @@ END APC : 2025-01-- -paul@f1:~ % apcaccess -h f0.lan.buetow.org | grep Percent -LOADPCT : 12.0 Percent -BCHARGE : 94.0 Percent -MBATTCHG : 5 Percent +paul@f1:~ % apcaccess -h f0.lan.buetow.org | grep Percent +LOADPCT : 12.0 Percent +BCHARGE : 94.0 Percent +MBATTCHG : 5 Percent
But I want the daemon to be configured and enabled in such a way that it connects to the master UPS node (the one with the UPS connected via USB) so that it can also initiate a system shutdown when the UPS battery reaches low levels. For that, apcupsd itself needs to be aware of the UPS status.
@@ -284,52 +289,52 @@ MBATTCHG : 5 Percent by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f2:/usr/local/etc/apcupsd % diff -u apcupsd.conf.sample apcupsd.conf ---- apcupsd.conf.sample 2024-11-01 16:40:42.000000000 +0200 -+++ apcupsd.conf 2025-01-26 15:52:45.108469000 +0200 -@@ -31,7 +31,7 @@ - # 940-1524C, 940-0024G, 940-0095A, 940-0095B, - # 940-0095C, 940-0625A, M-04-02-2000 - # --UPSCABLE smart -+UPSCABLE ether +paul@f2:/usr/local/etc/apcupsd % diff -u apcupsd.conf.sample apcupsd.conf +--- apcupsd.conf.sample 2024-11-01 16:40:42.000000000 +0200 ++++ apcupsd.conf 2025-01-26 15:52:45.108469000 +0200 +@@ -31,7 +31,7 @@ + # 940-1524C, 940-0024G, 940-0095A, 940-0095B, + # 940-0095C, 940-0625A, M-04-02-2000 + # +-UPSCABLE smart ++UPSCABLE ether - # To get apcupsd to work, in addition to defining the cable - # above, you must also define a UPSTYPE, which corresponds to -@@ -52,7 +52,6 @@ - # Network Information Server. This is used if the - # UPS powering your computer is connected to a - # different computer for monitoring. --# - # snmp hostname:port:vendor:community - # SNMP network link to an SNMP-enabled UPS device. - # Hostname is the ip address or hostname of the UPS -@@ -88,8 +87,8 @@ - # that apcupsd binds to that particular unit - # (helpful if you have more than one USB UPS). - # --UPSTYPE apcsmart --DEVICE /dev/usv -+UPSTYPE net -+DEVICE f0.lan.buetow.org:3551 + # To get apcupsd to work, in addition to defining the cable + # above, you must also define a UPSTYPE, which corresponds to +@@ -52,7 +52,6 @@ + # Network Information Server. This is used if the + # UPS powering your computer is connected to a + # different computer for monitoring. +-# + # snmp hostname:port:vendor:community + # SNMP network link to an SNMP-enabled UPS device. + # Hostname is the ip address or hostname of the UPS +@@ -88,8 +87,8 @@ + # that apcupsd binds to that particular unit + # (helpful if you have more than one USB UPS). + # +-UPSTYPE apcsmart +-DEVICE /dev/usv ++UPSTYPE net ++DEVICE f0.lan.buetow.org:3551 - # POLLTIME <int> - # Interval (in seconds) at which apcupsd polls the UPS for status. This -@@ -147,12 +146,12 @@ - # If during a power failure, the remaining battery percentage - # (as reported by the UPS) is below or equal to BATTERYLEVEL, - # apcupsd will initiate a system shutdown. --BATTERYLEVEL 5 -+BATTERYLEVEL 10 + # POLLTIME <int> + # Interval (in seconds) at which apcupsd polls the UPS for status. This +@@ -147,12 +146,12 @@ + # If during a power failure, the remaining battery percentage + # (as reported by the UPS) is below or equal to BATTERYLEVEL, + # apcupsd will initiate a system shutdown. +-BATTERYLEVEL 5 ++BATTERYLEVEL 10 - # If during a power failure, the remaining runtime in minutes - # (as calculated internally by the UPS) is below or equal to MINUTES, - # apcupsd, will initiate a system shutdown. --MINUTES 3 -+MINUTES 6 + # If during a power failure, the remaining runtime in minutes + # (as calculated internally by the UPS) is below or equal to MINUTES, + # apcupsd, will initiate a system shutdown. +-MINUTES 3 ++MINUTES 6 - # If during a power failure, the UPS has run on batteries for TIMEOUT - # many seconds or longer, apcupsd will initiate a system shutdown. + # If during a power failure, the UPS has run on batteries for TIMEOUT + # many seconds or longer, apcupsd will initiate a system shutdown.So I also ran the following commands on f1 and f2:
@@ -338,10 +343,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f1:/usr/local/etc/apcupsd % doas sysrc apcupsd_enable=YES -apcupsd_enable: -> YES -paul@f1:/usr/local/etc/apcupsd % doas service apcupsd start -Starting apcupsd. +paul@f1:/usr/local/etc/apcupsd % doas sysrc apcupsd_enable=YES +apcupsd_enable: -> YES +paul@f1:/usr/local/etc/apcupsd % doas service apcupsd start +Starting apcupsd.
And then I was able to connect to localhost via the apcaccess command:
@@ -350,10 +355,10 @@ Starting apcupsd. by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f1:~ % doas apcaccess | grep Percent -LOADPCT : 5.0 Percent -BCHARGE : 95.0 Percent -MBATTCHG : 5 Percent +paul@f1:~ % doas apcaccess | grep Percent +LOADPCT : 5.0 Percent +BCHARGE : 95.0 Percent +MBATTCHG : 5 Percent
Power outage simulation
@@ -375,8 +380,8 @@ Power failure. Running on UPS batteries. by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:/usr/local/etc/apcupsd % apcaccess -p TIMELEFT -63.9 Minutes +paul@f0:/usr/local/etc/apcupsd % apcaccess -p TIMELEFT +63.9 Minutes
And after around one hour (f1 and f2 a bit earlier, f0 a bit later due to the different BATTERYLEVEL and MINUTES settings outlined earlier), the following broadcast was sent out:
@@ -438,11 +443,12 @@ Jan 26 17:36:32 f2 apcupsd[2159]: apcupsd shutdown succeeded
Back to the main site
+ diff --git a/gemfeed/2025-02-08-random-weird-things-ii.html b/gemfeed/2025-02-08-random-weird-things-ii.html index 64a37731..6214f252 100644 --- a/gemfeed/2025-02-08-random-weird-things-ii.html +++ b/gemfeed/2025-02-08-random-weird-things-ii.html @@ -2,12 +2,17 @@ +Random Weird Things - Part Ⅱ - + + + + + @@ -80,25 +85,25 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -package main +package main -import "log" +import "log" -type fun func() string +type fun func() string -func (f fun) Bar() string { - return "Bar" -} +func (f fun) Bar() string { + return "Bar" +} -func main() { - var f fun = func() string { - return "Foo" - } - log.Println("Example 1: ", f()) - log.Println("Example 2: ", f.Bar()) - log.Println("Example 3: ", fun(f.Bar).Bar()) - log.Println("Example 4: ", fun(fun(f.Bar).Bar).Bar()) -} +func main() { + var f fun = func() string { + return "Foo" + } + log.Println("Example 1: ", f()) + log.Println("Example 2: ", f.Bar()) + log.Println("Example 3: ", fun(f.Bar).Bar()) + log.Println("Example 4: ", fun(fun(f.Bar).Bar).Bar()) +}
It runs just fine:
@@ -107,11 +112,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -❯ go run main.go -2025/02/07 22:56:14 Example 1: Foo -2025/02/07 22:56:14 Example 2: Bar -2025/02/07 22:56:14 Example 3: Bar -2025/02/07 22:56:14 Example 4: Bar +❯ go run main.go +2025/02/07 22:56:14 Example 1: Foo +2025/02/07 22:56:14 Example 2: Bar +2025/02/07 22:56:14 Example 3: Bar +2025/02/07 22:56:14 Example 4: Bar
macOS
@@ -130,20 +135,20 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -❯ touch Maß -❯ ls -l --rw-r--r--@ 1 paul wheel 0 Feb 7 23:02 Maß -❯ touch Mass -❯ ls -l --rw-r--r--@ 1 paul wheel 0 Feb 7 23:02 Maß -❯ rm Mass -❯ ls -l +❯ touch Maß +❯ ls -l +-rw-r--r--@ 1 paul wheel 0 Feb 7 23:02 Maß +❯ touch Mass +❯ ls -l +-rw-r--r--@ 1 paul wheel 0 Feb 7 23:02 Maß +❯ rm Mass +❯ ls -l -❯ touch Mass -❯ ls -ltr --rw-r--r--@ 1 paul wheel 0 Feb 7 23:02 Mass -❯ rm Maß -❯ ls -l +❯ touch Mass +❯ ls -ltr +-rw-r--r--@ 1 paul wheel 0 Feb 7 23:02 Mass +❯ rm Maß +❯ ls -l
@@ -187,16 +192,16 @@ ADFS::4.$.Documents.Techwriter.Myfile by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -arr = {10, 20, 30, 40, 50} -print(arr[1]) -- Accessing the first element +arr = {10, 20, 30, 40, 50} +print(arr[1]) -- Accessing the first element
-❯ lua foo.lua -10 +❯ lua foo.lua +10
One-based indexing is more natural for human-readable, mathematical, and theoretical contexts, where counting traditionally starts from one.
@@ -211,45 +216,45 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# (C) 2006 by Paul C. Buetow +# (C) 2006 by Paul C. Buetow -Christmas:{time;#!!! +Christmas:{time;#!!! -Children: do tell $wishes; +Children: do tell $wishes; -Santa: for $each (@children) { -BEGIN { read $each, $their, wishes and study them; use Memoize#ing +Santa: for $each (@children) { +BEGIN { read $each, $their, wishes and study them; use Memoize#ing -} use constant gift, 'wrapping'; -package Gifts; pack $each, gift and bless $each and goto deliver -or do import if not local $available,!!! HO, HO, HO; +} use constant gift, 'wrapping'; +package Gifts; pack $each, gift and bless $each and goto deliver +or do import if not local $available,!!! HO, HO, HO; -redo Santa, pipe $gifts, to_childs; -redo Santa and do return if last one, is, delivered; +redo Santa, pipe $gifts, to_childs; +redo Santa and do return if last one, is, delivered; -deliver: gift and require diagnostics if our $gifts ,not break; -do{ use NEXT; time; tied $gifts} if broken and dump the, broken, ones; -The_children: sleep and wait for (each %gift) and try { to => untie $gifts }; +deliver: gift and require diagnostics if our $gifts ,not break; +do{ use NEXT; time; tied $gifts} if broken and dump the, broken, ones; +The_children: sleep and wait for (each %gift) and try { to => untie $gifts }; -redo Santa, pipe $gifts, to_childs; -redo Santa and do return if last one, is, delivered; +redo Santa, pipe $gifts, to_childs; +redo Santa and do return if last one, is, delivered; -The_christmas_tree: formline s/ /childrens/, $gifts; -alarm and warn if not exists $Christmas{ tree}, @t, $ENV{HOME}; -write <<EMail - to the parents to buy a new christmas tree!!!!111 - and send the -EMail -;wait and redo deliver until defined local $tree; +The_christmas_tree: formline s/ /childrens/, $gifts; +alarm and warn if not exists $Christmas{ tree}, @t, $ENV{HOME}; +write <<EMail + to the parents to buy a new christmas tree!!!!111 + and send the +EMail +;wait and redo deliver until defined local $tree; -redo Santa, pipe $gifts, to_childs; -redo Santa and do return if last one, is, delivered ;} +redo Santa, pipe $gifts, to_childs; +redo Santa and do return if last one, is, delivered ;} -END {} our $mission and do sleep until next Christmas ;} +END {} our $mission and do sleep until next Christmas ;} -__END__ +__END__ -This is perl, v5.8.8 built for i386-freebsd-64int +This is perl, v5.8.8 built for i386-freebsd-64int
More Perl Poetry of mine
@@ -284,11 +289,12 @@ This is perl, v5.8.8 built - Generated with Gemtexter 3.0.1-develop | - served by OpenBSD/relayd(8)+httpd(8) | - Site Mirrors -
- Webring: previous | shring | next + Generated with Gemtexter 3.0.1-develop | + served by OpenBSD/relayd(8)+httpd(8) | + Site Mirrors +
+ Webring: previous | shring | next + diff --git a/gemfeed/2025-03-05-sharing-on-social-media-with-gos.html b/gemfeed/2025-03-05-sharing-on-social-media-with-gos.html index 2fe45356..902bbc78 100644 --- a/gemfeed/2025-03-05-sharing-on-social-media-with-gos.html +++ b/gemfeed/2025-03-05-sharing-on-social-media-with-gos.html @@ -2,12 +2,17 @@ +Sharing on Social Media with Gos v1.0.0 - + + + + + @@ -84,8 +89,8 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -git clone https://codeberg.org/snonux/gos.git -cd gos +git clone https://codeberg.org/snonux/gos.git +cd gos
Build the binaries:
@@ -94,10 +99,10 @@ cd gos by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -go build -o gos ./cmd/gos -go build -o gosc ./cmd/gosc -sudo mv gos ~/go/bin -sudo mv gosc ~/go/bin +go build -o gos ./cmd/gos +go build -o gosc ./cmd/gosc +sudo mv gos ~/go/bin +sudo mv gosc ~/go/bin
Or, if you want to use the Taskfile:
@@ -106,7 +111,7 @@ sudo mv gosc ~/go/bin by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -go-task install +go-task install
Configuration
@@ -119,13 +124,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -{ - "MastodonURL": "https://mastodon.example.com", - "MastodonAccessToken": "your-mastodon-access-token", - "LinkedInClientID": "your-linkedin-client-id", - "LinkedInSecret": "your-linkedin-client-secret", - "LinkedInRedirectURL": "http://localhost:8080/callback", -} +{ + "MastodonURL": "https://mastodon.example.com", + "MastodonAccessToken": "your-mastodon-access-token", + "LinkedInClientID": "your-linkedin-client-id", + "LinkedInSecret": "your-linkedin-client-secret", + "LinkedInRedirectURL": "http://localhost:8080/callback", +}
Configuration fields
@@ -181,7 +186,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -./gos --dry +./gos --dry
*Normal run*
@@ -192,7 +197,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -./gos +./gos
:-)
@@ -357,7 +362,7 @@ Hello World :-) by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -gos --geminiSummaryFor 202410,202411,202412 +gos --geminiSummaryFor 202410,202411,202412
This outputs the summary for the three specified months, as shown in the example. The summary includes posts from all social media networks but removes duplicates.
@@ -369,7 +374,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -gos --gemtexterEnable --geminiSummaryFor 202410,202411,202412 +gos --gemtexterEnable --geminiSummaryFor 202410,202411,202412
Gemtexter
@@ -380,7 +385,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -gos --gemtexterEnable --geminiSummaryFor 202410,202411,202412 --geminiCapsules "foo.zone,paul.buetow.org" +gos --gemtexterEnable --geminiSummaryFor 202410,202411,202412 --geminiCapsules "foo.zone,paul.buetow.org"
It will then also generate Gemini Gemtext links in the summary page and flag them with (Gemini).
@@ -393,11 +398,12 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
+ diff --git a/gemfeed/2025-04-05-f3s-kubernetes-with-freebsd-part-4.html b/gemfeed/2025-04-05-f3s-kubernetes-with-freebsd-part-4.html index 0a87081a..5e06dc03 100644 --- a/gemfeed/2025-04-05-f3s-kubernetes-with-freebsd-part-4.html +++ b/gemfeed/2025-04-05-f3s-kubernetes-with-freebsd-part-4.html @@ -2,12 +2,17 @@ +f3s: Kubernetes with FreeBSD - Part 4: Rocky Linux Bhyve VMs - + + + + + @@ -83,10 +88,10 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % dmesg | grep 'Features2=.*POPCNT' - Features2=0x7ffafbbf<SSE3,PCLMULQDQ,DTES64,MON,DS_CPL,VMX,EST,TM2,SSSE3,SDBG, - FMA,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,TSCDLT,AESNI,XSAVE, - OSXSAVE,AVX,F16C,RDRAND> +paul@f0:~ % dmesg | grep 'Features2=.*POPCNT' + Features2=0x7ffafbbf<SSE3,PCLMULQDQ,DTES64,MON,DS_CPL,VMX,EST,TM2,SSSE3,SDBG, + FMA,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,TSCDLT,AESNI,XSAVE, + OSXSAVE,AVX,F16C,RDRAND>
So it's there! All good.
@@ -103,15 +108,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas pkg install vm-bhyve bhyve-firmware -paul@f0:~ % doas sysrc vm_enable=YES -vm_enable: -> YES -paul@f0:~ % doas sysrc vm_dir=zfs:zroot/bhyve -vm_dir: -> zfs:zroot/bhyve -paul@f0:~ % doas zfs create zroot/bhyve -paul@f0:~ % doas vm init -paul@f0:~ % doas vm switch create public -paul@f0:~ % doas vm switch add public re0 +paul@f0:~ % doas pkg install vm-bhyve bhyve-firmware +paul@f0:~ % doas sysrc vm_enable=YES +vm_enable: -> YES +paul@f0:~ % doas sysrc vm_dir=zfs:zroot/bhyve +vm_dir: -> zfs:zroot/bhyve +paul@f0:~ % doas zfs create zroot/bhyve +paul@f0:~ % doas vm init +paul@f0:~ % doas vm switch create public +paul@f0:~ % doas vm switch add public re0
Bhyve stores all its data in the /bhyve of the zroot ZFS pool:
@@ -120,8 +125,8 @@ paul@f0:~ % doas vm switch add public re0 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % zfs list | grep bhyve -zroot/bhyve 1.74M 453G 1.74M /zroot/bhyve +paul@f0:~ % zfs list | grep bhyve +zroot/bhyve 1.74M 453G 1.74M /zroot/bhyve
For convenience, we also create this symlink:
@@ -130,7 +135,7 @@ zroot/bhyve 1.74M by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas ln -s /zroot/bhyve/ /bhyve +paul@f0:~ % doas ln -s /zroot/bhyve/ /bhyve
@@ -140,8 +145,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas vm list -NAME DATASTORE LOADER CPU MEMORY VNC AUTO STATE +paul@f0:~ % doas vm list +NAME DATASTORE LOADER CPU MEMORY VNC AUTO STATE
Rocky Linux VMs
@@ -162,10 +167,10 @@ NAME DATASTORE LOADER CPU MEMORY VNC AUTO STATE by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas vm iso \ - https://download.rockylinux.org/pub/rocky/9/isos/x86_64/Rocky-9.5-x86_64-minimal.iso -/zroot/bhyve/.iso/Rocky-9.5-x86_64-minimal.iso 1808 MB 4780 kBps 06m28s -paul@f0:/bhyve % doas vm create rocky +paul@f0:~ % doas vm iso \ + https://download.rockylinux.org/pub/rocky/9/isos/x86_64/Rocky-9.5-x86_64-minimal.iso +/zroot/bhyve/.iso/Rocky-9.5-x86_64-minimal.iso 1808 MB 4780 kBps 06m28s +paul@f0:/bhyve % doas vm create rocky
VM configuration
@@ -176,16 +181,16 @@ paul@f0:/bhyve % doas vm create rocky by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:/bhyve/rocky % cat rocky.conf -loader="bhyveload" -cpu=1 -memory=256M -network0_type="virtio-net" -network0_switch="public" -disk0_type="virtio-blk" -disk0_name="disk0.img" -uuid="1c4655ac-c828-11ef-a920-e8ff1ed71ca0" -network0_mac="58:9c:fc:0d:13:3f" +paul@f0:/bhyve/rocky % cat rocky.conf +loader="bhyveload" +cpu=1 +memory=256M +network0_type="virtio-net" +network0_switch="public" +disk0_type="virtio-blk" +disk0_name="disk0.img" +uuid="1c4655ac-c828-11ef-a920-e8ff1ed71ca0" +network0_mac="58:9c:fc:0d:13:3f"
The uuid and the network0_mac differ for each of the three VMs (the ones being installed on f0, f1 and f2).
@@ -216,17 +221,17 @@ network0_mac="58:9c:fc:0d:13:3f" by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas vm install rocky Rocky-9.5-x86_64-minimal.iso -Starting rocky - * found guest in /zroot/bhyve/rocky - * booting... +paul@f0:~ % doas vm install rocky Rocky-9.5-x86_64-minimal.iso +Starting rocky + * found guest in /zroot/bhyve/rocky + * booting... -paul@f0:/bhyve/rocky % doas vm list -NAME DATASTORE LOADER CPU MEMORY VNC AUTO STATE -rocky default uefi 4 14G 0.0.0.0:5900 No Locked (f0.lan.buetow.org) +paul@f0:/bhyve/rocky % doas vm list +NAME DATASTORE LOADER CPU MEMORY VNC AUTO STATE +rocky default uefi 4 14G 0.0.0.0:5900 No Locked (f0.lan.buetow.org) -paul@f0:/bhyve/rocky % doas sockstat -4 | grep 5900 -root bhyve 6079 8 tcp4 *:5900 *:* +paul@f0:/bhyve/rocky % doas sockstat -4 | grep 5900 +root bhyve 6079 8 tcp4 *:5900 *:*
Port 5900 now also opens for VNC connections, so I connected it with a VNC client and ran through the installation dialogues. This could be done unattended or more automated, but there are only three VMs to install, and the automation doesn't seem worth it as we do it only once a year or less often.
@@ -239,9 +244,9 @@ root bhyve 6079 8 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:/bhyve/rocky % doas vm stop rocky -paul@f0:/bhyve/rocky % doas truncate -s 100G disk0.img -paul@f0:/bhyve/rocky % doas vm install rocky Rocky-9.5-x86_64-minimal.iso +paul@f0:/bhyve/rocky % doas vm stop rocky +paul@f0:/bhyve/rocky % doas truncate -s 100G disk0.img +paul@f0:/bhyve/rocky % doas vm install rocky Rocky-9.5-x86_64-minimal.iso
Connect to VNC
@@ -270,9 +275,9 @@ paul@f0:/bhyve/rocky % doas vm install rocky Rocky-9.5paul@f0:/bhyve/rocky % cat <<END | doas tee -a /etc/rc.conf -vm_list="rocky" -vm_delay="5" +paul@f0:/bhyve/rocky % cat <<END | doas tee -a /etc/rc.conf +vm_list="rocky" +vm_delay="5"
The vm_delay isn't really required. It is used to wait 5 seconds before starting each VM, but there is currently only one VM per host. Maybe later, when there are more, this will be useful. After adding, there's now a Yes indicator in the AUTO column.
@@ -281,9 +286,9 @@ vm_delay="5" by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas vm list -NAME DATASTORE LOADER CPU MEMORY VNC AUTO STATE -rocky default uefi 4 14G 0.0.0.0:5900 Yes [1] Running (2063) +paul@f0:~ % doas vm list +NAME DATASTORE LOADER CPU MEMORY VNC AUTO STATE +rocky default uefi 4 14G 0.0.0.0:5900 Yes [1] Running (2063)
Static IP configuration
@@ -302,11 +307,11 @@ rocky default uefi 4 14G -paul@f0:/bhyve/rocky % cat <<END | doas tee -a /etc/hosts -192.168.1.120 r0 r0.lan r0.lan.buetow.org -192.168.1.121 r1 r1.lan r1.lan.buetow.org -192.168.1.122 r2 r2.lan r2.lan.buetow.org -END +paul@f0:/bhyve/rocky % cat <<END | doas tee -a /etc/hosts +192.168.1.120 r0 r0.lan r0.lan.buetow.org +192.168.1.121 r1 r1.lan r1.lan.buetow.org +192.168.1.122 r2 r2.lan r2.lan.buetow.org +END
And we configure the IPs accordingly on the VMs themselves by opening a root shell via SSH to the VMs and entering the following commands on each of the VMs:
@@ -315,18 +320,18 @@ END by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~] % nmcli connection modify enp0s5 ipv4.address 192.168.1.120/24 -[root@r0 ~] % nmcli connection modify enp0s5 ipv4.gateway 192.168.1.1 -[root@r0 ~] % nmcli connection modify enp0s5 ipv4.DNS 192.168.1.1 -[root@r0 ~] % nmcli connection modify enp0s5 ipv4.method manual -[root@r0 ~] % nmcli connection down enp0s5 -[root@r0 ~] % nmcli connection up enp0s5 -[root@r0 ~] % hostnamectl set-hostname r0.lan.buetow.org -[root@r0 ~] % cat <<END >>/etc/hosts -192.168.1.120 r0 r0.lan r0.lan.buetow.org -192.168.1.121 r1 r1.lan r1.lan.buetow.org -192.168.1.122 r2 r2.lan r2.lan.buetow.org -END +[root@r0 ~] % nmcli connection modify enp0s5 ipv4.address 192.168.1.120/24 +[root@r0 ~] % nmcli connection modify enp0s5 ipv4.gateway 192.168.1.1 +[root@r0 ~] % nmcli connection modify enp0s5 ipv4.DNS 192.168.1.1 +[root@r0 ~] % nmcli connection modify enp0s5 ipv4.method manual +[root@r0 ~] % nmcli connection down enp0s5 +[root@r0 ~] % nmcli connection up enp0s5 +[root@r0 ~] % hostnamectl set-hostname r0.lan.buetow.org +[root@r0 ~] % cat <<END >>/etc/hosts +192.168.1.120 r0 r0.lan r0.lan.buetow.org +192.168.1.121 r1 r1.lan r1.lan.buetow.org +192.168.1.122 r2 r2.lan r2.lan.buetow.org +END
Whereas:
@@ -347,7 +352,7 @@ END by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -% for i in 0 1 2; do ssh-copy-id root@r$i.lan.buetow.org; done +% for i in 0 1 2; do ssh-copy-id root@r$i.lan.buetow.org; done
Then, we edit the /etc/ssh/sshd_config file again on all three VMs and configure PasswordAuthentication no to only allow SSH key authentication from now on.
@@ -358,8 +363,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~] % dnf update -[root@r0 ~] % reboot +[root@r0 ~] % dnf update +[root@r0 ~] % reboot
Stress testing CPU
@@ -370,28 +375,28 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -package main +package main -import "testing" +import "testing" -func BenchmarkCPUSilly1(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = i * i - } -} +func BenchmarkCPUSilly1(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = i * i + } +} -func BenchmarkCPUSilly2(b *testing.B) { - var sillyResult float64 - for i := 0; i < b.N; i++ { - sillyResult += float64(i) - sillyResult *= float64(i) - divisor := float64(i) + 1 - if divisor > 0 { - sillyResult /= divisor - } - } - _ = sillyResult // to avoid compiler optimization -} +func BenchmarkCPUSilly2(b *testing.B) { + var sillyResult float64 + for i := 0; i < b.N; i++ { + sillyResult += float64(i) + sillyResult *= float64(i) + divisor := float64(i) + 1 + if divisor > 0 { + sillyResult /= divisor + } + } + _ = sillyResult // to avoid compiler optimization +}
You can find the repository here:
@@ -406,10 +411,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas pkg install git go -paul@f0:~ % mkdir ~/git && cd ~/git && \ - git clone https://codeberg.org/snonux/sillybench && \ - cd sillybench +paul@f0:~ % doas pkg install git go +paul@f0:~ % mkdir ~/git && cd ~/git && \ + git clone https://codeberg.org/snonux/sillybench && \ + cd sillybench
And to run it:
@@ -418,18 +423,18 @@ paul@f0:~ % mkdir ~/git && cd ~/git && \ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~/git/sillybench % go version -go version go1.24.1 freebsd/amd64 +paul@f0:~/git/sillybench % go version +go version go1.24.1 freebsd/amd64 -paul@f0:~/git/sillybench % go test -bench=. -goos: freebsd -goarch: amd64 -pkg: codeberg.org/snonux/sillybench -cpu: Intel(R) N100 -BenchmarkCPUSilly1-4 1000000000 0.4022 ns/op -BenchmarkCPUSilly2-4 1000000000 0.4027 ns/op -PASS -ok codeberg.org/snonux/sillybench 0.891s +paul@f0:~/git/sillybench % go test -bench=. +goos: freebsd +goarch: amd64 +pkg: codeberg.org/snonux/sillybench +cpu: Intel(R) N100 +BenchmarkCPUSilly1-4 1000000000 0.4022 ns/op +BenchmarkCPUSilly2-4 1000000000 0.4027 ns/op +PASS +ok codeberg.org/snonux/sillybench 0.891s
Silly Rocky Linux VM @ Bhyve benchmark
@@ -440,10 +445,10 @@ ok codeberg.org/snonux/sillybench 0.891s by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~]# dnf install golang git -[root@r0 ~]# mkdir ~/git && cd ~/git && \ - git clone https://codeberg.org/snonux/sillybench && \ - cd sillybench +[root@r0 ~]# dnf install golang git +[root@r0 ~]# mkdir ~/git && cd ~/git && \ + git clone https://codeberg.org/snonux/sillybench && \ + cd sillybench
And to run it:
@@ -452,15 +457,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 sillybench]# go version -go version go1.22.9 (Red Hat 1.22.9-2.el9_5) linux/amd64 -[root@r0 sillybench]# go test -bench=. -goos: linux -goarch: amd64 -pkg: codeberg.org/snonux/sillybench -cpu: Intel(R) N100 -BenchmarkCPUSilly1-4 1000000000 0.4347 ns/op -BenchmarkCPUSilly2-4 1000000000 0.4345 ns/op +[root@r0 sillybench]# go version +go version go1.22.9 (Red Hat 1.22.9-2.el9_5) linux/amd64 +[root@r0 sillybench]# go test -bench=. +goos: linux +goarch: amd64 +pkg: codeberg.org/snonux/sillybench +cpu: Intel(R) N100 +BenchmarkCPUSilly1-4 1000000000 0.4347 ns/op +BenchmarkCPUSilly2-4 1000000000 0.4345 ns/op
The Linux benchmark is slightly slower than the FreeBSD one. The Go version is also a bit older. I tried the same with the up-to-date version of Go (1.24.x) with similar results. There could be a slight Bhyve overhead, or FreeBSD is just slightly more efficient in this benchmark. Overall, this shows that Bhyve performs excellently.
@@ -475,15 +480,15 @@ BenchmarkCPUSilly2-4 10000 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -root@freebsd:~/git/sillybench # go test -bench=. -goos: freebsd -goarch: amd64 -pkg: codeberg.org/snonux/sillybench -cpu: Intel(R) N100 -BenchmarkCPUSilly1 1000000000 0.4273 ns/op -BenchmarkCPUSilly2 1000000000 0.4286 ns/op -PASS -ok codeberg.org/snonux/sillybench 0.949s +root@freebsd:~/git/sillybench # go test -bench=. +goos: freebsd +goarch: amd64 +pkg: codeberg.org/snonux/sillybench +cpu: Intel(R) N100 +BenchmarkCPUSilly1 1000000000 0.4273 ns/op +BenchmarkCPUSilly2 1000000000 0.4286 ns/op +PASS +ok codeberg.org/snonux/sillybench 0.949s
It's a bit better than Linux! I am sure that this is not really a scientific benchmark, so take the results with a grain of salt!
@@ -500,16 +505,16 @@ ok codeberg.org/snonux/sillybench 0.949s by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas ubench -s 1 -Unix Benchmark Utility v.0.3 -Copyright (C) July, 1999 PhysTech, Inc. -Author: Sergei Viznyuk <sv@phystech.com> -http://www.phystech.com/download/ubench.html -FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64 -Ubench Single CPU: 671010 (0.40s) -Ubench Single MEM: 1705237 (0.48s) ------------------------------------ -Ubench Single AVG: 1188123 +paul@f0:~ % doas ubench -s 1 +Unix Benchmark Utility v.0.3 +Copyright (C) July, 1999 PhysTech, Inc. +Author: Sergei Viznyuk <sv@phystech.com> +http://www.phystech.com/download/ubench.html +FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64 +Ubench Single CPU: 671010 (0.40s) +Ubench Single MEM: 1705237 (0.48s) +----------------------------------- +Ubench Single AVG: 1188123
@@ -519,16 +524,16 @@ Ubench Single AVG: 1188123 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas ubench -Unix Benchmark Utility v.0.3 -Copyright (C) July, 1999 PhysTech, Inc. -Author: Sergei Viznyuk <sv@phystech.com> -http://www.phystech.com/download/ubench.html -FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64 -Ubench CPU: 2660220 -Ubench MEM: 3095182 --------------------- -Ubench AVG: 2877701 +paul@f0:~ % doas ubench +Unix Benchmark Utility v.0.3 +Copyright (C) July, 1999 PhysTech, Inc. +Author: Sergei Viznyuk <sv@phystech.com> +http://www.phystech.com/download/ubench.html +FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64 +Ubench CPU: 2660220 +Ubench MEM: 3095182 +-------------------- +Ubench AVG: 2877701
FreeBSD VM @ Bhyve ubench benchmark
@@ -539,16 +544,16 @@ Ubench AVG: 2877701 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -root@freebsd:~ # ubench -s 1 -Unix Benchmark Utility v.0.3 -Copyright (C) July, 1999 PhysTech, Inc. -Author: Sergei Viznyuk <sv@phystech.com> -http://www.phystech.com/download/ubench.html -FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64 -Ubench Single CPU: 672792 (0.40s) -Ubench Single MEM: 852757 (0.48s) ------------------------------------ -Ubench Single AVG: 762774 +root@freebsd:~ # ubench -s 1 +Unix Benchmark Utility v.0.3 +Copyright (C) July, 1999 PhysTech, Inc. +Author: Sergei Viznyuk <sv@phystech.com> +http://www.phystech.com/download/ubench.html +FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64 +Ubench Single CPU: 672792 (0.40s) +Ubench Single MEM: 852757 (0.48s) +----------------------------------- +Ubench Single AVG: 762774
Wow, the CPU in the VM was a tiny bit faster than on the host! So this was probably just a glitch in the matrix. Memory seems slower, though.
@@ -559,24 +564,24 @@ Ubench Single AVG: 762774 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -root@freebsd:~ # ubench -Unix Benchmark Utility v.0.3 -Copyright (C) July, 1999 PhysTech, Inc. -Author: Sergei Viznyuk <sv@phystech.com> -http://www.phystech.com/download/ubench.html -FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64 -Ubench CPU: 2652857 -swap_pager: out of swap space -swp_pager_getswapspace(27): failed -swap_pager: out of swap space -swp_pager_getswapspace(18): failed -Apr 4 23:02:43 freebsd kernel: pid 862 (ubench), jid 0, uid 0, was killed: failed to reclaim memory -swp_pager_getswapspace(6): failed -Apr 4 23:02:46 freebsd kernel: pid 863 (ubench), jid 0, uid 0, was killed: failed to reclaim memory -Apr 4 23:02:47 freebsd kernel: pid 864 (ubench), jid 0, uid 0, was killed: failed to reclaim memory -Apr 4 23:02:48 freebsd kernel: pid 865 (ubench), jid 0, uid 0, was killed: failed to reclaim memory -Apr 4 23:02:49 freebsd kernel: pid 861 (ubench), jid 0, uid 0, was killed: failed to reclaim memory -Apr 4 23:02:51 freebsd kernel: pid 839 (ubench), jid 0, uid 0, was killed: failed to reclaim memory +root@freebsd:~ # ubench +Unix Benchmark Utility v.0.3 +Copyright (C) July, 1999 PhysTech, Inc. +Author: Sergei Viznyuk <sv@phystech.com> +http://www.phystech.com/download/ubench.html +FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64 +Ubench CPU: 2652857 +swap_pager: out of swap space +swp_pager_getswapspace(27): failed +swap_pager: out of swap space +swp_pager_getswapspace(18): failed +Apr 4 23:02:43 freebsd kernel: pid 862 (ubench), jid 0, uid 0, was killed: failed to reclaim memory +swp_pager_getswapspace(6): failed +Apr 4 23:02:46 freebsd kernel: pid 863 (ubench), jid 0, uid 0, was killed: failed to reclaim memory +Apr 4 23:02:47 freebsd kernel: pid 864 (ubench), jid 0, uid 0, was killed: failed to reclaim memory +Apr 4 23:02:48 freebsd kernel: pid 865 (ubench), jid 0, uid 0, was killed: failed to reclaim memory +Apr 4 23:02:49 freebsd kernel: pid 861 (ubench), jid 0, uid 0, was killed: failed to reclaim memory +Apr 4 23:02:51 freebsd kernel: pid 839 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
The multi-CPU benchmark in the Bhyve VM ran with almost identical results to the FreeBSD host system. However, the memory benchmark failed with out-of-swap space errors. I am unsure why, as the VM has 14GB RAM, but I am not investigating further.
@@ -615,8 +620,8 @@ Apr 4 23: -[root@r0 ~]# dd if=/dev/zero of=/tmp/test bs=4k count=2000 oflag=dsync -8192000 bytes copied, 31.7058 s, 258 kB/s +[root@r0 ~]# dd if=/dev/zero of=/tmp/test bs=4k count=2000 oflag=dsync +8192000 bytes copied, 31.7058 s, 258 kB/s
The Solution: Switch to NVMe Emulation
@@ -631,14 +636,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~]# cat > /etc/dracut.conf.d/nvme.conf << EOF -add_drivers+=" nvme nvme_core " -hostonly=no -EOF +[root@r0 ~]# cat > /etc/dracut.conf.d/nvme.conf << EOF +add_drivers+=" nvme nvme_core " +hostonly=no +EOF -[root@r0 ~]# sed -i 's/# use_devicesfile = 1/use_devicesfile = 0/' /etc/lvm/lvm.conf -[root@r0 ~]# dracut -f -[root@r0 ~]# shutdown -h now +[root@r0 ~]# sed -i 's/# use_devicesfile = 1/use_devicesfile = 0/' /etc/lvm/lvm.conf +[root@r0 ~]# dracut -f +[root@r0 ~]# shutdown -h now
The hostonly=no setting ensures the initramfs includes drivers for hardware not currently present. The use_devicesfile = 0 tells LVM to scan all block devices rather than only those recorded in /etc/lvm/devices/system.devices - this is important because the device path changes from /dev/vda to /dev/nvme0n1.
@@ -651,8 +656,8 @@ EOF by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas vm stop rocky -paul@f0:~ % doas vm configure rocky +paul@f0:~ % doas vm stop rocky +paul@f0:~ % doas vm configure rocky
Change disk0_type from virtio-blk to nvme:
@@ -667,7 +672,7 @@ disk0_type="nvme" by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas vm start rocky +paul@f0:~ % doas vm start rocky
Benchmark Results
@@ -678,8 +683,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~]# dd if=/dev/zero of=/tmp/test bs=4k count=2000 oflag=dsync -8192000 bytes copied, 0.330718 s, 24.8 MB/s +[root@r0 ~]# dd if=/dev/zero of=/tmp/test bs=4k count=2000 oflag=dsync +8192000 bytes copied, 0.330718 s, 24.8 MB/s
That's approximately **100x faster** than before (24.8 MB/s vs 258 kB/s).
@@ -733,11 +738,12 @@ etcd_disk_wal_fsync_duration_seconds_bucket{le="0.004"} 408
Back to the main site
+ diff --git a/gemfeed/2025-04-19-when-book-notes.html b/gemfeed/2025-04-19-when-book-notes.html index 9fed5290..99de6991 100644 --- a/gemfeed/2025-04-19-when-book-notes.html +++ b/gemfeed/2025-04-19-when-book-notes.html @@ -2,12 +2,17 @@ +'When: The Scientific Secrets of Perfect Timing' book notes - + + + + + @@ -133,11 +138,12 @@ __ejm\___/________dwb`---`______________________
Back to the main site
+ diff --git a/gemfeed/2025-05-02-terminal-multiplexing-with-tmux-fish-edition.html b/gemfeed/2025-05-02-terminal-multiplexing-with-tmux-fish-edition.html index e179bd90..587f5056 100644 --- a/gemfeed/2025-05-02-terminal-multiplexing-with-tmux-fish-edition.html +++ b/gemfeed/2025-05-02-terminal-multiplexing-with-tmux-fish-edition.html @@ -2,12 +2,17 @@ +Terminal multiplexing with `tmux` - Fish edition - + + + + + @@ -192,14 +197,14 @@ set-option -g prefix C-g by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -function tmux::search - set -l session (tmux list-sessions | fzf | cut -d: -f1) - if test -z "$TMUX" - tmux attach-session -t $session - else - tmux switch -t $session - end -end +function tmux::search + set -l session (tmux list-sessions | fzf | cut -d: -f1) + if test -z "$TMUX" + tmux attach-session -t $session + else + tmux switch -t $session + end +end
All it does is list all currently open sessions in fzf, where one of them can be searched and selected through fuzzy find, and then either switch (if already inside a session) to the other session or attach to the other session (if not yet in Tmux).
@@ -408,11 +413,12 @@ bind-key r source-file ~/.config/tmux/tmux.conf \; display-message "tmux.conf re
Back to the main site
+ diff --git a/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html b/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html index 80f7c564..288ef718 100644 --- a/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html +++ b/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html @@ -2,12 +2,17 @@ +f3s: Kubernetes with FreeBSD - Part 5: WireGuard mesh network - + + + + + @@ -156,14 +161,14 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas freebsd-update fetch -paul@f0:~ % doas freebsd-update install -paul@f0:~ % doas shutdown -r now -.. -.. -paul@f0:~ % doas pkg update -paul@f0:~ % doas pkg upgrade -paul@f0:~ % reboot +paul@f0:~ % doas freebsd-update fetch +paul@f0:~ % doas freebsd-update install +paul@f0:~ % doas shutdown -r now +.. +.. +paul@f0:~ % doas pkg update +paul@f0:~ % doas pkg upgrade +paul@f0:~ % reboot
Next, we install wireguard-tools and configure the WireGuard service:
@@ -172,19 +177,19 @@ paul@f0:~ % reboot by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas pkg install wireguard-tools -paul@f0:~ % doas sysrc wireguard_interfaces=wg0 -wireguard_interfaces: -> wg0 -paul@f0:~ % doas sysrc wireguard_enable=YES -wireguard_enable: -> YES -paul@f0:~ % doas mkdir -p /usr/local/etc/wireguard -paul@f0:~ % doas touch /usr/local/etc/wireguard/wg0.conf -paul@f0:~ % doas service wireguard start -paul@f0:~ % doas wg show -interface: wg0 - public key: L+V9o0fNYkMVKNqsX7spBzD/9oSvxM/C7ZCZX1jLO3Q= - private key: (hidden) - listening port: 20246 +paul@f0:~ % doas pkg install wireguard-tools +paul@f0:~ % doas sysrc wireguard_interfaces=wg0 +wireguard_interfaces: -> wg0 +paul@f0:~ % doas sysrc wireguard_enable=YES +wireguard_enable: -> YES +paul@f0:~ % doas mkdir -p /usr/local/etc/wireguard +paul@f0:~ % doas touch /usr/local/etc/wireguard/wg0.conf +paul@f0:~ % doas service wireguard start +paul@f0:~ % doas wg show +interface: wg0 + public key: L+V9o0fNYkMVKNqsX7spBzD/9oSvxM/C7ZCZX1jLO3Q= + private key: (hidden) + listening port: 20246
We now have the WireGuard up and running, but it is not yet in any functional configuration. We will come back to that later.
@@ -195,34 +200,34 @@ interface: wg0 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % cat <<END | doas tee -a /etc/hosts +paul@f0:~ % cat <<END | doas tee -a /etc/hosts -192.168.1.120 r0 r0.lan r0.lan.buetow.org -192.168.1.121 r1 r1.lan r1.lan.buetow.org -192.168.1.122 r2 r2.lan r2.lan.buetow.org +192.168.1.120 r0 r0.lan r0.lan.buetow.org +192.168.1.121 r1 r1.lan r1.lan.buetow.org +192.168.1.122 r2 r2.lan r2.lan.buetow.org -192.168.2.130 f0.wg0 f0.wg0.wan.buetow.org -192.168.2.131 f1.wg0 f1.wg0.wan.buetow.org -192.168.2.132 f2.wg0 f2.wg0.wan.buetow.org +192.168.2.130 f0.wg0 f0.wg0.wan.buetow.org +192.168.2.131 f1.wg0 f1.wg0.wan.buetow.org +192.168.2.132 f2.wg0 f2.wg0.wan.buetow.org -192.168.2.120 r0.wg0 r0.wg0.wan.buetow.org -192.168.2.121 r1.wg0 r1.wg0.wan.buetow.org -192.168.2.122 r2.wg0 r2.wg0.wan.buetow.org +192.168.2.120 r0.wg0 r0.wg0.wan.buetow.org +192.168.2.121 r1.wg0 r1.wg0.wan.buetow.org +192.168.2.122 r2.wg0 r2.wg0.wan.buetow.org -192.168.2.110 blowfish.wg0 blowfish.wg0.wan.buetow.org -192.168.2.111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org +192.168.2.110 blowfish.wg0 blowfish.wg0.wan.buetow.org +192.168.2.111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org -fd42:beef:cafe:2::130 f0.wg0 f0.wg0.wan.buetow.org -fd42:beef:cafe:2::131 f1.wg0 f1.wg0.wan.buetow.org -fd42:beef:cafe:2::132 f2.wg0 f2.wg0.wan.buetow.org +fd42:beef:cafe:2::130 f0.wg0 f0.wg0.wan.buetow.org +fd42:beef:cafe:2::131 f1.wg0 f1.wg0.wan.buetow.org +fd42:beef:cafe:2::132 f2.wg0 f2.wg0.wan.buetow.org -fd42:beef:cafe:2::120 r0.wg0 r0.wg0.wan.buetow.org -fd42:beef:cafe:2::121 r1.wg0 r1.wg0.wan.buetow.org -fd42:beef:cafe:2::122 r2.wg0 r2.wg0.wan.buetow.org +fd42:beef:cafe:2::120 r0.wg0 r0.wg0.wan.buetow.org +fd42:beef:cafe:2::121 r1.wg0 r1.wg0.wan.buetow.org +fd42:beef:cafe:2::122 r2.wg0 r2.wg0.wan.buetow.org -fd42:beef:cafe:2::110 blowfish.wg0 blowfish.wg0.wan.buetow.org -fd42:beef:cafe:2::111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org -END +fd42:beef:cafe:2::110 blowfish.wg0 blowfish.wg0.wan.buetow.org +fd42:beef:cafe:2::111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org +END
As you can see, 192.168.1.0/24 is the network used in my LAN (with the fN and rN hosts) and 192.168.2.0/24 is the network used for the WireGuard mesh network. The wg0 interface will be used for all WireGuard traffic.
@@ -235,8 +240,8 @@ END by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~] dnf update -y -[root@r0 ~] reboot +[root@r0 ~] dnf update -y +[root@r0 ~] reboot
Next, we prepare WireGuard on them. Same as on the FreeBSD hosts, we will only prepare WireGuard without any useful configuration yet:
@@ -245,12 +250,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~] dnf install -y wireguard-tools -[root@r0 ~] mkdir -p /etc/wireguard -[root@r0 ~] touch /etc/wireguard/wg0.conf -[root@r0 ~] systemctl enable wg-quick@wg0.service -[root@r0 ~] systemctl start wg-quick@wg0.service -[root@r0 ~] systemctl disable firewalld +[root@r0 ~] dnf install -y wireguard-tools +[root@r0 ~] mkdir -p /etc/wireguard +[root@r0 ~] touch /etc/wireguard/wg0.conf +[root@r0 ~] systemctl enable wg-quick@wg0.service +[root@r0 ~] systemctl start wg-quick@wg0.service +[root@r0 ~] systemctl disable firewalld
We also update the hosts file accordingly:
@@ -259,34 +264,34 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~] cat <<END >>/etc/hosts +[root@r0 ~] cat <<END >>/etc/hosts -192.168.1.130 f0 f0.lan f0.lan.buetow.org -192.168.1.131 f1 f1.lan f1.lan.buetow.org -192.168.1.132 f2 f2.lan f2.lan.buetow.org +192.168.1.130 f0 f0.lan f0.lan.buetow.org +192.168.1.131 f1 f1.lan f1.lan.buetow.org +192.168.1.132 f2 f2.lan f2.lan.buetow.org -192.168.2.130 f0.wg0 f0.wg0.wan.buetow.org -192.168.2.131 f1.wg0 f1.wg0.wan.buetow.org -192.168.2.132 f2.wg0 f2.wg0.wan.buetow.org +192.168.2.130 f0.wg0 f0.wg0.wan.buetow.org +192.168.2.131 f1.wg0 f1.wg0.wan.buetow.org +192.168.2.132 f2.wg0 f2.wg0.wan.buetow.org -192.168.2.120 r0.wg0 r0.wg0.wan.buetow.org -192.168.2.121 r1.wg0 r1.wg0.wan.buetow.org -192.168.2.122 r2.wg0 r2.wg0.wan.buetow.org +192.168.2.120 r0.wg0 r0.wg0.wan.buetow.org +192.168.2.121 r1.wg0 r1.wg0.wan.buetow.org +192.168.2.122 r2.wg0 r2.wg0.wan.buetow.org -192.168.2.110 blowfish.wg0 blowfish.wg0.wan.buetow.org -192.168.2.111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org +192.168.2.110 blowfish.wg0 blowfish.wg0.wan.buetow.org +192.168.2.111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org -fd42:beef:cafe:2::130 f0.wg0 f0.wg0.wan.buetow.org -fd42:beef:cafe:2::131 f1.wg0 f1.wg0.wan.buetow.org -fd42:beef:cafe:2::132 f2.wg0 f2.wg0.wan.buetow.org +fd42:beef:cafe:2::130 f0.wg0 f0.wg0.wan.buetow.org +fd42:beef:cafe:2::131 f1.wg0 f1.wg0.wan.buetow.org +fd42:beef:cafe:2::132 f2.wg0 f2.wg0.wan.buetow.org -fd42:beef:cafe:2::120 r0.wg0 r0.wg0.wan.buetow.org -fd42:beef:cafe:2::121 r1.wg0 r1.wg0.wan.buetow.org -fd42:beef:cafe:2::122 r2.wg0 r2.wg0.wan.buetow.org +fd42:beef:cafe:2::120 r0.wg0 r0.wg0.wan.buetow.org +fd42:beef:cafe:2::121 r1.wg0 r1.wg0.wan.buetow.org +fd42:beef:cafe:2::122 r2.wg0 r2.wg0.wan.buetow.org -fd42:beef:cafe:2::110 blowfish.wg0 blowfish.wg0.wan.buetow.org -fd42:beef:cafe:2::111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org -END +fd42:beef:cafe:2::110 blowfish.wg0 blowfish.wg0.wan.buetow.org +fd42:beef:cafe:2::111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org +END
Unfortunately, the SELinux policy on Rocky Linux blocks WireGuard's operation. By making the wireguard_t domain permissive using semanage permissive -a wireguard_t, SELinux will no longer enforce restrictions for WireGuard, allowing it to work as intended:
@@ -295,9 +300,9 @@ END by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~] dnf install -y policycoreutils-python-utils -[root@r0 ~] semanage permissive -a wireguard_t -[root@r0 ~] reboot +[root@r0 ~] dnf install -y policycoreutils-python-utils +[root@r0 ~] semanage permissive -a wireguard_t +[root@r0 ~] reboot
https://github.com/angristan/wireguard-install/discussions/499
@@ -310,14 +315,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -blowfish$ doas pkg_add wireguard-tools -blowfish$ doas mkdir /etc/wireguard -blowfish$ doas touch /etc/wireguard/wg0.conf -blowsish$ cat <<END | doas tee /etc/hostname.wg0 -inet 192.168.2.110 255.255.255.0 NONE -up -!/usr/local/bin/wg setconf wg0 /etc/wireguard/wg0.conf -END +blowfish$ doas pkg_add wireguard-tools +blowfish$ doas mkdir /etc/wireguard +blowfish$ doas touch /etc/wireguard/wg0.conf +blowsish$ cat <<END | doas tee /etc/hostname.wg0 +inet 192.168.2.110 255.255.255.0 NONE +up +!/usr/local/bin/wg setconf wg0 /etc/wireguard/wg0.conf +END
Note that on blowfish, we configure 192.168.2.110 here in the hostname.wg, and on fishfinger, we configure 192.168.2.111. Those are the IP addresses of the WireGuard interfaces on those hosts.
@@ -328,34 +333,34 @@ END by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -blowfish$ cat <<END | doas tee -a /etc/hosts - -192.168.2.130 f0.wg0 f0.wg0.wan.buetow.org -192.168.2.131 f1.wg0 f1.wg0.wan.buetow.org -192.168.2.132 f2.wg0 f2.wg0.wan.buetow.org - -192.168.2.120 r0.wg0 r0.wg0.wan.buetow.org -192.168.2.121 r1.wg0 r1.wg0.wan.buetow.org -192.168.2.122 r2.wg0 r2.wg0.wan.buetow.org - -192.168.2.110 blowfish.wg0 blowfish.wg0.wan.buetow.org -192.168.2.111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org -192.168.2.200 earth.wg0 earth.wg0.wan.buetow.org -192.168.2.201 pixel7pro.wg0 pixel7pro.wg0.wan.buetow.org - -fd42:beef:cafe:2::130 f0.wg0 f0.wg0.wan.buetow.org -fd42:beef:cafe:2::131 f1.wg0 f1.wg0.wan.buetow.org -fd42:beef:cafe:2::132 f2.wg0 f2.wg0.wan.buetow.org - -fd42:beef:cafe:2::120 r0.wg0 r0.wg0.wan.buetow.org -fd42:beef:cafe:2::121 r1.wg0 r1.wg0.wan.buetow.org -fd42:beef:cafe:2::122 r2.wg0 r2.wg0.wan.buetow.org - -fd42:beef:cafe:2::110 blowfish.wg0 blowfish.wg0.wan.buetow.org -fd42:beef:cafe:2::111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org -fd42:beef:cafe:2::200 earth.wg0 earth.wg0.wan.buetow.org -fd42:beef:cafe:2::201 pixel7pro.wg0 pixel7pro.wg0.wan.buetow.org -END +blowfish$ cat <<END | doas tee -a /etc/hosts + +192.168.2.130 f0.wg0 f0.wg0.wan.buetow.org +192.168.2.131 f1.wg0 f1.wg0.wan.buetow.org +192.168.2.132 f2.wg0 f2.wg0.wan.buetow.org + +192.168.2.120 r0.wg0 r0.wg0.wan.buetow.org +192.168.2.121 r1.wg0 r1.wg0.wan.buetow.org +192.168.2.122 r2.wg0 r2.wg0.wan.buetow.org + +192.168.2.110 blowfish.wg0 blowfish.wg0.wan.buetow.org +192.168.2.111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org +192.168.2.200 earth.wg0 earth.wg0.wan.buetow.org +192.168.2.201 pixel7pro.wg0 pixel7pro.wg0.wan.buetow.org + +fd42:beef:cafe:2::130 f0.wg0 f0.wg0.wan.buetow.org +fd42:beef:cafe:2::131 f1.wg0 f1.wg0.wan.buetow.org +fd42:beef:cafe:2::132 f2.wg0 f2.wg0.wan.buetow.org + +fd42:beef:cafe:2::120 r0.wg0 r0.wg0.wan.buetow.org +fd42:beef:cafe:2::121 r1.wg0 r1.wg0.wan.buetow.org +fd42:beef:cafe:2::122 r2.wg0 r2.wg0.wan.buetow.org + +fd42:beef:cafe:2::110 blowfish.wg0 blowfish.wg0.wan.buetow.org +fd42:beef:cafe:2::111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org +fd42:beef:cafe:2::200 earth.wg0 earth.wg0.wan.buetow.org +fd42:beef:cafe:2::201 pixel7pro.wg0 pixel7pro.wg0.wan.buetow.org +END
To enable roaming clients (like earth and pixel7pro) to access the internet through the VPN, we need to configure NAT on the OpenBSD gateways. This allows the roaming clients to use the gateway's public IP address for outbound traffic. We add the following to /etc/pf.conf on both blowfish and fishfinger:
@@ -364,14 +369,14 @@ END by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# NAT for WireGuard clients to access internet -match out on vio0 from 192.168.2.0/24 to any nat-to (vio0) +# NAT for WireGuard clients to access internet +match out on vio0 from 192.168.2.0/24 to any nat-to (vio0) -# Allow inbound traffic on WireGuard interface -pass in on wg0 +# Allow inbound traffic on WireGuard interface +pass in on wg0 -# Allow all UDP traffic on WireGuard port -pass in inet proto udp from any to any port 56709 +# Allow all UDP traffic on WireGuard port +pass in inet proto udp from any to any port 56709
The NAT rule translates outgoing traffic from the WireGuard network (192.168.2.0/24) to the gateway's public IP. The firewall rules permit WireGuard traffic on the wg0 interface and UDP port 56709. After updating /etc/pf.conf, reload the firewall:
@@ -380,7 +385,7 @@ pass in inet proto udp from any to an by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -blowfish$ doas pfctl -f /etc/pf.conf +blowfish$ doas pfctl -f /etc/pf.conf
WireGuard configuration
@@ -539,10 +544,10 @@ PersistentKeepalive = 25 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -> git clone https://codeberg.org/snonux/wireguardmeshgenerator -> cd ./wireguardmeshgenerator -> bundle install -> sudo dnf install -y wireguard-tools +> git clone https://codeberg.org/snonux/wireguardmeshgenerator +> cd ./wireguardmeshgenerator +> bundle install +> sudo dnf install -y wireguard-tools
This assumes that Ruby and the bundler gem are already installed. If not, refer to the docs of your distribution.
@@ -742,39 +747,39 @@ hosts: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -begin - options = { hosts: [] } - OptionParser.new do |opts| - opts.banner = 'Usage: wireguardmeshgenerator.rb [options]' - opts.on('--generate', 'Generate Wireguard configs') do - options[:generate] = true - end - opts.on('--install', 'Install Wireguard configs') do - options[:install] = true - end - opts.on('--clean', 'Clean Wireguard configs') do - options[:clean] = true - end - opts.on('--hosts=HOSTS', 'Comma separated hosts to configure') do |hosts| - options[:hosts] = hosts.split(',') - end - end.parse! - - conf = YAML.load_file('wireguardmeshgenerator.yaml').freeze - conf['hosts'].keys.select { options[:hosts].empty? || options[:hosts].include?(_1) } - .each do |host| - # Generate Wireguard configuration for the host reload! - WireguardConfig.new(host, conf['hosts']).generate! if options[:generate] - # Install Wireguard configuration for the host. - InstallConfig.new(host, conf['hosts']).upload!.install!.reload! if options[:install] - # Clean Wireguard configuration for the host. - WireguardConfig.new(host, conf['hosts']).clean! if options[:clean] - end -rescue StandardError => e - puts "Error: #{e.message}" - puts e.backtrace.join("\n") - exit 2 -end +begin + options = { hosts: [] } + OptionParser.new do |opts| + opts.banner = 'Usage: wireguardmeshgenerator.rb [options]' + opts.on('--generate', 'Generate Wireguard configs') do + options[:generate] = true + end + opts.on('--install', 'Install Wireguard configs') do + options[:install] = true + end + opts.on('--clean', 'Clean Wireguard configs') do + options[:clean] = true + end + opts.on('--hosts=HOSTS', 'Comma separated hosts to configure') do |hosts| + options[:hosts] = hosts.split(',') + end + end.parse! + + conf = YAML.load_file('wireguardmeshgenerator.yaml').freeze + conf['hosts'].keys.select { options[:hosts].empty? || options[:hosts].include?(_1) } + .each do |host| + # Generate Wireguard configuration for the host reload! + WireguardConfig.new(host, conf['hosts']).generate! if options[:generate] + # Install Wireguard configuration for the host. + InstallConfig.new(host, conf['hosts']).upload!.install!.reload! if options[:install] + # Clean Wireguard configuration for the host. + WireguardConfig.new(host, conf['hosts']).clean! if options[:clean] + end +rescue StandardError => e + puts "Error: #{e.message}" + puts e.backtrace.join("\n") + exit 2 +end
And we also have a Rakefile:
@@ -783,19 +788,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -task :generate do - ruby 'wireguardmeshgenerator.rb', '--generate' -end +task :generate do + ruby 'wireguardmeshgenerator.rb', '--generate' +end -task :clean do - ruby 'wireguardmeshgenerator.rb', '--clean' -end +task :clean do + ruby 'wireguardmeshgenerator.rb', '--clean' +end -task :install do - ruby 'wireguardmeshgenerator.rb', '--install' -end +task :install do + ruby 'wireguardmeshgenerator.rb', '--install' +end -task default: :generate +task default: :generate
@@ -809,18 +814,18 @@ task default: :generate by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -> rake generate -/usr/bin/ruby wireguardmeshgenerator.rb --generate -Generating dist/f0/etc/wireguard/wg0.conf -Generating dist/f1/etc/wireguard/wg0.conf -Generating dist/f2/etc/wireguard/wg0.conf -Generating dist/r0/etc/wireguard/wg0.conf -Generating dist/r1/etc/wireguard/wg0.conf -Generating dist/r2/etc/wireguard/wg0.conf -Generating dist/blowfish/etc/wireguard/wg0.conf -Generating dist/fishfinger/etc/wireguard/wg0.conf -Generating dist/earth/etc/wireguard/wg0.conf -Generating dist/pixel7pro/etc/wireguard/wg0.conf +> rake generate +/usr/bin/ruby wireguardmeshgenerator.rb --generate +Generating dist/f0/etc/wireguard/wg0.conf +Generating dist/f1/etc/wireguard/wg0.conf +Generating dist/f2/etc/wireguard/wg0.conf +Generating dist/r0/etc/wireguard/wg0.conf +Generating dist/r1/etc/wireguard/wg0.conf +Generating dist/r2/etc/wireguard/wg0.conf +Generating dist/blowfish/etc/wireguard/wg0.conf +Generating dist/fishfinger/etc/wireguard/wg0.conf +Generating dist/earth/etc/wireguard/wg0.conf +Generating dist/pixel7pro/etc/wireguard/wg0.conf
It generated all the wg0.conf files listed in the output, plus those keys:
@@ -829,59 +834,59 @@ Generating dist/pixel7pro/etc/wireguard/wg0.conf by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -> find keys/ -type f -keys/f0/priv.key -keys/f0/pub.key -keys/psk/f0_f1.key -keys/psk/f0_f2.key -keys/psk/f0_r0.key -keys/psk/f0_r1.key -keys/psk/f0_r2.key -keys/psk/blowfish_f0.key -keys/psk/f0_fishfinger.key -keys/psk/f1_f2.key -keys/psk/f1_r0.key -keys/psk/f1_r1.key -keys/psk/f1_r2.key -keys/psk/blowfish_f1.key -keys/psk/f1_fishfinger.key -keys/psk/f2_r0.key -keys/psk/f2_r1.key -keys/psk/f2_r2.key -keys/psk/blowfish_f2.key -keys/psk/f2_fishfinger.key -keys/psk/r0_r1.key -keys/psk/r0_r2.key -keys/psk/blowfish_r0.key -keys/psk/fishfinger_r0.key -keys/psk/r1_r2.key -keys/psk/blowfish_r1.key -keys/psk/fishfinger_r1.key -keys/psk/blowfish_r2.key -keys/psk/fishfinger_r2.key -keys/psk/blowfish_fishfinger.key -keys/psk/blowfish_earth.key -keys/psk/earth_fishfinger.key -keys/psk/blowfish_pixel7pro.key -keys/psk/fishfinger_pixel7pro.key -keys/f1/priv.key -keys/f1/pub.key -keys/f2/priv.key -keys/f2/pub.key -keys/r0/priv.key -keys/r0/pub.key -keys/r1/priv.key -keys/r1/pub.key -keys/r2/priv.key -keys/r2/pub.key -keys/blowfish/priv.key -keys/blowfish/pub.key -keys/fishfinger/priv.key -keys/fishfinger/pub.key -keys/earth/priv.key -keys/earth/pub.key -keys/pixel7pro/priv.key -keys/pixel7pro/pub.key +> find keys/ -type f +keys/f0/priv.key +keys/f0/pub.key +keys/psk/f0_f1.key +keys/psk/f0_f2.key +keys/psk/f0_r0.key +keys/psk/f0_r1.key +keys/psk/f0_r2.key +keys/psk/blowfish_f0.key +keys/psk/f0_fishfinger.key +keys/psk/f1_f2.key +keys/psk/f1_r0.key +keys/psk/f1_r1.key +keys/psk/f1_r2.key +keys/psk/blowfish_f1.key +keys/psk/f1_fishfinger.key +keys/psk/f2_r0.key +keys/psk/f2_r1.key +keys/psk/f2_r2.key +keys/psk/blowfish_f2.key +keys/psk/f2_fishfinger.key +keys/psk/r0_r1.key +keys/psk/r0_r2.key +keys/psk/blowfish_r0.key +keys/psk/fishfinger_r0.key +keys/psk/r1_r2.key +keys/psk/blowfish_r1.key +keys/psk/fishfinger_r1.key +keys/psk/blowfish_r2.key +keys/psk/fishfinger_r2.key +keys/psk/blowfish_fishfinger.key +keys/psk/blowfish_earth.key +keys/psk/earth_fishfinger.key +keys/psk/blowfish_pixel7pro.key +keys/psk/fishfinger_pixel7pro.key +keys/f1/priv.key +keys/f1/pub.key +keys/f2/priv.key +keys/f2/pub.key +keys/r0/priv.key +keys/r0/pub.key +keys/r1/priv.key +keys/r1/pub.key +keys/r2/priv.key +keys/r2/pub.key +keys/blowfish/priv.key +keys/blowfish/pub.key +keys/fishfinger/priv.key +keys/fishfinger/pub.key +keys/earth/priv.key +keys/earth/pub.key +keys/pixel7pro/priv.key +keys/pixel7pro/pub.key
Those keys are embedded in the resulting wg0.conf, so later, we only need to install the wg0.conf files and not all the keys individually.
@@ -894,112 +899,112 @@ keys/pixel7pro/pub.key by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -> rake install -/usr/bin/ruby wireguardmeshgenerator.rb --install -Uploading dist/f0/etc/wireguard/wg0.conf to f0.lan.buetow.org:. -Installing Wireguard config on f0 -Uploading cmd.sh to f0.lan.buetow.org:. -+ [ ! -d /usr/local/etc/wireguard ] -+ doas chmod 700 /usr/local/etc/wireguard -+ doas mv -v wg0.conf /usr/local/etc/wireguard -wg0.conf -> /usr/local/etc/wireguard/wg0.conf -+ doas chmod 644 /usr/local/etc/wireguard/wg0.conf -+ rm cmd.sh -Reloading Wireguard on f0 -Uploading cmd.sh to f0.lan.buetow.org:. -+ doas service wireguard reload -+ rm cmd.sh -Uploading dist/f1/etc/wireguard/wg0.conf to f1.lan.buetow.org:. -Installing Wireguard config on f1 -Uploading cmd.sh to f1.lan.buetow.org:. -+ [ ! -d /usr/local/etc/wireguard ] -+ doas chmod 700 /usr/local/etc/wireguard -+ doas mv -v wg0.conf /usr/local/etc/wireguard -wg0.conf -> /usr/local/etc/wireguard/wg0.conf -+ doas chmod 644 /usr/local/etc/wireguard/wg0.conf -+ rm cmd.sh -Reloading Wireguard on f1 -Uploading cmd.sh to f1.lan.buetow.org:. -+ doas service wireguard reload -+ rm cmd.sh -Uploading dist/f2/etc/wireguard/wg0.conf to f2.lan.buetow.org:. -Installing Wireguard config on f2 -Uploading cmd.sh to f2.lan.buetow.org:. -+ [ ! -d /usr/local/etc/wireguard ] -+ doas chmod 700 /usr/local/etc/wireguard -+ doas mv -v wg0.conf /usr/local/etc/wireguard -wg0.conf -> /usr/local/etc/wireguard/wg0.conf -+ doas chmod 644 /usr/local/etc/wireguard/wg0.conf -+ rm cmd.sh -Reloading Wireguard on f2 -Uploading cmd.sh to f2.lan.buetow.org:. -+ doas service wireguard reload -+ rm cmd.sh -Uploading dist/r0/etc/wireguard/wg0.conf to r0.lan.buetow.org:. -Installing Wireguard config on r0 -Uploading cmd.sh to r0.lan.buetow.org:. -+ '[' '!' -d /etc/wireguard ']' -+ chmod 700 /etc/wireguard -+ mv -v wg0.conf /etc/wireguard -renamed 'wg0.conf' -> '/etc/wireguard/wg0.conf' -+ chmod 644 /etc/wireguard/wg0.conf -+ rm cmd.sh -Reloading Wireguard on r0 -Uploading cmd.sh to r0.lan.buetow.org:. -+ systemctl reload wg-quick@wg0.service -+ rm cmd.sh -Uploading dist/r1/etc/wireguard/wg0.conf to r1.lan.buetow.org:. -Installing Wireguard config on r1 -Uploading cmd.sh to r1.lan.buetow.org:. -+ '[' '!' -d /etc/wireguard ']' -+ chmod 700 /etc/wireguard -+ mv -v wg0.conf /etc/wireguard -renamed 'wg0.conf' -> '/etc/wireguard/wg0.conf' -+ chmod 644 /etc/wireguard/wg0.conf -+ rm cmd.sh -Reloading Wireguard on r1 -Uploading cmd.sh to r1.lan.buetow.org:. -+ systemctl reload wg-quick@wg0.service -+ rm cmd.sh -Uploading dist/r2/etc/wireguard/wg0.conf to r2.lan.buetow.org:. -Installing Wireguard config on r2 -Uploading cmd.sh to r2.lan.buetow.org:. -+ '[' '!' -d /etc/wireguard ']' -+ chmod 700 /etc/wireguard -+ mv -v wg0.conf /etc/wireguard -renamed 'wg0.conf' -> '/etc/wireguard/wg0.conf' -+ chmod 644 /etc/wireguard/wg0.conf -+ rm cmd.sh -Reloading Wireguard on r2 -Uploading cmd.sh to r2.lan.buetow.org:. -+ systemctl reload wg-quick@wg0.service -+ rm cmd.sh -Uploading dist/blowfish/etc/wireguard/wg0.conf to blowfish.buetow.org:. -Installing Wireguard config on blowfish -Uploading cmd.sh to blowfish.buetow.org:. -+ [ ! -d /etc/wireguard ] -+ doas chmod 700 /etc/wireguard -+ doas mv -v wg0.conf /etc/wireguard -wg0.conf -> /etc/wireguard/wg0.conf -+ doas chmod 644 /etc/wireguard/wg0.conf -+ rm cmd.sh -Reloading Wireguard on blowfish -Uploading cmd.sh to blowfish.buetow.org:. -+ doas sh /etc/netstart wg0 -+ rm cmd.sh -Uploading dist/fishfinger/etc/wireguard/wg0.conf to fishfinger.buetow.org:. -Installing Wireguard config on fishfinger -Uploading cmd.sh to fishfinger.buetow.org:. -+ [ ! -d /etc/wireguard ] -+ doas chmod 700 /etc/wireguard -+ doas mv -v wg0.conf /etc/wireguard -wg0.conf -> /etc/wireguard/wg0.conf -+ doas chmod 644 /etc/wireguard/wg0.conf -+ rm cmd.sh -Reloading Wireguard on fishfinger -Uploading cmd.sh to fishfinger.buetow.org:. -+ doas sh /etc/netstart wg0 -+ rm cmd.sh +> rake install +/usr/bin/ruby wireguardmeshgenerator.rb --install +Uploading dist/f0/etc/wireguard/wg0.conf to f0.lan.buetow.org:. +Installing Wireguard config on f0 +Uploading cmd.sh to f0.lan.buetow.org:. ++ [ ! -d /usr/local/etc/wireguard ] ++ doas chmod 700 /usr/local/etc/wireguard ++ doas mv -v wg0.conf /usr/local/etc/wireguard +wg0.conf -> /usr/local/etc/wireguard/wg0.conf ++ doas chmod 644 /usr/local/etc/wireguard/wg0.conf ++ rm cmd.sh +Reloading Wireguard on f0 +Uploading cmd.sh to f0.lan.buetow.org:. ++ doas service wireguard reload ++ rm cmd.sh +Uploading dist/f1/etc/wireguard/wg0.conf to f1.lan.buetow.org:. +Installing Wireguard config on f1 +Uploading cmd.sh to f1.lan.buetow.org:. ++ [ ! -d /usr/local/etc/wireguard ] ++ doas chmod 700 /usr/local/etc/wireguard ++ doas mv -v wg0.conf /usr/local/etc/wireguard +wg0.conf -> /usr/local/etc/wireguard/wg0.conf ++ doas chmod 644 /usr/local/etc/wireguard/wg0.conf ++ rm cmd.sh +Reloading Wireguard on f1 +Uploading cmd.sh to f1.lan.buetow.org:. ++ doas service wireguard reload ++ rm cmd.sh +Uploading dist/f2/etc/wireguard/wg0.conf to f2.lan.buetow.org:. +Installing Wireguard config on f2 +Uploading cmd.sh to f2.lan.buetow.org:. ++ [ ! -d /usr/local/etc/wireguard ] ++ doas chmod 700 /usr/local/etc/wireguard ++ doas mv -v wg0.conf /usr/local/etc/wireguard +wg0.conf -> /usr/local/etc/wireguard/wg0.conf ++ doas chmod 644 /usr/local/etc/wireguard/wg0.conf ++ rm cmd.sh +Reloading Wireguard on f2 +Uploading cmd.sh to f2.lan.buetow.org:. ++ doas service wireguard reload ++ rm cmd.sh +Uploading dist/r0/etc/wireguard/wg0.conf to r0.lan.buetow.org:. +Installing Wireguard config on r0 +Uploading cmd.sh to r0.lan.buetow.org:. ++ '[' '!' -d /etc/wireguard ']' ++ chmod 700 /etc/wireguard ++ mv -v wg0.conf /etc/wireguard +renamed 'wg0.conf' -> '/etc/wireguard/wg0.conf' ++ chmod 644 /etc/wireguard/wg0.conf ++ rm cmd.sh +Reloading Wireguard on r0 +Uploading cmd.sh to r0.lan.buetow.org:. ++ systemctl reload wg-quick@wg0.service ++ rm cmd.sh +Uploading dist/r1/etc/wireguard/wg0.conf to r1.lan.buetow.org:. +Installing Wireguard config on r1 +Uploading cmd.sh to r1.lan.buetow.org:. ++ '[' '!' -d /etc/wireguard ']' ++ chmod 700 /etc/wireguard ++ mv -v wg0.conf /etc/wireguard +renamed 'wg0.conf' -> '/etc/wireguard/wg0.conf' ++ chmod 644 /etc/wireguard/wg0.conf ++ rm cmd.sh +Reloading Wireguard on r1 +Uploading cmd.sh to r1.lan.buetow.org:. ++ systemctl reload wg-quick@wg0.service ++ rm cmd.sh +Uploading dist/r2/etc/wireguard/wg0.conf to r2.lan.buetow.org:. +Installing Wireguard config on r2 +Uploading cmd.sh to r2.lan.buetow.org:. ++ '[' '!' -d /etc/wireguard ']' ++ chmod 700 /etc/wireguard ++ mv -v wg0.conf /etc/wireguard +renamed 'wg0.conf' -> '/etc/wireguard/wg0.conf' ++ chmod 644 /etc/wireguard/wg0.conf ++ rm cmd.sh +Reloading Wireguard on r2 +Uploading cmd.sh to r2.lan.buetow.org:. ++ systemctl reload wg-quick@wg0.service ++ rm cmd.sh +Uploading dist/blowfish/etc/wireguard/wg0.conf to blowfish.buetow.org:. +Installing Wireguard config on blowfish +Uploading cmd.sh to blowfish.buetow.org:. ++ [ ! -d /etc/wireguard ] ++ doas chmod 700 /etc/wireguard ++ doas mv -v wg0.conf /etc/wireguard +wg0.conf -> /etc/wireguard/wg0.conf ++ doas chmod 644 /etc/wireguard/wg0.conf ++ rm cmd.sh +Reloading Wireguard on blowfish +Uploading cmd.sh to blowfish.buetow.org:. ++ doas sh /etc/netstart wg0 ++ rm cmd.sh +Uploading dist/fishfinger/etc/wireguard/wg0.conf to fishfinger.buetow.org:. +Installing Wireguard config on fishfinger +Uploading cmd.sh to fishfinger.buetow.org:. ++ [ ! -d /etc/wireguard ] ++ doas chmod 700 /etc/wireguard ++ doas mv -v wg0.conf /etc/wireguard +wg0.conf -> /etc/wireguard/wg0.conf ++ doas chmod 644 /etc/wireguard/wg0.conf ++ rm cmd.sh +Reloading Wireguard on fishfinger +Uploading cmd.sh to fishfinger.buetow.org:. ++ doas sh /etc/netstart wg0 ++ rm cmd.sh
Re-generating mesh and installing the wg0.conf files again
@@ -1010,9 +1015,9 @@ Uploading cmd.sh to fishfinger.buetow.org:. by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -> rake clean -> rake generate -> rake install +> rake clean +> rake generate +> rake install
That would also delete and re-generate all the keys involved.
@@ -1029,8 +1034,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -> sudo dnf install qrencode -> qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0.conf +> sudo dnf install qrencode +> qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0.conf
Scan the QR code with the WireGuard app to import the configuration. The phone will then route all traffic through the VPN when the tunnel is activated. Note that WireGuard does not support automatic failover between the two gateways (blowfish and fishfinger)—if one fails, manual disconnection and reconnection is required to switch to the other.
@@ -1043,10 +1048,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -> sudo cp dist/earth/etc/wireguard/wg0.conf /etc/wireguard/ -> sudo chmod 600 /etc/wireguard/wg0.conf -> sudo systemctl start wg-quick@wg0.service # Start manually -> sudo systemctl disable wg-quick@wg0.service # Prevent auto-start +> sudo cp dist/earth/etc/wireguard/wg0.conf /etc/wireguard/ +> sudo chmod 600 /etc/wireguard/wg0.conf +> sudo systemctl start wg-quick@wg0.service # Start manually +> sudo systemctl disable wg-quick@wg0.service # Prevent auto-start
The service is disabled from auto-start so the VPN is only active when manually started. This allows selective VPN usage based on need.
@@ -1090,19 +1095,19 @@ fd42:beef:cafe:2::201/64 - pixel7pro.wg0 (roaming phone) by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -def address - return '# No Address = ... for OpenBSD here' if hosts[myself]['os'] == 'OpenBSD' - - ipv4 = hosts[myself]['wg0']['ip'] - ipv6 = hosts[myself]['wg0']['ipv6'] - - # WireGuard supports multiple Address directives for dual-stack - if ipv6 - "Address = #{ipv4}\nAddress = #{ipv6}/64" - else - "Address = #{ipv4}" - end -end +def address + return '# No Address = ... for OpenBSD here' if hosts[myself]['os'] == 'OpenBSD' + + ipv4 = hosts[myself]['wg0']['ip'] + ipv6 = hosts[myself]['wg0']['ipv6'] + + # WireGuard supports multiple Address directives for dual-stack + if ipv6 + "Address = #{ipv4}\nAddress = #{ipv6}/64" + else + "Address = #{ipv4}" + end +end
**2. AllowedIPs generation (peers method)**
@@ -1113,14 +1118,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -if is_roaming - allowed_ips = '0.0.0.0/0, ::/0' -else - # For mesh peers, allow both IPv4 and IPv6 if present - ipv4 = data['wg0']['ip'] - ipv6 = data['wg0']['ipv6'] - allowed_ips = ipv6 ? "#{ipv4}/32, #{ipv6}/128" : "#{ipv4}/32" -end +if is_roaming + allowed_ips = '0.0.0.0/0, ::/0' +else + # For mesh peers, allow both IPv4 and IPv6 if present + ipv4 = data['wg0']['ip'] + ipv6 = data['wg0']['ipv6'] + allowed_ips = ipv6 ? "#{ipv4}/32, #{ipv6}/128" : "#{ipv4}/32" +end
Roaming clients keep AllowedIPs = 0.0.0.0/0, ::/0 to route all traffic (IPv4 and IPv6) through the VPN.
@@ -1152,7 +1157,7 @@ pass in inet6 proto udp from any to any port 56709 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -rex@blowfish:~ $ doas vi /etc/hostname.wg0 +rex@blowfish:~ $ doas vi /etc/hostname.wg0
Add the IPv6 address (note the order - IPv6 must be configured before up):
@@ -1172,9 +1177,9 @@ up by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -rex@blowfish:~ $ doas sh /etc/netstart wg0 -rex@blowfish:~ $ ifconfig wg0 | grep inet6 -inet6 fd42:beef:cafe:2::110 prefixlen 64 +rex@blowfish:~ $ doas sh /etc/netstart wg0 +rex@blowfish:~ $ ifconfig wg0 | grep inet6 +inet6 fd42:beef:cafe:2::110 prefixlen 64
Repeat for fishfinger with address fd42:beef:cafe:2::111.
@@ -1189,14 +1194,14 @@ inet6 fd42:beef:cafe:2::110 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# From r0 (Rocky Linux VM) -root@r0:~ # ping -c 2 192.168.2.130 # IPv4 to f0 -64 bytes from 192.168.2.130: icmp_seq=1 ttl=64 time=2.12 ms -64 bytes from 192.168.2.130: icmp_seq=2 ttl=64 time=0.681 ms - -root@r0:~ # ping6 -c 2 fd42:beef:cafe:2::130 # IPv6 to f0 -64 bytes from fd42:beef:cafe:2::130: icmp_seq=1 ttl=64 time=2.16 ms -64 bytes from fd42:beef:cafe:2::130: icmp_seq=2 ttl=64 time=0.909 ms +# From r0 (Rocky Linux VM) +root@r0:~ # ping -c 2 192.168.2.130 # IPv4 to f0 +64 bytes from 192.168.2.130: icmp_seq=1 ttl=64 time=2.12 ms +64 bytes from 192.168.2.130: icmp_seq=2 ttl=64 time=0.681 ms + +root@r0:~ # ping6 -c 2 fd42:beef:cafe:2::130 # IPv6 to f0 +64 bytes from fd42:beef:cafe:2::130: icmp_seq=1 ttl=64 time=2.16 ms +64 bytes from fd42:beef:cafe:2::130: icmp_seq=2 ttl=64 time=0.909 ms
The dual-stack configuration is backward compatible—hosts without the ipv6 field in the YAML configuration will continue to generate IPv4-only configs.
@@ -1219,52 +1224,52 @@ root@r0:~ # ping6 -c 2 fd42:beef:cafe:2::130 # IPv6 to by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas wg show -interface: wg0 - public key: Jm6YItMt94++dIeOyVi1I9AhNt2qQcryxCZezoX7X2Y= - private key: (hidden) - listening port: 56709 - -peer: 8PvGZH1NohHpZPVJyjhctBX9xblsNvYBhpg68FsFcns= - preshared key: (hidden) - endpoint: 46.23.94.99:56709 - allowed ips: 192.168.2.111/32, fd42:beef:cafe:2::111/128 - latest handshake: 1 minute, 46 seconds ago - transfer: 124 B received, 1.75 KiB sent - persistent keepalive: every 25 seconds - -peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns= - preshared key: (hidden) - endpoint: 23.88.35.144:56709 - allowed ips: 192.168.2.110/32, fd42:beef:cafe:2::110/128 - latest handshake: 1 minute, 52 seconds ago - transfer: 124 B received, 1.60 KiB sent - persistent keepalive: every 25 seconds - -peer: s3e93XoY7dPUQgLiVO4d8x/SRCFgEew+/wP7+zwgehI= - preshared key: (hidden) - endpoint: 192.168.1.120:56709 - allowed ips: 192.168.2.120/32, fd42:beef:cafe:2::120/128 - -peer: 2htXdNcxzpI2FdPDJy4T4VGtm1wpMEQu1AkQHjNY6F8= - preshared key: (hidden) - endpoint: 192.168.1.131:56709 - allowed ips: 192.168.2.131/32, fd42:beef:cafe:2::131/128 - -peer: 0Y/H20W8YIbF7DA1sMwMacLI8WS9yG+1/QO7m2oyllg= - preshared key: (hidden) - endpoint: 192.168.1.122:56709 - allowed ips: 192.168.2.122/32, fd42:beef:cafe:2::122/128 - -peer: Hhy9kMPOOjChXV2RA5WeCGs+J0FE3rcNPDw/TLSn7i8= - preshared key: (hidden) - endpoint: 192.168.1.121:56709 - allowed ips: 192.168.2.121/32, fd42:beef:cafe:2::121/128 - -peer: SlGVsACE1wiaRoGvCR3f7AuHfRS+1jjhS+YwEJ2HvF0= - preshared key: (hidden) - endpoint: 192.168.1.132:56709 - allowed ips: 192.168.2.132/32, fd42:beef:cafe:2::132/128 +paul@f0:~ % doas wg show +interface: wg0 + public key: Jm6YItMt94++dIeOyVi1I9AhNt2qQcryxCZezoX7X2Y= + private key: (hidden) + listening port: 56709 + +peer: 8PvGZH1NohHpZPVJyjhctBX9xblsNvYBhpg68FsFcns= + preshared key: (hidden) + endpoint: 46.23.94.99:56709 + allowed ips: 192.168.2.111/32, fd42:beef:cafe:2::111/128 + latest handshake: 1 minute, 46 seconds ago + transfer: 124 B received, 1.75 KiB sent + persistent keepalive: every 25 seconds + +peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns= + preshared key: (hidden) + endpoint: 23.88.35.144:56709 + allowed ips: 192.168.2.110/32, fd42:beef:cafe:2::110/128 + latest handshake: 1 minute, 52 seconds ago + transfer: 124 B received, 1.60 KiB sent + persistent keepalive: every 25 seconds + +peer: s3e93XoY7dPUQgLiVO4d8x/SRCFgEew+/wP7+zwgehI= + preshared key: (hidden) + endpoint: 192.168.1.120:56709 + allowed ips: 192.168.2.120/32, fd42:beef:cafe:2::120/128 + +peer: 2htXdNcxzpI2FdPDJy4T4VGtm1wpMEQu1AkQHjNY6F8= + preshared key: (hidden) + endpoint: 192.168.1.131:56709 + allowed ips: 192.168.2.131/32, fd42:beef:cafe:2::131/128 + +peer: 0Y/H20W8YIbF7DA1sMwMacLI8WS9yG+1/QO7m2oyllg= + preshared key: (hidden) + endpoint: 192.168.1.122:56709 + allowed ips: 192.168.2.122/32, fd42:beef:cafe:2::122/128 + +peer: Hhy9kMPOOjChXV2RA5WeCGs+J0FE3rcNPDw/TLSn7i8= + preshared key: (hidden) + endpoint: 192.168.1.121:56709 + allowed ips: 192.168.2.121/32, fd42:beef:cafe:2::121/128 + +peer: SlGVsACE1wiaRoGvCR3f7AuHfRS+1jjhS+YwEJ2HvF0= + preshared key: (hidden) + endpoint: 192.168.1.132:56709 + allowed ips: 192.168.2.132/32, fd42:beef:cafe:2::132/128
All the hosts are pingable as well, e.g.:
@@ -1273,65 +1278,65 @@ peer: SlGVsACE1wiaRoGvCR3f7AuHfRS+1jjhS+YwEJ2HvF0= by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % foreach peer ( f1 f2 r0 r1 r2 blowfish fishfinger ) -foreach? ping -c2 $peer.wg0 -foreach? echo -foreach? end -PING f1.wg0 (192.168.2.131): 56 data bytes -64 bytes from 192.168.2.131: icmp_seq=0 ttl=64 time=0.334 ms -64 bytes from 192.168.2.131: icmp_seq=1 ttl=64 time=0.260 ms - ---- f1.wg0 ping statistics --- -2 packets transmitted, 2 packets received, 0.0% packet loss -round-trip min/avg/max/stddev = 0.260/0.297/0.334/0.037 ms - -PING f2.wg0 (192.168.2.132): 56 data bytes -64 bytes from 192.168.2.132: icmp_seq=0 ttl=64 time=0.323 ms -64 bytes from 192.168.2.132: icmp_seq=1 ttl=64 time=0.303 ms - ---- f2.wg0 ping statistics --- -2 packets transmitted, 2 packets received, 0.0% packet loss -round-trip min/avg/max/stddev = 0.303/0.313/0.323/0.010 ms - -PING r0.wg0 (192.168.2.120): 56 data bytes -64 bytes from 192.168.2.120: icmp_seq=0 ttl=64 time=0.716 ms -64 bytes from 192.168.2.120: icmp_seq=1 ttl=64 time=0.406 ms - ---- r0.wg0 ping statistics --- -2 packets transmitted, 2 packets received, 0.0% packet loss -round-trip min/avg/max/stddev = 0.406/0.561/0.716/0.155 ms - -PING r1.wg0 (192.168.2.121): 56 data bytes -64 bytes from 192.168.2.121: icmp_seq=0 ttl=64 time=0.639 ms -64 bytes from 192.168.2.121: icmp_seq=1 ttl=64 time=0.629 ms - ---- r1.wg0 ping statistics --- -2 packets transmitted, 2 packets received, 0.0% packet loss -round-trip min/avg/max/stddev = 0.629/0.634/0.639/0.005 ms - -PING r2.wg0 (192.168.2.122): 56 data bytes -64 bytes from 192.168.2.122: icmp_seq=0 ttl=64 time=0.569 ms -64 bytes from 192.168.2.122: icmp_seq=1 ttl=64 time=0.479 ms - ---- r2.wg0 ping statistics --- -2 packets transmitted, 2 packets received, 0.0% packet loss -round-trip min/avg/max/stddev = 0.479/0.524/0.569/0.045 ms - -PING blowfish.wg0 (192.168.2.110): 56 data bytes -64 bytes from 192.168.2.110: icmp_seq=0 ttl=255 time=35.745 ms -64 bytes from 192.168.2.110: icmp_seq=1 ttl=255 time=35.481 ms - ---- blowfish.wg0 ping statistics --- -2 packets transmitted, 2 packets received, 0.0% packet loss -round-trip min/avg/max/stddev = 35.481/35.613/35.745/0.132 ms - -PING fishfinger.wg0 (192.168.2.111): 56 data bytes -64 bytes from 192.168.2.111: icmp_seq=0 ttl=255 time=33.992 ms -64 bytes from 192.168.2.111: icmp_seq=1 ttl=255 time=33.751 ms - ---- fishfinger.wg0 ping statistics --- -2 packets transmitted, 2 packets received, 0.0% packet loss -round-trip min/avg/max/stddev = 33.751/33.872/33.992/0.120 ms +paul@f0:~ % foreach peer ( f1 f2 r0 r1 r2 blowfish fishfinger ) +foreach? ping -c2 $peer.wg0 +foreach? echo +foreach? end +PING f1.wg0 (192.168.2.131): 56 data bytes +64 bytes from 192.168.2.131: icmp_seq=0 ttl=64 time=0.334 ms +64 bytes from 192.168.2.131: icmp_seq=1 ttl=64 time=0.260 ms + +--- f1.wg0 ping statistics --- +2 packets transmitted, 2 packets received, 0.0% packet loss +round-trip min/avg/max/stddev = 0.260/0.297/0.334/0.037 ms + +PING f2.wg0 (192.168.2.132): 56 data bytes +64 bytes from 192.168.2.132: icmp_seq=0 ttl=64 time=0.323 ms +64 bytes from 192.168.2.132: icmp_seq=1 ttl=64 time=0.303 ms + +--- f2.wg0 ping statistics --- +2 packets transmitted, 2 packets received, 0.0% packet loss +round-trip min/avg/max/stddev = 0.303/0.313/0.323/0.010 ms + +PING r0.wg0 (192.168.2.120): 56 data bytes +64 bytes from 192.168.2.120: icmp_seq=0 ttl=64 time=0.716 ms +64 bytes from 192.168.2.120: icmp_seq=1 ttl=64 time=0.406 ms + +--- r0.wg0 ping statistics --- +2 packets transmitted, 2 packets received, 0.0% packet loss +round-trip min/avg/max/stddev = 0.406/0.561/0.716/0.155 ms + +PING r1.wg0 (192.168.2.121): 56 data bytes +64 bytes from 192.168.2.121: icmp_seq=0 ttl=64 time=0.639 ms +64 bytes from 192.168.2.121: icmp_seq=1 ttl=64 time=0.629 ms + +--- r1.wg0 ping statistics --- +2 packets transmitted, 2 packets received, 0.0% packet loss +round-trip min/avg/max/stddev = 0.629/0.634/0.639/0.005 ms + +PING r2.wg0 (192.168.2.122): 56 data bytes +64 bytes from 192.168.2.122: icmp_seq=0 ttl=64 time=0.569 ms +64 bytes from 192.168.2.122: icmp_seq=1 ttl=64 time=0.479 ms + +--- r2.wg0 ping statistics --- +2 packets transmitted, 2 packets received, 0.0% packet loss +round-trip min/avg/max/stddev = 0.479/0.524/0.569/0.045 ms + +PING blowfish.wg0 (192.168.2.110): 56 data bytes +64 bytes from 192.168.2.110: icmp_seq=0 ttl=255 time=35.745 ms +64 bytes from 192.168.2.110: icmp_seq=1 ttl=255 time=35.481 ms + +--- blowfish.wg0 ping statistics --- +2 packets transmitted, 2 packets received, 0.0% packet loss +round-trip min/avg/max/stddev = 35.481/35.613/35.745/0.132 ms + +PING fishfinger.wg0 (192.168.2.111): 56 data bytes +64 bytes from 192.168.2.111: icmp_seq=0 ttl=255 time=33.992 ms +64 bytes from 192.168.2.111: icmp_seq=1 ttl=255 time=33.751 ms + +--- fishfinger.wg0 ping statistics --- +2 packets transmitted, 2 packets received, 0.0% packet loss +round-trip min/avg/max/stddev = 33.751/33.872/33.992/0.120 ms
Note that the loop above is a tcsh loop, the default shell used in FreeBSD. Of course, all other peers can ping their peers as well!
@@ -1342,60 +1347,60 @@ round-trip min/avg/max/stddev = 33.751/ -paul@f0:~ % doas wg show -interface: wg0 - public key: Jm6YItMt94++dIeOyVi1I9AhNt2qQcryxCZezoX7X2Y= - private key: (hidden) - listening port: 56709 - -peer: 0Y/H20W8YIbF7DA1sMwMacLI8WS9yG+1/QO7m2oyllg= - preshared key: (hidden) - endpoint: 192.168.1.122:56709 - allowed ips: 192.168.2.122/32, fd42:beef:cafe:2::122/128 - latest handshake: 10 seconds ago - transfer: 440 B received, 532 B sent - -peer: Hhy9kMPOOjChXV2RA5WeCGs+J0FE3rcNPDw/TLSn7i8= - preshared key: (hidden) - endpoint: 192.168.1.121:56709 - allowed ips: 192.168.2.121/32, fd42:beef:cafe:2::121/128 - latest handshake: 12 seconds ago - transfer: 440 B received, 564 B sent - -peer: s3e93XoY7dPUQgLiVO4d8x/SRCFgEew+/wP7+zwgehI= - preshared key: (hidden) - endpoint: 192.168.1.120:56709 - allowed ips: 192.168.2.120/32, fd42:beef:cafe:2::120/128 - latest handshake: 14 seconds ago - transfer: 440 B received, 564 B sent - -peer: SlGVsACE1wiaRoGvCR3f7AuHfRS+1jjhS+YwEJ2HvF0= - preshared key: (hidden) - endpoint: 192.168.1.132:56709 - allowed ips: 192.168.2.132/32, fd42:beef:cafe:2::132/128 - latest handshake: 17 seconds ago - transfer: 472 B received, 564 B sent - -peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns= - preshared key: (hidden) - endpoint: 23.88.35.144:56709 - allowed ips: 192.168.2.110/32, fd42:beef:cafe:2::110/128 - latest handshake: 55 seconds ago - transfer: 472 B received, 596 B sent - persistent keepalive: every 25 seconds - -peer: 8PvGZH1NohHpZPVJyjhctBX9xblsNvYBhpg68FsFcns= - preshared key: (hidden) - endpoint: 46.23.94.99:56709 - allowed ips: 192.168.2.111/32, fd42:beef:cafe:2::111/128 - latest handshake: 55 seconds ago - transfer: 472 B received, 596 B sent - persistent keepalive: every 25 seconds - -peer: 2htXdNcxzpI2FdPDJy4T4VGtm1wpMEQu1AkQHjNY6F8= - preshared key: (hidden) - endpoint: 192.168.1.131:56709 - allowed ips: 192.168.2.131/32, fd42:beef:cafe:2::131/128 +paul@f0:~ % doas wg show +interface: wg0 + public key: Jm6YItMt94++dIeOyVi1I9AhNt2qQcryxCZezoX7X2Y= + private key: (hidden) + listening port: 56709 + +peer: 0Y/H20W8YIbF7DA1sMwMacLI8WS9yG+1/QO7m2oyllg= + preshared key: (hidden) + endpoint: 192.168.1.122:56709 + allowed ips: 192.168.2.122/32, fd42:beef:cafe:2::122/128 + latest handshake: 10 seconds ago + transfer: 440 B received, 532 B sent + +peer: Hhy9kMPOOjChXV2RA5WeCGs+J0FE3rcNPDw/TLSn7i8= + preshared key: (hidden) + endpoint: 192.168.1.121:56709 + allowed ips: 192.168.2.121/32, fd42:beef:cafe:2::121/128 + latest handshake: 12 seconds ago + transfer: 440 B received, 564 B sent + +peer: s3e93XoY7dPUQgLiVO4d8x/SRCFgEew+/wP7+zwgehI= + preshared key: (hidden) + endpoint: 192.168.1.120:56709 + allowed ips: 192.168.2.120/32, fd42:beef:cafe:2::120/128 + latest handshake: 14 seconds ago + transfer: 440 B received, 564 B sent + +peer: SlGVsACE1wiaRoGvCR3f7AuHfRS+1jjhS+YwEJ2HvF0= + preshared key: (hidden) + endpoint: 192.168.1.132:56709 + allowed ips: 192.168.2.132/32, fd42:beef:cafe:2::132/128 + latest handshake: 17 seconds ago + transfer: 472 B received, 564 B sent + +peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns= + preshared key: (hidden) + endpoint: 23.88.35.144:56709 + allowed ips: 192.168.2.110/32, fd42:beef:cafe:2::110/128 + latest handshake: 55 seconds ago + transfer: 472 B received, 596 B sent + persistent keepalive: every 25 seconds + +peer: 8PvGZH1NohHpZPVJyjhctBX9xblsNvYBhpg68FsFcns= + preshared key: (hidden) + endpoint: 46.23.94.99:56709 + allowed ips: 192.168.2.111/32, fd42:beef:cafe:2::111/128 + latest handshake: 55 seconds ago + transfer: 472 B received, 596 B sent + persistent keepalive: every 25 seconds + +peer: 2htXdNcxzpI2FdPDJy4T4VGtm1wpMEQu1AkQHjNY6F8= + preshared key: (hidden) + endpoint: 192.168.1.131:56709 + allowed ips: 192.168.2.131/32, fd42:beef:cafe:2::131/128
Managing Roaming Client Tunnels
@@ -1422,8 +1427,8 @@ peer: 2htXdNcxzpI2FdPDJy4T4VGtm1wpMEQu1AkQHjNY6F8= by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0-blowfish.conf -qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0-fishfinger.conf +qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0-blowfish.conf +qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0-fishfinger.conf
Import both QR codes using the WireGuard app to create two separate tunnel profiles. You can then manually enable/disable each tunnel to select which gateway to use. Only enable one tunnel at a time.
@@ -1442,8 +1447,8 @@ qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -sudo cp dist/earth/etc/wireguard/wg0-blowfish.conf /etc/wireguard/ -sudo cp dist/earth/etc/wireguard/wg0-fishfinger.conf /etc/wireguard/ +sudo cp dist/earth/etc/wireguard/wg0-blowfish.conf /etc/wireguard/ +sudo cp dist/earth/etc/wireguard/wg0-fishfinger.conf /etc/wireguard/
This approach provides explicit control over which gateway handles roaming client traffic, useful when one gateway needs maintenance or experiences connectivity issues.
@@ -1456,27 +1461,27 @@ sudo cp dist/earth/etc/wireguard/wg0-fishfinger.con by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Start with blowfish gateway -earth$ sudo systemctl start wg-quick@wg0-blowfish.service - -# Or start with fishfinger gateway -earth$ sudo systemctl start wg-quick@wg0-fishfinger.service - -# Check tunnel status (example with blowfish gateway) -earth$ sudo wg show -interface: wg0 - public key: Mc1CpSS3rbLN9A2w9c75XugQyXUkGPHKI2iCGbh8DRo= - private key: (hidden) - listening port: 56709 - fwmark: 0xca6c - -peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns= - preshared key: (hidden) - endpoint: 23.88.35.144:56709 - allowed ips: 0.0.0.0/0, ::/0 - latest handshake: 5 seconds ago - transfer: 15.89 KiB received, 32.15 KiB sent - persistent keepalive: every 25 seconds +# Start with blowfish gateway +earth$ sudo systemctl start wg-quick@wg0-blowfish.service + +# Or start with fishfinger gateway +earth$ sudo systemctl start wg-quick@wg0-fishfinger.service + +# Check tunnel status (example with blowfish gateway) +earth$ sudo wg show +interface: wg0 + public key: Mc1CpSS3rbLN9A2w9c75XugQyXUkGPHKI2iCGbh8DRo= + private key: (hidden) + listening port: 56709 + fwmark: 0xca6c + +peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns= + preshared key: (hidden) + endpoint: 23.88.35.144:56709 + allowed ips: 0.0.0.0/0, ::/0 + latest handshake: 5 seconds ago + transfer: 15.89 KiB received, 32.15 KiB sent + persistent keepalive: every 25 seconds
Stopping the tunnel:
@@ -1485,12 +1490,12 @@ peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns= by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -earth$ sudo systemctl stop wg-quick@wg0-blowfish.service -# Or if using fishfinger: -earth$ sudo systemctl stop wg-quick@wg0-fishfinger.service +earth$ sudo systemctl stop wg-quick@wg0-blowfish.service +# Or if using fishfinger: +earth$ sudo systemctl stop wg-quick@wg0-fishfinger.service -earth$ sudo wg show -# No output - WireGuard interface is down +earth$ sudo wg show +# No output - WireGuard interface is down
Switching between gateways:
@@ -1499,9 +1504,9 @@ earth$ sudo wg show by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Switch from blowfish to fishfinger -earth$ sudo systemctl stop wg-quick@wg0-blowfish.service -earth$ sudo systemctl start wg-quick@wg0-fishfinger.service +# Switch from blowfish to fishfinger +earth$ sudo systemctl stop wg-quick@wg0-blowfish.service +earth$ sudo systemctl start wg-quick@wg0-fishfinger.service
The services remain disabled to prevent auto-start on boot, allowing manual control of when the VPN is active and which gateway to use.
@@ -1558,10 +1563,10 @@ earth$ sudo systemctl start wg-quick@wg0-fishfinger.service by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# From earth laptop: -earth$ ping -c2 blowfish.wg0 -earth$ ping -c2 fishfinger.wg0 -earth$ curl https://ifconfig.me # Should show gateway's public IP +# From earth laptop: +earth$ ping -c2 blowfish.wg0 +earth$ ping -c2 fishfinger.wg0 +earth$ curl https://ifconfig.me # Should show gateway's public IP
Check which gateway is active: Check the transfer statistics with sudo wg show on earth to see which peer shows recent handshakes and increasing transfer bytes. On Android, the WireGuard app shows the active tunnel with data transfer statistics.
@@ -1594,11 +1599,12 @@ earth$ curl https://ifconfig.me # Should show gateway's
Back to the main site
+ diff --git a/gemfeed/2025-06-07-a-monks-guide-to-happiness-book-notes.html b/gemfeed/2025-06-07-a-monks-guide-to-happiness-book-notes.html index 48774fd1..e528f825 100644 --- a/gemfeed/2025-06-07-a-monks-guide-to-happiness-book-notes.html +++ b/gemfeed/2025-06-07-a-monks-guide-to-happiness-book-notes.html @@ -2,12 +2,17 @@ +'A Monk's Guide to Happiness' book notes - + + + + + @@ -114,11 +119,12 @@
Back to the main site
+ diff --git a/gemfeed/2025-06-22-task-samurai.html b/gemfeed/2025-06-22-task-samurai.html index 9e10795d..49e6a820 100644 --- a/gemfeed/2025-06-22-task-samurai.html +++ b/gemfeed/2025-06-22-task-samurai.html @@ -2,12 +2,17 @@ +Task Samurai: An agentic coding learning experiment - + + + + + @@ -150,11 +155,12 @@
Back to the main site
+ diff --git a/gemfeed/2025-07-01-posts-from-january-to-june-2025.html b/gemfeed/2025-07-01-posts-from-january-to-june-2025.html index 09e3446f..16a1faa5 100644 --- a/gemfeed/2025-07-01-posts-from-january-to-june-2025.html +++ b/gemfeed/2025-07-01-posts-from-january-to-june-2025.html @@ -2,12 +2,17 @@ +Posts from January to June 2025 - + + + + + @@ -749,11 +754,12 @@
Back to the main site
+ diff --git a/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.html b/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.html index a66bad32..d3d00045 100644 --- a/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.html +++ b/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.html @@ -2,12 +2,17 @@ +f3s: Kubernetes with FreeBSD - Part 6: Storage - + + + + + @@ -119,16 +124,16 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas zpool create -m /data zdata /dev/ada1 -paul@f0:~ % zpool list -NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT -zdata 928G 12.1M 928G - - 0% 0% 1.00x ONLINE - -zroot 472G 29.0G 443G - - 0% 6% 1.00x ONLINE - +paul@f0:~ % doas zpool create -m /data zdata /dev/ada1 +paul@f0:~ % zpool list +NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT +zdata 928G 12.1M 928G - - 0% 0% 1.00x ONLINE - +zroot 472G 29.0G 443G - - 0% 6% 1.00x ONLINE - -paul@f0:/ % doas camcontrol devlist -<512GB SSD D910R170> at scbus0 target 0 lun 0 (pass0,ada0) -<Samsung SSD 870 EVO 1TB SVT03B6Q> at scbus1 target 0 lun 0 (pass1,ada1) -paul@f0:/ % +paul@f0:/ % doas camcontrol devlist +<512GB SSD D910R170> at scbus0 target 0 lun 0 (pass0,ada0) +<Samsung SSD 870 EVO 1TB SVT03B6Q> at scbus1 target 0 lun 0 (pass1,ada1) +paul@f0:/ %
To verify that we have a different SSD on the second node (the third node has the same drive as the first):
@@ -137,9 +142,9 @@ paul@f0:/ % by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f1:/ % doas camcontrol devlist -<512GB SSD D910R170> at scbus0 target 0 lun 0 (pass0,ada0) -<CT1000BX500SSD1 M6CR072> at scbus1 target 0 lun 0 (pass1,ada1) +paul@f1:/ % doas camcontrol devlist +<512GB SSD D910R170> at scbus0 target 0 lun 0 (pass0,ada0) +<CT1000BX500SSD1 M6CR072> at scbus1 target 0 lun 0 (pass1,ada1)
ZFS encryption keys
@@ -177,21 +182,21 @@ paul@f0:/ % by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:/ % doas newfs /dev/da0 -/dev/da0: 15000.0MB (30720000 sectors) block size 32768, fragment size 4096 - using 24 cylinder groups of 625.22MB, 20007 blks, 80128 inodes. - with soft updates -super-block backups (for fsck_ffs -b #) at: - 192, 1280640, 2561088, 3841536, 5121984, 6402432, 7682880, 8963328, 10243776, -11524224, 12804672, 14085120, 15365568, 16646016, 17926464, 19206912,k 20487360, -... +paul@f0:/ % doas newfs /dev/da0 +/dev/da0: 15000.0MB (30720000 sectors) block size 32768, fragment size 4096 + using 24 cylinder groups of 625.22MB, 20007 blks, 80128 inodes. + with soft updates +super-block backups (for fsck_ffs -b #) at: + 192, 1280640, 2561088, 3841536, 5121984, 6402432, 7682880, 8963328, 10243776, +11524224, 12804672, 14085120, 15365568, 16646016, 17926464, 19206912,k 20487360, +... -paul@f0:/ % echo '/dev/da0 /keys ufs rw 0 2' | doas tee -a /etc/fstab -/dev/da0 /keys ufs rw 0 2 -paul@f0:/ % doas mkdir /keys -paul@f0:/ % doas mount /keys -paul@f0:/ % df | grep keys -/dev/da0 14877596 8 13687384 0% /keys +paul@f0:/ % echo '/dev/da0 /keys ufs rw 0 2' | doas tee -a /etc/fstab +/dev/da0 /keys ufs rw 0 2 +paul@f0:/ % doas mkdir /keys +paul@f0:/ % doas mount /keys +paul@f0:/ % df | grep keys +/dev/da0 14877596 8 13687384 0% /keys
@@ -230,18 +235,18 @@ total 20 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:/keys % doas zfs create -o encryption=on -o keyformat=raw -o \ - keylocation=file:///keys/`hostname`:zdata.key zdata/enc -paul@f0:/ % zfs list | grep zdata -zdata 836K 899G 96K /data -zdata/enc 200K 899G 200K /data/enc +paul@f0:/keys % doas zfs create -o encryption=on -o keyformat=raw -o \ + keylocation=file:///keys/`hostname`:zdata.key zdata/enc +paul@f0:/ % zfs list | grep zdata +zdata 836K 899G 96K /data +zdata/enc 200K 899G 200K /data/enc -paul@f0:/keys % zfs get all zdata/enc | grep -E -i '(encryption|key)' -zdata/enc encryption aes-256-gcm - -zdata/enc keylocation file:///keys/f0.lan.buetow.org:zdata.key local -zdata/enc keyformat raw - -zdata/enc encryptionroot zdata/enc - -zdata/enc keystatus available - +paul@f0:/keys % zfs get all zdata/enc | grep -E -i '(encryption|key)' +zdata/enc encryption aes-256-gcm - +zdata/enc keylocation file:///keys/f0.lan.buetow.org:zdata.key local +zdata/enc keyformat raw - +zdata/enc encryptionroot zdata/enc - +zdata/enc keystatus available -
All future data sets within zdata/enc will inherit the same encryption key.
@@ -254,12 +259,12 @@ zdata/enc keystatus available - by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:/keys % doas vm stop rocky -Sending ACPI shutdown to rocky +paul@f0:/keys % doas vm stop rocky +Sending ACPI shutdown to rocky -paul@f0:/keys % doas vm list -NAME DATASTORE LOADER CPU MEMORY VNC AUTO STATE -rocky default uefi 4 14G - Yes [1] Stopped +paul@f0:/keys % doas vm list +NAME DATASTORE LOADER CPU MEMORY VNC AUTO STATE +rocky default uefi 4 14G - Yes [1] Stopped
After this, we rename the unencrypted data set to _old, create a new encrypted data set, and also snapshot it as @hamburger.
@@ -268,14 +273,14 @@ rocky default uefi 4 14G - Ye by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:/keys % doas zfs rename zroot/bhyve zroot/bhyve_old -paul@f0:/keys % doas zfs set mountpoint=/mnt zroot/bhyve_old -paul@f0:/keys % doas zfs snapshot zroot/bhyve_old/rocky@hamburger +paul@f0:/keys % doas zfs rename zroot/bhyve zroot/bhyve_old +paul@f0:/keys % doas zfs set mountpoint=/mnt zroot/bhyve_old +paul@f0:/keys % doas zfs snapshot zroot/bhyve_old/rocky@hamburger -paul@f0:/keys % doas zfs create -o encryption=on -o keyformat=raw -o \ - keylocation=file:///keys/`hostname`:bhyve.key zroot/bhyve -paul@f0:/keys % doas zfs set mountpoint=/zroot/bhyve zroot/bhyve -paul@f0:/keys % doas zfs set mountpoint=/zroot/bhyve/rocky zroot/bhyve/rocky +paul@f0:/keys % doas zfs create -o encryption=on -o keyformat=raw -o \ + keylocation=file:///keys/`hostname`:bhyve.key zroot/bhyve +paul@f0:/keys % doas zfs set mountpoint=/zroot/bhyve zroot/bhyve +paul@f0:/keys % doas zfs set mountpoint=/zroot/bhyve/rocky zroot/bhyve/rocky
Once done, we import the snapshot into the encrypted dataset and also copy some other metadata files from vm-bhyve back over.
@@ -295,17 +300,17 @@ paul@f0:/keys % doas cp -Rp /mnt/.iso /zroot/bhyve/ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:/keys % doas sysrc zfskeys_enable=YES -zfskeys_enable: -> YES -paul@f0:/keys % doas vm init -paul@f0:/keys % doas reboot -. -. -. -paul@f0:~ % doas vm list -paul@f0:~ % doas vm list -NAME DATASTORE LOADER CPU MEMORY VNC AUTO STATE -rocky default uefi 4 14G 0.0.0.0:5900 Yes [1] Running (2265) +paul@f0:/keys % doas sysrc zfskeys_enable=YES +zfskeys_enable: -> YES +paul@f0:/keys % doas vm init +paul@f0:/keys % doas reboot +. +. +. +paul@f0:~ % doas vm list +paul@f0:~ % doas vm list +NAME DATASTORE LOADER CPU MEMORY VNC AUTO STATE +rocky default uefi 4 14G 0.0.0.0:5900 Yes [1] Running (2265)
As you can see, the VM is running. This means the encrypted zroot/bhyve was mounted successfully after the reboot! Now we can destroy the old, unencrypted, and now unused bhyve dataset:
@@ -314,7 +319,7 @@ rocky default uefi 4 14G -paul@f0:~ % doas zfs destroy -R zroot/bhyve_old +paul@f0:~ % doas zfs destroy -R zroot/bhyve_old
To verify once again that zroot/bhyve and zroot/bhyve/rocky are now both encrypted, we run:
@@ -323,19 +328,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % zfs get all zroot/bhyve | grep -E '(encryption|key)' -zroot/bhyve encryption aes-256-gcm - -zroot/bhyve keylocation file:///keys/f0.lan.buetow.org:bhyve.key local -zroot/bhyve keyformat raw - -zroot/bhyve encryptionroot zroot/bhyve - -zroot/bhyve keystatus available - +paul@f0:~ % zfs get all zroot/bhyve | grep -E '(encryption|key)' +zroot/bhyve encryption aes-256-gcm - +zroot/bhyve keylocation file:///keys/f0.lan.buetow.org:bhyve.key local +zroot/bhyve keyformat raw - +zroot/bhyve encryptionroot zroot/bhyve - +zroot/bhyve keystatus available - -paul@f0:~ % zfs get all zroot/bhyve/rocky | grep -E '(encryption|key)' -zroot/bhyve/rocky encryption aes-256-gcm - -zroot/bhyve/rocky keylocation none default -zroot/bhyve/rocky keyformat raw - -zroot/bhyve/rocky encryptionroot zroot/bhyve - -zroot/bhyve/rocky keystatus available - +paul@f0:~ % zfs get all zroot/bhyve/rocky | grep -E '(encryption|key)' +zroot/bhyve/rocky encryption aes-256-gcm - +zroot/bhyve/rocky keylocation none default +zroot/bhyve/rocky keyformat raw - +zroot/bhyve/rocky encryptionroot zroot/bhyve - +zroot/bhyve/rocky keystatus available -
ZFS Replication with zrepl
@@ -370,7 +375,7 @@ zroot/bhyve/rocky keystatus available - by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas pkg install -y zrepl +paul@f0:~ % doas pkg install -y zrepl
Then, we verify the pools and datasets on both hosts:
@@ -379,25 +384,25 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# On f0 -paul@f0:~ % doas zpool list -NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT -zdata 928G 1.03M 928G - - 0% 0% 1.00x ONLINE - -zroot 472G 26.7G 445G - - 0% 5% 1.00x ONLINE - +# On f0 +paul@f0:~ % doas zpool list +NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT +zdata 928G 1.03M 928G - - 0% 0% 1.00x ONLINE - +zroot 472G 26.7G 445G - - 0% 5% 1.00x ONLINE - -paul@f0:~ % doas zfs list -r zdata/enc -NAME USED AVAIL REFER MOUNTPOINT -zdata/enc 200K 899G 200K /data/enc +paul@f0:~ % doas zfs list -r zdata/enc +NAME USED AVAIL REFER MOUNTPOINT +zdata/enc 200K 899G 200K /data/enc -# On f1 -paul@f1:~ % doas zpool list -NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT -zdata 928G 956K 928G - - 0% 0% 1.00x ONLINE - -zroot 472G 11.7G 460G - - 0% 2% 1.00x ONLINE - +# On f1 +paul@f1:~ % doas zpool list +NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT +zdata 928G 956K 928G - - 0% 0% 1.00x ONLINE - +zroot 472G 11.7G 460G - - 0% 2% 1.00x ONLINE - -paul@f1:~ % doas zfs list -r zdata/enc -NAME USED AVAIL REFER MOUNTPOINT -zdata/enc 200K 899G 200K /data/enc +paul@f1:~ % doas zfs list -r zdata/enc +NAME USED AVAIL REFER MOUNTPOINT +zdata/enc 200K 899G 200K /data/enc
Since we have a WireGuard tunnel between f0 and f1, we'll use TCP transport over the secure tunnel instead of SSH. First, check the WireGuard IP addresses:
@@ -406,12 +411,12 @@ zdata/enc 200K 899G 200K /data/enc by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Check WireGuard interface IPs -paul@f0:~ % ifconfig wg0 | grep inet - inet 192.168.2.130 netmask 0xffffff00 +# Check WireGuard interface IPs +paul@f0:~ % ifconfig wg0 | grep inet + inet 192.168.2.130 netmask 0xffffff00 -paul@f1:~ % ifconfig wg0 | grep inet - inet 192.168.2.131 netmask 0xffffff00 +paul@f1:~ % ifconfig wg0 | grep inet + inet 192.168.2.131 netmask 0xffffff00
Let's create a dedicated dataset for NFS data that will be replicated:
@@ -420,8 +425,8 @@ paul@f1:~ % ifconfig wg0 | grep inet by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Create the nfsdata dataset that will hold all data exposed via NFS -paul@f0:~ % doas zfs create zdata/enc/nfsdata +# Create the nfsdata dataset that will hold all data exposed via NFS +paul@f0:~ % doas zfs create zdata/enc/nfsdata
Afterwards, we create the zrepl configuration on f0:
@@ -430,68 +435,68 @@ paul@f0:~ % doas zfs create zdata/enc/nfsdata by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas tee /usr/local/etc/zrepl/zrepl.yml <<'EOF' -global: - logging: - - type: stdout - level: info - format: human - -jobs: - - name: f0_to_f1_nfsdata - type: push - connect: - type: tcp - address: "192.168.2.131:8888" - filesystems: - "zdata/enc/nfsdata": true - send: - encrypted: true - snapshotting: - type: periodic - prefix: zrepl_ - interval: 1m - pruning: - keep_sender: - - type: last_n - count: 10 - - type: grid - grid: 4x7d | 6x30d - regex: "^zrepl_.*" - keep_receiver: - - type: last_n - count: 10 - - type: grid - grid: 4x7d | 6x30d - regex: "^zrepl_.*" - - - name: f0_to_f1_freebsd - type: push - connect: - type: tcp - address: "192.168.2.131:8888" - filesystems: - "zroot/bhyve/freebsd": true - send: - encrypted: true - snapshotting: - type: periodic - prefix: zrepl_ - interval: 10m - pruning: - keep_sender: - - type: last_n - count: 10 - - type: grid - grid: 4x7d - regex: "^zrepl_.*" - keep_receiver: - - type: last_n - count: 10 - - type: grid - grid: 4x7d - regex: "^zrepl_.*" -EOF +paul@f0:~ % doas tee /usr/local/etc/zrepl/zrepl.yml <<'EOF' +global: + logging: + - type: stdout + level: info + format: human + +jobs: + - name: f0_to_f1_nfsdata + type: push + connect: + type: tcp + address: "192.168.2.131:8888" + filesystems: + "zdata/enc/nfsdata": true + send: + encrypted: true + snapshotting: + type: periodic + prefix: zrepl_ + interval: 1m + pruning: + keep_sender: + - type: last_n + count: 10 + - type: grid + grid: 4x7d | 6x30d + regex: "^zrepl_.*" + keep_receiver: + - type: last_n + count: 10 + - type: grid + grid: 4x7d | 6x30d + regex: "^zrepl_.*" + + - name: f0_to_f1_freebsd + type: push + connect: + type: tcp + address: "192.168.2.131:8888" + filesystems: + "zroot/bhyve/freebsd": true + send: + encrypted: true + snapshotting: + type: periodic + prefix: zrepl_ + interval: 10m + pruning: + keep_sender: + - type: last_n + count: 10 + - type: grid + grid: 4x7d + regex: "^zrepl_.*" + keep_receiver: + - type: last_n + count: 10 + - type: grid + grid: 4x7d + regex: "^zrepl_.*" +EOF
We're using two separate replication jobs with different intervals:
@@ -516,29 +521,29 @@ EOF by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# First, create a dedicated sink dataset -paul@f1:~ % doas zfs create zdata/sink +# First, create a dedicated sink dataset +paul@f1:~ % doas zfs create zdata/sink -paul@f1:~ % doas tee /usr/local/etc/zrepl/zrepl.yml <<'EOF' -global: - logging: - - type: stdout - level: info - format: human +paul@f1:~ % doas tee /usr/local/etc/zrepl/zrepl.yml <<'EOF' +global: + logging: + - type: stdout + level: info + format: human -jobs: - - name: sink - type: sink - serve: - type: tcp - listen: "192.168.2.131:8888" - clients: - "192.168.2.130": "f0" - recv: - placeholder: - encryption: inherit - root_fs: "zdata/sink" -EOF +jobs: + - name: sink + type: sink + serve: + type: tcp + listen: "192.168.2.131:8888" + clients: + "192.168.2.130": "f0" + recv: + placeholder: + encryption: inherit + root_fs: "zdata/sink" +EOF
Enabling and starting zrepl services
@@ -549,17 +554,17 @@ EOF by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# On f0 -paul@f0:~ % doas sysrc zrepl_enable=YES -zrepl_enable: -> YES -paul@f0:~ % doas service `zrepl` start -Starting zrepl. +# On f0 +paul@f0:~ % doas sysrc zrepl_enable=YES +zrepl_enable: -> YES +paul@f0:~ % doas service `zrepl` start +Starting zrepl. -# On f1 -paul@f1:~ % doas sysrc zrepl_enable=YES -zrepl_enable: -> YES -paul@f1:~ % doas service `zrepl` start -Starting zrepl. +# On f1 +paul@f1:~ % doas sysrc zrepl_enable=YES +zrepl_enable: -> YES +paul@f1:~ % doas service `zrepl` start +Starting zrepl.
To check the replication status, we run:
@@ -568,35 +573,35 @@ Starting zrepl. by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# On f0, check `zrepl` status (use raw mode for non-tty) -paul@f0:~ % doas pkg install jq -paul@f0:~ % doas zrepl status --mode raw | grep -A2 "Replication" | jq . -"Replication":{"StartAt":"2025-07-01T22:31:48.712143123+03:00"... +# On f0, check `zrepl` status (use raw mode for non-tty) +paul@f0:~ % doas pkg install jq +paul@f0:~ % doas zrepl status --mode raw | grep -A2 "Replication" | jq . +"Replication":{"StartAt":"2025-07-01T22:31:48.712143123+03:00"... -# Check if services are running -paul@f0:~ % doas service zrepl status -zrepl is running as pid 2649. +# Check if services are running +paul@f0:~ % doas service zrepl status +zrepl is running as pid 2649. -paul@f1:~ % doas service zrepl status -zrepl is running as pid 2574. +paul@f1:~ % doas service zrepl status +zrepl is running as pid 2574. -# Check for `zrepl` snapshots on source -paul@f0:~ % doas zfs list -t snapshot -r zdata/enc | grep zrepl -zdata/enc@zrepl_20250701_193148_000 0B - 176K - +# Check for `zrepl` snapshots on source +paul@f0:~ % doas zfs list -t snapshot -r zdata/enc | grep zrepl +zdata/enc@zrepl_20250701_193148_000 0B - 176K - -# On f1, verify the replicated datasets -paul@f1:~ % doas zfs list -r zdata | grep f0 -zdata/f0 576K 899G 200K none -zdata/f0/zdata 376K 899G 200K none -zdata/f0/zdata/enc 176K 899G 176K none +# On f1, verify the replicated datasets +paul@f1:~ % doas zfs list -r zdata | grep f0 +zdata/f0 576K 899G 200K none +zdata/f0/zdata 376K 899G 200K none +zdata/f0/zdata/enc 176K 899G 176K none -# Check replicated snapshots on f1 -paul@f1:~ % doas zfs list -t snapshot -r zdata | grep zrepl -zdata/f0/zdata/enc@zrepl_20250701_193148_000 0B - 176K - -zdata/f0/zdata/enc@zrepl_20250701_194148_000 0B - 176K - -. -. -. +# Check replicated snapshots on f1 +paul@f1:~ % doas zfs list -t snapshot -r zdata | grep zrepl +zdata/f0/zdata/enc@zrepl_20250701_193148_000 0B - 176K - +zdata/f0/zdata/enc@zrepl_20250701_194148_000 0B - 176K - +. +. +.
Monitoring replication
@@ -607,7 +612,7 @@ zdata/f0/zdata/enc@zrepl_20250701_194148_000 0B by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas zrepl status +paul@f0:~ % doas zrepl status
@@ -630,29 +635,29 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % uptime -11:17PM up 1 min, 0 users, load averages: 0.16, 0.06, 0.02 +paul@f0:~ % uptime +11:17PM up 1 min, 0 users, load averages: 0.16, 0.06, 0.02 -paul@f0:~ % doas service `zrepl` status -zrepl is running as pid 2366. +paul@f0:~ % doas service `zrepl` status +zrepl is running as pid 2366. -paul@f1:~ % doas service `zrepl` status -zrepl is running as pid 2309. +paul@f1:~ % doas service `zrepl` status +zrepl is running as pid 2309. -# Check that new snapshots are being created and replicated -paul@f0:~ % doas zfs list -t snapshot | grep `zrepl` | tail -2 -zdata/enc/nfsdata@zrepl_20250701_202530_000 0B - 200K - -zroot/bhyve/freebsd@zrepl_20250701_202530_000 0B - 2.97G - -. -. -. +# Check that new snapshots are being created and replicated +paul@f0:~ % doas zfs list -t snapshot | grep `zrepl` | tail -2 +zdata/enc/nfsdata@zrepl_20250701_202530_000 0B - 200K - +zroot/bhyve/freebsd@zrepl_20250701_202530_000 0B - 2.97G - +. +. +. -paul@f1:~ % doas zfs list -t snapshot -r zdata/sink | grep 202530 -zdata/sink/f0/zdata/enc/nfsdata@zrepl_20250701_202530_000 0B - 176K - -zdata/sink/f0/zroot/bhyve/freebsd@zrepl_20250701_202530_000 0B - 2.97G - -. -. -. +paul@f1:~ % doas zfs list -t snapshot -r zdata/sink | grep 202530 +zdata/sink/f0/zdata/enc/nfsdata@zrepl_20250701_202530_000 0B - 176K - +zdata/sink/f0/zroot/bhyve/freebsd@zrepl_20250701_202530_000 0B - 2.97G - +. +. +.
The timestamps confirm that replication resumed automatically after the reboot, ensuring continuous data protection. We can also write a test file to the NFS data directory on f0 and verify whether it appears on f1 after a minute.
@@ -677,14 +682,14 @@ zdata/sink/f0/zroot/bhyve/freebsd@zrepl_20250701_20 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# On f0 - set mountpoint for the primary nfsdata -paul@f0:~ % doas zfs set mountpoint=/data/nfs zdata/enc/nfsdata -paul@f0:~ % doas mkdir -p /data/nfs +# On f0 - set mountpoint for the primary nfsdata +paul@f0:~ % doas zfs set mountpoint=/data/nfs zdata/enc/nfsdata +paul@f0:~ % doas mkdir -p /data/nfs -# Verify it's mounted -paul@f0:~ % df -h /data/nfs -Filesystem Size Used Avail Capacity Mounted on -zdata/enc/nfsdata 899G 204K 899G 0% /data/nfs +# Verify it's mounted +paul@f0:~ % df -h /data/nfs +Filesystem Size Used Avail Capacity Mounted on +zdata/enc/nfsdata 899G 204K 899G 0% /data/nfs
On f1, we need to handle the encryption key and mount the standby copy:
@@ -693,27 +698,27 @@ zdata/enc/nfsdata 899G 204K 899G 0% by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# On f1 - first check encryption status -paul@f1:~ % doas zfs get keystatus zdata/sink/f0/zdata/enc/nfsdata -NAME PROPERTY VALUE SOURCE -zdata/sink/f0/zdata/enc/nfsdata keystatus unavailable - +# On f1 - first check encryption status +paul@f1:~ % doas zfs get keystatus zdata/sink/f0/zdata/enc/nfsdata +NAME PROPERTY VALUE SOURCE +zdata/sink/f0/zdata/enc/nfsdata keystatus unavailable - -# Load the encryption key (using f0's key stored on the USB) -paul@f1:~ % doas zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key \ - zdata/sink/f0/zdata/enc/nfsdata +# Load the encryption key (using f0's key stored on the USB) +paul@f1:~ % doas zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key \ + zdata/sink/f0/zdata/enc/nfsdata -# Set mountpoint and mount (same path as f0 for easier failover) -paul@f1:~ % doas mkdir -p /data/nfs -paul@f1:~ % doas zfs set mountpoint=/data/nfs zdata/sink/f0/zdata/enc/nfsdata -paul@f1:~ % doas zfs mount zdata/sink/f0/zdata/enc/nfsdata +# Set mountpoint and mount (same path as f0 for easier failover) +paul@f1:~ % doas mkdir -p /data/nfs +paul@f1:~ % doas zfs set mountpoint=/data/nfs zdata/sink/f0/zdata/enc/nfsdata +paul@f1:~ % doas zfs mount zdata/sink/f0/zdata/enc/nfsdata -# Make it read-only to prevent accidental writes that would break replication -paul@f1:~ % doas zfs set readonly=on zdata/sink/f0/zdata/enc/nfsdata +# Make it read-only to prevent accidental writes that would break replication +paul@f1:~ % doas zfs set readonly=on zdata/sink/f0/zdata/enc/nfsdata -# Verify -paul@f1:~ % df -h /data/nfs -Filesystem Size Used Avail Capacity Mounted on -zdata/sink/f0/zdata/enc/nfsdata 896G 204K 896G 0% /data/nfs +# Verify +paul@f1:~ % df -h /data/nfs +Filesystem Size Used Avail Capacity Mounted on +zdata/sink/f0/zdata/enc/nfsdata 896G 204K 896G 0% /data/nfs
Note: The dataset is mounted at the same path (/data/nfs) on both hosts to simplify failover procedures. The dataset on f1 is set to readonly=on to prevent accidental modifications, which, as mentioned earlier, would break replication. If we did, replication from f0 to f1 would fail like this:
@@ -726,11 +731,11 @@ zdata/sink/f0/zdata/enc/nfsdata 896G 204K by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Option 1: Rollback to the last common snapshot (loses local changes) -paul@f1:~ % doas zfs rollback zdata/sink/f0/zdata/enc/nfsdata@zrepl_20250701_204054_000 +# Option 1: Rollback to the last common snapshot (loses local changes) +paul@f1:~ % doas zfs rollback zdata/sink/f0/zdata/enc/nfsdata@zrepl_20250701_204054_000 -# Option 2: Make it read-only to prevent accidents again -paul@f1:~ % doas zfs set readonly=on zdata/sink/f0/zdata/enc/nfsdata +# Option 2: Make it read-only to prevent accidents again +paul@f1:~ % doas zfs set readonly=on zdata/sink/f0/zdata/enc/nfsdata
And replication should work again!
@@ -743,8 +748,8 @@ paul@f1:~ % doas zfs set -paul@f0:~ % doas zfs list -o name,mountpoint,mounted | grep nfsdata -zdata/enc/nfsdata /data/nfs yes +paul@f0:~ % doas zfs list -o name,mountpoint,mounted | grep nfsdata +zdata/enc/nfsdata /data/nfs yes
If it shows no, the dataset isn't mounted! This means files are being written to the root filesystem, not ZFS. Next, we should check whether the encryption key is loaded:
@@ -753,12 +758,12 @@ zdata/enc/nfsdata /data/nfs yes by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas zfs get keystatus zdata/enc/nfsdata -NAME PROPERTY VALUE SOURCE -zdata/enc/nfsdata keystatus available - -# If "unavailable", load the key: -paul@f0:~ % doas zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key zdata/enc/nfsdata -paul@f0:~ % doas zfs mount zdata/enc/nfsdata +paul@f0:~ % doas zfs get keystatus zdata/enc/nfsdata +NAME PROPERTY VALUE SOURCE +zdata/enc/nfsdata keystatus available - +# If "unavailable", load the key: +paul@f0:~ % doas zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key zdata/enc/nfsdata +paul@f0:~ % doas zfs mount zdata/enc/nfsdata
You can also verify that files are in the snapshot (not just the directory):
@@ -767,7 +772,7 @@ paul@f0:~ % doas zfs mount zdata/enc/nfsdata by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % ls -la /data/nfs/.zfs/snapshot/zrepl_*/ +paul@f0:~ % ls -la /data/nfs/.zfs/snapshot/zrepl_*/
This issue commonly occurs after a reboot if the encryption keys aren't configured to load automatically.
@@ -780,26 +785,26 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# On f0 - configure all encrypted datasets -paul@f0:~ % doas sysrc zfskeys_enable=YES -zfskeys_enable: YES -> YES -paul@f0:~ % doas sysrc zfskeys_datasets="zdata/enc zdata/enc/nfsdata zroot/bhyve" -zfskeys_datasets: -> zdata/enc zdata/enc/nfsdata zroot/bhyve +# On f0 - configure all encrypted datasets +paul@f0:~ % doas sysrc zfskeys_enable=YES +zfskeys_enable: YES -> YES +paul@f0:~ % doas sysrc zfskeys_datasets="zdata/enc zdata/enc/nfsdata zroot/bhyve" +zfskeys_datasets: -> zdata/enc zdata/enc/nfsdata zroot/bhyve -# Set correct key locations for all datasets -paul@f0:~ % doas zfs set \ - keylocation=file:///keys/f0.lan.buetow.org:zdata.key zdata/enc/nfsdata +# Set correct key locations for all datasets +paul@f0:~ % doas zfs set \ + keylocation=file:///keys/f0.lan.buetow.org:zdata.key zdata/enc/nfsdata -# On f1 - include the replicated dataset -paul@f1:~ % doas sysrc zfskeys_enable=YES -zfskeys_enable: YES -> YES -paul@f1:~ % doas sysrc \ - zfskeys_datasets="zdata/enc zroot/bhyve zdata/sink/f0/zdata/enc/nfsdata" -zfskeys_datasets: -> zdata/enc zroot/bhyve zdata/sink/f0/zdata/enc/nfsdata +# On f1 - include the replicated dataset +paul@f1:~ % doas sysrc zfskeys_enable=YES +zfskeys_enable: YES -> YES +paul@f1:~ % doas sysrc \ + zfskeys_datasets="zdata/enc zroot/bhyve zdata/sink/f0/zdata/enc/nfsdata" +zfskeys_datasets: -> zdata/enc zroot/bhyve zdata/sink/f0/zdata/enc/nfsdata -# Set key location for replicated dataset -paul@f1:~ % doas zfs set \ - keylocation=file:///keys/f0.lan.buetow.org:zdata.key zdata/sink/f0/zdata/enc/nfsdata +# Set key location for replicated dataset +paul@f1:~ % doas zfs set \ + keylocation=file:///keys/f0.lan.buetow.org:zdata.key zdata/sink/f0/zdata/enc/nfsdata
Important notes:
@@ -822,13 +827,13 @@ paul@f1:~ % doas zfs set \ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Check service status on both f0 and f1 -paul@f0:~ % doas service zrepl status -paul@f1:~ % doas service zrepl status +# Check service status on both f0 and f1 +paul@f0:~ % doas service zrepl status +paul@f1:~ % doas service zrepl status -# If not running, start the service -paul@f0:~ % doas service zrepl start -paul@f1:~ % doas service zrepl start +# If not running, start the service +paul@f0:~ % doas service zrepl start +paul@f1:~ % doas service zrepl start
Check zrepl Status for Errors
@@ -839,11 +844,11 @@ paul@f1:~ % doas service zrepl start by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Check detailed status (use --mode raw for non-tty environments) -paul@f0:~ % doas zrepl status --mode raw +# Check detailed status (use --mode raw for non-tty environments) +paul@f0:~ % doas zrepl status --mode raw -# Look for error messages in the replication section -# Common errors include "no common snapshot" or connection failures +# Look for error messages in the replication section +# Common errors include "no common snapshot" or connection failures
Fixing "No Common Snapshot" Errors
@@ -866,27 +871,27 @@ no common snapshot or suitable bookmark between sender and receiver by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# First, identify the destination dataset on f1 -paul@f1:~ % doas zfs list | grep sink +# First, identify the destination dataset on f1 +paul@f1:~ % doas zfs list | grep sink -# Check existing snapshots on the problematic dataset -paul@f1:~ % doas zfs list -t snapshot | grep nfsdata +# Check existing snapshots on the problematic dataset +paul@f1:~ % doas zfs list -t snapshot | grep nfsdata -# If you see snapshots with different naming (e.g., @daily-*, @weekly-*) -# these conflict with zrepl's @zrepl_* snapshots +# If you see snapshots with different naming (e.g., @daily-*, @weekly-*) +# these conflict with zrepl's @zrepl_* snapshots -# Destroy the entire destination dataset to allow clean replication -paul@f1:~ % doas zfs destroy -r zdata/sink/f0/zdata/enc/nfsdata +# Destroy the entire destination dataset to allow clean replication +paul@f1:~ % doas zfs destroy -r zdata/sink/f0/zdata/enc/nfsdata -# For VM replication, do the same for the freebsd dataset -paul@f1:~ % doas zfs destroy -r zdata/sink/f0/zroot/bhyve/freebsd +# For VM replication, do the same for the freebsd dataset +paul@f1:~ % doas zfs destroy -r zdata/sink/f0/zroot/bhyve/freebsd -# Wake up zrepl to start fresh replication -paul@f0:~ % doas zrepl signal wakeup f0_to_f1_nfsdata -paul@f0:~ % doas zrepl signal wakeup f0_to_f1_freebsd +# Wake up zrepl to start fresh replication +paul@f0:~ % doas zrepl signal wakeup f0_to_f1_nfsdata +paul@f0:~ % doas zrepl signal wakeup f0_to_f1_freebsd -# Check replication status -paul@f0:~ % doas zrepl status --mode raw +# Check replication status +paul@f0:~ % doas zrepl status --mode raw
**Verification that replication is working:**
@@ -895,14 +900,14 @@ paul@f0:~ % doas zrepl status --mode raw by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Look for "stepping" state and active zfs send processes -paul@f0:~ % doas zrepl status --mode raw | grep -A5 "State.*stepping" +# Look for "stepping" state and active zfs send processes +paul@f0:~ % doas zrepl status --mode raw | grep -A5 "State.*stepping" -# Check for active ZFS commands -paul@f0:~ % doas zrepl status --mode raw | grep -A10 "ZFSCmds.*Active" +# Check for active ZFS commands +paul@f0:~ % doas zrepl status --mode raw | grep -A10 "ZFSCmds.*Active" -# Monitor progress - bytes replicated should be increasing -paul@f0:~ % doas zrepl status --mode raw | grep BytesReplicated +# Monitor progress - bytes replicated should be increasing +paul@f0:~ % doas zrepl status --mode raw | grep BytesReplicated
Network Connectivity Issues
@@ -913,14 +918,14 @@ paul@f0:~ % doas zrepl status --mode raw | grep BytesReplicated by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Test connectivity between nodes -paul@f0:~ % nc -zv 192.168.2.131 8888 +# Test connectivity between nodes +paul@f0:~ % nc -zv 192.168.2.131 8888 -# Check if zrepl is listening on f1 -paul@f1:~ % doas netstat -an | grep 8888 +# Check if zrepl is listening on f1 +paul@f1:~ % doas netstat -an | grep 8888 -# Verify WireGuard tunnel is working -paul@f0:~ % ping 192.168.2.131 +# Verify WireGuard tunnel is working +paul@f0:~ % ping 192.168.2.131
Encryption Key Issues
@@ -931,13 +936,13 @@ paul@f0:~ % ping 192.168.2.13 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Verify encryption keys are available on both nodes -paul@f0:~ % doas zfs get keystatus zdata/enc/nfsdata -paul@f1:~ % doas zfs get keystatus zdata/sink/f0/zdata/enc/nfsdata +# Verify encryption keys are available on both nodes +paul@f0:~ % doas zfs get keystatus zdata/enc/nfsdata +paul@f1:~ % doas zfs get keystatus zdata/sink/f0/zdata/enc/nfsdata -# Load keys if unavailable -paul@f1:~ % doas zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key \ - zdata/sink/f0/zdata/enc/nfsdata +# Load keys if unavailable +paul@f1:~ % doas zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key \ + zdata/sink/f0/zdata/enc/nfsdata
Monitoring Ongoing Replication
@@ -948,18 +953,18 @@ paul@f1:~ % doas zfs load-key -L file:///keys/f0.la by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Monitor replication progress (run repeatedly to check status) -paul@f0:~ % doas zrepl status --mode raw | grep -A10 BytesReplicated +# Monitor replication progress (run repeatedly to check status) +paul@f0:~ % doas zrepl status --mode raw | grep -A10 BytesReplicated -# Or install watch from ports and use it -paul@f0:~ % doas pkg install watch -paul@f0:~ % watch -n 5 'doas zrepl status --mode raw | grep -A10 BytesReplicated' +# Or install watch from ports and use it +paul@f0:~ % doas pkg install watch +paul@f0:~ % watch -n 5 'doas zrepl status --mode raw | grep -A10 BytesReplicated' -# Check for new snapshots being created -paul@f0:~ % doas zfs list -t snapshot | grep zrepl | tail -5 +# Check for new snapshots being created +paul@f0:~ % doas zfs list -t snapshot | grep zrepl | tail -5 -# Verify snapshots appear on receiver -paul@f1:~ % doas zfs list -t snapshot -r zdata/sink | grep zrepl | tail -5 +# Verify snapshots appear on receiver +paul@f1:~ % doas zfs list -t snapshot -r zdata/sink | grep zrepl | tail -5
This troubleshooting process resolves the most common zrepl issues and ensures continuous data replication between your storage nodes.
@@ -992,11 +997,11 @@ paul@f1:~ % doas zfs list -t snapshot -r zdata/sink | grep zrepl | tail - -# On f0 - The virtual IP 192.168.1.138 will float between f0 and f1 -ifconfig_re0_alias0="inet vhid 1 pass testpass alias 192.168.1.138/32" +# On f0 - The virtual IP 192.168.1.138 will float between f0 and f1 +ifconfig_re0_alias0="inet vhid 1 pass testpass alias 192.168.1.138/32" -# On f1 - Higher advskew means lower priority, so f0 wins elections -ifconfig_re0_alias0="inet vhid 1 advskew 100 pass testpass alias 192.168.1.138/32" +# On f1 - Higher advskew means lower priority, so f0 wins elections +ifconfig_re0_alias0="inet vhid 1 advskew 100 pass testpass alias 192.168.1.138/32"
Whereas:
@@ -1026,16 +1031,16 @@ fd42:beef:cafe:2::138 f3s-storage-ha f3s-storage-ha.wg0 f3s-storage-ha.wg0.wan.b by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % cat <<END | doas tee -a /etc/devd.conf -notify 0 { - match "system" "CARP"; - match "subsystem" "[0-9]+@[0-9a-z.]+"; - match "type" "(MASTER|BACKUP)"; - action "/usr/local/bin/carpcontrol.sh $subsystem $type"; -}; -END +paul@f0:~ % cat <<END | doas tee -a /etc/devd.conf +notify 0 { + match "system" "CARP"; + match "subsystem" "[0-9]+@[0-9a-z.]+"; + match "type" "(MASTER|BACKUP)"; + action "/usr/local/bin/carpcontrol.sh $subsystem $type"; +}; +END -paul@f0:~ % doas service devd restart +paul@f0:~ % doas service devd restart
Next, we create the CARP control script that will restart stunnel when the CARP state changes:
@@ -1046,29 +1051,29 @@ paul@f0:~ % doas service devd restart by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas tee /usr/local/bin/carpcontrol.sh <<'EOF' -#!/bin/sh -# CARP state change control script +paul@f0:~ % doas tee /usr/local/bin/carpcontrol.sh <<'EOF' +#!/bin/sh +# CARP state change control script -case "$2" in - MASTER) - logger "CARP state changed to MASTER, starting services" - ;; - BACKUP) - logger "CARP state changed to BACKUP, stopping services" - ;; - *) - logger "CARP state changed to $2 (unhandled)" - ;; -esac -EOF +case "$2" in + MASTER) + logger "CARP state changed to MASTER, starting services" + ;; + BACKUP) + logger "CARP state changed to BACKUP, stopping services" + ;; + *) + logger "CARP state changed to $2 (unhandled)" + ;; +esac +EOF -paul@f0:~ % doas chmod +x /usr/local/bin/carpcontrol.sh +paul@f0:~ % doas chmod +x /usr/local/bin/carpcontrol.sh -# Copy the same script to f1 -paul@f0:~ % scp /usr/local/bin/carpcontrol.sh f1:/tmp/ -paul@f1:~ % doas mv /tmp/carpcontrol.sh /usr/local/bin/ -paul@f1:~ % doas chmod +x /usr/local/bin/carpcontrol.sh +# Copy the same script to f1 +paul@f0:~ % scp /usr/local/bin/carpcontrol.sh f1:/tmp/ +paul@f1:~ % doas mv /tmp/carpcontrol.sh /usr/local/bin/ +paul@f1:~ % doas chmod +x /usr/local/bin/carpcontrol.sh
Note that carpcontrol.sh doesn't do anything useful yet. We will provide more details (including starting and stopping services upon failover) later in this blog post.
@@ -1079,10 +1084,10 @@ paul@f1:~ % doas chmod +x /usr/local/bin/carpcontrol.sh by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % echo 'carp_load="YES"' | doas tee -a /boot/loader.conf -carp_load="YES" -paul@f1:~ % echo 'carp_load="YES"' | doas tee -a /boot/loader.conf -carp_load="YES" +paul@f0:~ % echo 'carp_load="YES"' | doas tee -a /boot/loader.conf +carp_load="YES" +paul@f1:~ % echo 'carp_load="YES"' | doas tee -a /boot/loader.conf +carp_load="YES"
Then reboot both hosts or run doas kldload carp to load the module immediately.
@@ -1099,18 +1104,18 @@ carp_load="YES" by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas sysrc nfs_server_enable=YES -nfs_server_enable: YES -> YES -paul@f0:~ % doas sysrc nfsv4_server_enable=YES -nfsv4_server_enable: YES -> YES -paul@f0:~ % doas sysrc nfsuserd_enable=YES -nfsuserd_enable: YES -> YES -paul@f0:~ % doas sysrc nfsuserd_flags="-domain lan.buetow.org" -nfsuserd_flags: "" -> "-domain lan.buetow.org" -paul@f0:~ % doas sysrc mountd_enable=YES -mountd_enable: NO -> YES -paul@f0:~ % doas sysrc rpcbind_enable=YES -rpcbind_enable: NO -> YES +paul@f0:~ % doas sysrc nfs_server_enable=YES +nfs_server_enable: YES -> YES +paul@f0:~ % doas sysrc nfsv4_server_enable=YES +nfsv4_server_enable: YES -> YES +paul@f0:~ % doas sysrc nfsuserd_enable=YES +nfsuserd_enable: YES -> YES +paul@f0:~ % doas sysrc nfsuserd_flags="-domain lan.buetow.org" +nfsuserd_flags: "" -> "-domain lan.buetow.org" +paul@f0:~ % doas sysrc mountd_enable=YES +mountd_enable: NO -> YES +paul@f0:~ % doas sysrc rpcbind_enable=YES +rpcbind_enable: NO -> YES
Update: 08.08.2025: I've added the domain to nfsuserd_flags
@@ -1121,14 +1126,14 @@ rpcbind_enable: NO -> YES by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# First, ensure the dataset is mounted -paul@f0:~ % doas zfs get mounted zdata/enc/nfsdata -NAME PROPERTY VALUE SOURCE -zdata/enc/nfsdata mounted yes - +# First, ensure the dataset is mounted +paul@f0:~ % doas zfs get mounted zdata/enc/nfsdata +NAME PROPERTY VALUE SOURCE +zdata/enc/nfsdata mounted yes - -# Create the k3svolumes directory -paul@f0:~ % doas mkdir -p /data/nfs/k3svolumes -paul@f0:~ % doas chmod 755 /data/nfs/k3svolumes +# Create the k3svolumes directory +paul@f0:~ % doas mkdir -p /data/nfs/k3svolumes +paul@f0:~ % doas chmod 755 /data/nfs/k3svolumes
We also create the /etc/exports file. Since we're using stunnel for encryption, ALL clients must connect through stunnel, which appears as localhost (127.0.0.1) to the NFS server:
@@ -1137,10 +1142,10 @@ paul@f0:~ % doas chmod 755 /data/nfs/k3svolumes by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas tee /etc/exports <<'EOF' -V4: /data/nfs -sec=sys -/data/nfs -alldirs -maproot=root -network 127.0.0.1 -mask 255.255.255.255 -EOF +paul@f0:~ % doas tee /etc/exports <<'EOF' +V4: /data/nfs -sec=sys +/data/nfs -alldirs -maproot=root -network 127.0.0.1 -mask 255.255.255.255 +EOF
The exports configuration:
@@ -1156,14 +1161,14 @@ EOF by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas service rpcbind start -Starting rpcbind. -paul@f0:~ % doas service mountd start -Starting mountd. -paul@f0:~ % doas service nfsd start -Starting nfsd. -paul@f0:~ % doas service nfsuserd start -Starting nfsuserd. +paul@f0:~ % doas service rpcbind start +Starting rpcbind. +paul@f0:~ % doas service mountd start +Starting mountd. +paul@f0:~ % doas service nfsd start +Starting nfsd. +paul@f0:~ % doas service nfsuserd start +Starting nfsuserd.
Configuring Stunnel for NFS Encryption with CARP Failover
@@ -1202,32 +1207,32 @@ Starting nfsuserd. by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# On f0 - Create CA -paul@f0:~ % doas mkdir -p /usr/local/etc/stunnel/ca -paul@f0:~ % cd /usr/local/etc/stunnel/ca -paul@f0:~ % doas openssl genrsa -out ca-key.pem 4096 -paul@f0:~ % doas openssl req -new -x509 -days 3650 -key ca-key.pem -out ca-cert.pem \ - -subj '/C=US/ST=State/L=City/O=F3S Storage/CN=F3S Stunnel CA' +# On f0 - Create CA +paul@f0:~ % doas mkdir -p /usr/local/etc/stunnel/ca +paul@f0:~ % cd /usr/local/etc/stunnel/ca +paul@f0:~ % doas openssl genrsa -out ca-key.pem 4096 +paul@f0:~ % doas openssl req -new -x509 -days 3650 -key ca-key.pem -out ca-cert.pem \ + -subj '/C=US/ST=State/L=City/O=F3S Storage/CN=F3S Stunnel CA' -# Create server certificate -paul@f0:~ % cd /usr/local/etc/stunnel -paul@f0:~ % doas openssl genrsa -out server-key.pem 4096 -paul@f0:~ % doas openssl req -new -key server-key.pem -out server.csr \ - -subj '/C=US/ST=State/L=City/O=F3S Storage/CN=f3s-storage-ha.lan' -paul@f0:~ % doas openssl x509 -req -days 3650 -in server.csr -CA ca/ca-cert.pem \ - -CAkey ca/ca-key.pem -CAcreateserial -out server-cert.pem +# Create server certificate +paul@f0:~ % cd /usr/local/etc/stunnel +paul@f0:~ % doas openssl genrsa -out server-key.pem 4096 +paul@f0:~ % doas openssl req -new -key server-key.pem -out server.csr \ + -subj '/C=US/ST=State/L=City/O=F3S Storage/CN=f3s-storage-ha.lan' +paul@f0:~ % doas openssl x509 -req -days 3650 -in server.csr -CA ca/ca-cert.pem \ + -CAkey ca/ca-key.pem -CAcreateserial -out server-cert.pem -# Create client certificates for authorised clients -paul@f0:~ % cd /usr/local/etc/stunnel/ca -paul@f0:~ % doas sh -c 'for client in r0 r1 r2 earth; do - openssl genrsa -out ${client}-key.pem 4096 - openssl req -new -key ${client}-key.pem -out ${client}.csr \ - -subj "/C=US/ST=State/L=City/O=F3S Storage/CN=${client}.lan.buetow.org" - openssl x509 -req -days 3650 -in ${client}.csr -CA ca-cert.pem \ - -CAkey ca-key.pem -CAcreateserial -out ${client}-cert.pem - # Combine cert and key into a single file for stunnel client - cat ${client}-cert.pem ${client}-key.pem > ${client}-stunnel.pem -done' +# Create client certificates for authorised clients +paul@f0:~ % cd /usr/local/etc/stunnel/ca +paul@f0:~ % doas sh -c 'for client in r0 r1 r2 earth; do + openssl genrsa -out ${client}-key.pem 4096 + openssl req -new -key ${client}-key.pem -out ${client}.csr \ + -subj "/C=US/ST=State/L=City/O=F3S Storage/CN=${client}.lan.buetow.org" + openssl x509 -req -days 3650 -in ${client}.csr -CA ca-cert.pem \ + -CAkey ca-key.pem -CAcreateserial -out ${client}-cert.pem + # Combine cert and key into a single file for stunnel client + cat ${client}-cert.pem ${client}-key.pem > ${client}-stunnel.pem +done'
Install and Configure Stunnel on f0
@@ -1236,35 +1241,35 @@ paul@f0:~ % doas sh -c 'for client in r0 r1 r2 earth; do < by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Install stunnel -paul@f0:~ % doas pkg install -y stunnel +# Install stunnel +paul@f0:~ % doas pkg install -y stunnel -# Configure stunnel server with client certificate authentication -paul@f0:~ % doas tee /usr/local/etc/stunnel/stunnel.conf <<'EOF' -cert = /usr/local/etc/stunnel/server-cert.pem -key = /usr/local/etc/stunnel/server-key.pem +# Configure stunnel server with client certificate authentication +paul@f0:~ % doas tee /usr/local/etc/stunnel/stunnel.conf <<'EOF' +cert = /usr/local/etc/stunnel/server-cert.pem +key = /usr/local/etc/stunnel/server-key.pem -setuid = stunnel -setgid = stunnel +setuid = stunnel +setgid = stunnel -[nfs-tls] -accept = 192.168.1.138:2323 -connect = 127.0.0.1:2049 -CAfile = /usr/local/etc/stunnel/ca/ca-cert.pem -verify = 2 -requireCert = yes -EOF +[nfs-tls] +accept = 192.168.1.138:2323 +connect = 127.0.0.1:2049 +CAfile = /usr/local/etc/stunnel/ca/ca-cert.pem +verify = 2 +requireCert = yes +EOF -# Enable and start stunnel -paul@f0:~ % doas sysrc stunnel_enable=YES -stunnel_enable: -> YES -paul@f0:~ % doas service stunnel start -Starting stunnel. +# Enable and start stunnel +paul@f0:~ % doas sysrc stunnel_enable=YES +stunnel_enable: -> YES +paul@f0:~ % doas service stunnel start +Starting stunnel. -# Restart stunnel to apply the CARP VIP binding -paul@f0:~ % doas service stunnel restart -Stopping stunnel. -Starting stunnel. +# Restart stunnel to apply the CARP VIP binding +paul@f0:~ % doas service stunnel restart +Stopping stunnel. +Starting stunnel.
The configuration includes:
@@ -1282,30 +1287,30 @@ Starting stunnel. by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f1:~ % doas sysrc nfs_server_enable=YES -nfs_server_enable: NO -> YES -paul@f1:~ % doas sysrc nfsv4_server_enable=YES -nfsv4_server_enable: NO -> YES -paul@f1:~ % doas sysrc nfsuserd_enable=YES -nfsuserd_enable: NO -> YES -paul@f1:~ % doas sysrc mountd_enable=YES -mountd_enable: NO -> YES -paul@f1:~ % doas sysrc rpcbind_enable=YES -rpcbind_enable: NO -> YES +paul@f1:~ % doas sysrc nfs_server_enable=YES +nfs_server_enable: NO -> YES +paul@f1:~ % doas sysrc nfsv4_server_enable=YES +nfsv4_server_enable: NO -> YES +paul@f1:~ % doas sysrc nfsuserd_enable=YES +nfsuserd_enable: NO -> YES +paul@f1:~ % doas sysrc mountd_enable=YES +mountd_enable: NO -> YES +paul@f1:~ % doas sysrc rpcbind_enable=YES +rpcbind_enable: NO -> YES -paul@f1:~ % doas tee /etc/exports <<'EOF' -V4: /data/nfs -sec=sys -/data/nfs -alldirs -maproot=root -network 127.0.0.1 -mask 255.255.255.255 -EOF +paul@f1:~ % doas tee /etc/exports <<'EOF' +V4: /data/nfs -sec=sys +/data/nfs -alldirs -maproot=root -network 127.0.0.1 -mask 255.255.255.255 +EOF -paul@f1:~ % doas service rpcbind start -Starting rpcbind. -paul@f1:~ % doas service mountd start -Starting mountd. -paul@f1:~ % doas service nfsd start -Starting nfsd. -paul@f1:~ % doas service nfsuserd start -Starting nfsuserd. +paul@f1:~ % doas service rpcbind start +Starting rpcbind. +paul@f1:~ % doas service mountd start +Starting mountd. +paul@f1:~ % doas service nfsd start +Starting nfsd. +paul@f1:~ % doas service nfsuserd start +Starting nfsuserd.
And to configure stunnel on f1, we run:
@@ -1314,42 +1319,42 @@ Starting nfsuserd. by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Install stunnel -paul@f1:~ % doas pkg install -y stunnel +# Install stunnel +paul@f1:~ % doas pkg install -y stunnel -# Copy certificates from f0 -paul@f0:~ % doas tar -cf /tmp/stunnel-certs.tar \ - -C /usr/local/etc/stunnel server-cert.pem server-key.pem ca -paul@f0:~ % scp /tmp/stunnel-certs.tar f1:/tmp/ +# Copy certificates from f0 +paul@f0:~ % doas tar -cf /tmp/stunnel-certs.tar \ + -C /usr/local/etc/stunnel server-cert.pem server-key.pem ca +paul@f0:~ % scp /tmp/stunnel-certs.tar f1:/tmp/ -paul@f1:~ % cd /usr/local/etc/stunnel && doas tar -xf /tmp/stunnel-certs.tar +paul@f1:~ % cd /usr/local/etc/stunnel && doas tar -xf /tmp/stunnel-certs.tar -# Configure stunnel server on f1 with client certificate authentication -paul@f1:~ % doas tee /usr/local/etc/stunnel/stunnel.conf <<'EOF' -cert = /usr/local/etc/stunnel/server-cert.pem -key = /usr/local/etc/stunnel/server-key.pem +# Configure stunnel server on f1 with client certificate authentication +paul@f1:~ % doas tee /usr/local/etc/stunnel/stunnel.conf <<'EOF' +cert = /usr/local/etc/stunnel/server-cert.pem +key = /usr/local/etc/stunnel/server-key.pem -setuid = stunnel -setgid = stunnel +setuid = stunnel +setgid = stunnel -[nfs-tls] -accept = 192.168.1.138:2323 -connect = 127.0.0.1:2049 -CAfile = /usr/local/etc/stunnel/ca/ca-cert.pem -verify = 2 -requireCert = yes -EOF +[nfs-tls] +accept = 192.168.1.138:2323 +connect = 127.0.0.1:2049 +CAfile = /usr/local/etc/stunnel/ca/ca-cert.pem +verify = 2 +requireCert = yes +EOF -# Enable and start stunnel -paul@f1:~ % doas sysrc stunnel_enable=YES -stunnel_enable: -> YES -paul@f1:~ % doas service stunnel start -Starting stunnel. +# Enable and start stunnel +paul@f1:~ % doas sysrc stunnel_enable=YES +stunnel_enable: -> YES +paul@f1:~ % doas service stunnel start +Starting stunnel. -# Restart stunnel to apply the CARP VIP binding -paul@f1:~ % doas service stunnel restart -Stopping stunnel. -Starting stunnel. +# Restart stunnel to apply the CARP VIP binding +paul@f1:~ % doas service stunnel restart +Stopping stunnel. +Starting stunnel.
CARP Control Script for Clean Failover
@@ -1376,54 +1381,54 @@ Starting stunnel. by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Create CARP control script on both f0 and f1 -paul@f0:~ % doas tee /usr/local/bin/carpcontrol.sh <<'EOF' -#!/bin/sh -# CARP state change control script - -HOSTNAME=`hostname` - -if [ ! -f /data/nfs/nfs.DO_NOT_REMOVE ]; then - logger '/data/nfs not mounted, mounting it now!' - if [ "$HOSTNAME" = 'f0.lan.buetow.org' ]; then - zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key zdata/enc/nfsdata - zfs set mountpoint=/data/nfs zdata/enc/nfsdata - else - zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key zdata/sink/f0/zdata/enc/nfsdata - zfs set mountpoint=/data/nfs zdata/sink/f0/zdata/enc/nfsdata - zfs mount zdata/sink/f0/zdata/enc/nfsdata - zfs set readonly=on zdata/sink/f0/zdata/enc/nfsdata - fi - service nfsd stop 2>&1 - service mountd stop 2>&1 -fi - - -case "$2" in - MASTER) - logger "CARP state changed to MASTER, starting services" - service rpcbind start >/dev/null 2>&1 - service mountd start >/dev/null 2>&1 - service nfsd start >/dev/null 2>&1 - service nfsuserd start >/dev/null 2>&1 - service stunnel restart >/dev/null 2>&1 - logger "CARP MASTER: NFS and stunnel services started" - ;; - BACKUP) - logger "CARP state changed to BACKUP, stopping services" - service stunnel stop >/dev/null 2>&1 - service nfsd stop >/dev/null 2>&1 - service mountd stop >/dev/null 2>&1 - service nfsuserd stop >/dev/null 2>&1 - logger "CARP BACKUP: NFS and stunnel services stopped" - ;; - *) - logger "CARP state changed to $2 (unhandled)" - ;; -esac -EOF - -paul@f0:~ % doas chmod +x /usr/local/bin/carpcontrol.sh +# Create CARP control script on both f0 and f1 +paul@f0:~ % doas tee /usr/local/bin/carpcontrol.sh <<'EOF' +#!/bin/sh +# CARP state change control script + +HOSTNAME=`hostname` + +if [ ! -f /data/nfs/nfs.DO_NOT_REMOVE ]; then + logger '/data/nfs not mounted, mounting it now!' + if [ "$HOSTNAME" = 'f0.lan.buetow.org' ]; then + zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key zdata/enc/nfsdata + zfs set mountpoint=/data/nfs zdata/enc/nfsdata + else + zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key zdata/sink/f0/zdata/enc/nfsdata + zfs set mountpoint=/data/nfs zdata/sink/f0/zdata/enc/nfsdata + zfs mount zdata/sink/f0/zdata/enc/nfsdata + zfs set readonly=on zdata/sink/f0/zdata/enc/nfsdata + fi + service nfsd stop 2>&1 + service mountd stop 2>&1 +fi + + +case "$2" in + MASTER) + logger "CARP state changed to MASTER, starting services" + service rpcbind start >/dev/null 2>&1 + service mountd start >/dev/null 2>&1 + service nfsd start >/dev/null 2>&1 + service nfsuserd start >/dev/null 2>&1 + service stunnel restart >/dev/null 2>&1 + logger "CARP MASTER: NFS and stunnel services started" + ;; + BACKUP) + logger "CARP state changed to BACKUP, stopping services" + service stunnel stop >/dev/null 2>&1 + service nfsd stop >/dev/null 2>&1 + service mountd stop >/dev/null 2>&1 + service nfsuserd stop >/dev/null 2>&1 + logger "CARP BACKUP: NFS and stunnel services stopped" + ;; + *) + logger "CARP state changed to $2 (unhandled)" + ;; +esac +EOF + +paul@f0:~ % doas chmod +x /usr/local/bin/carpcontrol.sh
CARP Management Script
@@ -1434,113 +1439,113 @@ paul@f0:~ % doas chmod +x /usr/local/bin/carpcontrol.sh by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Create the CARP management script -paul@f0:~ % doas tee /usr/local/bin/carp <<'EOF' -#!/bin/sh -# CARP state management script -# Usage: carp [master|backup|auto-failback enable|auto-failback disable] -# Without arguments: shows current state - -# Find the interface with CARP configured -CARP_IF=$(ifconfig -l | xargs -n1 | while read if; do - ifconfig "$if" 2>/dev/null | grep -q "carp:" && echo "$if" && break -done) - -if [ -z "$CARP_IF" ]; then - echo "Error: No CARP interface found" - exit 1 -fi - -# Get CARP VHID -VHID=$(ifconfig "$CARP_IF" | grep "carp:" | sed -n 's/.*vhid \([0-9]*\).*/\1/p') - -if [ -z "$VHID" ]; then - echo "Error: Could not determine CARP VHID" - exit 1 -fi - -# Function to get the current state -get_state() { - ifconfig "$CARP_IF" | grep "carp:" | awk '{print $2}' -} - -# Check for auto-failback block file -BLOCK_FILE="/data/nfs/nfs.NO_AUTO_FAILBACK" -check_auto_failback() { - if [ -f "$BLOCK_FILE" ]; then - echo "WARNING: Auto-failback is DISABLED (file exists: $BLOCK_FILE)" - fi -} - -# Main logic -case "$1" in - "") - # No argument - show current state - STATE=$(get_state) - echo "CARP state on $CARP_IF (vhid $VHID): $STATE" - check_auto_failback - ;; - master) - # Force to MASTER state - echo "Setting CARP to MASTER state..." - ifconfig "$CARP_IF" vhid "$VHID" state master - sleep 1 - STATE=$(get_state) - echo "CARP state on $CARP_IF (vhid $VHID): $STATE" - check_auto_failback - ;; - backup) - # Force to BACKUP state - echo "Setting CARP to BACKUP state..." - ifconfig "$CARP_IF" vhid "$VHID" state backup - sleep 1 - STATE=$(get_state) - echo "CARP state on $CARP_IF (vhid $VHID): $STATE" - check_auto_failback - ;; - auto-failback) - case "$2" in - enable) - if [ -f "$BLOCK_FILE" ]; then - rm "$BLOCK_FILE" - echo "Auto-failback ENABLED (removed $BLOCK_FILE)" - else - echo "Auto-failback was already enabled" - fi - ;; - disable) - if [ ! -f "$BLOCK_FILE" ]; then - touch "$BLOCK_FILE" - echo "Auto-failback DISABLED (created $BLOCK_FILE)" - else - echo "Auto-failback was already disabled" - fi - ;; - *) - echo "Usage: $0 auto-failback [enable|disable]" - echo " enable: Remove block file to allow automatic failback" - echo " disable: Create block file to prevent automatic failback" - exit 1 - ;; - esac - ;; - *) - echo "Usage: $0 [master|backup|auto-failback enable|auto-failback disable]" - echo " Without arguments: show current CARP state" - echo " master: force this node to become CARP MASTER" - echo " backup: force this node to become CARP BACKUP" - echo " auto-failback enable: allow automatic failback to f0" - echo " auto-failback disable: prevent automatic failback to f0" - exit 1 - ;; -esac -EOF - -paul@f0:~ % doas chmod +x /usr/local/bin/carp - -# Copy to f1 as well -paul@f0:~ % scp /usr/local/bin/carp f1:/tmp/ -paul@f1:~ % doas cp /tmp/carp /usr/local/bin/carp && doas chmod +x /usr/local/bin/carp +# Create the CARP management script +paul@f0:~ % doas tee /usr/local/bin/carp <<'EOF' +#!/bin/sh +# CARP state management script +# Usage: carp [master|backup|auto-failback enable|auto-failback disable] +# Without arguments: shows current state + +# Find the interface with CARP configured +CARP_IF=$(ifconfig -l | xargs -n1 | while read if; do + ifconfig "$if" 2>/dev/null | grep -q "carp:" && echo "$if" && break +done) + +if [ -z "$CARP_IF" ]; then + echo "Error: No CARP interface found" + exit 1 +fi + +# Get CARP VHID +VHID=$(ifconfig "$CARP_IF" | grep "carp:" | sed -n 's/.*vhid \([0-9]*\).*/\1/p') + +if [ -z "$VHID" ]; then + echo "Error: Could not determine CARP VHID" + exit 1 +fi + +# Function to get the current state +get_state() { + ifconfig "$CARP_IF" | grep "carp:" | awk '{print $2}' +} + +# Check for auto-failback block file +BLOCK_FILE="/data/nfs/nfs.NO_AUTO_FAILBACK" +check_auto_failback() { + if [ -f "$BLOCK_FILE" ]; then + echo "WARNING: Auto-failback is DISABLED (file exists: $BLOCK_FILE)" + fi +} + +# Main logic +case "$1" in + "") + # No argument - show current state + STATE=$(get_state) + echo "CARP state on $CARP_IF (vhid $VHID): $STATE" + check_auto_failback + ;; + master) + # Force to MASTER state + echo "Setting CARP to MASTER state..." + ifconfig "$CARP_IF" vhid "$VHID" state master + sleep 1 + STATE=$(get_state) + echo "CARP state on $CARP_IF (vhid $VHID): $STATE" + check_auto_failback + ;; + backup) + # Force to BACKUP state + echo "Setting CARP to BACKUP state..." + ifconfig "$CARP_IF" vhid "$VHID" state backup + sleep 1 + STATE=$(get_state) + echo "CARP state on $CARP_IF (vhid $VHID): $STATE" + check_auto_failback + ;; + auto-failback) + case "$2" in + enable) + if [ -f "$BLOCK_FILE" ]; then + rm "$BLOCK_FILE" + echo "Auto-failback ENABLED (removed $BLOCK_FILE)" + else + echo "Auto-failback was already enabled" + fi + ;; + disable) + if [ ! -f "$BLOCK_FILE" ]; then + touch "$BLOCK_FILE" + echo "Auto-failback DISABLED (created $BLOCK_FILE)" + else + echo "Auto-failback was already disabled" + fi + ;; + *) + echo "Usage: $0 auto-failback [enable|disable]" + echo " enable: Remove block file to allow automatic failback" + echo " disable: Create block file to prevent automatic failback" + exit 1 + ;; + esac + ;; + *) + echo "Usage: $0 [master|backup|auto-failback enable|auto-failback disable]" + echo " Without arguments: show current CARP state" + echo " master: force this node to become CARP MASTER" + echo " backup: force this node to become CARP BACKUP" + echo " auto-failback enable: allow automatic failback to f0" + echo " auto-failback disable: prevent automatic failback to f0" + exit 1 + ;; +esac +EOF + +paul@f0:~ % doas chmod +x /usr/local/bin/carp + +# Copy to f1 as well +paul@f0:~ % scp /usr/local/bin/carp f1:/tmp/ +paul@f1:~ % doas cp /tmp/carp /usr/local/bin/carp && doas chmod +x /usr/local/bin/carp
Now you can easily manage CARP states and auto-failback:
@@ -1549,27 +1554,27 @@ paul@f1:~ % doas cp /tmp/carp /usr/local/bin/carp && doas chmod +x /usr/ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Check current CARP state -paul@f0:~ % doas carp -CARP state on re0 (vhid 1): MASTER +# Check current CARP state +paul@f0:~ % doas carp +CARP state on re0 (vhid 1): MASTER -# If auto-failback is disabled, you'll see a warning -paul@f0:~ % doas carp -CARP state on re0 (vhid 1): MASTER -WARNING: Auto-failback is DISABLED (file exists: /data/nfs/nfs.NO_AUTO_FAILBACK) +# If auto-failback is disabled, you'll see a warning +paul@f0:~ % doas carp +CARP state on re0 (vhid 1): MASTER +WARNING: Auto-failback is DISABLED (file exists: /data/nfs/nfs.NO_AUTO_FAILBACK) -# Force f0 to become BACKUP (triggers failover to f1) -paul@f0:~ % doas carp backup -Setting CARP to BACKUP state... -CARP state on re0 (vhid 1): BACKUP +# Force f0 to become BACKUP (triggers failover to f1) +paul@f0:~ % doas carp backup +Setting CARP to BACKUP state... +CARP state on re0 (vhid 1): BACKUP -# Disable auto-failback (useful for maintenance) -paul@f0:~ % doas carp auto-failback disable -Auto-failback DISABLED (created /data/nfs/nfs.NO_AUTO_FAILBACK) +# Disable auto-failback (useful for maintenance) +paul@f0:~ % doas carp auto-failback disable +Auto-failback DISABLED (created /data/nfs/nfs.NO_AUTO_FAILBACK) -# Enable auto-failback -paul@f0:~ % doas carp auto-failback enable -Auto-failback ENABLED (removed /data/nfs/nfs.NO_AUTO_FAILBACK) +# Enable auto-failback +paul@f0:~ % doas carp auto-failback enable +Auto-failback ENABLED (removed /data/nfs/nfs.NO_AUTO_FAILBACK)
Automatic Failback After Reboot
@@ -1582,60 +1587,60 @@ Auto-failback ENABLED (removed /data/nfs/nfs.NO_AUTO_FAILBACK) by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas tee /usr/local/bin/carp-auto-failback.sh <<'EOF' -#!/bin/sh -# CARP automatic failback script for f0 -# Ensures f0 reclaims MASTER role after reboot when storage is ready +paul@f0:~ % doas tee /usr/local/bin/carp-auto-failback.sh <<'EOF' +#!/bin/sh +# CARP automatic failback script for f0 +# Ensures f0 reclaims MASTER role after reboot when storage is ready -LOGFILE="/var/log/carp-auto-failback.log" -MARKER_FILE="/data/nfs/nfs.DO_NOT_REMOVE" -BLOCK_FILE="/data/nfs/nfs.NO_AUTO_FAILBACK" +LOGFILE="/var/log/carp-auto-failback.log" +MARKER_FILE="/data/nfs/nfs.DO_NOT_REMOVE" +BLOCK_FILE="/data/nfs/nfs.NO_AUTO_FAILBACK" -log_message() { - echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOGFILE" -} +log_message() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOGFILE" +} -# Check if we're already MASTER -CURRENT_STATE=$(/usr/local/bin/carp | awk '{print $NF}') -if [ "$CURRENT_STATE" = "MASTER" ]; then - exit 0 -fi +# Check if we're already MASTER +CURRENT_STATE=$(/usr/local/bin/carp | awk '{print $NF}') +if [ "$CURRENT_STATE" = "MASTER" ]; then + exit 0 +fi -# Check if /data/nfs is mounted -if ! mount | grep -q "on /data/nfs "; then - log_message "SKIP: /data/nfs not mounted" - exit 0 -fi +# Check if /data/nfs is mounted +if ! mount | grep -q "on /data/nfs "; then + log_message "SKIP: /data/nfs not mounted" + exit 0 +fi -# Check if the marker file exists -# (identifies that the ZFS data set is properly mounted) -if [ ! -f "$MARKER_FILE" ]; then - log_message "SKIP: Marker file $MARKER_FILE not found" - exit 0 -fi +# Check if the marker file exists +# (identifies that the ZFS data set is properly mounted) +if [ ! -f "$MARKER_FILE" ]; then + log_message "SKIP: Marker file $MARKER_FILE not found" + exit 0 +fi -# Check if failback is blocked (for maintenance) -if [ -f "$BLOCK_FILE" ]; then - log_message "SKIP: Failback blocked by $BLOCK_FILE" - exit 0 -fi +# Check if failback is blocked (for maintenance) +if [ -f "$BLOCK_FILE" ]; then + log_message "SKIP: Failback blocked by $BLOCK_FILE" + exit 0 +fi -# All conditions met - promote to MASTER -log_message "CONDITIONS MET: Promoting to MASTER (was $CURRENT_STATE)" -/usr/local/bin/carp master +# All conditions met - promote to MASTER +log_message "CONDITIONS MET: Promoting to MASTER (was $CURRENT_STATE)" +/usr/local/bin/carp master -# Log result -sleep 2 -NEW_STATE=$(/usr/local/bin/carp | awk '{print $NF}') -log_message "Failback complete: State is now $NEW_STATE" +# Log result +sleep 2 +NEW_STATE=$(/usr/local/bin/carp | awk '{print $NF}') +log_message "Failback complete: State is now $NEW_STATE" -# If successful, log to the system log too -if [ "$NEW_STATE" = "MASTER" ]; then - logger "CARP: f0 automatically reclaimed MASTER role" -fi -EOF +# If successful, log to the system log too +if [ "$NEW_STATE" = "MASTER" ]; then + logger "CARP: f0 automatically reclaimed MASTER role" +fi +EOF -paul@f0:~ % doas chmod +x /usr/local/bin/carp-auto-failback.sh +paul@f0:~ % doas chmod +x /usr/local/bin/carp-auto-failback.sh
The marker file identifies that the ZFS data set is mounted correctly. We create it with:
@@ -1644,7 +1649,7 @@ paul@f0:~ % doas chmod +x /usr/local/bin/carp-auto-failback.sh by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas touch /data/nfs/nfs.DO_NOT_REMOVE +paul@f0:~ % doas touch /data/nfs/nfs.DO_NOT_REMOVE
We add a cron job to check every minute:
@@ -1653,7 +1658,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % echo "* * * * * /usr/local/bin/carp-auto-failback.sh" | doas crontab - +paul@f0:~ % echo "* * * * * /usr/local/bin/carp-auto-failback.sh" | doas crontab -
The enhanced CARP script provides integrated control over auto-failback. To temporarily turn off automatic failback (e.g., for f0 maintenance), we run:
@@ -1662,8 +1667,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas carp auto-failback disable -Auto-failback DISABLED (created /data/nfs/nfs.NO_AUTO_FAILBACK) +paul@f0:~ % doas carp auto-failback disable +Auto-failback DISABLED (created /data/nfs/nfs.NO_AUTO_FAILBACK)
And to re-enable it:
@@ -1672,8 +1677,8 @@ Auto-failback DISABLED (created /data/nfs/nfs.NO_AUTO_FAILBACK) by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas carp auto-failback enable -Auto-failback ENABLED (removed /data/nfs/nfs.NO_AUTO_FAILBACK) +paul@f0:~ % doas carp auto-failback enable +Auto-failback ENABLED (removed /data/nfs/nfs.NO_AUTO_FAILBACK)
To check whether auto-failback is enabled, we run:
@@ -1682,9 +1687,9 @@ Auto-failback ENABLED (removed /data/nfs/nfs.NO_AUTO_FAILBACK) by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f0:~ % doas carp -CARP state on re0 (vhid 1): MASTER -# If disabled, you'll see: WARNING: Auto-failback is DISABLED +paul@f0:~ % doas carp +CARP state on re0 (vhid 1): MASTER +# If disabled, you'll see: WARNING: Auto-failback is DISABLED
The failback attempts are logged to /var/log/carp-auto-failback.log!
@@ -1711,29 +1716,29 @@ CARP state on re0 (vhid 1): MASTER by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Install stunnel on client (example for `r0`) -[root@r0 ~]# dnf install -y stunnel nfs-utils +# Install stunnel on client (example for `r0`) +[root@r0 ~]# dnf install -y stunnel nfs-utils -# Copy client certificate and CA certificate from f0 -[root@r0 ~]# scp f0:/usr/local/etc/stunnel/ca/r0-stunnel.pem /etc/stunnel/ -[root@r0 ~]# scp f0:/usr/local/etc/stunnel/ca/ca-cert.pem /etc/stunnel/ +# Copy client certificate and CA certificate from f0 +[root@r0 ~]# scp f0:/usr/local/etc/stunnel/ca/r0-stunnel.pem /etc/stunnel/ +[root@r0 ~]# scp f0:/usr/local/etc/stunnel/ca/ca-cert.pem /etc/stunnel/ -# Configure stunnel client with certificate authentication -[root@r0 ~]# tee /etc/stunnel/stunnel.conf <<'EOF' -cert = /etc/stunnel/r0-stunnel.pem -CAfile = /etc/stunnel/ca-cert.pem -client = yes -verify = 2 +# Configure stunnel client with certificate authentication +[root@r0 ~]# tee /etc/stunnel/stunnel.conf <<'EOF' +cert = /etc/stunnel/r0-stunnel.pem +CAfile = /etc/stunnel/ca-cert.pem +client = yes +verify = 2 -[nfs-ha] -accept = 127.0.0.1:2323 -connect = 192.168.1.138:2323 -EOF +[nfs-ha] +accept = 127.0.0.1:2323 +connect = 192.168.1.138:2323 +EOF -# Enable and start stunnel -[root@r0 ~]# systemctl enable --now stunnel +# Enable and start stunnel +[root@r0 ~]# systemctl enable --now stunnel -# Repeat for r1 and r2 with their respective certificates +# Repeat for r1 and r2 with their respective certificates
Note: Each client must use its certificate file (r0-stunnel.pem, r1-stunnel.pem, r2-stunnel.pem, or earth-stunnel.pem - the latter is for my Laptop, which can also mount the NFS shares).
@@ -1759,8 +1764,8 @@ Domain = lan.buetow.org by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~]# echo 'fs.inotify.max_user_instances = 512' > /etc/sysctl.d/99-inotify.conf -[root@r0 ~]# sysctl -w fs.inotify.max_user_instances=512 +[root@r0 ~]# echo 'fs.inotify.max_user_instances = 512' > /etc/sysctl.d/99-inotify.conf +[root@r0 ~]# sysctl -w fs.inotify.max_user_instances=512
And afterwards, we need to run the following on all 3 Rocky hosts:
@@ -1769,8 +1774,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~]# systemctl start nfs-idmapd -[root@r0 ~]# systemctl enable --now nfs-client.target +[root@r0 ~]# systemctl start nfs-idmapd +[root@r0 ~]# systemctl enable --now nfs-client.target
and then, safest, reboot those.
@@ -1783,21 +1788,21 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Create a mount point -[root@r0 ~]# mkdir -p /data/nfs/k3svolumes +# Create a mount point +[root@r0 ~]# mkdir -p /data/nfs/k3svolumes -# Mount through stunnel (using localhost and NFSv4) -[root@r0 ~]# mount -t nfs4 -o port=2323 127.0.0.1:/k3svolumes /data/nfs/k3svolumes +# Mount through stunnel (using localhost and NFSv4) +[root@r0 ~]# mount -t nfs4 -o port=2323 127.0.0.1:/k3svolumes /data/nfs/k3svolumes -# Verify mount -[root@r0 ~]# mount | grep k3svolumes -127.0.0.1:/k3svolumes on /data/nfs/k3svolumes - type nfs4 (rw,relatime,vers=4.2,rsize=131072,wsize=131072, - namlen=255,hard,proto=tcp,port=2323,timeo=600,retrans=2,sec=sys, - clientaddr=127.0.0.1,local_lock=none,addr=127.0.0.1) +# Verify mount +[root@r0 ~]# mount | grep k3svolumes +127.0.0.1:/k3svolumes on /data/nfs/k3svolumes + type nfs4 (rw,relatime,vers=4.2,rsize=131072,wsize=131072, + namlen=255,hard,proto=tcp,port=2323,timeo=600,retrans=2,sec=sys, + clientaddr=127.0.0.1,local_lock=none,addr=127.0.0.1) -# For persistent mount, add to /etc/fstab: -127.0.0.1:/k3svolumes /data/nfs/k3svolumes nfs4 port=2323,_netdev,soft,timeo=10,retrans=2,intr 0 0 +# For persistent mount, add to /etc/fstab: +127.0.0.1:/k3svolumes /data/nfs/k3svolumes nfs4 port=2323,_netdev,soft,timeo=10,retrans=2,intr 0 0
Note: The mount uses localhost (127.0.0.1) because stunnel is listening locally and forwarding the encrypted traffic to the remote server.
@@ -1810,20 +1815,20 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# On f0 (current MASTER) - trigger failover -paul@f0:~ % doas ifconfig re0 vhid 1 state backup +# On f0 (current MASTER) - trigger failover +paul@f0:~ % doas ifconfig re0 vhid 1 state backup -# On f1 - verify it becomes MASTER -paul@f1:~ % ifconfig re0 | grep carp - inet 192.168.1.138 netmask 0xffffffff broadcast 192.168.1.138 vhid 1 +# On f1 - verify it becomes MASTER +paul@f1:~ % ifconfig re0 | grep carp + inet 192.168.1.138 netmask 0xffffffff broadcast 192.168.1.138 vhid 1 -# Check stunnel is now listening on f1 -paul@f1:~ % doas sockstat -l | grep 2323 -stunnel stunnel 4567 3 tcp4 192.168.1.138:2323 *:* +# Check stunnel is now listening on f1 +paul@f1:~ % doas sockstat -l | grep 2323 +stunnel stunnel 4567 3 tcp4 192.168.1.138:2323 *:* -# On client - verify NFS mount still works -[root@r0 ~]# ls /data/nfs/k3svolumes/ -[root@r0 ~]# echo "Test after failover" > /data/nfs/k3svolumes/failover-test.txt +# On client - verify NFS mount still works +[root@r0 ~]# ls /data/nfs/k3svolumes/ +[root@r0 ~]# echo "Test after failover" > /data/nfs/k3svolumes/failover-test.txt
After a CARP failover, NFS clients may experience "Stale file handle" errors because they cached file handles from the previous server. To resolve this manually, we can run:
@@ -1832,9 +1837,9 @@ stunnel stunnel 4567 3 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# Force unmount and remount -[root@r0 ~]# umount -f /data/nfs/k3svolumes -[root@r0 ~]# mount /data/nfs/k3svolumes +# Force unmount and remount +[root@r0 ~]# umount -f /data/nfs/k3svolumes +[root@r0 ~]# mount /data/nfs/k3svolumes
For the automatic recovery, we create a script:
@@ -1843,72 +1848,72 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~]# cat > /usr/local/bin/check-nfs-mount.sh << 'EOF' -#!/bin/bash -# Fast NFS mount health monitor - runs every 10 seconds via systemd timer - -MOUNT_POINT="/data/nfs/k3svolumes" -LOCK_FILE="/var/run/nfs-mount-check.lock" - -# Use a lock file to prevent concurrent runs -if [ -f "$LOCK_FILE" ]; then - exit 0 -fi -touch "$LOCK_FILE" -trap "rm -f $LOCK_FILE" EXIT - -fix_mount () { - echo "Attempting to remount NFS mount $MOUNT_POINT" - if mount -o remount -f "$MOUNT_POINT" 2>/dev/null; then - echo "Remount command issued for $MOUNT_POINT" - else - echo "Failed to remount NFS mount $MOUNT_POINT" - fi - - echo "Checking if $MOUNT_POINT is a mountpoint" - if mountpoint "$MOUNT_POINT" >/dev/null 2>&1; then - echo "$MOUNT_POINT is a valid mountpoint" - else - echo "$MOUNT_POINT is not a valid mountpoint, attempting mount" - if mount "$MOUNT_POINT"; then - echo "Successfully mounted $MOUNT_POINT" - return - else - echo "Failed to mount $MOUNT_POINT" - fi - fi - - echo "Attempting to unmount $MOUNT_POINT" - if umount -f "$MOUNT_POINT" 2>/dev/null; then - echo "Successfully unmounted $MOUNT_POINT" - else - echo "Failed to unmount $MOUNT_POINT (it might not be mounted)" - fi - - echo "Attempting to mount $MOUNT_POINT" - if mount "$MOUNT_POINT"; then - echo "NFS mount $MOUNT_POINT mounted successfully" - return - else - echo "Failed to mount NFS mount $MOUNT_POINT" - fi - - echo "Failed to fix NFS mount $MOUNT_POINT" - exit 1 -} - -if ! mountpoint "$MOUNT_POINT" >/dev/null 2>&1; then - echo "NFS mount $MOUNT_POINT not found" - fix_mount -fi - -if ! timeout 2s stat "$MOUNT_POINT" >/dev/null 2>&1; then - echo "NFS mount $MOUNT_POINT appears to be unresponsive" - fix_mount -fi -EOF - -[root@r0 ~]# chmod +x /usr/local/bin/check-nfs-mount.sh +[root@r0 ~]# cat > /usr/local/bin/check-nfs-mount.sh << 'EOF' +#!/bin/bash +# Fast NFS mount health monitor - runs every 10 seconds via systemd timer + +MOUNT_POINT="/data/nfs/k3svolumes" +LOCK_FILE="/var/run/nfs-mount-check.lock" + +# Use a lock file to prevent concurrent runs +if [ -f "$LOCK_FILE" ]; then + exit 0 +fi +touch "$LOCK_FILE" +trap "rm -f $LOCK_FILE" EXIT + +fix_mount () { + echo "Attempting to remount NFS mount $MOUNT_POINT" + if mount -o remount -f "$MOUNT_POINT" 2>/dev/null; then + echo "Remount command issued for $MOUNT_POINT" + else + echo "Failed to remount NFS mount $MOUNT_POINT" + fi + + echo "Checking if $MOUNT_POINT is a mountpoint" + if mountpoint "$MOUNT_POINT" >/dev/null 2>&1; then + echo "$MOUNT_POINT is a valid mountpoint" + else + echo "$MOUNT_POINT is not a valid mountpoint, attempting mount" + if mount "$MOUNT_POINT"; then + echo "Successfully mounted $MOUNT_POINT" + return + else + echo "Failed to mount $MOUNT_POINT" + fi + fi + + echo "Attempting to unmount $MOUNT_POINT" + if umount -f "$MOUNT_POINT" 2>/dev/null; then + echo "Successfully unmounted $MOUNT_POINT" + else + echo "Failed to unmount $MOUNT_POINT (it might not be mounted)" + fi + + echo "Attempting to mount $MOUNT_POINT" + if mount "$MOUNT_POINT"; then + echo "NFS mount $MOUNT_POINT mounted successfully" + return + else + echo "Failed to mount NFS mount $MOUNT_POINT" + fi + + echo "Failed to fix NFS mount $MOUNT_POINT" + exit 1 +} + +if ! mountpoint "$MOUNT_POINT" >/dev/null 2>&1; then + echo "NFS mount $MOUNT_POINT not found" + fix_mount +fi + +if ! timeout 2s stat "$MOUNT_POINT" >/dev/null 2>&1; then + echo "NFS mount $MOUNT_POINT appears to be unresponsive" + fix_mount +fi +EOF + +[root@r0 ~]# chmod +x /usr/local/bin/check-nfs-mount.sh
And we create the systemd service as follows:
@@ -1917,17 +1922,17 @@ EOF by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~]# cat > /etc/systemd/system/nfs-mount-monitor.service << 'EOF' -[Unit] -Description=NFS Mount Health Monitor -After=network-online.target +[root@r0 ~]# cat > /etc/systemd/system/nfs-mount-monitor.service << 'EOF' +[Unit] +Description=NFS Mount Health Monitor +After=network-online.target -[Service] -Type=oneshot -ExecStart=/usr/local/bin/check-nfs-mount.sh -StandardOutput=journal -StandardError=journal -EOF +[Service] +Type=oneshot +ExecStart=/usr/local/bin/check-nfs-mount.sh +StandardOutput=journal +StandardError=journal +EOF
And we also create the systemd timer (runs every 10 seconds):
@@ -1936,19 +1941,19 @@ EOF by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~]# cat > /etc/systemd/system/nfs-mount-monitor.timer << 'EOF' -[Unit] -Description=Run NFS Mount Health Monitor every 10 seconds -Requires=nfs-mount-monitor.service +[root@r0 ~]# cat > /etc/systemd/system/nfs-mount-monitor.timer << 'EOF' +[Unit] +Description=Run NFS Mount Health Monitor every 10 seconds +Requires=nfs-mount-monitor.service -[Timer] -OnBootSec=30s -OnUnitActiveSec=10s -AccuracySec=1s +[Timer] +OnBootSec=30s +OnUnitActiveSec=10s +AccuracySec=1s -[Install] -WantedBy=timers.target -EOF +[Install] +WantedBy=timers.target +EOF
To enable and start the timer, we run:
@@ -1957,19 +1962,19 @@ EOF by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -[root@r0 ~]# systemctl daemon-reload -[root@r0 ~]# systemctl enable nfs-mount-monitor.timer -[root@r0 ~]# systemctl start nfs-mount-monitor.timer +[root@r0 ~]# systemctl daemon-reload +[root@r0 ~]# systemctl enable nfs-mount-monitor.timer +[root@r0 ~]# systemctl start nfs-mount-monitor.timer -# Check status -[root@r0 ~]# systemctl status nfs-mount-monitor.timer -● nfs-mount-monitor.timer - Run NFS Mount Health Monitor every 10 seconds - Loaded: loaded (/etc/systemd/system/nfs-mount-monitor.timer; enabled) - Active: active (waiting) since Sat 2025-07-06 10:00:00 EEST - Trigger: Sat 2025-07-06 10:00:10 EEST; 8s left +# Check status +[root@r0 ~]# systemctl status nfs-mount-monitor.timer +● nfs-mount-monitor.timer - Run NFS Mount Health Monitor every 10 seconds + Loaded: loaded (/etc/systemd/system/nfs-mount-monitor.timer; enabled) + Active: active (waiting) since Sat 2025-07-06 10:00:00 EEST + Trigger: Sat 2025-07-06 10:00:10 EEST; 8s left -# Monitor logs -[root@r0 ~]# journalctl -u nfs-mount-monitor -f +# Monitor logs +[root@r0 ~]# journalctl -u nfs-mount-monitor -f
Note: Stale file handles are inherent to NFS failover because file handles are server-specific. The best approach depends on your application's tolerance for brief disruptions. Of course, all the changes made to r0 above must also be applied to r1 and r2.
@@ -1982,30 +1987,30 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -# 1. Check the initial state -paul@f0:~ % ifconfig re0 | grep carp - carp: MASTER vhid 1 advbase 1 advskew 0 -paul@f1:~ % ifconfig re0 | grep carp - carp: BACKUP vhid 1 advbase 1 advskew 100 +# 1. Check the initial state +paul@f0:~ % ifconfig re0 | grep carp + carp: MASTER vhid 1 advbase 1 advskew 0 +paul@f1:~ % ifconfig re0 | grep carp + carp: BACKUP vhid 1 advbase 1 advskew 100 -# 2. Create a test file from a client -[root@r0 ~]# echo "test before failover" > /data/nfs/k3svolumes/test-before.txt +# 2. Create a test file from a client +[root@r0 ~]# echo "test before failover" > /data/nfs/k3svolumes/test-before.txt -# 3. Trigger failover (f0 → f1) -paul@f0:~ % doas ifconfig re0 vhid 1 state backup +# 3. Trigger failover (f0 → f1) +paul@f0:~ % doas ifconfig re0 vhid 1 state backup -# 4. Monitor client behaviour -[root@r0 ~]# ls /data/nfs/k3svolumes/ -ls: cannot access '/data/nfs/k3svolumes/': Stale file handle +# 4. Monitor client behaviour +[root@r0 ~]# ls /data/nfs/k3svolumes/ +ls: cannot access '/data/nfs/k3svolumes/': Stale file handle -# 5. Check automatic recovery (within 10 seconds) -[root@r0 ~]# journalctl -u nfs-mount-monitor -f -Jul 06 10:15:32 r0 nfs-monitor[1234]: NFS mount unhealthy detected at \ - Sun Jul 6 10:15:32 EEST 2025 -Jul 06 10:15:32 r0 nfs-monitor[1234]: Attempting to fix stale NFS mount at \ - Sun Jul 6 10:15:32 EEST 2025 -Jul 06 10:15:33 r0 nfs-monitor[1234]: NFS mount fixed at \ - Sun Jul 6 10:15:33 EEST 2025 +# 5. Check automatic recovery (within 10 seconds) +[root@r0 ~]# journalctl -u nfs-mount-monitor -f +Jul 06 10:15:32 r0 nfs-monitor[1234]: NFS mount unhealthy detected at \ + Sun Jul 6 10:15:32 EEST 2025 +Jul 06 10:15:32 r0 nfs-monitor[1234]: Attempting to fix stale NFS mount at \ + Sun Jul 6 10:15:32 EEST 2025 +Jul 06 10:15:33 r0 nfs-monitor[1234]: NFS mount fixed at \ + Sun Jul 6 10:15:33 EEST 2025
Failover Timeline:
@@ -2063,7 +2068,7 @@ Jul 06 10: -paul@f0:~ % doas zpool online -e /dev/ada1 +paul@f0:~ % doas zpool online -e /dev/ada1
@@ -2076,15 +2081,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
paul@f0:~ % doas zpool list -NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT -zdata 3.63T 677G 2.97T - - 3% 18% 1.00x ONLINE - -zroot 472G 68.4G 404G - - 13% 14% 1.00x ONLINE - +paul@f0:~ % doas zpool list +NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT +zdata 3.63T 677G 2.97T - - 3% 18% 1.00x ONLINE - +zroot 472G 68.4G 404G - - 13% 14% 1.00x ONLINE - -paul@f0:~ % doas camcontrol devlist -<512GB SSD D910R170> at scbus0 target 0 lun 0 (pass0,ada0) -<SD Ultra 3D 4TB 530500WD> at scbus1 target 0 lun 0 (pass1,ada1) -<Generic Flash Disk 8.07> at scbus2 target 0 lun 0 (da0,pass2) +paul@f0:~ % doas camcontrol devlist +<512GB SSD D910R170> at scbus0 target 0 lun 0 (pass0,ada0) +<SD Ultra 3D 4TB 530500WD> at scbus1 target 0 lun 0 (pass1,ada1) +<Generic Flash Disk 8.07> at scbus2 target 0 lun 0 (da0,pass2)
We're still using different SSD models on f1 (WD Blue SA510 4TB) to avoid simultaneous failures:
@@ -2093,10 +2098,10 @@ paul@f0:~ % doas camcontrol devlist by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -paul@f1:~ % doas camcontrol devlist -<512GB SSD D910R170> at scbus0 target 0 lun 0 (pass0,ada0) -<WD Blue SA510 2.5 4TB 530500WD> at scbus1 target 0 lun 0 (pass1,ada1) -<Generic Flash Disk 8.07> at scbus2 target 0 lun 0 (da0,pass2) +paul@f1:~ % doas camcontrol devlist +<512GB SSD D910R170> at scbus0 target 0 lun 0 (pass0,ada0) +<WD Blue SA510 2.5 4TB 530500WD> at scbus1 target 0 lun 0 (pass1,ada1) +<Generic Flash Disk 8.07> at scbus2 target 0 lun 0 (da0,pass2)
Conclusion
@@ -2165,11 +2170,12 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
+ diff --git a/gemfeed/2025-08-05-local-coding-llm-with-ollama.html b/gemfeed/2025-08-05-local-coding-llm-with-ollama.html index f2a9d99d..fab22a5e 100644 --- a/gemfeed/2025-08-05-local-coding-llm-with-ollama.html +++ b/gemfeed/2025-08-05-local-coding-llm-with-ollama.html @@ -2,12 +2,17 @@ +Local LLM for Coding with Ollama on macOS - + + + + + @@ -105,9 +110,9 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -brew install ollama -rehash -ollama serve +brew install ollama +rehash +ollama serve
Which started up the Ollama server with something like this (the screenshots shows already some requests made):
@@ -120,7 +125,7 @@ ollama serve by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -ollama pull qwen2.5-coder:14b-instruct +ollama pull qwen2.5-coder:14b-instruct
Now, I was ready to go! It wasn't so difficult. Now, let's see how I used this model for coding tasks.
@@ -133,27 +138,27 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -time echo "Write a function in golang to print out the Nth fibonacci number, \ - only the function without the boilerplate" | ollama run qwen2.5-coder:14b-instruct - -Output: - -func fibonacci(n int) int { - if n <= 1 { - return n - } - a, b := 0, 1 - for i := 2; i <= n; i++ { - a, b = b, a+b - } - return b -} - -Execution Metrics: - -Executed in 4.90 secs fish external - usr time 15.54 millis 0.31 millis 15.24 millis - sys time 19.68 millis 1.02 millis 18.66 millis +time echo "Write a function in golang to print out the Nth fibonacci number, \ + only the function without the boilerplate" | ollama run qwen2.5-coder:14b-instruct + +Output: + +func fibonacci(n int) int { + if n <= 1 { + return n + } + a, b := 0, 1 + for i := 2; i <= n; i++ { + a, b = b, a+b + } + return b +} + +Execution Metrics: + +Executed in 4.90 secs fish external + usr time 15.54 millis 0.31 millis 15.24 millis + sys time 19.68 millis 1.02 millis 18.66 millis
Note, after having written this blog post, I tried the same with the newer model qwen3-coder:30b-a3b-q4_K_M (which "just" came out, and it's a quantised 30B model), and it was much faster:
@@ -178,9 +183,9 @@ Executed in 1.83 secs fish external by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -brew install aider -mkdir -p ~/git/aitest && cd ~/git/aitest && git init -aider --model ollama_chat/qwen2.5-coder:14b-instruct +brew install aider +mkdir -p ~/git/aitest && cd ~/git/aitest && git init +aider --model ollama_chat/qwen2.5-coder:14b-instruct
https://aider.chat
@@ -223,11 +228,11 @@ Create a Go project with these files: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -go build cmd/aitest/main.go -./main -v -0.0.0 -./main -dir . -Number of files in directory .: 4 +go build cmd/aitest/main.go +./main -v +0.0.0 +./main -dir . +Number of files in directory .: 4
The code
@@ -238,38 +243,38 @@ Number of files in directory .: -package main - -import ( - "flag" - "fmt" - "os" - - "aitest/internal" -) - -func main() { - var versionFlag bool - flag.BoolVar(&versionFlag, "v", false, "print version") - dir := flag.String("dir", "", "directory to count files in") - flag.Parse() - - if versionFlag { - fmt.Println(internal.GetVersion()) - return - } - - if *dir != "" { - fileCount, err := internal.CountFiles(*dir) - if err != nil { - fmt.Fprintf(os.Stderr, "Error counting files: %v\n", err) - os.Exit(1) - } - fmt.Printf("Number of files in directory %s: %d\n", *dir, fileCount) - } else { - fmt.Println("No directory specified. No count given.") - } -} +package main + +import ( + "flag" + "fmt" + "os" + + "aitest/internal" +) + +func main() { + var versionFlag bool + flag.BoolVar(&versionFlag, "v", false, "print version") + dir := flag.String("dir", "", "directory to count files in") + flag.Parse() + + if versionFlag { + fmt.Println(internal.GetVersion()) + return + } + + if *dir != "" { + fileCount, err := internal.CountFiles(*dir) + if err != nil { + fmt.Fprintf(os.Stderr, "Error counting files: %v\n", err) + os.Exit(1) + } + fmt.Printf("Number of files in directory %s: %d\n", *dir, fileCount) + } else { + fmt.Println("No directory specified. No count given.") + } +}
The ./internal/version.go file:
@@ -278,13 +283,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -package internal +package internal -var Version = "0.0.0" +var Version = "0.0.0" -func GetVersion() string { - return Version -} +func GetVersion() string { + return Version +}
The ./internal/count.go file:
@@ -293,27 +298,27 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -package internal - -import ( - "os" -) - -func CountFiles(dir string) (int, error) { - files, err := os.ReadDir(dir) - if err != nil { - return 0, err - } - - count := 0 - for _, file := range files { - if !file.IsDir() { - count++ - } - } - - return count, nil -} +package internal + +import ( + "os" +) + +func CountFiles(dir string) (int, error) { + files, err := os.ReadDir(dir) + if err != nil { + return 0, err + } + + count := 0 + for _, file := range files { + if !file.IsDir() { + count++ + } + } + + return count, nil +}
The code is quite straightforward, especially for generating boilerplate code this will be useful for many use cases!
@@ -333,7 +338,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -cargo install lsp-ai +cargo install lsp-ai
Helix Configuration
@@ -471,11 +476,12 @@ content = "{CODE}"
Back to the main site
+ diff --git a/gemfeed/2025-08-15-random-weird-things-iii.html b/gemfeed/2025-08-15-random-weird-things-iii.html index 748c828c..8498b924 100644 --- a/gemfeed/2025-08-15-random-weird-things-iii.html +++ b/gemfeed/2025-08-15-random-weird-things-iii.html @@ -2,12 +2,17 @@ +Random Weird Things - Part Ⅲ - + + + + + @@ -129,11 +134,12 @@
Back to the main site
+ diff --git a/gemfeed/2025-09-14-bash-golf-part-4.html b/gemfeed/2025-09-14-bash-golf-part-4.html index 457b15d8..94c6b881 100644 --- a/gemfeed/2025-09-14-bash-golf-part-4.html +++ b/gemfeed/2025-09-14-bash-golf-part-4.html @@ -2,12 +2,17 @@ +Bash Golf Part 4 - + + + + + @@ -70,9 +75,9 @@ somecommand \ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -printf 'a\nb\n' \ - | tee >(sed 's/.*/X:&/; s/$/ :c1/') >(tr a-z A-Z | sed 's/$/ :c2/') \ - | sed 's/$/ :c3/' +printf 'a\nb\n' \ + | tee >(sed 's/.*/X:&/; s/$/ :c1/') >(tr a-z A-Z | sed 's/$/ :c2/') \ + | sed 's/$/ :c3/'
Output:
@@ -94,8 +99,8 @@ X:b :c1 :c3 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -/bin/sh -c 'echo hi | tee >(cat)' -# /bin/sh: 1: Syntax error: "(" unexpected +/bin/sh -c 'echo hi | tee >(cat)' +# /bin/sh: 1: Syntax error: "(" unexpected
Combine with set -o pipefail if failures in side branches should fail the whole pipeline.
@@ -106,9 +111,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -set -o pipefail -printf 'ok\n' | tee >(false) | cat >/dev/null -echo $? # 1 because a side branch failed +set -o pipefail +printf 'ok\n' | tee >(false) | cat >/dev/null +echo $? # 1 because a side branch failed
Further reading:
@@ -123,19 +128,19 @@ echo $? # 1 because a side branch failed by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -ssh "$SSH_USER@$SSH_HOST" <<EOF - # Go to the work directory - cd "$WORK_DIR" - - # Make a git pull - git pull - - # Export environment variables required for the service to run -