Christos Margiolis: Writings Christos Margiolis http://margiolis.net/w/ PostgreSQL 15 and pgAdmin4 on FreeBSD http://margiolis.net/w/postgres/ Thu, 07 Sep 2023 00:00:00 +1200 <p>Tested on FreeBSD 13.2. This article is also mirrored on the <a href="https://wiki.freebsd.org/PostgreSQL/Setup">FreeBSD Wiki</a>.</p> <h2 id="postgresql">PostgreSQL</h2> <p>Install PostgreSQL:</p> <pre tabindex="0"><code># pkg install postgresql15-server postgresql15-client </code></pre><p>Enable the PostgreSQL service:</p> <pre tabindex="0"><code># sysrc postgresql_enable=&#34;YES&#34; </code></pre><p>Initialize PostgreSQL and start the service:</p> <pre tabindex="0"><code># /usr/local/etc/rc.d/postgresql initdb # service postgresql start </code></pre><p>Make sure the service is running properly on IPv4 and IPv6 port 5432:</p> <pre tabindex="0"><code>$ sockstat -46 | grep 5432 </code></pre><p>Set a password for the default <code>postgres</code> user:</p> <pre tabindex="0"><code># passwd postgres </code></pre><p>Create an owner user for your database:</p> <pre tabindex="0"><code># su - postgres $ createuser admin $ createdb foo_db -O admin </code></pre><p>Inside the <code>psql</code> prompt, set an encrypted password for <code>admin</code> and grant exclusive access to <code>foo_db</code> to the <code>admin</code> user:</p> <pre tabindex="0"><code>$ psql foo_db foo_db=# alter role admin with encrypted password &#39;yourpassword&#39;; foo_db=# grant all privileges on database foo_db to admin; foo_db=# exit $ exit </code></pre><p>Make PostgreSQL listen to all addresses, instead of just <code>localhost</code>.</p> <pre tabindex="0"><code># vi /var/db/postgres/data15/postgresql.conf </code></pre><p>Uncomment the <code>listen_addresses</code> line and set it to:</p> <pre tabindex="0"><code>listen_addresses = &#39;*&#39; </code></pre><p>Change the security settings:</p> <pre tabindex="0"><code># vi /var/db/postgres/data15/pg_hba.conf </code></pre><p>Go to the bottom of the file and set <code>trust</code> to <code>md5</code> for all existing entries:</p> <pre tabindex="0"><code># TYPE DATABASE USER ADDRESS METHOD # &#34;local&#34; is for Unix domain socket connections only local all all md5 # IPv4 local connections: host all all 127.0.0.1/32 md5 # IPv6 local connections: host all all ::1/128 md5 # Allow replication connections from localhost, by a user with the # replication privilege. local replication all md5 host replication all 127.0.0.1/32 md5 host replication all ::1/128 md5 </code></pre><p>Also, allow remote IPv4 and IPv6 connections to our new database as <code>admin</code> only:</p> <pre tabindex="0"><code># Allow remote connections to foo_db as admin host foo_db admin 0.0.0.0/0 md5 host foo_db admin ::/0 md5 </code></pre><p>Restart the service:</p> <pre tabindex="0"><code># serivce postgresql restart </code></pre><h2 id="pgadmin4">pgAdmin4</h2> <p>Install the needed packages:</p> <pre tabindex="0"><code># pkg install python py39-pip py39-virtualenv py39-sqlite3 ca_root_nss openjpeg rust </code></pre><p>Upgrade pip to the latest version:</p> <pre tabindex="0"><code>$ pip install --upgrade pip </code></pre><p>Initialize the <code>virtualenv</code> for pgAdmin4 in your home directory:</p> <pre tabindex="0"><code>$ cd $ virtualenv pgadmin4 </code></pre><p>Enter the <code>virtualenv</code> and install pgAdmin4. This will take a while:</p> <pre tabindex="0"><code>$ . pgadmin4/bin/activate (pgadmin4) $ pip install pgadmin4 </code></pre><p>Create a local config file:</p> <pre tabindex="0"><code>(pgadmin4) # mkdir -p /var/lib/pgadmin /var/log/pgadmin (pgadmin4) $ cp pgadmin4/lib/python3.9/site-packages/pgadmin4/config.py pgadmin4/lib/python3.9/site-packages/pgadmin4/config_local.py (pgadmin4) $ vi pgadmin4/lib/python3.9/site-packages/pgadmin4/config_local.py </code></pre><p>Inside <code>config_local.py</code>, edit the following values:</p> <pre tabindex="0"><code>DEFAULT_SERVER = &#39;0.0.0.0&#39; DEFAULT_SERVER_PORT = 5050 </code></pre><p>Start pgAdmin4 (always inside the <code>virtualenv</code>) and set up your account:</p> <pre tabindex="0"><code>(pgadmin4) # pgadmin4 Email address: you@mail.com Password: yourpassword Retype password: yourpassword </code></pre><p>Open a web browser and enter pgAdmin4 (replace the IP with the appropriate one) and log in with the email and password you just entered in the <code>pgadmin4</code> prompt:</p> <pre tabindex="0"><code>http://192.168.1.11:5050 </code></pre><p>Inside the web interface, go to <code>File -&gt; Preferences -&gt; Binary Paths</code> and under the <code>PostgreSQL Binary Path</code> section, check <code>PostgreSQL 15</code> and set the binary path to <code>/usr/local/bin/</code>. Then click <code>Save</code>.</p> <p>From now on, you can run <code>pgadmin4</code> in the background like so:</p> <pre tabindex="0"><code>(pgadmin4) # pgadmin4 &amp; </code></pre><h3 id="upgrading-pgadmin4">Upgrading pgAdmin4</h3> <p>To upgrade pgAdmin4, you only have to delete and reinstall the <code>virtualenv</code>:</p> <pre tabindex="0"><code>$ cd $ rm -rf pgadmin4 $ virtualenv pgadmin4 $ . pgadmin4/bin/activate (pgadmin4) $ pip install --upgrade pgadmin4 </code></pre><p>You also have to re-create <code>config_local.py</code> and set <code>DEFAULT_SERVER</code> (see above).</p> Inline function tracing with the kinst DTrace provider http://margiolis.net/w/kinst_inline/ Tue, 18 Jul 2023 00:00:00 +1200 <h2 id="table-of-contents">Table of Contents</h2> <ol> <li><a href="#quick-background">Quick background</a></li> <li><a href="#usage">Usage</a></li> <li><a href="#inline-function-tracing">Inline function tracing</a> <ul> <li><a href="#how-it-works">How it works</a></li> <li><a href="#syntactic-transformations">Syntactic transformations</a></li> <li><a href="#heuristic-entry-return">Heuristic for calculating the <code>entry</code> and <code>return</code> offsets</a></li> </ul> </li> </ol> <h2 id="quick-background">Quick background</h2> <p><a href="https://illumos.org/books/dtrace/preface.html">DTrace</a> is a framework that gives administrators and kernel developers the ability to observe kernel behavior in real time. DTrace has modules called &ldquo;providers&rdquo;, that perform a particular instrumentation in the kernel (and sometimes userland) using &ldquo;probes&rdquo;.</p> <p><a href="http://margiolis.net/files/kinst.pdf">kinst</a> is a new low-level DTrace provider co-authored by Christos Margiolis and Mark Johnston for the FreeBSD operating system, which allows the user to trace arbitrary instructions in kernel functions. It is part of the base system as of FreeBSD 14.0.</p> <p>kinst probes take the form of <code>kinst::&lt;function&gt;:&lt;instruction&gt;</code>, where <code>&lt;function&gt;</code> is the kernel function to be traced, and <code>&lt;instruction&gt;</code> is the offset to the instruction, relative to the beginning of the function, and can be obtained from the function&rsquo;s disassembly. If the <code>&lt;instruction&gt;</code> field is left empty, kinst will trace all instructions in that function. Unlike <a href="https://illumos.org/books/dtrace/chp-fbt.html">FBT</a>, kinst can also trace the entry and return points of inline functions (see <a href="#inline-function-tracing">Inline function tracing</a>).</p> <p>The origin of the name is inspired from an <a href="https://www.usenix.org/legacy/publications/library/proceedings/osdi99/full_papers/tamches/tamches.pdf">early paper written by A. Tamches and B. Miller</a> discussing a tracing tool they developed called &ldquo;KernInst&rdquo;.</p> <h2 id="usage">Usage</h2> <p>Find the offset corresponding to the third instruction in <code>vm_fault()</code> and trace it, printing the contents of the RSI register:</p> <pre tabindex="0"><code># kgdb (kgdb) disas /r vm_fault Dump of assembler code for function vm_fault: 0xffffffff80f4e470 &lt;+0&gt;: 55 push %rbp 0xffffffff80f4e471 &lt;+1&gt;: 48 89 e5 mov %rsp,%rbp 0xffffffff80f4e474 &lt;+4&gt;: 41 57 push %r15 ... # dtrace -n &#39;kinst::vm_fault:4 {printf(&#34;%#x&#34;, regs[R_RSI]);}&#39; 2 81500 vm_fault:4 0x827c56000 2 81500 vm_fault:4 0x827878000 2 81500 vm_fault:4 0x1fab9bef0000 2 81500 vm_fault:4 0xe16cf749000 0 81500 vm_fault:4 0x13587c366000 ^C </code></pre><p>Trace the return point of <code>critical_enter()</code>, which is an inline function:</p> <pre tabindex="0"><code># dtrace -n &#39;kinst::critical_enter:return&#39; dtrace: description &#39;kinst::critical_enter:return&#39; matched 130 probes CPU ID FUNCTION:NAME 1 71024 spinlock_enter:53 0 71024 spinlock_enter:53 1 70992 uma_zalloc_arg:49 1 70925 malloc_type_zone_allocated:21 1 70994 uma_zfree_arg:365 1 70924 malloc_type_freed:21 1 71024 spinlock_enter:53 0 71024 spinlock_enter:53 0 70947 _epoch_enter_preempt:122 0 70949 _epoch_exit_preempt:28 ^C </code></pre><h2 id="inline-function-tracing">Inline function tracing</h2> <h3 id="how-it-works">How it works</h3> <p>To trace inline functions, libdtrace makes use of the <a href="http://margiolis.net/w/dwarf_inline">DWARF Debugging Standard</a>, to detect if the function specified is an inline call. If it is, D syntax is transformed to create kinst probes for each of the inline copies found. All work is done in libdtrace, instead of kinst(4). This feature has been added to FreeBSD with <a href="https://reviews.freebsd.org/D38825">this patch</a>.</p> <p>Contrary to how kinst expects a <code>&lt;function&gt;:&lt;instruction&gt;</code> tuple to create probes, for inline functions, <code>&lt;instruction&gt;</code> is replaced by <code>entry</code> and <code>return</code>.</p> <h3 id="syntactic-transformations">Syntactic transformations</h3> <p>Suppose the user wants to trace a probe of the form:</p> <pre tabindex="0"><code>kinst::&lt;func&gt;:&lt;entry|return&gt; /&lt;pred&gt;/ { &lt;acts&gt; } </code></pre><p>libdtrace sees that we have specified <code>entry</code> or <code>return</code>, instead of an offset, which is what a regular kinst probe would look like, so it loops through all loaded kernel modules and parses their DWARF and ELF info to see if this function is an inline &mdash; if <em>not</em>, the probe is converted to an FBT one, so that we don&rsquo;t duplicate FBT&rsquo;s functionality in kinst:</p> <pre tabindex="0"><code># dtrace -dn &#39;kinst::malloc:entry {exit(0);}&#39; fbt::malloc:entry { exit(0x0); } dtrace: description &#39;kinst::malloc:entry &#39; matched 1 probe CPU ID FUNCTION:NAME 2 31144 malloc:entry </code></pre><p>If the function however <em>is</em> an inline, libdtrace will find all calls refering to this function and create new probes for each one of the inline copies found.</p> <pre tabindex="0"><code># dtrace -dn &#39;kinst::cam_iosched_has_more_trim:entry { printf(&#34;\t%d\t%s&#34;, pid, execname); }&#39; kinst::cam_iosched_get_trim:13, kinst::cam_iosched_next_bio:13, kinst::cam_iosched_schedule:40 { printf(&#34;\t%d\t%s&#34;, pid, execname); } dtrace: description &#39;kinst::cam_iosched_has_more_trim:entry &#39; matched 4 probes CPU ID FUNCTION:NAME 0 81502 cam_iosched_schedule:40 2 clock 0 81501 cam_iosched_next_bio:13 2 clock 2 81502 cam_iosched_schedule:40 2 clock 1 81502 cam_iosched_next_bio:13 0 kernel 1 81503 cam_iosched_schedule:40 0 kernel ^C </code></pre><p>There can also be both inline and non-inline definitions of the same function. In this case, kinst creates an additional FBT probe for the non-inline definition.</p> <p>The <code>-d</code> flag used in these examples to dump the D script after libdtrace has applied syntactic transformations, has been added to DTrace in <a href="https://cgit.freebsd.org/src/commit/?id=1e136a9cbd3a9d137037e47a53c1dba3be7f6925">commit 1e136a9cbd3a</a>.</p> <h3 id="heuristic-entry-return">Heuristic for calculating the <code>entry</code> and <code>return</code> offsets</h3> <p>libdtrace reuses parts of the mechanism implemented in my <a href="https://git.sr.ht/~crm/inlinecall">inlinecall(1)</a> program, which finds and prints all call sites of a given inline function:</p> <pre tabindex="0"><code>$ ./inlinecall vm_page_mvqueue /usr/lib/debug/boot/kernel/kernel.debug /usr/src/sys/vm/vm_page.c:4142 [0xffffffff80f91541 - 0xffffffff80f91599] /usr/src/sys/vm/vm_page.c:4195 vm_page_readahead_finish() [0xffffffff80f915f5 - 0xffffffff80f91603] /usr/src/sys/vm/vm_page.c:4195 vm_page_readahead_finish() [0xffffffff80f9163d - 0xffffffff80f916c2] /usr/src/sys/vm/vm_page.c:4184 vm_page_activate() [0xffffffff80f916cd - 0xffffffff80f916e5] /usr/src/sys/vm/vm_page.c:4184 vm_page_activate() [0xffffffff80f916fe - 0xffffffff80f91747] /usr/src/sys/vm/vm_page.c:4195 vm_page_deactivate() [0xffffffff80f91750 - 0xffffffff80f91768] /usr/src/sys/vm/vm_page.c:4195 vm_page_deactivate() [0xffffffff80f94a59 - 0xffffffff80f94aa9] /usr/src/sys/vm/vm_page.c:4195 vm_page_reclaim_contig_domain() [0xffffffff80f94de4 - 0xffffffff80f94df9] /usr/src/sys/vm/vm_page.c:4195 vm_page_reclaim_contig_domain() [0xffffffff80f9661e - 0xffffffff80f96667] /usr/src/sys/vm/vm_page.c:4202 vm_page_deactivate_noreuse() [0xffffffff80f96670 - 0xffffffff80f96688] /usr/src/sys/vm/vm_page.c:4202 vm_page_deactivate_noreuse() [0xffffffff80f9669e - 0xffffffff80f966ea] /usr/src/sys/vm/vm_page.c:4212 vm_page_launder() [0xffffffff80f966f3 - 0xffffffff80f9670b] /usr/src/sys/vm/vm_page.c:4212 vm_page_launder() [0xffffffff80f96d4c - 0xffffffff80f96dac] /usr/src/sys/vm/vm_page.c:4212 vm_page_advise() [0xffffffff80f96dac - 0xffffffff80f96e07] /usr/src/sys/vm/vm_page.c:4202 vm_page_advise() </code></pre><p>Most of the entries above appear twice but with different boundaries:</p> <pre tabindex="0"><code> [0xffffffff80f9163d - 0xffffffff80f916c2] /usr/src/sys/vm/vm_page.c:4184 vm_page_activate() [0xffffffff80f916cd - 0xffffffff80f916e5] /usr/src/sys/vm/vm_page.c:4184 vm_page_activate() </code></pre><p>This means that the inline copy&rsquo;s boundaries are split into more than one parts, which can be caused by having early <code>return</code>s inside the inline function. By using this assumption, we can deduce that the entry point of the function is <code>0xffffffff80f9163d</code>, and each of the upper boundaries are the return points (i.e <code>0xffffffff80f916c2</code> and <code>0xffffffff80f916e5</code>), so we end up with one <code>entry</code> and two <code>return</code> trace points.</p> <p>However, this is <strong>not exactly true</strong>, because the final return address given by DWARF corresponds to the instruction <em>after</em> the actual return instruction. We can verify this in GDB using the two return addresses from the example above:</p> <pre tabindex="0"><code># kgdb (kgdb) disas vm_page_activate ... 0xffffffff80f916c0 &lt;+144&gt;: jmp 0xffffffff80f91673 &lt;vm_page_activate+67&gt; 0xffffffff80f916c2 &lt;+146&gt;: add $0x8,%rsp &lt;-- first address given by DWARF ... 0xffffffff80f916e0 &lt;+176&gt;: call 0xffffffff80bed360 &lt;panic&gt; &lt;-- last instruction &lt;-- second address should be here </code></pre><p>It turns out that <code>0xffffffff80f916e5</code> is, in fact, outside <code>vm_page_activate()</code> altogether!</p> <pre tabindex="0"><code>(kgdb) x/i 0xffffffff80f916e5 0xffffffff80f916e5: data16 cs nopw 0x0(%rax,%rax,1) </code></pre><p>After running a couple of tests, I came to the conclusion that there are two possible cases with return addresses:</p> <ul> <li>If <a href="http://margiolis.net/w/dwarf_inline/#lowpc-highpc">the inline copy&rsquo;s DIE has <code>DW_AT_lowpc</code> and <code>DW_AT_highpc</code> set</a>, the return address is <em>always</em> outside the inline function&rsquo;s boundaries.</li> <li>If <a href="http://margiolis.net/w/dwarf_inline/#ranges">the inline copy&rsquo;s DIE has <code>DW_AT_ranges</code> set</a>, only the last return address is outside the inline function&rsquo;s boundaries.</li> <li>Combining the two bullets above, if the return address of the inline function is the same as the upper boundary of the caller function, it&rsquo;s outside <em>both</em> the inline copy&rsquo;s and the caller function&rsquo;s boundaries.</li> </ul> <p>In order to fix this, we have to go one instruction back whenever we come across one of those 3 cases.</p> <p>Finally the <code>entry</code> offset is calculated as:</p> <pre tabindex="0"><code>inline_bound_lo - caller_bound_lo </code></pre><p>And the <code>return</code> one, including the modifications (if any) discussed above, as:</p> <pre tabindex="0"><code>inline_bound_hi - caller_bound_lo </code></pre><p>These offsets are then used to create regular kinst probes of the form <code>kinst::&lt;func&gt;:&lt;instruction&gt;</code>, which is what kinst actually expects:</p> <pre tabindex="0"><code># dtrace -dn &#39;kinst::vm_page_mvqueue:entry,kinst::vm_page_mvqueue:return&#39; dtrace: description &#39;kinst::vm_page_mvqueue:entry,kinst::vm_page_mvqueue:return&#39; matched 22 probes kinst::vm_page_readahead_finish:33, kinst::vm_page_readahead_finish:121, kinst::vm_page_activate:13, kinst::vm_page_deactivate:14, kinst::vm_page_reclaim_contig_domain:1961, kinst::vm_page_deactivate_noreuse:14, kinst::vm_page_launder:14, kinst::vm_page_advise:236, kinst::vm_page_advise:332, kinst::vm_page_readahead_finish:220, kinst::vm_page_activate:146, kinst::vm_page_activate:176, kinst::vm_page_deactivate:87, kinst::vm_page_deactivate:115, kinst::vm_page_reclaim_contig_domain:2041, kinst::vm_page_reclaim_contig_domain:2884, kinst::vm_page_deactivate_noreuse:87, kinst::vm_page_deactivate_noreuse:115, kinst::vm_page_launder:90, kinst::vm_page_launder:118, kinst::vm_page_advise:330, kinst::vm_page_advise:421 { } CPU ID FUNCTION:NAME 3 95381 vm_page_activate:13 3 95389 vm_page_activate:146 2 95381 vm_page_activate:13 2 95389 vm_page_activate:146 1 95387 vm_page_advise:332 1 95400 vm_page_advise:421 1 95387 vm_page_advise:332 1 95400 vm_page_advise:421 1 95387 vm_page_advise:332 ^C </code></pre> Using DWARF to find call sites of inline functions http://margiolis.net/w/dwarf_inline/ Tue, 07 Feb 2023 00:00:00 +1200 <h2 id="table-of-contents">Table of contents</h2> <ol> <li><a href="#what-is-dwarf">What is DWARF?</a></li> <li><a href="#inline-functions-in-dwarf">Inline functions in DWARF</a></li> <li><a href="#calculating-call-boundaries">Calculating call boundaries</a> <ul> <li><a href="#lowpc-highpc">The DIE has <code>DW_AT_low_pc</code> and <code>DW_AT_high_pc</code></a></li> <li><a href="#ranges">The DIE has <code>DW_AT_ranges</code></a></li> </ul> </li> <li><a href="#finding-the-caller-function">Finding the caller function</a></li> <li><a href="#inlinecall">inlinecall(1)</a> <ul> <li><a href="#nested-inline-functions">Nested inline functions</a></li> </ul> </li> </ol> <h2 id="what-is-dwarf">What is DWARF?</h2> <p>From the <a href="https://dwarfstd.org/">DWARF Debugging Standard&rsquo;s documentation</a>:</p> <blockquote> <p>This document defines a format for describing programs to facilitate user source level debugging. This description can be generated by compilers, assemblers and linkage editors. It can be used by debuggers and other tools.</p> </blockquote> <p>Debugging information entries (DIEs) are represented as a tree, one per compilation unit (CU). Each DIE has a tag (<code>DW_TAG_*</code>, see DWARF PDF Figure 1) denoting its class, and attributes (<code>DW_AT_*</code>, see DWARF PDF Figure 2) denoting its various characteristics, associated with it.</p> <p>The next entry of a DIE is a child DIE. If a DIE doesn&rsquo;t have children, the next entry is a &ldquo;sibling&rdquo;.</p> <p>Consider the following structure:</p> <pre tabindex="0"><code>CU1 (DW_TAG_compile_unit) func1 (DW_TAG_subprogram) DW_AT_foo DW_AT_bar func2 (DW_TAG_subprogram) DW_AT_foo DW_AT_bar myvar (DW_TAG_variable) DW_AT_foo DW_AT_bar CU2 (DW_TAG_compile_unit) ... </code></pre><p><code>CU1</code> has <code>func1</code> and <code>myvar</code> as chidren and <code>CU2</code> as siblings. <code>func1</code> has <code>func2</code> as a child.</p> <p>The debug file can be generated by compiling with the <code>-g</code> option. To dump DWARF info you can use <code>readelf -wi &lt;file&gt;</code> and <code>dwarfdump &lt;file&gt;</code>.</p> <h2 id="inline-functions-in-dwarf">Inline functions in DWARF</h2> <p>DIEs of inline function declarations have the <code>DW_TAG_subprogram</code> tag and the <code>DW_AT_inline</code> attribute. DIEs of inline copies of this function will have the <code>DW_TAG_inlined_subroutine</code> tag.</p> <p>Attributes inline copies can have include:</p> <ul> <li><code>DW_AT_abstract_origin</code>: DIE offset to the inline declaration.</li> <li><code>DW_AT_call_file</code>: Integer denoting the file the function is called in.</li> <li><code>DW_AT_call_line</code>: File line.</li> <li><code>DW_AT_call_column</code>: File column.</li> <li><code>DW_AT_low_pc</code> and <code>DW_AT_high_pc</code>: Lower and upper call boundaries. <a href="#3.1">Explained in the next section</a>.</li> <li><code>DW_AT_ranges</code>: <a href="#3.2">Explained in the next section</a>.</li> </ul> <p>For example, if we dump the DWARF info for my FreeBSD kernel:</p> <pre tabindex="0"><code>$ readelf -wi /usr/lib/debug/boot/kernel/kernel.debug &gt; ~/foo </code></pre><p>We find that <code>vfs_freevnodes_dec</code> gets inlined:</p> <pre tabindex="0"><code> &lt;1&gt;&lt;1dfa144&gt;: Abbrev Number: 94 (DW_TAG_subprogram) &lt;1dfa145&gt; DW_AT_name : (indirect string) vfs_freevnodes_dec &lt;1dfa149&gt; DW_AT_decl_file : 1 &lt;1dfa14a&gt; DW_AT_decl_line : 1447 &lt;1dfa14c&gt; DW_AT_prototyped : 1 &lt;1dfa14c&gt; DW_AT_inline : 1 </code></pre><p>Inline copies will have <code>DW_AT_abstract_origin</code> point to the declaration&rsquo;s DIEs offset, in this case <code>0x1dfa144</code>. If we look for <code>0x1dfa144</code>, we do indeed find a few inline copies.</p> <pre tabindex="0"><code> &lt;3&gt;&lt;1dfe45e&gt;: Abbrev Number: 24 (DW_TAG_inlined_subroutine) &lt;1dfe45f&gt; DW_AT_abstract_origin: &lt;0x1dfa144&gt; &lt;1dfe463&gt; DW_AT_low_pc : 0xffffffff80cf701d &lt;1dfe46b&gt; DW_AT_high_pc : 0x38 &lt;1dfe46f&gt; DW_AT_call_file : 1 &lt;1dfe470&gt; DW_AT_call_line : 3458 &lt;1dfe472&gt; DW_AT_call_column : 5 &lt;3&gt;&lt;1dfd2e2&gt;: Abbrev Number: 58 (DW_TAG_inlined_subroutine) &lt;1dfd2e3&gt; DW_AT_abstract_origin: &lt;0x1dfa144&gt; &lt;1dfd2e7&gt; DW_AT_ranges : 0x1f1290 &lt;1dfd2eb&gt; DW_AT_call_file : 1 &lt;1dfd2ec&gt; DW_AT_call_line : 3405 &lt;1dfd2ee&gt; DW_AT_call_column : 3 ...there are more </code></pre><p>As I described in the <a href="#1">first section</a>, a debug file may consist of multiple CUs that define the same inline function. We want treat each CU independently, that is, each inline copy is handled relative to its CU.</p> <h2 id="calculating-call-boundaries">Calculating call boundaries</h2> <p>There are 2 cases we have to take care of when calculating the actual call boundaries of an inline copy.</p> <h3 id="lowpc-highpc">The DIE has <code>DW_AT_low_pc</code> and <code>DW_AT_high_pc</code></h3> <pre tabindex="0"><code> &lt;3&gt;&lt;1dfe45e&gt;: Abbrev Number: 24 (DW_TAG_inlined_subroutine) &lt;1dfe45f&gt; DW_AT_abstract_origin: &lt;0x1dfa144&gt; &lt;1dfe463&gt; DW_AT_low_pc : 0xffffffff80cf701d &lt;1dfe46b&gt; DW_AT_high_pc : 0x38 &lt;1dfe46f&gt; DW_AT_call_file : 1 &lt;1dfe470&gt; DW_AT_call_line : 3458 &lt;1dfe472&gt; DW_AT_call_column : 5 </code></pre><p>In this case, the lower boundary is <code>low_pc</code> and the upper boundary is <code>low_pc + high_pc</code>, which, for the DIE shown in this example, the boundaries are:</p> <pre tabindex="0"><code>low = 0xffffffff80cf701d high = 0xffffffff80cf701d + 0x38 = 0xffffffff80cf7055 </code></pre><h3 id="ranges">The DIE has <code>DW_AT_ranges</code></h3> <pre tabindex="0"><code> &lt;3&gt;&lt;1dfd2e2&gt;: Abbrev Number: 58 (DW_TAG_inlined_subroutine) &lt;1dfd2e3&gt; DW_AT_abstract_origin: &lt;0x1dfa144&gt; &lt;1dfd2e7&gt; DW_AT_ranges : 0x1f1290 &lt;1dfd2eb&gt; DW_AT_call_file : 1 &lt;1dfd2ec&gt; DW_AT_call_line : 3405 &lt;1dfd2ee&gt; DW_AT_call_column : 3 </code></pre><p>This is a bit more involved. <code>DW_AT_ranges</code> refers to the <code>.debug_ranges</code> section found in debug files. We can dump the ranges:</p> <pre tabindex="0"><code>$ dwarfdump -N /usr/lib/debug/boot/kernel/kernel.debug .debug_ranges Ranges group 0: ranges: 3 at .debug_ranges offset 0 (0x00000000) (48 bytes) [ 0] range entry 0x00000019 0x00000073 [ 1] range entry 0x0000007e 0x00000106 [ 2] range end 0x00000000 0x00000000 Ranges group 1: ranges: 3 at .debug_ranges offset 48 (0x00000030) (48 bytes) [ 0] range entry 0x00000022 0x0000006a [ 1] range entry 0x0000007e 0x00000106 [ 2] range end 0x00000000 0x00000000 ... </code></pre><p>If we search for <code>0x1f1290</code> (the inline copy&rsquo;s ranges), we find its range group:</p> <pre tabindex="0"><code> Ranges group 38809: ranges: 3 at .debug_ranges offset 2036368 (0x001f1290) (48 bytes) [ 0] range entry 0x000025c8 0x000025f9 [ 1] range entry 0x0000261a 0x00002621 [ 2] range end 0x00000000 0x00000000 </code></pre><p>To get the call boundaries, we add each <code>range entry</code>&rsquo;s boundaries to the <code>DW_AT_low_pc</code> of the root DIE of the CU. The root DIE is found programmatically, but I happen to know that in this case, the root DIE is:</p> <pre tabindex="0"><code> &lt;0&gt;&lt;1dee9fb&gt;: Abbrev Number: 1 (DW_TAG_compile_unit) &lt;1dee9fc&gt; DW_AT_producer : (indirect string) FreeBSD clang version 13.0.0 (git@github.com:llvm/llvm-project.git llvmorg-13.0.0-0-gd7b669b3a303) &lt;1deea00&gt; DW_AT_language : 12 (C99) &lt;1deea02&gt; DW_AT_name : (indirect string) /usr/src/sys/kern/vfs_subr.c &lt;1deea06&gt; DW_AT_stmt_list : 0x6cb448 &lt;1deea0a&gt; DW_AT_comp_dir : (indirect string) /usr/obj/usr/src/amd64.amd64/sys/GENERIC &lt;1deea0e&gt; DW_AT_low_pc : 0xffffffff80cf4020 &lt;1deea16&gt; DW_AT_high_pc : 0xde3d </code></pre><p>Finally, we end up with the following boundaries:</p> <pre tabindex="0"><code>low = 0xffffffff80cf4020 + 0x000025c8 = 0xffffffff80cf65e8 high = 0xffffffff80cf4020 + 0x000025f9 = 0xffffffff80cf6619 low = 0xffffffff80cf4020 + 0x0000261a = 0xffffffff80cf663a high = 0xffffffff80cf4020 + 0x00002621 = 0xffffffff80cf6641 </code></pre><h2 id="finding-the-caller-function">Finding the caller function</h2> <p>There are cases where we want to know which function an inline function is being called from. Because DWARF does not encode that information, we&rsquo;ll have to scan ELF symbol tables.</p> <pre tabindex="0"><code>$ readelf -s /usr/lib/debug/boot/kernel/kernel.debug </code></pre><p>Since we know the inline copy&rsquo;s boundaries, we only have to find which symbol&rsquo;s boundaries the inline copy is inside. In other words, the following condition has to be met:</p> <pre tabindex="0"><code>sym_lower_bound &lt;= inline_lower_bound &lt;= inline_upper_bound &lt;= sym_upper_bound </code></pre><p>Because searching through ELF symbol tables manually and doing calculations by hand would take too long, the best way to do this is programmatically through <a href="https://sourceforge.net/p/elftoolchain/wiki/libelf/">LibELF</a>.</p> <h2 id="inlinecall">inlinecall(1)</h2> <p><a href="https://git.sr.ht/~crm/inlinecall">I wrote a little program</a> that does everything I talked about in this post automatically. It works on FreeBSD as-is, and most likely needs some modification to get it to work on other platforms.</p> <p>The program takes an inline function name and a debug file as arguments:</p> <pre tabindex="0"><code>inlinecall &lt;function&gt; &lt;file&gt; </code></pre><p>And outputs the results in the following form:</p> <pre tabindex="0"><code>cu1_func_declaration_file:line [low_bound - high_bound] inline_copy1_file:line caller_func() [low_bound - high_bound] inline_copy2_file:line caller_func() ... cu2_func_declaration_file:line ... ... </code></pre><p>For example:</p> <pre tabindex="0"><code>$ inlinecall critical_enter /usr/lib/debug/boot/kernel/kernel.debug /usr/src/sys/sys/systm.h:175 [0xffffffff809eb51f - 0xffffffff809eb526] /usr/src/sys/kern/kern_intr.c:1387 intr_event_handle() /usr/src/sys/sys/systm.h:175 [0xffffffff80a051f4 - 0xffffffff80a05208] /usr/src/sys/kern/kern_malloc.c:431 malloc_type_freed() [0xffffffff80a0514c - 0xffffffff80a0515b] /usr/src/sys/kern/kern_malloc.c:388 malloc_type_zone_allocated() /usr/src/sys/sys/systm.h:175 [0xffffffff80a263c4 - 0xffffffff80a263d3] /usr/src/sys/kern/kern_resource.c:509 rtp_to_pri() /usr/src/sys/sys/systm.h:175 [0xffffffff80a28f59 - 0xffffffff80a28f5f] /usr/src/sys/kern/kern_rmlock.c:775 _rm_assert() [0xffffffff80a29087 - 0xffffffff80a2908d] /usr/src/sys/kern/kern_rmlock.c:801 _rm_assert() [0xffffffff80a29eb0 - 0xffffffff80a29eb7] /usr/src/sys/kern/kern_rmlock.c:645 _rm_rlock_debug() [0xffffffff80a28c4b - 0xffffffff80a28c5a] /usr/src/sys/kern/kern_rmlock.c:160 unlock_rm() ...more </code></pre><h3 id="nested-inline-functions">Nested inline functions</h3> <p>inlinecall(1) resolves nested inline functions recursively:</p> <pre tabindex="0"><code>$ ./inlinecall critical_enter /usr/lib/debug/boot/kernel/kernel.debug /usr/src/sys/sys/systm.h:175 [0xffffffff80a19d7a - 0xffffffff80a19d8b] /usr/src/sys/sys/buf_ring.h:80 drbr_enqueue() /usr/src/sys/sys/systm.h:175 [0xffffffff80a6387a - 0xffffffff80a6388b] /usr/src/sys/sys/buf_ring.h:80 drbr_enqueue() ... </code></pre><p>Looking at the definition of <code>critical_enter()</code>&rsquo;s caller function in <code>buf_ring.h</code>:</p> <pre tabindex="0"><code>static __inline int buf_ring_enqueue(struct buf_ring *br, void *buf) { ... critical_enter(); ... } </code></pre><p>Even though inlinecall(1) reported that <code>critical_enter()</code> is called from <code>drbr_enqueue()</code> in <code>buf_ring.h:80</code>, we see that it&rsquo;s called from <code>buf_ring_enqueue()</code> instead, but <code>buf_ring_enqueue()</code> is also an inline function:</p> <pre tabindex="0"><code>$ ./inlinecall buf_ring_enqueue /usr/lib/debug/boot/kernel/kernel.debug /usr/src/sys/sys/buf_ring.h:63 [0xffffffff80a19d7a - 0xffffffff80a19dcd] /usr/src/sys/net/ifq.h:337 drbr_enqueue() [0xffffffff80a19ddc - 0xffffffff80a19e18] /usr/src/sys/net/ifq.h:337 drbr_enqueue() [0xffffffff80a19e1f - 0xffffffff80a19e3b] /usr/src/sys/net/ifq.h:337 drbr_enqueue() /usr/src/sys/sys/buf_ring.h:63 [0xffffffff80a6387a - 0xffffffff80a638cd] /usr/src/sys/net/ifq.h:337 drbr_enqueue() [0xffffffff80a638dc - 0xffffffff80a63918] /usr/src/sys/net/ifq.h:337 drbr_enqueue() [0xffffffff80a6391f - 0xffffffff80a6393b] /usr/src/sys/net/ifq.h:337 drbr_enqueue() /usr/src/sys/sys/buf_ring.h:63 [0xffffffff80d1f81a - 0xffffffff80d1f879] /usr/src/sys/net/ifq.c:57 drbr_enqueue() [0xffffffff80d1f91d - 0xffffffff80d1f964] /usr/src/sys/net/ifq.c:57 drbr_enqueue() [0xffffffff80d1f9dd - 0xffffffff80d1f9f5] /usr/src/sys/net/ifq.c:57 drbr_enqueue() /usr/src/sys/sys/buf_ring.h:63 [0xffffffff80ff07ba - 0xffffffff80ff080d] /usr/src/sys/net/ifq.h:337 drbr_enqueue() [0xffffffff80ff081c - 0xffffffff80ff0858] /usr/src/sys/net/ifq.h:337 drbr_enqueue() [0xffffffff80ff085f - 0xffffffff80ff087b] /usr/src/sys/net/ifq.h:337 drbr_enqueue() </code></pre><p>Here <code>drbr_enqueue()</code> is defined twice &mdash; once in <code>ifq.h</code> and once in <code>ifq.c</code>. The definition in <code>ifq.h</code> is also an inline definition, and in <code>ifq.c</code> it&rsquo;s a non-inline one. We know that <code>buf_ring_enqueue()</code> is called from the non-inline version of <code>drbr_enqueue()</code>, otherwise inlinecall(1) would have reported the function which calls the inline version of <code>drbr_enqueue()</code>.</p> Rolf Dobelli: Avoid the News http://margiolis.net/w/news/ Sat, 24 Dec 2022 00:00:00 +1200 <p><em>The article was written by Rolf Dobelli</em>. I reformatted it to HTML and added a table of contents, because I couldn&rsquo;t find a non-<a href="https://www.gwern.net/docs/culture/2010-dobelli.pdf">PDF version</a> of it.</p> <h2 id="related-reading">Related reading</h2> <ul> <li><a href="http://margiolis.net/w/gellman">The Gell-Mann Amnesia Effect</a></li> <li><a href="http://margiolis.net/w/socialmedia">Social media is ruining our lives</a></li> </ul> <h2 id="table-of-contents">Table of contents</h2> <ol> <li><a href="#prologue">Prologue</a></li> <li><a href="#sugar">News is to the mind what sugar is to the body</a></li> <li><a href="#1">No 1 &mdash; News misleads us systematically</a></li> <li><a href="#2">No 2 &mdash; News is irrelevant</a></li> <li><a href="#3">No 3 &mdash; News limits understanding</a></li> <li><a href="#4">No 4 &mdash; News is toxic to your body</a></li> <li><a href="#5">No 5 &mdash; News massively increases cognitive errors</a></li> <li><a href="#6">No 6 &mdash; News inhibits thinking</a></li> <li><a href="#7">No 7 &mdash; News changes the structure of your brain</a></li> <li><a href="#8">No 8 &mdash; News is costly</a></li> <li><a href="#9">No 9 &mdash; News sunders the relationship between reputation and achievement</a></li> <li><a href="#10">No 10 &mdash; News is produced by journalists</a></li> <li><a href="#11">No 11 &mdash; Reported facts are sometimes wrong, forecasts always</a></li> <li><a href="#12">No 12 &mdash; News is manipulative</a></li> <li><a href="#13">No 13 &mdash; News makes us passive</a></li> <li><a href="#14">No 14 &mdash; News gives us the illusion of caring</a></li> <li><a href="#15">No 15 &mdash; News kills creativity</a></li> <li><a href="#whattodo">What to do instead</a></li> <li><a href="#goodnews">Good news</a></li> <li><a href="#disclaimer">Disclaimer</a></li> <li><a href="#notes">Notes</a></li> </ol> <h2 id="prologue">Prologue</h2> <p>This article is the antidote to news. It is long, and you probably won’t be able to skim it. Thanks to heavy news consumption, many people have lost the reading habit and struggle to absorb more than four pages straight. This article will show you how to get out of this trap &mdash; if you are not already too deeply in it.</p> <h2 id="sugar">News is to the mind what sugar is to the body</h2> <p>We are so well informed and yet we know so little. Why?</p> <p>We are in this sad condition because 200 years ago we invented a toxic form of knowledge called “news.” The time has come to recognize the detrimental effects that news has on individuals and societies, and to take the necessary steps to shield yourself from its dangers.</p> <p>At core, human beings are cavemen in suits and dresses. Our brains are optimized for our original hunter-gatherer environment where we lived in small bands of 25 to 100 individuals with limited sources of food and information. Our brains (and our bodies) now live in a world that is the opposite of what we are designed to handle. This leads to great risk and to inappropriate, outright dangerous behavior.</p> <p>In the past few decades, the fortunate among us have recognized the hazards of living with an overabundance of food (obesity, diabetes) and have started to shift our diets. But most of us do not yet understand that news is to the mind what sugar is to the body. News is easy to digest. The media feeds us small bites of trivial matter, tidbits that don’t really concern our lives and don’t require thinking. That’s why we experience almost no saturation. Unlike reading books and long, deep magazine articles (which requires thinking), we can swallow limitless quantities of news flashes, like bright-colored candies for the mind.</p> <p>Today, we have reached the same point in relation to information overload that we faced 20 years ago in regard to food intake. We are beginning to recognize how toxic news can be and we are learning to take the first steps toward an information diet.</p> <p>This is my attempt to clarify the toxic dangers of news – and to recommend some ways to deal with it. I have now gone without news for a year, so I can see, feel and report the effects of this freedom first hand: less disruption, more time, less anxiety, deeper thinking, more insights. It’s not easy, but it&rsquo;s worth it.</p> <p>My good friend Nassim Nicholas Taleb, author of <em>The Black Swan</em>, was one of the first people to recognize news consumption as a serious problem. I owe many of the following insights to him.</p> <h2 id="1">No 1 &mdash; News misleads us systematically</h2> <p>News reports do not represent the real world.</p> <p>Our brains are wired to pay attention to visible, large, scandalous, sensational, shocking, people-related, story-formatted, fast changing, loud, graphic onslaughts of stimuli. Our brains have limited attention to spend on more subtle pieces of intelligence that are small, abstract, ambivalent, complex, slow to develop and quiet, much less silent. News organizations systematically exploit this bias.</p> <p>News media outlets, by and large, focus on the highly visible. They display whatever information they can convey with gripping stories and lurid pictures, and they systematically ignore the subtle and insidious, even if that material is more important. News grabs our attention; that’s how its business model works. Even if the advertising model didn’t exist, we would still soak up news pieces because they are easy to digest and superficially quite tasty.</p> <p>The highly visible misleads us.</p> <p>Take the following event. A car drives over a bridge, and the bridge collapses. What does the news media focus on? On the car. On the person in the car. Where he came from. Where he planned to go. How he experienced the crash (if he survived). What kind of person he is (was). But &mdash; that is all completely irrelevant. What’s relevant? The structural stability of the bridge. That’s the underlying risk that has been lurking and could lurk in other bridges. That is the lesson to be learned from this event.</p> <p>The car doesn’t matter at all. Any car could have caused the bridge to collapse. It could have been a strong wind or a dog walking over the bridge. So, why does the media cover the car? Because it’s flashy, it’s dramatic, it’s a person (non-abstract), and it’s news that’s cheap to produce.</p> <p>As a result of news, we walk around with the completely <em>wrong risk map</em> in our heads.</p> <ul> <li>Terrorism is overrated. Chronic stress is underrated.</li> <li>The collapse of Lehman Brothers is overrated. Fiscal irresponsibility is underrated.</li> <li>Astronauts are overrated. Nurses are underrated.</li> <li>Britney Spears is overrated. IPCC reports are underrated.</li> <li>Airplane crashes are overrated. Resistance to antibiotics is underrated.</li> </ul> <p>We are not rational enough to be exposed to the news-mongering press. It is a very dangerous thing, because the probabilistic mapping we get from consuming news is entirely different from the actual risks that we face. Watching an airplane crash on television is going to change your attitude toward that risk regardless of its real probability, no matter your intellectual sophistication. If you think you can compensate for this bias with the strength of your own inner contemplation, you are wrong. Bankers and economists &mdash; who have powerful incentives to compensate for newsborne hazards &mdash; have shown that they cannot. The only solution: cut yourself off from news consumption entirely.</p> <h2 id="2">No 2 &mdash; News is irrelevant</h2> <p>Out of the approximately 10,000 news stories you have read in the last 12 months, name one that &mdash; because you consumed it &mdash; allowed you to make a better decision about a serious matter affecting your life, your career, your business &mdash; compared to what you would have known if you hadn’t swallowed that morsel of news.</p> <p>The point is: the consumption of news is irrelevant to the forces that really matter in your life. At its best, it is entertaining, but it is still irrelevant.</p> <p>Assume that, against all odds, you found one piece of news that substantially increased the quality of your life &mdash; compared to how your life would have unfolded if you hadn’t read or seen it. How much trivia did your brain have to digest to get to that one relevant nugget? Even that question is a hindsight analysis. Looking forward, we can’t possibly identify the value of a piece of news before we see it, so we are forced to digest everything on the news buffet line. Is that worthwhile? Probably not.</p> <p>In 1914, the news story about the assassination in Sarajevo dwarfed all other reports in terms of its global significance. But, the murder in Sarajevo was just one of several thousand stories in circulation that day. No news organization treated this historically pivotal homicide as anything more than just another politically inspired assassination.</p> <p>The first Internet browser debuted in 1995. The public birth of this hugely relevant piece of software barely made it into the press despite its vast future impact.</p> <p>People find it very difficult to recognize what’s <em>relevant</em>. It’s much easier to recognize what’s <em>new</em>. We are not equipped with sensory organs for relevance. Relevance doesn’t come naturally. News does. That’s why the media plays on the new. (If our minds were structured the other way round, the media would certainly play on the relevant.) The relevant versus the new is the fundamental battle of the modern man.</p> <p>News floods you with a worldview that is not relevant to your life. What does relevance mean? It means: what is important to you personally. <em>Relevance is a personal choice</em>. Don’t take the media’s view for it. To the media, any tale that sells lots of copies is relevant – Darfur, Paris Hilton, a train crash in China, some idiotic world record (like someone who ate 78 cheeseburgers in an hour). This swindle is at the core of the news industry’s business model. It sells the relevant, but delivers the new.</p> <p>Media organizations want you to believe that news offers individuals some sort of a competitive advantage. Many people fall for that. We get anxious when we’re cut off from the flow of news. We fear we’re missing something important. In reality, news consumption is a <em>competitive disadvantage</em>. The less news you consume the bigger the advantage you have.</p> <p>Afraid you will miss “something important”? From my experience, if something really important happens, you will hear about it, even if you live in a cocoon that protects you from the news. Friends and colleagues will tell you about relevant events far more reliably than any news organization. They will fill you in with the added benefit of meta-information, since they know your priorities and you know how they think. You will learn far more about really important events and societal shifts by reading about them in specialized journals, in-depth magazines or good books and by talking to the people who know.</p> <h2 id="3">No 3 &mdash; News limits understanding</h2> <p>News has no explanatory power. News items are little bubbles popping on the surface of a deeper world.</p> <p>News organizations pride themselves on correctly reporting the facts, but the facts that they prize are just epiphenomena of deeper causes. Both news organizations and news consumers mistake knowing a litany of facts for understanding the world.</p> <p>It’s not “news facts” that are important, but the threads that connect them. What we really want is to <em>understand the underlying processes</em>, how things happen. Unfortunately, precariously few news organizations manage to explain causation because the underlying processes that govern significant social, political and environmental movements mostly are invisible. They are complex, non-linear and hard for our (and the journalists’) brains to digest. Why do news organizations go for the light stuff, the anecdotes, scandals, people-stories and pictures? The answer is simple: because they are cheap to produce.</p> <p>The important stories are non-stories: slow, powerful movements that develop below the journalists’ radar but have a transforming effect.</p> <p>Most people believe that having more information helps them make better decisions. News organizations support this belief. Hell, it’s in their interest. Will accumulating facts help you understand the world? Sadly, no. The relationship is actually inverted. The more “news factoids” you digest, the less of the big picture you will understand.</p> <p>No evidence exists to indicate that information junkies are better decision makers. They are certainly not more successful than the average Joe. If more information leads to higher economic success, we would expect journalists to be at the top of the pyramid. That’s not the case. Quite the contrary. We don’t know what makes people successful, but amassing news tidbits is certainly not it.</p> <p><em>Reading news to understand the world is worse than not reading anything.</em> What’s best: cut yourself off from daily news consumption entirely. Read books and thoughtful journals instead of gulping down flashing headlines.</p> <h2 id="4">No 4 &mdash; News is toxic to your body</h2> <p>News constantly triggers the limbic system. Panicky stories spur the release of cascades of glucocordicoid (cortisol). This deregulates your immune system and inhibits the release of growth hormones. In other words, your body finds itself in a state of chronic stress. High glucocordicoid levels cause impaired digestion, lack of growth (cell, hair, bone), nervousness and susceptibility to infections. <em>News consumers risk impairing their physical health</em>. The other potential side effects of news include fear, aggression, tunnel-vision and desensitization.</p> <h2 id="5">No 5 &mdash; News massively increases cognitive errors</h2> <p>News feeds the mother of all cognitive errors: <em>confirmation bias</em>. We automatically, systematically filter out evidence that contradicts our preconceptions in favor of evidence that confirms our beliefs. In the words of Warren Buffett: “What the human being is best at doing is interpreting all new information so that their prior conclusions remain intact.” That is the confirmation bias. News consumption, especially customized news intake, exacerbates this human flaw. The result is that we walk around in a cloud of seemingly confirming data &mdash; even when our theories about the world and ourselves may be wrong. We become prone to overconfidence, take stupid risks and misjudge opportunities.</p> <p>News not only feeds the confirmation bias, it exacerbates another cognitive error: the story bias. Our brains crave stories that “make sense” &mdash; even if they don’t correspond to reality. And news organizations are happy to deliver those fake stories. Instead of just reporting that the stock market declined (or increased) by 2%, TV news anchors proclaim, “The market declined by 2% <em>because of X.</em>” This X could be a bank profit forecast, fear about the Euro, non-farm payroll statistics, a Fed decision, a terrorist attack in Madrid, a subway strike in New York, a handshake between two presidents, anything, really.</p> <p>This reminds me of high school. My history textbook specified seven reasons (not six, not eight) why the French Revolution erupted. The fact is, we don’t know why the French Revolution broke out. And especially not why it exploded specifically in 1789. And we don’t know why the stock market moves as it moves. Too many factors go into such shifts. We don’t know why a war breaks out, a technological breakthrough is achieved or why the oil price jumps. Any journalist who writes, “The market moved because of X” or “the company went bankrupt because of Y” is an idiot. Of course, X might have had a casual influence, but it’s far from established, and other influences may be much more meaningful. To a large degree, news reports consist of nothing but stories and anecdotes that end up substituting for coherent analyses. I am fed up with this cheap way of “explaining” the world. It’s inappropriate. It’s irrational. It’s forgery. And I refuse to let it contaminate my thinking.</p> <h2 id="6">No 6 &mdash; News inhibits thinking</h2> <p>Thinking requires concentration. Concentration requires uninterrupted time. News items are like free-floating radicals that interfere with clear thinking. News pieces are specifically engineered to interrupt you. They are like viruses that steal attention for their own purposes. This is not about stealing time (see reason 8). This is about the inability to think clearly because you have opened yourself up to the disruptive factoid stream.</p> <p><em>News makes us shallow thinkers.</em> But it’s worse than that. News severely <em>affects memory.</em></p> <p>There are two types of memory. Long-range memory&rsquo;s capacity is nearly infinite, but working memory is limited to a minimum amount of slippery data (try repeating a 10-digit phone number after you hear it for the first time). The path from short-term to long-term memory is a choke-point in the brain, but anything you want to understand must past through it. If this passageway is disrupted, nothing passes through. Because news disrupts concentration, it actively weakens comprehension.</p> <p>You don’t visit Paris for just one hour or speed through the Museum of Modern Art in two minutes. Why not? Because the brain needs spool- up time. Building up concentration takes a minimum of a 10-minute read. Given less time, your brain will process the information superficially and barely store it. News pieces are like wind hitting your cheek. Ask yourself: What are the top ten news items from a month ago (that are no longer in the news today)? If you have a hard time remembering, you are not alone. Why would you want to consume something that doesn’t add to your body of knowledge?</p> <p>The online news has an even worse impact. In a 2001 study[1] two scholars in Canada showed that comprehension declines as the number of hyperlinks in a document increase. Why? Because whenever a link appears, your brain has to at least make the choice not to click, which in itself is distracting.</p> <p>News consumers are suckers for irrelevancy, and online news consumers are the biggest suckers. News is an interruption system. It seizes your attention only to scramble it. Besides a lack of glucose in your blood stream, news distraction is the biggest barricade to clear thinking.</p> <h2 id="7">No 7 &mdash; News changes the structure of your brain</h2> <p>News works like a drug. As stories develop, we naturally want to know how they continue. With hundreds of arbitrary story lines in our heads, this craving is increasingly compelling and hard to ignore.</p> <p>Why is news addictive? Once you get into the habit of checking the news, you are driven to check it even more often. Your attention is set on fast-breaking events, so you hunger for more data about them. This has to do with a process called “long-term potentiation” (LTP) and the reward circuits in your brain. Addicts seek more of an addictive substance to get their fix, because they need more stimulation than non-addicts to reach a satisfying reward threshold. If you set your attention on other things – like literature, science, art, history, cooking, pet grooming, whatever – you will become more focused on those things. That’s just how the brain works.</p> <p>Science used to think that our brain, the dense connections formed among the 100 billion neurons inside our skulls, was largely fixed by the time we reached adulthood. Today we know that this is not the case. The human brain is highly plastic. Nerve cells routinely break old connections and form new ones. When we adapt to a new cultural phenomenon, including the consumption of news, we end up with a different brain. Adaptation to news occurs at a biological level. News reprograms us. That means our brain works differently even when we’re not consuming news. And that’s dangerous.</p> <p>The more news we consume, the more we exercise the neural circuits devoted to skimming and multitasking while ignoring those used for reading deeply and thinking with profound focus. Most news consumers &mdash; even if they used to be avid book readers &mdash; have lost the ability to read and absorb lengthy articles or books. After four, five pages they get tired, their concentration vanishes, they become restless. It’s not because they got older or their schedules became more onerous. It’s because the physical structure of their brains has changed. In the words of Professor Michael Merzenich (University of California, San Francisco), a pioneer in the field of neuroplasticity: “We are training our brains to pay attention to the crap.”</p> <p>Deep reading is indistinguishable from deep thinking. When you consume news, your brain structurally changes. This means that the way you think changes. Regaining the capacity for concentration and contemplation will take nothing less than a radical news-free diet.</p> <h2 id="8">No 8 &mdash; News is costly</h2> <p>News wastes time. It exacts exorbitant costs.</p> <p>News taxes productivity three ways. First, count the consumption-time that news demands. That’s the time you actually waste reading, listening to or watching the news.</p> <p>Second, tally up the refocusing time &mdash; or switching cost. That’s the time you waste trying to get back to what you were doing before the news interrupted you. You have to collect your thoughts. What were you about to do? Every time you disrupt your work to check the news, reorienting yourself wastes more time.</p> <p>Third, news distracts us even hours after we’ve digested today’s hot items. News stories and images may pop into your mind hours, sometimes days later, constantly interrupting your train of thought. Why would you want to do that to yourself?</p> <p>If you read the newspaper for 15 minutes each morning, then check the news for 15 minutes during lunch and 15 minutes before you go to bed, you’re eating substantial time. Then, add five minutes here and there when you’re at work, plus distraction and refocusing time. You will lose productive hours totaling at least <em>half a day every week</em>. Half a day &mdash; and for what?</p> <p>On a global level, the loss in potential productivity is huge. Take the 2008 terror attacks in Mumbai, where terrorists murdered some 200 people in an act of chilling exhibitionism. Imagine that a billion people devoted, on average, one hour of their attention to the Mumbai tragedy: following the news, watching some talking head on TV, thinking about it. The number is a wild guess, but the guess is far from a wild number. India, alone, has more than a billion people. Many of them spent whole days following the drama. One billion people times one hour is one billion hours, which is more than 100,000 years. The global average life expectancy is today 66 years. So nearly 2,000 lives were swallowed by news consumption. It’s far more than the number of people murdered. In a sense, the newscasters became unwilling bedfellows of the terrorists. At least the Mumbai attacks had actual impact. Look at the hours lost when Michael Jackson died &mdash; no real content in the stories, and millions of hours wasted.</p> <p>Information is no longer a scarce commodity. But attention is. Why give it away so easily? You are not that irresponsible with your money, your reputation or your health. Why give away your mind?</p> <h2 id="9">No 9 &mdash; News sunders the relationship between reputation and achievement</h2> <p>Reputation affects how people cooperate in society. In our ancestral past, a person’s reputation was directly linked to his or her achievements. You saw that your fellow tribe member killed a tiger single handedly and you spread word of his bravery. With the advent of mass-produced news, the strange concept of “fame” entered our society.</p> <p>Fame is misleading because generally people become famous for reasons that have little relevance to our lives. The media grants fame to movie stars and news anchors for scant reason. News sunders the relationship between reputation and achievement. The tragedy is that pop notoriety crowds out the achievements of those who make more substantive contributions.</p> <h2 id="10">No 10 &mdash; News is produced by journalists</h2> <p>Good professional journalists take time with their stories, authenticate their facts and try to think things through. But like any profession, journalism has some incompetent, unfair practitioners who don’t have the time &mdash; or the capacity &mdash; for deep analysis. You might not be able to tell the difference between a polished professional report and a rushed, glib, paid-by-the-piece article by a writer with an ax to grind. It all looks like news.</p> <p>My estimate: fewer than 10% of the news stories are original. Less than 1% are truly investigative. And only once every 50 years do journalists uncover a Watergate. Many reporters cobble together the rest of the news from other people’s reports, common knowledge, shallow thinking and whatever the journalist can find on the internet. Some reporters copy from each other or refer to old pieces, without necessarily catching up with any interim corrections. The copying and the copying of the copies multiply the flaws in the stories and their irrelevance.</p> <h2 id="11">No 11 &mdash; Reported facts are sometimes wrong, forecasts always</h2> <p>Sometimes, reported facts are simply mistaken. With reduced editorial budgets at major publications, fact checking may be an endangered step in the news process.</p> <p>The <em>New Yorker magazine</em> is legendary for its fact checking. The story goes that when an article mentioned the Empire State Building, someone from the fact-checking department would go out and visually verify that, in fact, the building was still standing. I don’t know if the story is true, but it highlights a point. Today, the fact checker is an endangered species at most news companies (though still alive and well at <em>The New Yorker</em>).</p> <p>Many news stories include predictions, but accurately predicting anything in a complex world is impossible. Overwhelming evidence indicates that forecasts by journalists and by experts in finance, social development, global conflicts and technology are almost always completely wrong. So, why consume that junk?</p> <p>Did the newspapers predict World War I, the Great Depression, the sexual revolution, the fall of the Soviet empire, the rise of the Internet, resistance to antibiotics, the fall of Europe’s birth rate or the explosion in depression cases? Maybe, you’d find one or two correct predictions in a sea of millions of mistaken ones. <em>Incorrect forecast are not only useless, they are harmful.</em></p> <p>To increase the accuracy of your predictions, cut out the news and roll the dice or, if you are ready for depth, read books and knowledgeable journals to understand the invisible generators that affect our world.</p> <h2 id="12">No 12 &mdash; News is manipulative</h2> <p>Our evolutionary past has equipped us with a good bullshit detector for face-to-face interactions. We automatically use many clues to detect manipulation, clues that go beyond the verbal message and include gesture, facial expression, and signs of nervousness such as sweaty palms, blushing and body odor. Living in small bands of people, we almost always knew the background of the messenger. Information always came with a rich set of meta-data. Today, even conscientious readers find that distinguishing even-handed news stories from ones that have a private agenda is difficult and energy consuming. Why go through that?</p> <p>Stories are selected or slanted to please advertisers (advertising bias) or the owners of the media (corporate bias), and each media outlet has a tendency to report what everyone else is reporting, and to avoid stories that will offend anyone (mainstream bias).</p> <p>The public relations (PR) industry is as large as the news reporting industry &mdash; the best proof that journalists and news organizations <em>can</em> be manipulated, or at least influenced or swayed. Corporations, interest groups and other organizations would not expend such huge sums on PR if it didn’t work. If spinmeisters can manipulate journalists, who have a natural skepticism toward powerful organizations, what makes you think you can escape their trickery?</p> <p>Take the Nurse Nayirah story. Nayirah was a 15- year-old Kuwaiti girl who testified to the U.S. Congress during the run up to the 1991 Gulf War. She alleged that she had witnessed the murder of infant children by Iraqi soldiers in Kuwait. Virtually every media outlet covered the story. The U.S. public was outraged, which in turn pushed Congress closer to approving the war. Her testimony, which all media outlets regarded as credible at the time, has since come to be regarded as wartime propaganda.</p> <p>Journalism shapes a common picture of the world and a common set of narratives for discussing it. It sets the public agenda. Hold on: do we really want news reporters to set the public agenda? I believe that agenda setting by the media is just bad democracy.</p> <h2 id="13">No 13 &mdash; News makes us passive</h2> <p>News stories are overwhelmingly about things you cannot influence. This sets readers up to have a fatalistic outlook on the world.</p> <p>Compare this with our ancestral past, where you could act upon practically every bit of news. Our evolutionary past prepared us to act on information, but the daily repetition of news about things we can’t act upon makes us passive. It saps our energy. It grinds us down until we adopt a worldview that is pessimistic, desensitized, sarcastic and fatalistic.</p> <p>If the human brain encounters a barrage of ambiguous information without being able to act upon that information, it can react with passivity and a sense of victimhood. The scientific term is <em>learned helplessness</em>. It’s a bit of a stretch, but I would not be surprised if news consumption at least partially contributes to the widespread disease of depression. Viewed on a timeline, the spread of depression coincides almost perfectly with the growth and maturity of the mass media. Maybe it’s a coincidence, or maybe the constant onslaught of fire, famine, flood and failure adds to depression, even if these sad reports come from far away.</p> <h2 id="14">No 14 &mdash; News gives us the illusion of caring</h2> <p>Kathleen Norris (even if I don’t share most of her ideas) said it best: “We may want to believe that we are still concerned, as our eyes drift from a news anchor announcing the latest atrocity to the NBA scores and stock market quotes streaming across the bottom of the screen. But the ceaseless bombardment of image and verbiage makes us impervious to caring.”</p> <p>News wraps us in a warm global feeling. We are all world citizens. We are all connected. The planet is just one global village. We sing “We Are the World” and wave the little flame of our lighters in perfect harmony with thousands of others. This gives us a glowing, fuzzy feeling that delivers the illusion of caring but doesn’t get us anywhere. This allure of anything bespeaking global brotherhood smells like a gigantic chimera. The fact is, consuming news does not make us more connected to each other. We are connected because we interact and trade.</p> <h2 id="15">No 15 &mdash; News kills creativity</h2> <p>Things we already know limit our creativity. This is one reason that mathematicians, novelists, composers and entrepreneurs often produce their most creative works at a young age. They are oblivious to much that has been tried before. Their brains enjoy a wide, uninhabited space that emboldens them to come up with and pursue novel ideas.</p> <p>I don’t know a single truly creative mind who is a news junkie – not a writer, not a composer, mathematician, physician, scientist, musician, designer, architect or painter. On the other hand, I know a whole bunch of viciously uncreative minds who consume news like drugs.</p> <p>The creativity-killing effect of news might also be due to something simpler we’ve discussed before: distraction. I just can’t imagine producing novel ideas with the distraction that news always delivers. <em>If you want to come up with old solutions, read news.</em> If you are looking for new solutions, don&rsquo;t read news.</p> <h2 id="whattodo">What to do instead</h2> <p>Go without news. Cut it out completely. Go cold turkey.</p> <p>Make news as inaccessible as possible. Delete the news apps from your iPhone. Sell your TV. Cancel your newspaper subscriptions. Do not pick up newspapers and magazines that lie around in airports and train stations. Do not set your browser default to a news site. Pick a site that never changes. The more stale the better. Delete all news sites from your browser’s favorites list. Delete the news widgets from your desktop.</p> <p>If you want to keep the illusion of “not missing anything important”, I suggest you glance through the summary page of the Economist once a week. Don’t spend more than five minutes on it.</p> <p>Read magazines and books which explain the world &mdash; <em>Science, Nature, The New Yorker, The Atlantic Monthly</em>. Go for magazines that connect the dots and don’t shy away from presenting the complexities of life &mdash; or from purely entertaining you. The world is complicated, and we can do nothing about it. So, you must read longish and deep articles and books that represent its complexity. Try reading a book a week. Better two or three. History is good. Biology. Psychology. That way you’ll learn to understand the underlying mechanisms of the world. Go deep instead of broad. Enjoy material that truly interests you. Have fun reading.</p> <p>The first week will be the hardest. Deciding not to check the news while you are thinking, writing or reading takes discipline. You are fighting your brain’s built-in tendency. Initially, you will feel out of touch or even socially isolated. Every day you will be tempted to check your favorite news Web site. Don’t do it. Stick to the cold-turkey plan. Go 30 days without news. After 30 days, you will have a more relaxed attitude toward the news. You will find that you have more time, more concentration and a better understanding of the world.</p> <p>After a while, you will realize that despite your personal news blackout, you have not missed &mdash; and you’re not going to miss &mdash; any important facts. If some bit of information is truly important to your profession, your company, your family or your community, you will hear it in time – from your friends, your mother-in-law or whomever you talk to or see. When you are with your friends, ask them if anything important is happening in the world. The question is a great conversation starter. Most of the time, the answer will be: “not really.”</p> <p>Are you afraid that living a news-free existence will make you an outcast at parties? Well, you might not know that Lindsay Lohan went to jail, but you will have more intelligent facts to share &mdash; about the cultural meaning of the food you are eating or the discovery of exosolar planets. Never be shy about discussing your news diet. People will be fascinated.</p> <h2 id="goodnews">Good News</h2> <p>Society needs journalism &mdash; but in a different way.</p> <p>Investigative journalism is relevant in any society. We need more hard-core journalists digging into meaningful stories. We need reporting that polices our society and uncovers the truth. The best example is Watergate. But important findings don’t have to arrive in the form of news. Often, reporting is not time sensitive. Long journal articles and in-depth books are fine forums for investigative journalism &mdash; and now that you’ve gone cold turkey on the news, you’ll have time to read them.</p> <h2 id="disclaimer">Disclaimer</h2> <p>Disclaimer The above statements reflect the most truthful viewpoint I can achieve at the time of this writing. I reserve the right to revise my views at any time. I might even indulge in the freedom of contradicting myself. I have done so in the past and will most certainly do so in the future. The only reason I would change my views (a switch which would undoubtedly be noticed by the “consistency police” (usually journalists with good high-school degrees) is because the new version is closer to the truth, not ever because I would gain any personal advantage.</p> <h2 id="notes">Notes</h2> <p>[1] Nicholas Carr: The Web Shatters Focus, Rewires Brains, <em>Wired</em>, May 2010</p> In defense of the Old Web http://margiolis.net/w/oldweb/ Wed, 17 Aug 2022 00:00:00 +1200 <p>The internet has reached the point where almost everything has been concertrated in just a few platforms. This is both opposed to the original idea of a decentralized and free internet, and is also used as a <a href="http://margiolis.net/w/socialmedia">tool for control</a>, since the flow of information is &ldquo;managed&rdquo; only by a handful of companies.</p> <p>My rather romanticized proposal is that we should make an attempt return to the old ways of the internet (1990s-2000s) &mdash; when personal websites and small communities were thriving &mdash; not because of nostalgia, but because it espoused better values and promoted creativity, despite its problems (spam, malware, bad security practices, unencrypted traffic). This might very well be a pipe dream at this point, but I think it would be great to at least see the existing movement attracting more people than it already does.</p> <p>I was initially working on a full article explaining my viewpoint in depth, but the following articles express them better than I could ever have:</p> <ul> <li><a href="https://neustadt.fr/essays/against-a-user-hostile-web/">Against an Increasingly User-Hostile Web</a></li> <li><a href="https://neustadt.fr/essays/the-small-web/">Rediscovering the Small Web</a></li> <li><a href="https://benhoyt.com/writings/the-small-web-is-beautiful/">The small web is beautiful</a></li> <li><a href="https://webdirections.org/blog/the-website-obesity-crisis/">The Website Obesity Crisis</a></li> </ul> <p>So, how can you be part of the small web? To start off, you need:</p> <ul> <li><a href="https://landchad.net/basic/domain/">A domain name</a>.</li> <li>Somewhere to host your website/service. I rent a <a href="https://vultr.com">Vultr</a> VPS with the cheapest plan available, which is more than enough for my needs, but you can even do self-hosting at home if you want. If you do rent from Vultr, you can <a href="https://landchad.net/basic/server/">follow this guide</a>.</li> <li>To <a href="https://landchad.net/basic/dns/">connect the domain name with the server</a>, so that your website can be reached.</li> <li>A stable and secure operating system for servers, such as <a href="https://openbsd.org">OpenBSD</a>.</li> </ul> <p>A list of guides I&rsquo;ve written:</p> <ul> <li><a href="http://margiolis.net/w/openbsd_web">Set up an OpenBSD web server</a>.</li> <li><a href="http://margiolis.net/w/rss">Create an RSS feed for your website</a>.</li> <li><a href="http://margiolis.net/w/rsync">Edit files locally and upload them to the server</a>.</li> <li><a href="http://margiolis.net/w/openbsd_git">Set up an OpenBSD Git server</a> with a mimimal <a href="http://margiolis.net/w/stagit_frontend">web frontend</a>.</li> </ul> <p>To make finding other websites easier, have a <a href="http://margiolis.net/links">links page</a>, and consider being part of a <a href="https://en.wikipedia.org/wiki/Webring">webring</a>.</p> <p>The tools you choose to manage your website depend on personal preference and needs, but I firmly believe that because building a website is not rocket science, and because <a href="https://idlewords.com/talks/website_obesity.htm">the web has been getting severely bloated</a> you don&rsquo;t need anything more than a few command line utilities, a text editor, and perhaps a <a href="https://staticsitegenerators.net/">static site generator</a>. Modern web frameworks tend to cause more headaches than actually improve workflow, so I&rsquo;ll refrain from recommending them. I like writing articles in <a href="https://daringfireball.net/projects/markdown/basics">Markdown</a>, and using <a href="https://gohugo.io/">Hugo</a> for static site generation and templating.</p> Making a character device kernel module on FreeBSD http://margiolis.net/w/cdev/ Sun, 10 Jul 2022 00:00:00 +1200 <!-- raw HTML omitted --> <p>This article assumes advanced knowledge of C and a basic understanding of the FreeBSD kernel and programming environment. It is also meant to serve as a template/reference and not a complete implementation.</p> <p><a href="https://git.sr.ht/~crm/random/tree/master/item/mydev_freebsd">Sample code can be found here</a>.</p> <p>Also mirrored on the <a href="https://wiki.freebsd.org/CDevModule">FreeBSD Wiki</a>.</p> <h2 id="table-of-contents">Table of contents</h2> <ul> <li><a href="#implementing-the-device">Implementing the device</a> <ul> <li><a href="#malloc-declaration">malloc declaration</a></li> <li><a href="#cdevsw-structure"><code>cdevsw</code> structure</a></li> <li><a href="#open-and-close">open() and close()</a></li> <li><a href="#read-and-write">read() and write()</a></li> <li><a href="#ioctl">ioctl()</a></li> </ul> </li> <li><a href="#creating-and-destroying-the-device">Creating and destroying the device</a></li> <li><a href="#module-declaration">Module declaration</a></li> <li><a href="#makefile">Makefile</a></li> <li><a href="#running-the-module">Running the module</a></li> <li><a href="#testing">Testing</a></li> </ul> <h2 id="implementing-the-device">Implementing the device</h2> <h3 id="malloc-declaration">malloc declaration</h3> <p>Kernel modules have their own malloc types, which are defined as follows:</p> <pre tabindex="0"><code>MALLOC_DECLARE(M_MYDEV); MALLOC_DEFINE(M_MYDEV, &#34;mydev&#34;, &#34;device description&#34;); </code></pre><p>Then, you can use malloc(9) and free(9) as:</p> <pre tabindex="0"><code>p = malloc(sizeof(foo), M_MYDEV, M_WAITOK | M_ZERO); free(p, M_MYDEV); </code></pre><h3 id="cdevsw-structure"><code>cdevsw</code> structure</h3> <p>The device&rsquo;s properties and methods are stored in a <code>cdevsw</code> (Character Device Switch) structure, defined in <code>sys/conf.h</code>. The fields we care about most of the time are the following:</p> <pre tabindex="0"><code>struct cdevsw { int d_version; u_int d_flags; const char *d_name; d_open_t *d_open; d_fdopen_t *d_fdopen; d_close_t *d_close; d_read_t *d_read; d_write_t *d_write; d_ioctl_t *d_ioctl; d_poll_t *d_poll; d_mmap_t *d_mmap; d_strategy_t *d_strategy; dumper_t *d_dump; d_kqfilter_t *d_kqfilter; d_purge_t *d_purge; d_mmap_single_t *d_mmap_single; ... }; </code></pre><p>All the <code>*_t</code> pointers are pointers to functions meant to be implemented by the driver. Not all functions have to be implemented however, but we usually do need to implement open(), close(), read(), write() and ioctl().</p> <p>Declare the functions using some handy typedefs:</p> <pre tabindex="0"><code>static d_open_t mydev_open; static d_close_t mydev_close; static d_read_t mydev_read; static d_write_t mydev_write; static d_ioctl_t mydev_ioctl; </code></pre><p>Declare the <code>cdevsw</code> structure:</p> <pre tabindex="0"><code>static struct cdevsw mydev_cdevsw = { .d_name = &#34;mydev&#34;, .d_version = D_VERSION, .d_flags = D_TRACKCLOSE, .d_open = mydev_open, .d_close = mydev_close, .d_read = mydev_read, .d_write = mydev_write, .d_ioctl = mydev_ioctl, }; </code></pre><p>The <code>D_TRACKCLOSE</code> flag tells the kernel to track when the device closes so that it can close normally in case something goes wrong.</p> <h3 id="open-and-close">open() and close()</h3> <p>Those two functions are mainly used for resource allocation/deallocation and environment preparation:</p> <pre tabindex="0"><code>static int mydev_open(struct cdev *dev, int flags, int devtype, struct thread *td) { int error = 0; /* do stuff */ return (error); } static int mydev_close(struct cdev *dev, int flags, int devtype, struct thread *td) { int error = 0; /* do stuff */ return (error); } </code></pre><h3 id="read-and-write">read() and write()</h3> <p>It&rsquo;s good practice to keep an internal buffer. Below is a very simplified example. The buffer in this example is allocated and deallocated on <a href="#module-declaration">module load and unload respectively</a>:</p> <pre tabindex="0"><code>#define BUFSIZE (1 &lt;&lt; 16) struct foo { char buf[BUFSIZE + 1]; size_t len; }; static struct foo *foo; </code></pre><p>Data to be received or sent back is stored in <code>uio</code> and the copy from user to kernel memory is done through uiomove(9), defined in <code>sys/uio.h</code>:</p> <pre tabindex="0"><code>static int mydev_read(struct cdev *dev, struct uio *uio, int ioflag) { size_t amnt; int v, error = 0; /* * Determine how many bytes we have to read. We&#39;ll either read the * remaining bytes (uio-&gt;uio_resid) or the number of bytes requested by * the caller. */ v = uio-&gt;uio_offset &gt;= foo-&gt;len + 1 ? 0 : foo-&gt;len + 1 - uio-&gt;uio_offset; amnt = MIN(uio-&gt;uio_resid, v); /* Move the bytes from foo-&gt;buf to uio. */ if ((error = uiomove(foo-&gt;buf, amnt, uio)) != 0) { /* error handling */ } /* do stuff */ return (error); } static int mydev_write(struct cdev *dev, struct uio *uio, int ioflag) { size_t amnt; int error = 0; /* Do not allow random access. */ if (uio-&gt;uio_offset != 0 &amp;&amp; (uio-&gt;uio_offset != foo-&gt;len)) return (EINVAL); /* We&#39;re not appending, reset length. */ else if (uio-&gt;uio_offset == 0) foo-&gt;len = 0; amnt = MIN(uio-&gt;uio_resid, (BUFSIZE - foo-&gt;len)); if ((error = uiomove(foo-&gt;buf + uio-&gt;uio_offset, amnt, uio)) != 0) { /* error handling */ } foo-&gt;len = uio-&gt;uio_offset; foo-&gt;buf[foo-&gt;len] = &#39;\0&#39;; /* do stuff */ return (error); } </code></pre><h3 id="ioctl">ioctl()</h3> <p>To create an ioctl, you give it a name and <code>#define</code> it using one of the following <code>_IO*</code> macros defined in <code>sys/ioccom.h</code>:</p> <ul> <li><code>_IO</code>: No parameters.</li> <li><code>_IOR</code>: Copy out parameters. Read from device.</li> <li><code>_IOW</code>: Copy in paramters. Write to device.</li> <li><code>_IOWR</code>: Copy parameters in and out. Write to device and read the modified data back.</li> </ul> <p>Each of those macros* takes 3 arguments:</p> <ul> <li>An arbitrary one-byte &ldquo;class&rdquo; identifier.</li> <li>A unique ID.</li> <li>The parameter type (can be anything), which is used to calculate the parameter&rsquo;s size. The macro expands the type to <code>sizeof(type)</code>.</li> </ul> <p>* <code>_IO</code> takes only the first 2 arguments (class and ID) since it doesn&rsquo;t use parameters.</p> <p>We can now define a few ioctls that take <code>foo_t</code> as a parameter. This is usually done in a separate header file so that programs can use the ioctls:</p> <pre tabindex="0"><code>#include &lt;sys/ioccom.h&gt; typedef struct { int x; int y; } foo_t; #define MYDEVIOC_READ _IOR(&#39;a&#39;, 1, foo_t) #define MYDEVIOC_WRITE _IOW(&#39;a&#39;, 2, foo_t) #define MYDEVIOC_RDWR _IOWR(&#39;a&#39;, 3, foo_t) </code></pre><p><code>mydev_ioctl()</code> is responsible for handling the ioctls we declared:</p> <pre tabindex="0"><code>static int mydev_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { foo_t *fp; int error = 0; switch (cmd) { case MYDEVIOC_READ: fp = (foo_t *)addr; /* do stuff */ break; case MYDEVIOC_WRITE: fp = (foo_t *)addr; /* do stuff */ break; case MYDEVIOC_RDWR: fp = (foo_t *)addr; /* do stuff */ break; default: error = ENOTTY; break; } return (error); } </code></pre><h2 id="creating-and-destroying-the-device">Creating and destroying the device</h2> <p>Character devices are given a <code>struct cdev</code> handle upon creation, which we usually store as a global variable:</p> <pre tabindex="0"><code>static struct cdev *mydev_cdev; </code></pre><p>Devices are created with the <code>make_dev()</code> function, which is defined as:</p> <pre tabindex="0"><code>struct cdev * make_dev(struct cdevsw *cdevsw, int unit, uid_t uid, gid_t gid, int perms, const char *fmt, ...); </code></pre><p><code>sys/conf.h</code> has the definitions of all available flags.</p> <p>Create the device:</p> <pre tabindex="0"><code>mydev_cdev = make_dev(&amp;mydev_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, &#34;mydev&#34;); </code></pre><p>When done, destroy the device:</p> <pre tabindex="0"><code>destroy_dev(mydev_cdev); </code></pre><h2 id="module-declaration">Module declaration</h2> <p>Necessary includes:</p> <pre tabindex="0"><code>#include &lt;sys/types.h&gt; #include &lt;sys/param.h&gt; #include &lt;sys/conf.h&gt; #include &lt;sys/systm.h&gt; #include &lt;sys/kernel.h&gt; #include &lt;sys/module.h&gt; #include &lt;sys/malloc.h&gt; #include &lt;sys/uio.h&gt; </code></pre><p>Implement the module&rsquo;s event handler. This function is called at module load and unload. Since we&rsquo;re dealing with a character device, it makes sense to create the device upon load and destroy it upon unload:</p> <pre tabindex="0"><code>static int mydev_modevent(module_t mod, int type, void *arg) { int error = 0; switch (type) { case MOD_LOAD: mydev_cdev = make_dev(&amp;mydev_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, &#34;mydev&#34;); foo = malloc(sizeof(foo_t), M_MYDEV, M_WAITOK | M_ZERO); foo-&gt;buf[0] = &#39;\0&#39;; foo-&gt;len = 0; break; case MOD_UNLOAD: /* FALLTHROUGH */ case MOD_SHUTDOWN: free(foo, M_MYDEV); destroy_dev(mydev_cdev); break; default: error = EOPNOTSUPP; break; } return (error); } </code></pre><p>Lastly, declare the module. The first argument is the module&rsquo;s name, the second one is a pointer to the event handler and the last one is any data we want to supply the event handler with, i.e the <code>arg</code> argument in <code>mydev_modevent()</code>:</p> <pre tabindex="0"><code>DEV_MODULE(mydev, mydev_modevent, NULL); </code></pre><h2 id="makefile">Makefile</h2> <pre tabindex="0"><code>KMOD= mydev SRCS= mydev.c .include &lt;bsd.kmod.mk&gt; </code></pre><h2 id="running-the-module">Running the module</h2> <pre tabindex="0"><code>$ make # kldload ./mydev.ko ... # kldunload ./mydev.ko $ make clean cleandepend </code></pre><h2 id="testing">Testing</h2> <p>To test the module, load it, and create a simple program that opens the device, and makes a few calls to ioctl(2), read(2) and write(2).</p> Social media is ruining our lives http://margiolis.net/w/socialmedia/ Sat, 18 Jun 2022 00:00:00 +1200 <p>In its infancy, the idea of social media might have had good intentions behind it. In hindsight though, social media as we know it today, seems to have caused more harm than good, for multiple (perhaps too many) reasons. The very nature of the centralization of public discourse on the internet and the mechanisms employed for reacting to content (likes, dislikes, etc), is what lead to social media becoming what it is today: a tool for manipulation.</p> <p>This article is not a scientific paper, and as such, I don&rsquo;t want to bore with percentages and details that you can find on more reliable sources (I do link to some of them). These are merely my thoughts, anecdotal experiences and my attempt to spread awareness about a subject I feel is genuinely harming our society.</p> <h2 id="table-of-contents">Table of contents</h2> <ol> <li><a href="#addictiveness">Addictiveness and the drug-like nature of social media</a></li> <li><a href="#effects-on-mental-health">Effects on mental health</a></li> <li><a href="#the-economy-of-attention">The economy of attention</a></li> <li><a href="#social-approval-indicators">Social approval indicators</a></li> <li><a href="#substitute-for-real-life">Social media is not a substitute for real-life interaction</a></li> <li><a href="#future-generations">Future generations and the ability to focus</a></li> <li><a href="#social-control-and-propaganda">Social control and propaganda</a></li> <li><a href="#the-solution">The solution</a></li> </ol> <h2 id="addictiveness">Addictiveness and the drug-like nature of social media</h2> <p>Social media <a href="https://www.addictioncenter.com/drugs/social-media-addiction/">arguably statisfies most of the requirements to be classified as a drug</a>. It&rsquo;s addictive, it&rsquo;s a coping mechanism, it&rsquo;s hard to quit and it affects our mental and even physical health, especially on young people (more on that in the next section).</p> <p>Social media companies are competing to keep us engaged and make their services as addictive as possible. Many people spend a large portion of their day endlessly scrolling their feeds, consuming content that is carefully curated by complex AI to match what is most likely to keep us scrolling. Scrolling your feed isn&rsquo;t necessarily exciting or fun, but it&rsquo;s addicitive by design. Sometimes we don&rsquo;t even want to scroll our feeds, but we do it nonetheless, like a junkie who can&rsquo;t stop doing drugs, because our brains love that dopamine hit we get every time we see a new post or video popping up. Social media has mastered the &ldquo;art&rdquo; of exploiting our human weaknesses and subconscious, so that we are glued to our screens; that&rsquo;s their business model, the more time you spend on their platform and the more ads you see, the more data you give away, the bigger their profits. It&rsquo;s essentially a slot machine on steroids.</p> <figure ><img src="http://margiolis.net/files/monkeyslot.jpg"><figcaption>Scrolling the feed.</figcaption></figure> <blockquote> <p>We now know that many of the major social media companies hire individuals called Attention Engineers, who borrow principles from Las Vegas casino gambling, among other places, to try to make these products as addictive as possible. That is the desired use case of these products; is that you use it in an addictive fashion because that maximizes the profit that can be extracted from your attention and data.</p> <p>And something I think we&rsquo;re going to be hearing more about in the near future, is that there&rsquo;s a fundamental mismatch between the way our brains are wired, and this behavior of exposing yourself to stimuli with intermittent rewards throughout all of your waking hours. It&rsquo;s one thing to spend a couple of hours at a slot machine in Las Vegas, but if you bring one with you, and you pull that handle all day long, from when you wake up to when you go to bed: we&rsquo;re not wired from it. It short-circuits the brain, and we&rsquo;re starting to find that it has actual cognitive consequences &mdash; one of them being this sort of pervasive background hum of anxiety.&quot;</p> <p><em>&mdash; Cal Newport: <a href="https://www.youtube.com/watch?v=3E7hkPZ-HTk">Quit social media</a></em></p> </blockquote> <p>If you&rsquo;re addicted to gambling, smoking, or anything, the best thing you can do is quit, not minimize usage. Not smoking was unheard of a few decades ago, but now that we know about the harms caused by smoking we try to get people to quit it, not live with it because it&rsquo;s the norm. The same applies to social media; because everyone uses it, it doesn&rsquo;t mean you should too.</p> <p>Over the years I&rsquo;ve noticed the following recurring patterns that indicate social media addiction:</p> <ul> <li>You can&rsquo;t stay for a few hours without reflexively picking up your phone to check social media, even if there&rsquo;s nothing to see.</li> <li>You compulsively refresh your feed every second without really knowing why.</li> <li>Your hand is already in the pocket the second a notification pops up, like you&rsquo;re reaching for a gun.</li> <li>You <a href="https://sitn.hms.harvard.edu/flash/2018/dopamine-smartphones-battle-time/">pick up your phone</a> the moment you&rsquo;re bored.</li> <li>Despite knowing the harms social media causes, you still spend hours on it everyday.</li> <li>You pause to check notifications when you&rsquo;re hanging out with someone, even when they might be talking to you.</li> <li>Checking social media is the first thing you do in the morning and/or the last thing <a href="https://www.sciencedaily.com/releases/2018/11/181127111044.htm">before sleep</a>. As a parallel, my father once told me the first thing he&rsquo;d do when he woke up was pick up a cigarrete and start smoking before he even got out of bed.</li> </ul> <blockquote> <p>They checked social media right before they went to sleep, and reached for their phone as soon as they woke up in the morning (they had to—all of them used it as their alarm clock). Their phone was the last thing they saw before they went to sleep and the first thing they saw when they woke up. If they woke in the middle of the night, they often ended up looking at their phone. Some used the language of addiction. “I know I shouldn’t, but I just can’t help it,” one said about looking at her phone while in bed.</p> <p><em>&mdash; Dr. Jean Twenge: <a href="https://www.theatlantic.com/magazine/archive/2017/09/has-the-smartphone-destroyed-a-generation/534198/">Have Smartphones Destroyed a Generation?</a></em></p> </blockquote> <p>Social media is so smartly engineered that it even gives you a &ldquo;high&rdquo; when your post is successful. That is done through likes, shares, retweets, and so on. We usually underestimate how powerful this simple mechanism is, but making a &ldquo;successful&rdquo; post feels like you accomplished something, because you managed to capture the public&rsquo;s attention.</p> <blockquote> <p>So we want to psychologically figure out how to manipulate you as fast as possible and then give you back that dopamine hit. We did that brilliantly at Facebook. Instagram has done it. Twitter has done it.</p> <p>And because we get rewarded in the short-term signals &mdash; hearts, likes, thumbs up &mdash; and we conflate that with value and truth. And instead, what it really is, is fake, brittle popularity, that&rsquo;s short-term and that leaves you even more, and admit it, vacant and empty than before you did it.</p> <p><em>&mdash; Chamath Palihapitiya (former growth VP at Facebook).</em></p> </blockquote> <p>The hours people spend every day staring at a piece of glass and plastic should be enough to convince you.</p> <h2 id="effects-on-mental-health">Effects on mental health</h2> <p>It&rsquo;s a known fact that extensive use of social media can lead to depression, anxiety and feelings of isolation, inadequacy, insecurity and low self-esteem. This mostly stems from the tendency humans have to compare themselves to others. In the past, we used to compare ourselves to a much smaller pool of people, mostly our immidiate circle and at worst TV-stars, but with the rise of social media the pool has increased to include pretty much most of the world. This is not really the problem per se though, but rather, the fact that much of what you see on social media isn&rsquo;t an actual reflection of real life. People will go to great lengths to show off their best moments and you now have access to everyone&rsquo;s curated representations of their lives, and so, it&rsquo;s not uncommon to feel inadequate when you see everyone around you living their best life, even though in reality that&rsquo;s only a small portion of it. We essentially compare our normal everyday lives to others&rsquo; highlight reels.</p> <p>Studies have shown that those kinds of comparisons are most prevalent among young women, who tend to feel negative about their appearance, and in some cases even starve themselves to look like their favorite influencers and models. What&rsquo;s even worse is that the influencers themselves often don&rsquo;t like that in real life anyway. Extensive use filters and editing is very common, and as a result, they set unrealistic and fake beauty standards. There is also <a href="https://wng.org/sift/the-link-between-social-media-and-suicide-in-young-women-1617252291">correlation between increase in female depression and suicide rates and use of social media</a>.</p> <blockquote> <p>Boys’ depressive symptoms increased by 21 percent from 2012 to 2015, while girls’ increased by 50 percent—more than twice as much. The rise in suicide, too, is more pronounced among girls. Although the rate increased for both sexes, three times as many 12-to-14-year-old girls killed themselves in 2015 as in 2007, compared with twice as many boys. The suicide rate is still higher for boys, in part because they use more-lethal methods, but girls are beginning to close the gap.</p> <p><em>&mdash; Dr. Jean Twenge: <a href="https://www.theatlantic.com/magazine/archive/2017/09/has-the-smartphone-destroyed-a-generation/534198/">Have Smartphones Destroyed a Generation?</a></em></p> </blockquote> <p><a href="https://www.afterbabel.com/p/algorithms-hijacked-my-generation">Teenagers in general seem to be the most badly affected demographic from social media use</a>, and there&rsquo;s enough research to suggest that. Depression and suicide attempts have greatly increased since the start of the 2010s, which, is no surprise that at that time, social media and smartphones started being widely adopted by the younger generations.</p> <blockquote> <p>Smartphone ownership crossed the 50 percent threshold in late 2012 &ndash; right when teen depression and suicide began to increase. By 2015, 73 percent of teens had access to a smartphone.</p> <p>Not only did smartphone use and depression increase in tandem, but time spent online was linked to mental health issues across two different data sets. We found that teens who spent five or more hours a day online were 71 percent more likely than those who spent less than an hour a day to have at least one suicide risk factor (depression, thinking about suicide, making a suicide plan or attempting suicide). Overall, suicide risk factors rose significantly after two or more hours a day of time online.</p> <p>Of course, it&rsquo;s possible that instead of time online causing depression, depression causes more time online. But three other studies show that is unlikely (at least, when viewed through social media use).</p> <p>Two followed people over time, with both studies finding that spending more time on social media led to unhappiness, while unhappiness did not lead to more social media use. A third randomly assigned participants to give up Facebook for a week versus continuing their usual use. Those who avoided Facebook reported feeling less depressed at the end of the week.</p> <p>The argument that depression might cause people to spend more time online doesn&rsquo;t also explain why depression increased so suddenly after 2012. Under that scenario, more teens became depressed for an unknown reason and then started buying smartphones, which doesn&rsquo;t seem too logical.</p> <p><em>&mdash; Dr. Jean Twenge: <a href="https://theconversation.com/with-teen-mental-health-deteriorating-over-five-years-theres-a-likely-culprit-86996">With teen mental health deteriorating over five years, there&rsquo;s a likely culprit</a></em></p> </blockquote> <h2 id="the-economy-of-attention">The economy of attention</h2> <p>I&rsquo;m not smart enough to have coined this phrase, but I think it explains the situation we&rsquo;re in really well. <a href="https://econreview.berkeley.edu/paying-attention-the-attention-economy/">This UC Berkley article goes a bit more in-depth on the subject</a>.</p> <p>We&rsquo;ve all heard the phrase <em>&ldquo;when the product is free, you are the product&rdquo;</em>. It usually refers to how the service is free, but our data is what makes social media companies immensely profitable. But I want to give a different spin on this phrase. The goal of a product is to <em>sell</em>, and since <em>we</em> are the product, the way to tell if our product sells, is through likes and comments. This is our currency, and as is expected, we want more of it, and so, we&rsquo;ll strive to take the perfect shot, to show the best moments of our lives, and we&rsquo;ll wait for the right time to post in order to maximize engagement. If the product sells, we&rsquo;ll create more of it. If it doesn&rsquo;t, we&rsquo;ll change it, and this translates to changing our appearance, our lifestyle, our identity, in order for the product to be &ldquo;valuable&rdquo;.</p> <p>In an &ldquo;actual&rdquo; economy you sell a product to make money, but in the case of the social media economy, you do it for validation and to feed your ego. Even if you don&rsquo;t want to admit it, for many individuals the underlying motivation behind posting on social media is to feel important, to feel that the world notices them, to project an ideal version of themselves. This is why people upload tens or hundreds of pictures of their faces and bodies, and spend hours every day perfecting their profiles and obsessing over how many likes and followers they got or who saw their stories.</p> <h2 id="social-approval-indicators">Social approval indicators</h2> <p><em>See Adam Alter (NYU professor) and Tristan Harris (Google whistleblower and former engineer)</em>.</p> <blockquote> <p>Hijacking the social apparatus in your brain is a good way to get people to keep looking back, and one thing they&rsquo;ll do for example, is they introduced a lot more &ldquo;social approval indicators&rdquo; into these apps. A social approval indicitator is some way that someone can indicate to you that they thought about you or were thinking about you. The original structure of social media didn&rsquo;t have a lot of this, but when they added things like the like button, there&rsquo;s a reason for that, because now the like button meant that&rsquo;s a lot more social approval indicators, and they added more and more of these things.</p> <p>Tens of millions of dollars were invested to figure out how to do the facial recognition required to do auto-tagging on photos. Why did they spend so much money to solve that really hard computer science vision problem, is because it was another stream of social approval indicators. They&rsquo;re always looking for ways that people can easily indicate that they are thinking about you, because human psychology says, if clicking on this app might reveal new social approval indicators, it&rsquo;s almost impossibly irresistible not to. If I click on this app and see an indication that someone was thinking about me, that&rsquo;s very hard to resist. And once they added those social approval indicators, usage minutes of the apps skyrocketed, because now, instead of it being something maybe you signed on to once a day to see what was going on, you had a constant reason to keep checking.</p> <p>And the you add on to that &ldquo;intermittent reinforcement&rdquo;. Sometimes when you click there is nothing and sometimes there is; now you&rsquo;re becoming almost impossible to avoid. Intermittent reinforcement is something that Las Vegas casino gambling has taken a lot of advantage of in their design of their games like slot machines.</p> <p>So you put those type of things together, which are all engineered; this didn&rsquo;t exist in the original social media, it&rsquo;s not necessary for a social media experience to be what it is. All of that makes clicking on these apps really difficult to avoid.</p> <p><em>&mdash; Cal Newport</em></p> </blockquote> <h2 id="substitute-for-real-life">Social media is not a substitute for real-life interaction</h2> <p>What makes real-life interaction meaningful is that it requires sacrifice, whereas a message or a like on social media doesn&rsquo;t; it&rsquo;s cheap. Taking the time to go outside and meet someone, reading each other&rsquo;s body language and showing actual emotions (instead of emoji reactions) is something that social media simply cannot replace. The human brain is not wired to perceive likes and chat discussions as meaningful interaction.</p> <p>A while ago I&rsquo;ve had an interesting conversation about musicians (and artists in general), and how one can show support for their art. Imagine the following two cases; 1) you upload your music online, and lots of people like and comment on it, 2) you organize a concert where only a few people who really enjoy your music come, but make the effort to pay and spend their time to actually come and see you play. Which of the two cases you think would make a musician feel appreciated?</p> <p>Spending more time on social media than in real-life not only causes a decline in social skills and an increase in &ldquo;social anxiety&rdquo; cases, but can also lead to depression and <a href="https://www.apa.org/science/about/psa/2009/06/sci-brief">feelings of isolation and loneliness</a>.</p> <figure ><img src="http://margiolis.net/files/funmeeting.jpg"><figcaption>What a lovely meeting.</figcaption></figure> <h2 id="future-generations">Future generations and the ability to focus</h2> <p>Do we really want a generation raised by TikTok and Instagram influencers? A future generation filled with mental health issues, one that is unable to have normal and healthy social lives, that is brainwashed and isolated? All that so that we make a bunch of tech companies profitable?</p> <p>Our ability to focus and attention span has plummeted, because our attention is fragmented by the stream of notifications and messages we get. We&rsquo;re so used to wasting hours on social media every day that we have stopped being conscious of how we spend our time. Doing important work is a struggle when you have to check your phone every few minutes, and that&rsquo;s an issue even for grown-up individuals, imagine what the generations who grow up with social media from day one are going to be like.</p> <p>Because social media provides constant stimuli and keeps you entertained all the time, we&rsquo;ve become averse to boredom. Being bored is good, it means you have time to sit with your own thoughts and ponder. It&rsquo;s a sign that you need to get a hobby and do something productive, and we lose those moments of peace and clarity when we fill our free time with noise. And I don&rsquo;t believe it&rsquo;s right to blame the users for this &mdash; social media apps are the result of billions of dollars worth of attention engineering.</p> <p>For those reasons, and to not repeat the damage that&rsquo;s been done to the current generations, it&rsquo;s essential that we control our children&rsquo;s tech usage in order to prevent them from becoming virtual junkies.</p> <h2 id="social-control-and-propaganda">Social control and propaganda</h2> <p>In the very first paragraph I mentioned how social media, in the beginning, might have actually had genuinely good intentions. In fact, I believe that, but as time went on, tech companies realized that they&rsquo;re in a position where they can use their platforms to &ldquo;program&rdquo; users and leverage this to promote their own political agendas and twist reality to their own advantage. Here are a few ways with which this is achieved:</p> <ul> <li>Self-regulation through likes and dislikes, content moderation, and Terms of Service. As I&rsquo;ve explained <a href="#3">here</a>, there&rsquo;s an incentive to being compliant and changing in order to be accepted. That, however also expands to beliefs and opinions, because the same mechanism can incentivize you to be a &ldquo;good boy&rdquo; and believe and say the right things, because that&rsquo;s what will get you the best reactions, or even keep you on the platform. Reddit&rsquo;s upvote/downvote system is a great example of this mechanism in action.</li> <li>Verified accounts and expert-based consensus, which is pretty much what I discussed in my article about <a href="http://margiolis.net/w/gellman">the Gell-Mann Amnesia effect</a>. An <a href="https://news.mit.edu/2018/study-twitter-false-news-travels-faster-true-stories-0308">MIT study shows fake news spreads faster than the truth</a>.</li> <li>Data collection. Just as data is used to show you the right ads in order to maximize profits, it can also be used to train AI to promote and recommend the content and narratives that the company thinks is &ldquo;correct&rdquo; and will push their ideological ambitions.</li> </ul> <p>Tech giants, and especially social media companies, have proven countless times how they <em>do</em> have political motivations behind their policies, but <em>I&rsquo;m going to let the reader look for evidence to this claim themselves this time (spoiler alert: it&rsquo;s endless)</em>. Considering their political nature and the fact that they are so big they now control the virtual public square, those platforms have switched from free speech to allowing only the narratives that benefit them, hence the various methods used for dealing with dissidents (e.g shadow-banning, demonetizing, bans for arbitrary ToS violations, etc).</p> <blockquote> <p>The smart way to keep people passive and obedient is to strictly limit the spectrum of acceptable opinion, but allow very lively debate within that spectrum.</p> <p><em>&mdash; Noam Chomsky</em></p> </blockquote> <p>But propaganda isn&rsquo;t merely only spread by bad actors, <a href="https://en.wikipedia.org/wiki/Useful_idiot">useful idiots</a>, and politically-driven tech companies. Social media also provides governments (yes, even Western ones) with the most effective way to spread propaganda, something that people like Joseph Göbbels and Joseph Stalin (probably has to do with the first name) could only dream of in their time. Governments can now just pay influencers to spread propaganda for them, and most people will not even realize it. If you think I&rsquo;m making things up, I&rsquo;ll let you do the research yourself for this claim as well.</p> <p>Here are a few quotes from an expert in the field that should ring an alarm:</p> <blockquote> <p>This is the secret of propaganda: Those who are to be persuaded by it should be completely immersed in the ideas of the propaganda, without ever noticing that they are being immersed in it.</p> <p>Propaganda works best when those who are being manipulated are confident they are acting on their own free will.</p> <p><em>&mdash; Joseph Göbbels</em></p> </blockquote> <h2 id="the-solution">The solution</h2> <p>The ideal solution is to simply quit social media altogether and seek alternative forms of online and interaction. Apart from all the unecessary harm it has caused, <a href="http://margiolis.net/w/oldweb">modern social media goes against the very notion of the decentralized (i.e normal) internet</a>, and the online world did, does, and will function perfectly fine &mdash; better even &mdash; without social media.</p> <p>However, I understand that we&rsquo;ve been so conditioned to accept social media as a necessary part of our lives, that not everyone is ready to make the decision and quit it. In this case, it&rsquo;s best to see social media as a purely logistical tool, which should <em>only</em> used to complement and improve our real life interactions, rather than replace them. This I find is rather hard to implement &mdash; especially for younger people who have been using social media all their lives &mdash; and be consistent with, without going back to the old habits. Social media is meant to addictive, it&rsquo;s not just a matter of willpower.</p> <p>A few steps you can take to start limiting social media usage:</p> <ul> <li>Delete all social media from your smartphone and only log in manually (i.e do not save passwords) from a computer.</li> <li>Keep only the pages and groups that are vital to your life/career and delete everything else. Log in every few days to stay up to date and log out without spending more time than is needed.</li> <li>Turn off notifications.</li> <li>Do not interact through likes and comments.</li> <li>Do not make posts.</li> <li>Make a phone call whenever you want to talk to a friend, instead of messaging them.</li> <li>Set a timer.</li> </ul> <p>And remember; not using social media isn&rsquo;t &ldquo;weird&rdquo; or &ldquo;anti-social&rdquo;, as you will hear some people say. Being mindful of the technologies you choose to use, having control over your time and attention, not wanting to be a passive consumer and be used as input for some mega-corporation&rsquo;s AI, and seeking meaningful and real socialization is, in fact, pretty healthy.</p> <figure ><img src="http://margiolis.net/files/nosalute.webp"></figure> RSS: Advantages and usage http://margiolis.net/w/rss/ Fri, 27 May 2022 00:00:00 +1200 <p><a href="https://en.wikipedia.org/wiki/RSS">RSS (Really Simple Syndication)</a> is a standardized protocol for creating and sharing web feeds. An RSS feed is an XML file consisting of entries, each of them usually corresponding to an article. Programs called &ldquo;RSS aggregators&rdquo;, parse these files and provide a nice display where you can nagivate through each entry as if it were a news feed. This is a very convenient way to &ldquo;follow&rdquo; websites because all you have to do is grab their RSS URL, add it to your aggregator, and read the articles once it parses the feed.</p> <p><img src="http://margiolis.net/rss.png" alt=""></p> <p>On the website owner&rsquo;s end, a new entry needs to be added whenever a new article is published. The entry usually includes a short summary of the article, or as is the case for my website, the whole article, so that you can read it all through your RSS reader.</p> <h2 id="why-bother">Why bother</h2> <p>RSS is an open protocol, it&rsquo;s decentralized, and very simple both in terms of parsing and maintaining. Because RSS is such a simple protocol, it can adapt to everyone&rsquo;s workflow. There are RSS readers for Android, Windows, UNIX, web browsers, email clients, etc.</p> <p>Apart from the technical advantages RSS provides, it&rsquo;s also a great way to have a personal, uncensored feed, following only the websites you want &mdash; even YouTube channels and Twitter accounts &mdash; without ads and additional noise and distractions.</p> <p>I personally use <a href="https://codemadness.org/sfeed-simple-feed-parser.html">sfeed</a> as an RSS reader, because I like the extensibility it offers. I&rsquo;ve written <a href="https://ftp.margiolis.net/patch/sfeed_bookmarks.diff">a small patch for sfeed_curses(1)</a>, so that you can bookmark feed entry URLs to a predefined file. I use this mostly to queue videos and podcasts and then stream them through <a href="https://mpv.io/">mpv</a> using <a href="https://git.sr.ht/~crm/scripts/tree/master/item/vdq">a very simple script I created</a>, which, all it does is read the &ldquo;queue&rdquo; file and pipe the URLs to mpv.</p> <h2 id="how-to-create-and-maintain-an-rss-feed">How to create and maintain an RSS feed</h2> <p>Below is a very basic RSS feed. Inside the <code>&lt;channel&gt;</code> tags is the whole feed, and inside <code>&lt;item&gt;</code> is each individual entry. The rest of the tags are pretty self-explanatory, if not, feel free to <a href="https://www.rssboard.org/rss-specification">read the specification</a>. The article/summary is placed inside <code>&lt;description&gt;</code> &mdash; that can be in plain text or <a href="https://www.rssboard.org/rss-encoding-examples">encoded HTML</a>:</p> <pre tabindex="0"><code>&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34; ?&gt; &lt;rss version=&#34;2.0&#34;&gt; &lt;channel&gt; &lt;title&gt;Example Org&lt;/title&gt; &lt;description&gt;Example Org&#39;s RSS feed&lt;/description&gt; &lt;link&gt;http://www.example.org/rss.xml&lt;/link&gt; &lt;item&gt; &lt;title&gt;Example entry&lt;/title&gt; &lt;link&gt;http://www.example.org/blog/post.html&lt;/link&gt; &lt;pubDate&gt;Sun, 06 Sep 2009 16:20:00 +0000&lt;/pubDate&gt; &lt;description&gt; Here is some text containing an interesting description. &lt;/description&gt; &lt;/item&gt; &lt;/channel&gt; &lt;/rss&gt; </code></pre><p>To create a new entry, you can either handwrite it in the XML file yourself, make a script or small program to do it automatically, or use some existing tool. I prefer to use my own script, since it&rsquo;s a dead simple thing to do.</p> <p>To share the feed, simply add the URL to your website and tell people to subscribe to it. For example, my website&rsquo;s RSS feed can be obtained from <a href="http://margiolis.net/w/rss.xml">https://margiolis.net/w/rss.xml</a>.</p> <h2 id="rss-for-youtube-and-social-media">RSS for YouTube and social media</h2> <p>Even though they purposefully make it hard to find, so that you can stay on their platform, YouTube, Twitter, Reddit, and other services, offer RSS feeds, and even if they don&rsquo;t, there are ways you can generate one.</p> <h3 id="youtube">YouTube</h3> <p>Go to the channel&rsquo;s home page, right-click and click on &ldquo;Page source&rdquo; and search for <code>channelId&quot; content</code>. Copy the hash inside <code>content=&quot;&quot;</code> and append it to the following URL (replace <code>hash</code>):</p> <pre tabindex="0"><code>https://www.youtube.com/feeds/videos.xml?channel_id=hash </code></pre><h3 id="github">GitHub</h3> <p>You can get an RSS feed for each new commit that happens in a given branch. Replace <code>username</code>, <code>repo</code> and <code>branch</code> with the correct values:</p> <pre tabindex="0"><code>https://github.com/username/repo/commits/branch.atom </code></pre><h3 id="twitter">Twitter</h3> <p>Twitter does not actually offer RSS feeds anymore, but there is a proxy site for Twitter called <a href="https://nitter.ca/">nitter</a> that does. Replace <code>username</code> with the account&rsquo;s username you want to follow:</p> <pre tabindex="0"><code>https://nitter.net/username/rss </code></pre><h3 id="reddit">Reddit</h3> <p>Each subreddit has an RSS feed under this URL (replace <code>subreddit</code>):</p> <pre tabindex="0"><code>https://www.reddit.com/r/subreddit.rss </code></pre><h3 id="facebook-and-others">Facebook and others</h3> <p>There&rsquo;s a nice program called <a href="https://rss-bridge.github.io/rss-bridge/">RSS-Bridge</a> that can generate RSS feeds for websites which don&rsquo;t have one. RSS-Bridge is installed and run on your server, but there are <a href="https://www.srss.nl/">public instances you can use</a>.</p> Share ZFS datasets with NFS http://margiolis.net/w/zfsnfs/ Sat, 21 May 2022 00:00:00 +1200 <p>Thanks to Mark Johnston &lt;markj@FreeBSD.org&gt; for recommending this configuration. The article is also mirrored on the <a href="https://wiki.freebsd.org/ZFS/ShareNFS">FreeBSD Wiki</a>.</p> <h2 id="host">Host</h2> <p>Add the following lines to <code>/etc/rc.conf</code>:</p> <pre tabindex="0"><code>nfs_server_enable=&#34;YES&#34; mountd_enable=&#34;YES&#34; mountd_flags=&#34;-n&#34; rpc_lockd_enable=&#34;YES&#34; rpc_statd_enable=&#34;YES&#34; rpcbind_enable=&#34;YES&#34; </code></pre><p>For some reason the NFS server needs to be restarted the first time after boot. A simple way to do this automatically is:</p> <pre tabindex="0"><code># echo &#34;service nfsd restart&#34; &gt;&gt; /etc/rc.local </code></pre><p>Set the <code>sharenfs</code> property to the dataset you want to share. Replace the IPs and <code>pool/dataset*</code> with your desired values. ZFS properties are documented in <a href="https://man.freebsd.org/zfsprops/7">zfsprops(8)</a>.</p> <p>We&rsquo;re going to share 2 datasets, one with read-write and one with read-only access:</p> <pre tabindex="0"><code># chmod -R 777 /pool/dataset_rw # zfs set sharenfs=&#39;-network=192.168.1.0/24,-alldirs&#39; pool/dataset_rw # zfs set sharenfs=&#39;ro=192.168.1.0/24,-alldirs&#39; pool/dataset_ro </code></pre><p>Start the NFS server:</p> <pre tabindex="0"><code># service nfsd start # service mountd reload </code></pre><h2 id="guest">Guest</h2> <p>Acquire the host&rsquo;s IP address using ifconfig(8) on it first. We&rsquo;ll assume it&rsquo;s 192.168.1.5.</p> <p>Mount the filesystems. <code>/pool/dataset*</code> corresponds to the actual mount point of the dataset in the host:</p> <pre tabindex="0"><code># mkdir -p /mnt/dataset_rw /mnt/dataset_ro # mount -t nfs -o rw 192.168.1.5:/pool/dataset_rw /mnt/dataset_rw # mount -t nfs -o ro 192.168.1.5:/pool/dataset_ro /mnt/dataset_ro </code></pre><p>When done, unmount:</p> <pre tabindex="0"><code># umount /mnt/dataset_rw /mnt/dataset_ro </code></pre><p>In case you want the filesystems to be mounted on boot, add the following lines to <code>/etc/fstab</code>:</p> <pre tabindex="0"><code>192.168.1.5:/dataset_rw /mnt/dataset_rw nfs rw 0 0 192.168.1.5:/dataset_ro /mnt/dataset_ro nfs ro 0 0 </code></pre><h2 id="further-reading">Further reading</h2> <ul> <li><a href="https://docs.freebsd.org/en/books/handbook/network-servers/">FreeBSD Handbook: Network Servers</a></li> </ul> Process queuing using lock files http://margiolis.net/w/procqueue/ Wed, 18 May 2022 00:00:00 +1200 <p>Locking will be done using the <a href="https://man.openbsd.org/fcntl">fcntl(2) system call</a>. I&rsquo;m aware of lockf(3) and flock(2), but both of them normally use fcntl(2) under the hood, and they are not as portable. <a href="https://en.wikipedia.org/wiki/File_locking">More information on file locking</a>.</p> <p>For a real use-case, I&rsquo;ve written a <a href="http://margiolis.net/w/nfy">notification program</a> which uses the same mechanism, so that notifications can be queued without having to run a daemon, such as D-Bus, in the background.</p> <p>First create the lock file with write permissions. <code>O_CREAT</code> is used to create the file in case it doesn&rsquo;t exist already:</p> <pre tabindex="0"><code>#include &lt;err.h&gt; #include &lt;fcntl.h&gt; ... char *lockfile = &#34;/tmp/foo.lock&#34;; int fd; if ((fd = open(lockfile, O_CREAT | O_WRONLY, 0600)) &lt; 0) err(1, &#34;open(%s)&#34;, lockfile); </code></pre><p>Locking commands operate on the <code>flock</code> structure. Before a call to fcntl(2) is made, we need to write the following fields:</p> <pre tabindex="0"><code>struct flock { off_t l_start; /* starting offset */ off_t l_len; /* len = 0 means until end of file */ short l_type; /* lock type: read/write, etc. */ short l_whence; /* type of l_start */ ... }; </code></pre><p>The starting offset, <code>l_len</code>, can be anything, but 0 is what makes the most sense. We&rsquo;ll set <code>l_len</code> to 0 as well, since we want each process to lock the entire file. The lock type, <code>l_type</code>, needs to be an exclusive lock (<code>F_WRLCK</code>), that is, a lock that prevents any other process from setting a lock on that area before it&rsquo;s released. <code>l_whence</code> will be set to <code>SEET_SET</code> to indicate that the relative offset <code>l_start</code> will be measured from the beginning of the file:</p> <pre tabindex="0"><code>struct flock fl; fl.l_len = 0; fl.l_start = 0; fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; </code></pre><p>The <code>F_SETLKW</code> command will make the calling process wait until the lock request can be satisfied. There&rsquo;s also <code>F_SETLK</code>, but it returns immidiately if the lock is already acquired, which is not very useful for queuing processes:</p> <pre tabindex="0"><code>if (fcntl(fd, F_SETLKW, &amp;fl) &lt; 0) err(1, &#34;fcntl(F_SETLKW)&#34;); </code></pre><p>When we get past the call to fcntl(2), it means that we have acquired the lock until a call to close(2) is made. Here is where we&rsquo;ll put the part of the code we want to queue, in this case a simple <code>printf</code> followed by a 3-second sleep to make sure queuing really works:</p> <pre tabindex="0"><code>printf(&#34;hello from %d\n&#34;, getpid()); sleep(3); </code></pre><p>When done, release the lock:</p> <pre tabindex="0"><code>close(fd); </code></pre><p>To test the code, open two terminals and run the program on both of them. You&rsquo;ll see the process that was run last will not execute the code after fcntl(2) until the first one has finished.</p> FreeBSD VNET Jails http://margiolis.net/w/vnetjail/ Sat, 30 Apr 2022 00:00:00 +1200 <p>This article is also mirrored on the <a href="https://wiki.freebsd.org/Jails/VNET">FreeBSD Wiki</a>.</p> <h2 id="configuration">Configuration</h2> <p>The VNET is set up using an <a href="https://man.freebsd.org/if_epair/4">if_epair(4)</a> interface bridged with the actual network interface, in my case <code>re0</code>. Just like if_tap(4) interfaces, epairs can be used by one jail at a time, so if you need to run more than one jail at the same time, you have to make more epairs.</p> <p>If you&rsquo;re using tap interfaces for bhyve VMs, you can just <code>addm</code> them to the bridge.</p> <p>Add the following lines to <code>/etc/rc.conf</code>:</p> <pre tabindex="0"><code>if_bridge_load=&#34;YES&#34; if_epair_load=&#34;YES&#34; cloned_interfaces=&#34;bridge0&#34; ifconfig_bridge0=&#34;addm re0 up&#34; </code></pre><p>Apply changes:</p> <pre tabindex="0"><code># /etc/netstart </code></pre><p>The jail needs to inherit <code>/dev/bpf*</code> from the host in order for networking to work at all. Make a new <code>/etc/devfs.rules</code> ruleset:</p> <pre tabindex="0"><code>[devfsrules_jails=5] add include $devfsrules_hide_all add include $devfsrules_unhide_basic add include $devfsrules_unhide_login add path &#39;bpf*&#39; unhide </code></pre><p>Restart devfs(8):</p> <pre tabindex="0"><code># service devfs restart </code></pre><p>In <code>/etc/jail.conf</code>, we&rsquo;ll name the jail <code>foo</code> and give it the other end of the epair we&rsquo;ll create as its network interface. Its IP address will be acquired using DHCP. The reason I&rsquo;m manually calling dhclient(8) is because adding <code>ifconfig_epair0b=&quot;DHCP&quot;</code> in the jail&rsquo;s <code>/etc/rc.conf</code> doesn&rsquo;t work. Options are detailed in <a href="https://man.freebsd.org/jail.conf/5">jail.conf(5)</a>:</p> <pre tabindex="0"><code>path = &#34;/usr/local/jail/$name&#34;; host.hostname=&#34;$name&#34;; exec.clean; exec.start = &#34;/bin/sh /etc/rc&#34;; exec.stop = &#34;/bin/sh /etc/rc.shutdown&#34;; allow.mount; allow.raw_sockets = 1; mount.devfs; devfs_ruleset=&#34;5&#34;; vnet; sysvmsg=new; sysvsem=new; sysvshm=new; foo { vnet.interface = &#34;epair0b&#34;; exec.start += &#34;dhclient epair0b&#34;; } </code></pre><h2 id="installation">Installation</h2> <pre tabindex="0"><code># mkdir -p /usr/local/jail/foo # bsdinstall jail /usr/local/jail/foo ... # ifconfig epair0 create # ifconfig bridge0 addm epair0a # ifconfig epair0a up # service jail onestart foo </code></pre><p>Test to see if the jail has networking:</p> <pre tabindex="0"><code># jexec foo ping google.com </code></pre><p>On jail shutdown, destroy the epair:</p> <pre tabindex="0"><code># service jail onestop foo # ifconfig epair0a destroy </code></pre><h2 id="delete-jail">Delete jail</h2> <p>Deleting jails isn&rsquo;t as straight forward, so I&rsquo;m leaving this here as well:</p> <pre tabindex="0"><code># service jail onestop foo # chflags -R noschg /usr/local/jail/foo # rm -rf /usr/local/jail/foo </code></pre><h2 id="further-reading">Further reading</h2> <ul> <li><a href="https://freebsdfoundation.org/freebsd-project/resources/introduction-to-freebsd-jails/">An Introduction to FreeBSD Jails</a></li> <li><a href="https://docs.freebsd.org/en/books/handbook/jails/">FreeBSD Handbook: Jails</a></li> <li><a href="https://klarasystems.com/articles/virtualize-your-network-on-freebsd-with-vnet/">Virtualize your network on FreeBSD with VNET</a></li> </ul> Learning languages effectively http://margiolis.net/w/language_learning/ Thu, 07 Apr 2022 00:00:00 +1200 <p>This article touches on a handful of important topics that are vital to learning a foreign language. Learning a new language is complicated, overwhelming at times, but highly interesting and rewarding if done properly.</p> <p>I&rsquo;ll refrain from going into specific methods, as if language learning can be explained in a simple recipe-like form. I think everyone learns differently, but the points I discuss in this article should be universal for everyone. It&rsquo;s up to you to find the particular method that works best for you.</p> <p><em>Special thanks to <a href="https://eiliv.no/">Eiliv</a> and Randel for helping me improve this article.</em></p> <h2 id="table-of-contents">Table of contents</h2> <ol> <li><a href="#too-many-languages">Don&rsquo;t learn too many languages at the same time</a></li> <li><a href="#learn-about-the-language">Learn about the language first</a></li> <li><a href="#proper-pronunciation">Proper pronunciation is key</a></li> <li><a href="#speak-early-on">Start speaking early on / challenge yourself</a></li> <li><a href="#learn-by-application">Learn by application, not memorization</a></li> <li><a href="#media-consumption">Media consumption: a double-edged sword</a></li> <li><a href="#read-books">Read books</a></li> <li><a href="#record-yourself">Record yourself</a></li> <li><a href="#think-in-your-target-language">Think in your target language</a></li> <li><a href="#word-for-word-translations">Avoid word-for-word translations</a></li> <li><a href="#srs">The SRS (Spaced Repetition System) fallacy</a></li> <li><a href="#travel">Travel if possible</a></li> <li><a href="#most-importantly">And most importantly&hellip;</a></li> <li><a href="#notes">Notes</a></li> </ol> <h2 id="too-many-languages">Don&rsquo;t learn too many languages at the same time</h2> <p>I&rsquo;ve fallen victim of this in the past as well. There are so many cool languages out there to learn, all with their own story, culture, and interesting features and sounds. Even though this rabbithole seems fun at first, I&rsquo;ve come to realize that you don&rsquo;t actually learn anything this way. You only gain superficial knowledge of each language and essentially become a real-life example of a <em>&ldquo;jack of all trades, master of none&rdquo;</em>.</p> <p>There&rsquo;s no official limit for how many languages a person can learn at the same time, as this is purely dependent on one&rsquo;s abilities and time available, but if you want to master something, you have to put in the hours, and mastering 10 languages at the same time just isn&rsquo;t feasible. Instead, as a rule of thumb, find one or two languages that really interest you and focus on them. I&rsquo;m sure if you&rsquo;re like me, this <em>does</em> sound very restricting, but at the end of the day, I&rsquo;d rather speak 2 languages great than 10 languages terribly.</p> <p>If you do decide to learn multiple languages, make sure you have some &ldquo;maintenance&rdquo; time for the languages you already speak so that you don&rsquo;t forget them.</p> <h2 id="learn-about-the-language">Learn about the language first</h2> <p>Before even jumping into learning the language, make sure you know some stuff about it. Which family does it belong to? Who speaks it? Is it related to a language you already know? What about its history? See what the language looks like. Read a bit about its grammar and vocabulary. This is all useful information that can help you decide if you really are interested in learning that language, and can also give you a general idea of what you&rsquo;re getting into, especially if you already have some experience with languages. All this can usually be found in the language&rsquo;s Wikipedia article, but further research is never bad. Gather material which you can reference early on. This includes textbooks, podcasts, articles. Don&rsquo;t get too caught up with this though, just find something to start with.</p> <h2 id="proper-pronunciation">Proper pronunciation is key</h2> <p>Having a solid understanding of the grammar and vocabulary of the language is obviously very important, but it&rsquo;s of no use when you cannot actually be understood due to bad pronunciation. Proper pronunciation makes you more pleasant to listen to, and you&rsquo;re better understood (or understood at all, depending on the language). Of course, you probably won&rsquo;t manage to get a 100% native accent, but you should try to, even if it&rsquo;s technically not an attainable goal. A subtle foreign accent is fine as long as communication is not impeded.</p> <p>To give an example of how wrong pronunciation can be a problem; English is taught in Greece from a very young age, but for some reason, (Greek speaking) teachers tend to not bother with pronunciation at all, hence why Greeks might speak good English on average, but are very hard to understand. A classic issue are the words &ldquo;sit&rdquo;, &ldquo;seat&rdquo;, &ldquo;shit&rdquo;, &ldquo;sheet&rdquo;. A Greek will most likely pronounce all 4 of them as /sit/, simply because Greek doesn&rsquo;t have long vowels or the [ɪ] and [ʃ] sounds.</p> <p>Learn the <a href="https://en.wikipedia.org/wiki/International_Phonetic_Alphabet">International Phonetic Alphabet (IPA)</a> first. Get a pronunciation chart (like <a href="https://en.wiktionary.org/wiki/Appendix:English_pronunciation">this one</a>) for your target language and say each individual sound out loud and try to get it as right as possible. Doing this from the very beginning will make you form good speaking habits. Watch/listen to people speaking the language and try to analyze <em>and mimic</em> their speech. Keep in mind that correct sentence-level pronunciation (i.e having a natural flow when pronouncing whole sentences) is different from pronouncing standalone words correctly.</p> <p>Each language has regional accents, so make sure you don&rsquo;t mix accents together. If you&rsquo;re not going for a specific dialect or accent, a good rule of thumb is to find material and speak the language in what would be considered the most stereotypical or &ldquo;standard&rdquo; accent.</p> <h2 id="speak-early-on">Start speaking early on / challenge yourself</h2> <p>A big mistake learners make is that they abstain from speaking early on, because they are afraid they don&rsquo;t speak the language well enough to have actual conversations. This is very counter-productive; for one, practice makes perfect &mdash; if you don&rsquo;t speak, you&rsquo;ll not learn to speak. Second, speaking and reading/writing are different, independent skills.</p> <p>Speaking introduces new variables, namely, <em>anxiety and pressure</em>, which are legitimate barriers to speaking well. You might be able to read and write a language perfectly, heck, even better than a native, but sound like an A1 beginner when speaking to a real person, which is hardly an exaggeration. The reasons are simple:</p> <ul> <li>Speaking occurs in real time. It doesn&rsquo;t give you the luxury to revise and carefully think a sentence through, unlike writing. Words have to follow thought instantaneously, and that&rsquo;s a skill that has to be developed, regardless of how good your reading/writing skills are.</li> <li>The stress factor and fear of sounding silly hinders your ability to express your thoughts properly. That&rsquo;s especially true for introverted people.</li> <li>You haven&rsquo;t practiced speaking enough, so producing sounds does not come out naturally and your speech has no flow.</li> <li>As I said, speaking is a separate skill. To give an analogy, you might know the engineering behind building a house, but actually using tools and your hands to build it is a whole different skill, which isn&rsquo;t learned by just having a theoretical background in construction.</li> </ul> <p>A nice way to go about using the language, is by joining Discord language servers [1] or some other language exchange platform. Especially with popular languages, those servers are very active, with lots of native speakers in them, so there&rsquo;s no excuse not to start speaking, or at least writing, from the very first day. Language learning/exchange patforms are fine too.</p> <p>In essence, get out of your comfort zone, ask questions, make mistakes, expose yourself, sound stupid and get laughed at. There&rsquo;s no way around it. That&rsquo;s all part of the learning process.</p> <h2 id="learn-by-application">Learn by application, not memorization</h2> <p>This can be said for pretty much everything when it comes to learning, and I could dedicate a whole article to how much I dislike memorization. Since I&rsquo;m a programmer, consider the following quote:</p> <blockquote> <p>The only way to learn a new programming language is by writing programs in it.</p> <p><em>&mdash; Dennis Ritchie</em></p> </blockquote> <p>We tend to remember the things we need or have been useful in the past. Make sure you don&rsquo;t try to squeeze in as much vocabulary, grammar [2] and all sorts of irrelevant stuff into your head at once when you don&rsquo;t really need it, especially if you&rsquo;re starting out. Truth is, if you don&rsquo;t put what you learn into practice, you don&rsquo;t really learn it. Always have real-world conversations as a goal &mdash; when you learn something, use it in a conversation and get feedback from native speakers. Write anything, from a diary to an essay.</p> <h2 id="media-consumption">Media consumption: a double-edged sword</h2> <p>Being immersed in a language means you&rsquo;ll inevitably have to consume media, be it movies, podcasts, music. This is a very good way to both see the language in action, and develop an ear for it. However, <em>just</em> passively consuming media won&rsquo;t get you far. If you don&rsquo;t put in the hours to actively hone your skills, consuming media will only be an excuse for you to be lazy. Immersion is very beneficial when combined with active learning. [3]</p> <p>The proper way to combine media consumption with actual learning is to note down the things you don&rsquo;t understand and study them afterwards. The big advantage of doing this is that you study in an audiovisual way, meaning you associate language with pictures. Our brains are great at pattern recognition, so being able to associate a word with a picture/gesture, creates longer-lasting and intuitive knowledge.</p> <p>When watching movies in a language you&rsquo;re learning, ALWAYS use subtitles in that language!</p> <h2 id="read-books">Read books</h2> <p>Reading has lots of benefits as well. The thing with reading is that it makes you focus solely on the language, which in turn lets you think and analyze more carefully, at a slower pace. Reading books is an ideal source of sophisticated vocabulary and more complex grammatical structures, that can make a big difference when used properly in speech. Confining yourself to the most basic and colloquial use of the language is still good if you only care about having simple conversations. Languages are highly complex and there are things that cannot be expressed with just basic vocabulary and grammar.</p> <p>Textbooks on the other hand, should be used only as reference, in my opinion.</p> <h2 id="record-yourself">Record yourself</h2> <p>I know, we all cringe listening to our voices, but let me explain. When speaking a new language, one tends to pay more attention to <em>what</em> he wants to say than his accent &mdash; this means that, inevitably, you&rsquo;ll make lots of minor mistakes that go unnoticed when speaking. Recording your voice and listening to it afterwards lets you focus only on your accent and the small details that could improve it.</p> <h2 id="think-in-your-target-language">Think in your target language</h2> <p>Part of internalizing a language is thinking in it. Start having inner monologues and dialogues in the language you&rsquo;re learning, name everything around you. You get the point.</p> <h2 id="word-for-word-translations">Avoid word-for-word translations</h2> <p>Word-for-word translating means that you learn a sentence by translating directly from your native language. For example:</p> <blockquote> <p>Greek: Πάω να κάνω μπάνιο (lit. &ldquo;I&rsquo;m going to do bath&rdquo;).<br> English: I&rsquo;m going to take a shower.</p> </blockquote> <p>Think how absurd it would&rsquo;ve sounded if instead of saying &ldquo;I&rsquo;m going to take a shower&rdquo;, I had translated from Greek and said &ldquo;I&rsquo;m going to do bath&rdquo;. All languages are different. They have their own rules, patterns, lexical and grammatical structures. This means that, when you learn a language, you have to learn the idioms and phrases that are used in <em>that</em> particular language, not try and translate words. Idioms are a great way to improve fluency and get a cultural insight behind the lingusitic differences that make a language unique.</p> <h2 id="srs">The SRS (Spaced Repetition System) fallacy</h2> <p>I&rsquo;m aware of the research being done on SRS, as well as its benefits. What I want to argue, however, is that languages are best learned in <em>context</em> and SRS alone won&rsquo;t provide that. It can be a useful tool if used correctly, but relying on it will give you the impression of learning, when in reality you&rsquo;ll be grinding your way through vocabulary and grammar, without any meaningful context.</p> <p>Learning a language isn&rsquo;t simply a matter of filling sentences on Duolingo or mindlessly memorizing your Anki cards. Those platforms (or better yet, games) should be used to complement your knowledge or to spend your time productively in the toilet, not as your primary source of learning. SRS is not a magic potion that can teach you what people spend years to learn, this just a get-rich-quick scheme. Languages are more than a flashcard game.</p> <p>Again, there <em>is</em> merit to Spaced Repetition, but use it as a complementary tool only.</p> <h2 id="travel">Travel if possible</h2> <p>If travelling is a possibility, make sure to plan a trip and practice the language there once you have a solid foundation. I would go as far as to say that you should put yourself in a situation where you have no other option but to speak the language. This is probably the best practice possible &mdash; you get real-world immersion, you speak the language in real life with locals, and, as a bonus, you&rsquo;ll have a great time.</p> <h2 id="most-importantly">And most importantly&hellip;</h2> <p>Have fun learning and be consistent!</p> <p>Everything I rambled about in this article is all fine, but it&rsquo;s totally useless if you are not consistent with your learning. Languages, like any other skill, take patience, practice and consistent efforts in order to master. Instead of studying for 7 hours once a week, study 20, 30 minutes to, ideally, 1 hour each day. The brain needs continuous stimulation in order to really <em>learn</em> something, so practicing once a week, even though you might practice for 7 hours straight, is less effective.</p> <p>The amount of time you dedicate to learning will determine how fast you&rsquo;ll learn, but consistency will determine whether or not you WILL learn at all.</p> <h2 id="notes">Notes</h2> <ol> <li>Discord is <a href="https://spyware.neocities.org/articles/discord.html">not one of the best platforms I can think of</a>, but unfortunately it&rsquo;s where many language learning communities are, so at least take advantage of this. There&rsquo;s a <a href="https://www.reddit.com/r/languagelearning/comments/5m5426/discord_language_learning_servers_masterlist/">list of language servers</a> where you can ask questions, speak and write the language.</li> <li>A nice chance to look up grammar is when analyzing phrases you already know, but don&rsquo;t quite get their structure. It&rsquo;s important to always understand the grammar behind a language, because it&rsquo;ll both make recognizing patterns easier, but also save you lots of headaches.</li> <li>A good example of this is people who grew up in billingual families, but never learned to speak their second language. They might be able to understand the language perfectly, but are unable to speak. That&rsquo;s because they never actively practiced learning the language, so they ended up only knowing how to recognize phrases and words they&rsquo;ve been hearing over and over.</li> </ol> nfy(1) http://margiolis.net/w/nfy/ Sat, 02 Apr 2022 00:00:00 +1200 <p><em>A minimal and daemonless notification program for X.</em></p> <p><a href="https://git.sr.ht/~crm/nfy">Source code</a> | <a href="https://ftp.margiolis.net/nfy/nfy-0.2.tar.gz">Download 0.2</a> | <a href="http://margiolis.net/files/nfy.1.html">Man page</a></p> <h2 id="features">Features</h2> <ul> <li>Very lightweight. ~250 lines of C.</li> <li>Probably no dependencies, apart from the pre-installed X11 ones. <code>Xlib</code>, <code>libXft</code> and <code>libXrandr</code>.</li> <li>Multiple notifications are <a href="http://margiolis.net/w/procqueue">queued using a lock file</a>. This avoids the trouble of having a daemon (e.g D-Bus) constantly running in the background.</li> <li>Non-blocking behavior, so that it can be used inside scripts easily.</li> <li>Configuration is done by editing <code>config.h</code> and recompiling the source code, in a similar fashion to <a href="https://suckless.org/">suckless utilities</a>. No knowledge of C is required to edit the file. Compilation takes around 1 second.</li> <li>Clicking on the notification window will make it disappear.</li> </ul> <p>nfy works by piping text into it. Text cannot be passed as argument:</p> <pre tabindex="0"><code>$ echo &#39;hello world&#39; | nfy $ nfy &lt; foo.txt </code></pre><p>If <code>stdin</code> is empty, it exits with an error:</p> <pre tabindex="0"><code>$ nfy nfy: stdin is empty </code></pre><p><img src="http://margiolis.net/files/nfy.png" alt=""></p> <h2 id="install">Install</h2> <p>First make sure all settings in <code>config.mk</code> are set according to your system&rsquo;s configuration, then:</p> <pre tabindex="0"><code># make install clean </code></pre><h2 id="notes">Notes</h2> <p>If you want to use nfy(1) with cron(8), make sure to export the X Display variable inside the script running nfy(1):</p> <pre tabindex="0"><code>export DISPLAY=&#34;:0.0&#34; </code></pre><p>Alternatively, export the variable in the crontab file directly.</p> <p>Send feedback and bugs to <code>&lt;christos@margiolis.net&gt;</code>.</p> Political labels considered harmful http://margiolis.net/w/political_labels/ Fri, 01 Apr 2022 00:00:00 +1200 <p>I&rsquo;m an anarcho-syndicalist, a reactionary, a classical liberal, a socialist! And the list goes on&hellip;</p> <blockquote> <p>ideologue /ˈaɪ.di.ə.lɑɡ/: an often blindly partisan advocate or adherent of a particular ideology.</p> <p><em>&mdash; Merriam-Webster dictionary</em></p> </blockquote> <p>My point is not to suggest that we shouldn&rsquo;t discuss ideology or that ideology in and of itself is useless. My issue is the urge to attach labels, and people basing their whole identity around a set of labels, which results in more division, stifling free thought, and viewing things from an &ldquo;us an them&rdquo; standpoint.</p> <h2 id="labels-are-restrictive-not-descriptive">Labels are restrictive, not descriptive</h2> <blockquote> <p>&hellip;because people are complex; so too, are their politics. As such, political labels tend to be more restrictive than descriptive and, ultimately, they miss a key point &mdash; people aren&rsquo;t defined by politics; people define politics. Labels are no substitute for experience. Allowing them to become so has rendered possible many of our most divisive political issues &mdash; from identity politics and partisanship to fake news and echo chambers.</p> <p><strong>Too often, labels become an attempt to turn the complicated into the simple. And people and politics aren&rsquo;t simple.</strong></p> <p>Individuals are incredibly diverse and often hold a wide-range of political views depending on the subject or circumstances. It&rsquo;s why political labels need so many caveats and hyphens &mdash; Alt-right, Far Left (or Right), Progressive Liberal, Moderate Republican, etc. It&rsquo;s also why political labels are rife for exploitation.</p> <p>For many, the complexity and gravity of political issues leaves them perplexed and frustrated. Understandably, people assume labels to align with political ideologies in the hope of having their views represented; however, labels divide just as easily as they associate</p> <p><em>&mdash; <a href="https://medium.com/matadornetwork/beyond-description-the-burden-of-political-labels-afc097b7a709">Excerpts from this article</a></em></p> </blockquote> <h2 id="labels-as-a-tool-for-ostracization">Labels as a tool for ostracization</h2> <p>Oftentimes we use labels to dismiss someone&rsquo;s ideas. Say for example someone has views against immigration. An open-borders person&rsquo;s first reaction would be to shout racist accusations at them, and thus label them as a racist, far-right, or what have you, without really judging their points based on merit or facts, but based on emotion and bias. This is a very simple an easy way to gain moral high ground in a debate or ostracize someone. And this has always happened. Christians, muslims, nazis, communists, all attaching labels to their enemies to portray them as undesirables and worthy of contempt.</p> <p>Labels are not always attached to someone based on their actual views, but on someone else&rsquo;s (usually limited) perception of their views. How does this make labelling <em>at least accurate</em> in the first place?</p> <p>Between the 1940s and 1970s, Greece, my home country, had experienced a civil war, political persecution and a CIA-backed military junta. During the civil war, there were two camps you could join: the nationalists, backed by NATO, and communists, backed by the Soviet Union. Eventually, the communists lost, but polarization was so high, that persecution of them continued during the dictatorship (1967-1974). The issue was, that one was labelled arbitrarily, and simply landing on the &ldquo;wrong&rdquo; side was as easy as simply listening to the wrong music or having been born in a family with leftist ancestors. Others, snitched on their neighbours, or even family members, on suspicion of political heresy. Mind you, there were persecuted people that had nothing to do with leftism or communism, but not being sympathetic to the regime meant you were automatically a communist.</p> <p>The reason behind this tangent is to give an example of how misleading labelling can be, and how easy it is to use it as a tool to dehumanize someone. In times of turmoil, labels are a weapon.</p> <p>Another example is the Bolshevik regime using the term <em>kulak</em> to target dissident peasants:</p> <blockquote> <p><em>Kulak</em>, in particular, became a term of abuse directed by party propaganda against peasants who incurred the wrath of the authorities through failure to comply with demands for the delivery of grain.</p> <p><em>&mdash; Edward Hallet Carr in &ldquo;The Russian Revolution from Lenin to Stalin, 1919-1927&rdquo;</em></p> </blockquote> <h2 id="my-proposal-and-personal-approach">My proposal and personal approach</h2> <p>Whenever I get asked about my ideology, people who expect me to respond with a simplistic term such as &ldquo;left&rdquo; or &ldquo;right&rdquo;, are usually annoyed, or think I am just avoiding the question. For one, I really don&rsquo;t think I 100% agree with any particular ideology, and second, I don&rsquo;t want to reduce the capacity to think for myself to a political label and create all sorts of biases before I even start to express any view. I find it nearly impossible to explain my whole political worldview with just a term &mdash; and that&rsquo;s not because I think I am some kind of intellectual with very unique thoughts, but because one&rsquo;s politics really <em>cannot</em> be accurately defined with just a word.</p> <p>There are ideologies which I think have more merit than others, that I&rsquo;m more attracted towards and tend to agree with, but I don&rsquo;t like the role-playing aspect of ideologies. It&rsquo;s almost always the case that people who are keen on labelling are the most insufferable in a discussion, because their goal is not to have a healthy conversation, but to prove that their side is the right one.</p> <p>So, what am I proposing, anyway? I think the single most important point I&rsquo;d make is to develop critical and <em>independent</em> thinking. Blindly accepting or denying an idea because the ideology you&rsquo;re subscribed to says so, is really no different than following a religion. Instead, my approach is to study and discuss various ideologies, extract the good things from each one of them, use my intuition and common sense, and form my <em>own</em> worldview.</p> <p>Of course, anyone who takes a political stance <em>does</em> fall into some broad category, but there really is no point in obsessing over the label you go with, like it&rsquo;s a football team.</p> Why reinvent the wheel http://margiolis.net/w/reinvent_wheel/ Fri, 25 Mar 2022 00:00:00 +1200 <p>I&rsquo;ve got a few reasons for &ldquo;reinventing the wheel&rdquo;, some personal and some practical:</p> <ul> <li>I&rsquo;m curious and want to know how things really work.</li> <li>I like DIY from scratch projects.</li> <li>Bottom-up learning is valuable.</li> <li>There are things that are fundamentally flawed and need to be done again from scratch.</li> <li>Existing tools don&rsquo;t always match the criteria for your specific task and/or introduce too much complexity.</li> <li>Because there&rsquo;s already a way to do something, it doesn&rsquo;t mean it&rsquo;s the right way to do it.</li> </ul> <p>Some of the greatest innovations have in fact been a reinvention of the wheel, because what already existed at the time wasn&rsquo;t good enough. Imagine someone telling James Watt he shouldn&rsquo;t waste his time reinventing the wheel. Improving what already exists means we have stable foundations and better tools to work with in the future. The wheel itself has been reinvented multiple times, hence we don&rsquo;t use Bronze Age wheels anymore.</p> <p><img src="http://margiolis.net/files/wheel.jpg" alt=""></p> <p>A good example in the software world of why sometimes reinventing the wheel is a good idea is UNIX vs Windows. UNIX was written from scratch to replace Multics which had serious flaws, so they ended up with a new and clean system, built right from the bottom-up. They reinvented the wheel and made huge innovations in the process. Windows on the other hand, is built on top of MS-DOS which even at the time was a mess, so what Microsoft is left with is an OS which up to this day is overly complicated, because of fundamental design flaws that cannot be fixed unless they do away with the whole thing and start from scratch. Instead, they are coming up with half-assed solutions and add complexity to the system with each update.</p> <p>Then there&rsquo;s quality and efficiency. Writing your own tools means they can be tailored to perform a specific task perfectly &mdash; something a big generalized framework or piece of software can&rsquo;t do in many cases. We tend to ignore the improvements better software can bring in the long term and instead spend time fighting with not-so-great tools and also rely on the power of modern hardware to cope for our mediocre solutions (see modern web tools or the web in general).</p> <p>So, am I proposing that we&rsquo;d be better off writing machine code and building our own programs for everything? Absolutely not. What I AM saying though, is that instead of being allergic to reinventing the wheel, we can start to see the merits in it, without overdoing it. This approach is benefecial to our personal improvement as engineers, which means an improvement in software as a whole. Just as you learn any other skill by mastering and understanding the basics, programming has to be understood from the bottom-up, not the opposite way. There&rsquo;s no point in knowing all kinds of fancy frameworks if all you see are black boxes, or cannot even implement a linked list if asked to. After all, it&rsquo;s a fact that software is getting worse.</p> <p>And to save myself from sounding ignorant, <strong>there are cases where reinventing the wheel is a problem</strong>, namely:</p> <ul> <li>Companies rolling their own proprietary solutions for everything, and ending up creating protocols and programs, which are arguably worse, expensive, harder to maintain, <em>and</em> incompatible with existing software.</li> <li>People who like to reinvent the wheel for the sake of just having &ldquo;their own&rdquo; version of something, even when there actually <em>is</em> good software out there that covers their needs just as well.</li> </ul> <p>Knowing how things really work will enable you to trace bugs easier, write/design more performant software, and overall know what the hell you&rsquo;re doing. And that can only be achieved by, sometimes, reinventing the wheel. Nothing can teach you what getting your hands dirty will.</p> <p><a href="https://medium.com/codex/dont-reinvent-the-wheel-and-other-web-developer-cop-outs-ed9dc4d6c9e3">A related article I found funny.</a></p> The Gell-Mann Amnesia Effect http://margiolis.net/w/gellman/ Tue, 22 Mar 2022 00:00:00 +1200 <blockquote> <p>Briefly stated, the Gell-Mann Amnesia effect is as follows. You open the newspaper to an article on some subject you know well. In Murray&rsquo;s case, physics. In mine, show business. You read the article and see the journalist has absolutely no understanding of either the facts or the issues. Often, the article is so wrong it actually presents the story backward&mdash;reversing cause and effect. I call these the &ldquo;wet streets cause rain&rdquo; stories. Paper&rsquo;s full of them. In any case, you read with exasperation or amusement the multiple errors in a story, and then turn the page to national or international affairs, and read as if the rest of the newspaper was somehow more accurate about Palestine than the baloney you just read. You turn the page, and forget what you know.</p> <p><em>&mdash; Michael Crichton (1942-2008)</em></p> </blockquote> <p><a href="https://eu.usatoday.com/story/news/2022/06/16/usa-today-audit-reporter/7647731001/">USA TODAY removed 23 fabricated stories from their website</a>, and, what&rsquo;s interesting is that most of these 23 articles are mundane and boring. One can only imagine how frequent this is when it comes to politically and economically-related topics.</p> <p><a href="https://larrysanger.org/2020/05/wikipedia-is-badly-biased/">The co-founder himself has confirmed that Wikipedia is biased</a>.</p> Git on 9front http://margiolis.net/w/git9/ Sun, 13 Mar 2022 00:00:00 +1200 <p>This article is also mirrored on the <a href="http://wiki.9front.org/git9">9front Wiki</a>.</p> <h2 id="resources">Resources</h2> <ul> <li><a href="http://man.9front.org/1/git">9front git man page</a></li> <li><a href="https://orib.dev/git9.html">Ori&rsquo;s article on Git9</a></li> </ul> <h2 id="user-configuration">User configuration</h2> <pre tabindex="0"><code>; mkdir $home/lib/git ; cat &gt; $home/lib/git/config [user] name=Your Name email=me@example.org ^D </code></pre><h2 id="examples">Examples</h2> <p>Create, commit and push a repo:</p> <pre tabindex="0"><code>; cd repo ; git/init # add a remote in .git/config... ; git/add . ; git/commit -m &#39;commitmsg&#39; foo.c heads/front: 817a3f121083091291c45f1ddfcd1b042343efab ; git/push </code></pre><p>Clone and push changes to a repo:</p> <pre tabindex="0"><code>; git/clone git://git.example.org/repo ; cd repo # make changes... ; git/commit foo.c ; git/push </code></pre><p>Make a patch:</p> <pre tabindex="0"><code># make changes... ; git/commit -m &#39;commitmsg&#39; foo.c heads/front: 817a3f121083091291c45f1ddfcd1b042343efab ; git/export &gt; patch.diff </code></pre><p>Apply a patch:</p> <pre tabindex="0"><code>; git/import &lt; patch.diff applying commitmsg </code></pre><p>See which files have changed:</p> <pre tabindex="0"><code>; git/diff -s M foo.c M bar.c </code></pre><h2 id="shithub-usage">Shithub usage</h2> <p>First ask Ori (ori AT eigenstate DOT org) for a user.</p> <p>Create and push a repository:</p> <pre tabindex="0"><code>; rcpu -u $user -h shithub.us -c \ newrepo -d &#39;description&#39; -c &#39;me@example.org&#39; reponame ; git/push -u hjgit://shithub.us/$user/reponame </code></pre><p>Repositories live under <code>/usr/git/$user</code>. Each repo contains editable files in <code>/usr/git/$user/repo/.git</code>:</p> <dl> <dt><code>webpublish</code></dt> <dd>If this file exists, then the repository is published in the public web list of repositories.</dd> <dt><code>desc, description</code></dt> <dd>The short description of the repository. It shows up in the repo list.</dd> <dt><code>contact</code></dt> <dd>Contact information for submitting patches. Shows up on the repository info page.</dd> </dl> 9front CPU server setup http://margiolis.net/w/9front_cpu/ Mon, 07 Mar 2022 00:00:00 +1200 <p>The contents of <code>/net/ndb</code> will be useful throughout the whole process; have them visible in another window:</p> <pre tabindex="0"><code>; ip/ipconfig ; cat /net/ndb ip=192.168.1.152 ipmask=255.255.255.0 ipgw=192.168.1.3 sys=cirno dns=192.168.1.1 dns=192.168.1.1 </code></pre><h2 id="boot-settings">Boot settings</h2> <p>The <code>9fat</code> partition contains the kernel, bootloader and boot settings. Add the following lines to <code>plan9.ini</code>. Make sure the values match your own configuration:</p> <pre tabindex="0"><code>; 9fs 9fat ; cd /n/9fat ; cat &gt;&gt; plan9.ini user=glenda cpu=192.168.1.152 auth=192.168.1.152 authdom=someauth service=cpu ^D </code></pre><p><code>user</code> is kinda like the root user on UNIX systems, <code>cpu</code> and <code>auth</code> define the IP addresses for the CPU and auth server respectively, <code>authdom</code> is the domain name of our the server, and <code>service=cpu</code> starts the CPU listener at boot time.</p> <p>9front needs to boot hands-free, so the <code>bootargs</code> prompt has to be skipped. To do that, change the <code>bootargs</code> variable (its values might be different) in <code>plan9.ini</code>:</p> <pre tabindex="0"><code>bootargs=local!/dev/sdE0/fs # change to... noobootprompt=local!/dev/sdE0/fs -m 291 -A -a tcp!*!564 </code></pre><h2 id="auth-server">Auth server</h2> <p>Set up the auth server hostowner&rsquo;s name and password; <code>authid</code> and <code>authdom</code> have to match the values in <code>plan9.ini</code>:</p> <pre tabindex="0"><code>; auth/wrkey bad nvram des key bad authentication id bad authentication domain authid: glenda authdom: someauth secstore key: password: &lt;your_pass&gt; </code></pre><p>Add <code>glenda</code>&rsquo;s password. This <em>has</em> to be the same as the one you typed in <code>wrkey(8)</code>. Always run <code>keyfs(4)</code> before modifying keys:</p> <pre tabindex="0"><code>; auth/keyfs ; auth/changeuser glenda Password: &lt;your_pass&gt; Confirm password: &lt;your_pass&gt; assign new Inferno/POP secret? [y/n]: y make it the same as Plan 9 password? [y/n]: y Expiration date (YYYYMMDD or never)[never]: Post id: User&#39;s full name: Department #: User&#39;s email address: user glenda installed for Plan 9 ; auth/enable glenda </code></pre><h2 id="network-database">Network database</h2> <p>Add the following lines to <code>/lib/ndb/local</code> (pay close attention to detail, especially <code>ip=</code>):</p> <pre tabindex="0"><code>; cat &gt;&gt; /lib/ndb/local authdom=someauth auth=192.168.1.152 ipnet=iphome ip=192.168.1.0 ipmask=255.255.255.0 ipgw=192.168.1.3 auth=192.168.1.152 authdom=someauth fs=192.168.1.152 cpu=192.168.1.152 dns=192.168.1.1 ^D </code></pre><p><code>ipnet</code> can be anything.</p> <p>Sync the changes and reboot the system:</p> <pre tabindex="0"><code>; echo sync &gt;&gt; /srv/hjfs.cmd ; fshalt -r </code></pre><h2 id="on-reboot">On reboot</h2> <p>Enable networking:</p> <pre tabindex="0"><code>; bind -a &#39;#l0&#39; /net ; ip/ipconfig ; ip/ipconfig ether /net/ether0 </code></pre><p>Drawterm as <code>glenda</code>:</p> <pre tabindex="0"><code>$ drawterm -u glenda cpu[cpu]: 192.168.1.152 auth[192.168.1.152]: glenda@someauth dp9ik password: &lt;your_pass&gt; </code></pre><p>Start a <code>rio(1)</code> instance:</p> <pre tabindex="0"><code>cpu% rio -s -i riostart </code></pre><p><code>/cfg/$sysname/cpustart</code> contains commands that run on boot. You can put whatever you want in it &mdash; I&rsquo;m going to just add the same commands I ran after we rebooted to make my life easier.</p> <pre tabindex="0"><code>; mkdir /cfg/$sysname ; cat &gt; /cfg/$sysname/cpustart bind -a &#39;#l0&#39; /net ip/ipconfig ip/ipconfig ether /net/ether0 cat /net/ndb ^D </code></pre><h2 id="new-user">New user</h2> <p>Add the new user to the file server, add it to some standard groups and assign it a password:</p> <pre tabindex="0"><code>; echo newuser christos &gt;&gt; /srv/hjfs.cmd ; echo newuser sys +christos &gt;&gt; /srv/hjfs.cmd ; echo newuser adm +christos &gt;&gt; /srv/hjfs.cmd ; echo newuser upas +christos &gt;&gt; /srv/hjfs.cmd ; echo newuser cron +christos &gt;&gt; /srv/hjfs.cmd ; auth/keyfs ; auth/changeuser christos ... ; auth/enable christos </code></pre><p>Let the new user &ldquo;speak for&rdquo; for other users by adding the following lines to <code>/lib/ndb/auth</code>:</p> <pre tabindex="0"><code>; cat &gt;&gt; /lib/ndb/auth hostid=christos uid=!sys uid=!adm uid=* ^D </code></pre><p>Reboot for the changes to take effect:</p> <pre tabindex="0"><code>; fshalt -r </code></pre><p>Drawterm as <code>christos</code> and run <code>newuser(8)</code> to set up his home directory:</p> <pre tabindex="0"><code>$ drawterm -u christos ... ; /sys/lib/newuser </code></pre> Acme editor notes http://margiolis.net/w/acme_notes/ Sun, 06 Mar 2022 00:00:00 +1200 <p><img src="http://margiolis.net/files/acme.jpg" alt=""></p> <h2 id="resources">Resources</h2> <ul> <li><a href="https://acme.cat-v.org/">The Acme User Interface for Programmers</a></li> <li><a href="https://www.youtube.com/watch?v=dP1xVpMPn8M">A Tour of the Acme Editor</a> - Watch this if you&rsquo;re completely new.</li> <li><a href="https://www.youtube.com/watch?v=dopu3ZtdCsg">Plan9 Acme Intro - Part 1</a></li> <li><a href="https://benghancock.github.io/blog/2022/tao-of-acme.html">The Tao of Acme</a></li> <li><a href="https://mostlymaths.net/2013/03/extensibility-programming-acme-text-editor.html/">Extensibility in the Acme text editor</a></li> </ul> <h2 id="mouse">Mouse</h2> <p>Acme needs a 3-button mouse to work properly. All buttons can be used to select text, but their function is different upon release. The left button is used for just selecting text. The middle button is used for executing text. The right button is for searching text and loading files or directories. Text doesn&rsquo;t have to first be selected when using button 2 and 3 (middle and right) if the selection is only one word.</p> <p>Acme also makes use of mouse chording (e.g pressing more than one button).</p> <table border="solid"> <tr> <td>Button 1-2</td> <td>Cut.</td> </tr> <tr> <td>Button 1-3</td> <td>Paste.</td> </tr> <tr> <td>Select text + Button 2-1</td> <td>Execute with selection as argument.</td> </tr> </table> <h2 id="move-around">Move around</h2> <table border="solid"> <tr> <td><code>:2</code></td> <td>Go to 2nd line.</td> </tr> <tr> <td><code>:0</code></td> <td>Go to beginning of file.</td> </tr> <tr> <td><code>:$</code></td> <td>Go to end of file.</td> </tr> <tr> <td><code>CTRL-A</code></td> <td>Go to beginning of line.</td> </tr> <tr> <td><code>CTRL-E</code></td> <td>Go to end of line.</td> </tr> <tr> <td><code>CTRL-F</code></td> <td>Filepath autocompletion.</td> </tr> <tr> <td><code>Edit =</code></td> <td>Find current line number.</td> </tr> </table> <h2 id="search-and-select">Search and select</h2> <table border="solid"> <tr> <td><code>:,</code> <br> <code>Edit ,</code></td> <td>Select all lines.</td> </tr> <tr> <td><code>:1,5</code> <br> <code>Edit 1,5</code></td> <td>Select lines 1 to 5.</td> </tr> <tr> <td><code>:/regexp/</code></td> <td>Select lines matching regexp.</td> </tr> <tr> <td><code>:/regexp1/,/regexp2/</code></td> <td>Select lines between regexp1 and regexp2.</td> </tr> <tr> <td> <code>Edit +/foo</code><br> <code>:/foo</code><br> <code>:foo</code><br> Right click on word. </td> <td>Forward search.</td> </tr> <tr> <td> <code>Edit -/foo</code><br> <code>:-/foo</code><br> <code>:-foo</code><br> </td> <td>Backward search.</td> </tr> </table> <h2 id="edit-text">Edit text</h2> <table border="solid"> <tr> <td><code>CTRL-U</code></td> <td>Delete from cursor to beginning of line.</td> </tr> <tr> <td><code>CTRL-W</code></td> <td>Delete before cursor.</td> </tr> <tr> <td><code>CTRL-H</code></td> <td>Delete character before cursor.</td> </tr> <tr> <td><code>Edit , d</code></td> <td>Clear window.</td> </tr> <tr> <td> <code>Edit , s/foo/bar/g</code><br> <code>Edit , | sed 's/foo/bar/g' </td> <td>Global replace.</td> </tr> <tr> <td><code>Edit s/foo/bar/g</code></td> <td>Replace in selection.</td> </tr> <tr> <td><code>Edit 2 d</code></td> <td>Delete 2nd line.</td> </tr> <tr> <td><code>Edit 2 c/foo</code></td> <td>Change 2nd line.</td> </tr> <tr> <td><code>Edit 2 a/foo</code></td> <td>Append after 2nd line.</td> </tr> <tr> <td><code>Edit 2 i/foo</code></td> <td>Insert before 2nd line.</td> </tr> </table> <h2 id="work-with-external-commands">Work with external commands</h2> <table border="solid"> <tr> <td><code>cmd</code></td> <td>Run cmd with the file as argument.</td> </tr> <tr> <td><code>|cmd</code></td> <td>Pipe selection through cmd and also apply changes.</td> </tr> <tr> <td><code>&gt;cmd</code></td> <td>Send selection to cmd and see result in a temporary window.</td> </tr> <tr> <td><code>&lt;cmd</code></td> <td>Paste output of cmd in current window (no selection required).</td> </tr> </table> <p>Any command can be used, these are just a few examples.</p> <table border="solid"> <tr> <td><code>Edit , &lt; echo foo</code></td> <td>Replace window body with some text (works with any command).</td> </tr> <tr> <td><code>echo foo | 9p write acme/$winid/body</code></td> <td>Append to end of current window body.</td> </tr> <tr> <td><code>Edit , &gt; wc -l</code></td> <td>Count lines.</td> </tr> <tr> <td><code>Edit , | sort</code></td> <td>Sort lines.</td> </tr> <tr> <td><code>Edit ,x/regexp/ &lt; date</code></td> <td>Replace regexp with the output of date(1).</td> </tr> <tr> <td>Select text + <code>| sed '' &gt; foo.txt</code></td> <td>Cut to foo.txt.</td> </tr> <tr> <td>Select text + <code>&gt; sed '' &gt; foo.txt</code></td> <td>Copy to foo.txt.</td> </tr> </table> <h2 id="files">Files</h2> <p>As mentioned earlier, files are opened using the right mouse click.</p> <table border="solid"> <tr> <td><code>foo.c</code></td> <td>Open file.</td> </tr> <tr> <td><code>foo.c:3</code></td> <td>Open file to line 3.</td> </tr> <tr> <td><code>foo.c:3:9</code></td> <td>Open file to line 3 column 9.</td> </tr> <tr> <td><code>foo.c:/^func</code></td> <td>Open file to the line starting with "func".</td> </tr> <tr> <td><code>foo.c:/bar/,/baz/</code></td> <td>Open file with a selection from "bar" to "baz".</td> </tr> </table> <h2 id="useful-acme-commands">Useful Acme commands</h2> <table border="solid"> <tr> <td><code>win</code></td> <td>Spawn shell as a window.</td> </tr> <tr> <td><code>web URL</code></td> <td>Open URL in browser.</td> </tr> <tr> <td><code>Dump</code></td> <td>Save current state.</td> </tr> <tr> <td><code>Load</code></td> <td>Load dump.</td> </tr> <tr> <td><code>Tab 8</code></td> <td>Set tab width to 8.</td> </tr> </table> <h2 id="variables">Variables</h2> <table border="solid"> <tr> <td><code>$%</code><br><code>$samfile</code></td> <td>Current file name.</td> </tr> <tr> <td><code>$winid</code></td> <td>Current window.</td> </tr> </table> FreeBSD sound mixer improvements http://margiolis.net/w/mixer_improvements/ Fri, 25 Feb 2022 00:00:00 +1200 <p>This project was part of Google Summer of Code 2021, but development is still active. The development report can be found on the <a href="https://wiki.freebsd.org/SummerOfCode2021Projects/SoundMixerImprovements">FreeBSD Wiki</a>. The reason behind this project is that the FreeBSD&rsquo;s OSS mixer capabilities were really basic and outdated &mdash; even un/muting didn&rsquo;t exist and one had to write custom scripts for such a basic task. Setting default audio devices had to be done by tweaking sysctls and programs needing to use the mixer required DIY implementations as there was no mixer library available. The project was merged to upstream on FreeBSD 14.0.</p> <h2 id="table-of-contents">Table of contents</h2> <ul> <li><a href="#kernel-patches">Kernel patches</a> <ul> <li><a href="#un-muting">Un/muting</a></li> <li><a href="#mode-configuration">Playback/recording mode information</a></li> </ul> </li> <li><a href="#userland">Userland</a> <ul> <li><a href="#libmixer-implementation">mixer(3) implementation</a></li> <li><a href="#mixer-rewrite">mixer(8) rewrite</a></li> </ul> </li> <li><a href="#code-and-manuals">Code and manuals</a></li> </ul> <h2 id="kernel-patches">Kernel patches</h2> <h3 id="un-muting">Un/muting (<a href="https://cgit.freebsd.org/src/commit/?id=0f8dafb45859569aa36b63ca2bb4a1c35c970d1e">commit</a>)</h3> <p>I decided that un/muting is better to be implemented in sound(4) in order to avoid having to write daemons or use files. The way this works is by implementing the <code>SOUND_MIXER_READ_MUTE</code> and <code>SOUND_MIXER_WRITE_MUTE</code> ioctls, which <em>did</em> exist in older OSS implementations, but were considered obselete. One thing to note is that the functionality isn&rsquo;t the same as their old one. Older OSS versions had those 2 ioctls take/return an integer with a value of 0 or 1, which indicated whether the <em>whole</em> mixer is muted or not. My implementation takes/returns a bitmask that tells which devices are muted. This allows us to mute and unmute only the devices we want, instead of the whole mixer. If you&rsquo;re familiar with the <a href="http://manuals.opensound.com/developer/">OSS API</a>, this bitmask works the same way as <code>DEVMASK</code>, <code>RECMASK</code> and <code>RECSRC</code>.</p> <h3 id="mode-configuration">Playback/recording mode information (<a href="https://cgit.freebsd.org/src/commit/?id=ed2196e5df0c8b5b81563d2fffdcb32bb7ebe966">commit</a>)</h3> <p>Here I implemented a sysctl (<code>dev.pcm.&lt;N&gt;.mode</code>) which gives information about a device&rsquo;s playback/recording mode. The rationale for this control is to include <code>/dev/sndstat</code>&rsquo;s mixer information in the output of the new mixer(8). The sysctl can return the following values (NOTE: these values are OR&rsquo;ed together if more than one mode is supported):</p> <table border="solid"> <tr> <th>Value</th> <th>Meaning</th> </tr> <tr> <td>0x01</td> <td>Mixer</td> </tr> <tr> <td>0x02</td> <td>Playback device</td> </tr> <tr> <td>0x04</td> <td>Recording device</td> </tr> </table> <h2 id="userland">Userland</h2> <h3 id="libmixer-implementation">mixer(3) implementation (<a href="https://cgit.freebsd.org/src/commit/?id=903873ce15600fc02a0ea42cbf888cff232b411d">commit</a>)</h3> <p>mixer(3) provides a simple interface for working with the OSS mixer. <a href="https://man.freebsd.org/cgi/man.cgi?query=mixer&amp;apropos=0&amp;sektion=3&amp;manpath=FreeBSD+15.0-CURRENT&amp;arch=default&amp;format=html">The man page</a> explains how the library works, including some examples, so there&rsquo;s no need to repeat myself. You can see the library in action in <a href="https://cgit.freebsd.org/src/tree/usr.sbin/mixer/mixer.c">the source code for mixer(8)</a>.</p> <p>The basic structure of a program looks like this (link with <code>-lmixer</code>):</p> <pre tabindex="0"><code>#include &lt;err.h&gt; #include &lt;mixer.h&gt; int main(int argc, char *argv[]) { struct mixer *m; const char *name = &#34;/dev/mixer0&#34;; if ((m = mixer_open(name)) == NULL) err(1, &#34;mixer_open(%s)&#34;, name); /* do stuff */ mixer_close(m); return (0); } </code></pre><h3 id="mixer-rewrite">mixer(8) rewrite (<a href="https://cgit.freebsd.org/src/commit/?id=903873ce15600fc02a0ea42cbf888cff232b411d">commit</a>)</h3> <p>This implementation is a complete rewrite of the old mixer(8) utility. It now uses mixer(3) as a backend and implements all the new features the library provides. It&rsquo;s got more command line options and works with a control-oriented interface inspired by <a href="https://man.openbsd.org/mixerctl">OpenBSD&rsquo;s mixerctl(8)</a>. Again, everything is detailed in <a href="https://man.freebsd.org/cgi/man.cgi?query=mixer&amp;apropos=0&amp;sektion=8&amp;manpath=FreeBSD+15.0-CURRENT&amp;arch=default&amp;format=html">the man page</a>.</p> <p>Old mixer(8) output:</p> <pre tabindex="0"><code>$ mixer.old Mixer vol is currently set to 85:85 Mixer pcm is currently set to 100:100 Mixer speaker is currently set to 74:74 Mixer line is currently set to 1:1 Mixer mic is currently set to 67:67 Mixer mix is currently set to 74:74 Mixer rec is currently set to 37:37 Mixer igain is currently set to 0:0 Mixer ogain is currently set to 100:100 Mixer monitor is currently set to 67:67 Recording source: mic </code></pre><p>New mixer(8) output:</p> <pre tabindex="0"><code>$ mixer pcm0:mixer: &lt;Realtek ALC662 rev3 (Analog 2.0+HP/2.0)&gt; on hdaa0 kld snd_hda (play/rec) (default) vol = 0.85:0.85 pbk pcm = 1.00:1.00 pbk speaker = 0.74:0.74 rec line = 0.01:0.01 rec mic = 0.67:0.67 rec src mix = 0.74:0.74 rec rec = 0.37:0.37 pbk igain = 0.00:0.00 pbk ogain = 1.00:1.00 pbk monitor = 0.67:0.67 rec </code></pre><h2 id="code-and-manuals">Code and manuals</h2> <ul> <li><a href="https://man.freebsd.org/cgi/man.cgi?query=mixer&amp;apropos=0&amp;sektion=3&amp;manpath=FreeBSD+15.0-CURRENT&amp;arch=default&amp;format=html">mixer(3) man page</a></li> <li><a href="https://cgit.freebsd.org/src/tree/lib/libmixer">mixer(3) source code</a></li> <li><a href="https://man.freebsd.org/cgi/man.cgi?query=mixer&amp;apropos=0&amp;sektion=8&amp;manpath=FreeBSD+15.0-CURRENT&amp;arch=default&amp;format=html">mixer(8) man page</a></li> <li><a href="https://cgit.freebsd.org/src/tree/usr.sbin/mixer/">mixer(8) source code</a></li> <li><a href="http://manuals.opensound.com/developer/">OSS 4.x Programmer&rsquo;s Guide</a></li> </ul> Thermometer using PIC16F877A and BME280 http://margiolis.net/w/pic_therm/ Mon, 14 Feb 2022 00:00:00 +1200 <p>This embedded system measures temperature and humidity and outputs the data on an LCD. The BME280 sensor can also do pressure but I didn&rsquo;t implement it because the MCU had no more space available. <a href="https://git.sr.ht/~crm/pic_therm">The whole project can be found here</a>; I&rsquo;ve also included datasheets for the MCU, sensor and LCD.</p> <p>Development was done on FreeBSD using <a href="http://margiolis.net/w/pic_freebsd">the workflow I describe here</a>. If you&rsquo;re using Microchip&rsquo;s own tools, you&rsquo;ll have to make some small changes to the code, but nothing too extreme.</p> <p><a href="https://git.sr.ht/~crm/pic_therm/tree/master/item/src">The source code</a>.</p> <p><img src="http://margiolis.net/files/pic_therm_main.jpg" alt=""></p> <h2 id="components">Components</h2> <ul> <li>Microchip PIC16F877A-I/P microcontroller.</li> <li>Adafruit BME280 temperature, humidity and pressure sensor.</li> <li>16x2 LCD.</li> <li>1x 16MHz crystal oscillator.</li> <li>2x 10kΩ resistor.</li> <li>2x 300Ω resistor.</li> <li>1x 10kΩ potentiometer.</li> <li>2x 22pF ceramic capacitor.</li> <li>2x LED.</li> <li>2x push-button.</li> <li>Breadboard and wires.</li> <li>3x AAA batteries (4.5V total) or a 9V battery with a 5V voltage divider.</li> </ul> <h2 id="safe-temperature-range">Safe temperature range</h2> <table border="solid"> <tr> <th>Component</th> <th>Operating temperature</th> </tr> <tr> <td>PIC16F877A</td> <td>-40°C - 85°C</td> </tr> <tr> <td>BME280</td> <td>-40°C - 85°C</td> </tr> <tr> <td>LCD</td> <td>-20°C - 70°C</td> </tr> </table> <p>So, it&rsquo;s best for the system to operate in the -20°C to 70°C range.</p> <h2 id="schematic">Schematic</h2> <p>You can also get the <a href="https://git.sr.ht/~crm/pic_therm/tree/master/item/schem/pic.pdf">PDF version</a>.</p> <p><img src="http://margiolis.net/files/pic_therm_schem.png" alt=""></p> PIC microcontroller development on FreeBSD http://margiolis.net/w/pic_freebsd/ Sun, 23 Jan 2022 00:00:00 +1200 <p>Tested on FreeBSD 13.0. This article has also been mirrored on the <a href="https://wiki.freebsd.org/Microcontrollers/PIC">FreeBSD Wiki</a>.</p> <h2 id="prerequisites">Prerequisites</h2> <p><a href="http://sdcc.sourceforge.net/">sdcc</a> is a C compiler for microprocessors. It says that PIC microprocessors are unmaintained, but I&rsquo;ve found it to be pretty reliable so far (take this with a grain of salt, I&rsquo;m no expert). The port can be found under:</p> <pre tabindex="0"><code>lang/sdcc </code></pre><p>Page 75 of the <a href="http://sdcc.sourceforge.net/doc/sdccman.pdf">sdcc user manual</a> lists the supported PIC devices. Header files can be found under <code>/usr/local/share/sdcc</code>.</p> <p>For programming the MCU, I&rsquo;ve found pk2cmd to work alright with PICKit2 (or Chinese clones), but there&rsquo;s no port for FreeBSD anymore. The Makefile won&rsquo;t install files properly, so we have some extra work to do afterwards:</p> <pre tabindex="0"><code>$ git clone https://github.com/psmay/pk2cmd.git $ cd pk2cmd/pk2cmd # gmake freebsd install clean # mv /usr/share/pk2/PK2DeviceFile.dat /usr/local/bin # rm -rf /usr/share/pk2 </code></pre><p>Supported devices for pk2cmd are listed <a href="https://github.com/psmay/pk2cmd/blob/master/pk2cmd/ReadmeForPK2CMDLinux2-6.txt">here</a>.</p> <h2 id="detecting-and-programming-the-mcu">Detecting and programming the MCU</h2> <p>Avoid using just the -P option to auto-detect the MCU, as the VPP the PICKit2 applies to the chip trying to detect it can damage the MCU. Instead, use the chip number beforehand as shown below. Also, use the -C option to check if the chip is blank.</p> <p>If any of the following pk2cmd commands fail, make sure everything <em>really is</em> wired properly:</p> <pre tabindex="0"><code>$ pk2cmd -P PIC16F877A -C Device is blank Operation Succeeded </code></pre><p>Compile your source code. The target executable is the <code>.hex</code> file sdcc will output. Replace <code>pic14</code> and <code>16f877a</code> with the appropriate names for your device:</p> <pre tabindex="0"><code>$ sdcc --use-non-free -mpic14 -p16f877a main.c </code></pre><p>Erase the PIC (if it wasn&rsquo;t already blank) and flash the new code. Again, use the appropriate names:</p> <pre tabindex="0"><code>$ pk2cmd -P PIC16F877A -E $ pk2cmd -P PIC16F877A -X -M -F main.hex </code></pre><p>If all went well, you should get an output similar to this:</p> <pre tabindex="0"><code>PICkit 2 Program Report 23-1-2022, 21:01:29 Device Type: PIC16F877A Program Succeeded. Operation Succeeded </code></pre> Email-driven Git workflow http://margiolis.net/w/email_git/ Sun, 16 Jan 2022 00:00:00 +1200 <p>Basic Git configuration (in case you haven&rsquo;t already done that):</p> <pre tabindex="0"><code>$ git config --global user.email you@example.org $ git config --global user.name &#34;Your Name&#34; </code></pre><p>Git email configuration:</p> <pre tabindex="0"><code>$ git config --global sendemail.smtpserver your_mail_server $ git config --global sendemail.smtpuser you@example.org $ git config --global sendemail.smtpserverport your_smtp_port $ git config --global sendemail.smtpencryption your_encryption_type </code></pre><h2 id="examples">Examples</h2> <p>Apply a patch (or simply make a commit) and send it to a mailing list:</p> <pre tabindex="0"><code>$ git am &lt; some_patch $ git send-email --to=list@example.org HEAD^ </code></pre><p>Fix last commit and send it:</p> <pre tabindex="0"><code>$ git commit -a --amend $ git send-email --annotate -v2 HEAD^ </code></pre><p>Send 3 last commits (see <a href="https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection">Revision Selection</a> for more info on the notation):</p> <pre tabindex="0"><code>$ git send-email HEAD~3 </code></pre><p>Send the last commit to list@example.org and make the subject look like &ldquo;<code>[reponame][PATCH] commitmsg</code>&rdquo;. This is useful for sending patches to mailing lists or programmers with multiple projects:</p> <pre tabindex="0"><code>$ git send-email --subject-prefix=&#34;${PWD##*/}][PATCH&#34; \ --to=list@example.org -1 </code></pre><p><a href="https://linux.die.net/man/1/git-send-email">git-send-email(1)</a> manual page. Simple as that. None of that fork &amp; pull request crap.</p> <h2 id="further-reading">Further reading</h2> <ol> <li><a href="https://begriffs.com/posts/2018-06-05-mailing-list-vs-github.html">Mailing lists vs GitHub</a></li> <li><a href="https://web.archive.org/web/20180522180815/https://dpc.pw/blog/2017/08/youre-using-git-wrong/">You&rsquo;re using Git wrong</a></li> <li><a href="https://drewdevault.com/2018/07/02/Email-driven-git.html">The advantages of an email-driven Git workflow</a></li> </ol> How to jump start your car http://margiolis.net/w/jumpstart/ Wed, 12 Jan 2022 00:00:00 +1200 <p>There are tons of articles and videos out there already, but all of them are too long.</p> <p>Requirements: jumper cables and a live car. Order matters.</p> <p><img src="http://margiolis.net/files/carpush.jpg" alt=""></p> <ol> <li>Turn off the live car&rsquo;s engine and remove the key. Make sure the emergency brake is on and the transmission in neutral. Also make sure that your cables are always apart and away from your body.</li> <li>Dead car: connect the positive (red) end of the cable to the battery&rsquo;s positive terminal. The terminals are distinguished from their colors (red for positive and black for negative) and/or their sign (+ for positive, - for negative).</li> <li>Live car: connect the other positive end of the cable to the battery&rsquo;s positive terminal</li> <li>Live car: connect the negative (black) end of the cable to the battery&rsquo;s negative terminal.</li> <li>Dead car: connect the other negative end of the cable to a ground, for example a bolt on your hood.</li> <li>Keep the cables away from hot components, such as the radiator or the engine.</li> <li>Start the live car and slightly rev up the engine every now and then. Do this for a few minutes.</li> <li>Dead (now alive) car: disconnect the ground.</li> <li>Live car: disconnect the negative terminal.</li> <li>Live car: disconnect the positive terminal.</li> <li>Dead car: disconnect the positive terminal.</li> </ol> <p><a href="https://youtu.be/28MexgFvq7E?t=7">DO NOT confuse the terminals</a> or you will destroy your car&rsquo;s computer system.</p> C coding style http://margiolis.net/w/cstyle/ Sat, 01 Jan 2022 00:00:00 +1200 <p>First of all, I wanna wish all 0 readers of this blog a happy new year! There&rsquo;s no reason to write a whole article repeating what better articles have already covered, so here&rsquo;s a list with proper C coding style guides:</p> <ul> <li><a href="https://www.lysator.liu.se/c/pikestyle.html">Notes on Programming in C</a> by Rob Pike.</li> <li><a href="https://man.openbsd.org/style">OpenBSD style guide</a></li> <li><a href="https://suckless.org/coding_style/">suckless.org style guide</a></li> <li><a href="https://www.kernel.org/doc/Documentation/process/coding-style.rst">Linux kernel coding style</a></li> </ul> <p><img src="http://margiolis.net/files/dmr.jpg" alt=""></p> 9front using bhyve(8) on FreeBSD http://margiolis.net/w/9front_bhyve/ Tue, 23 Nov 2021 00:00:00 +1200 <p>This article is also mirrored on the <a href="https://wiki.freebsd.org/bhyve/9front">FreeBSD Wiki</a>. and <a href="http://wiki.9front.org/freebsd-bhyve">9front Wiki</a>.</p> <p>Required ports:</p> <pre tabindex="0"><code>sysutils/bhyve-firmware sysutils/uefi-edk2-bhyve sysutils/uefi-edk2-bhyve-csm net/tigervnc-viewer </code></pre><p>Add the following lines to <code>/etc/rc.conf</code>. Replace <code>re0</code> with your own network interface. It&rsquo;s good practice to assign each VM a unique <code>tap</code> interface in case you need to run multiple VMs at the same time. For simplicity&rsquo;s sake, this setup uses only one <code>tap</code>:</p> <pre tabindex="0"><code>if_bridge_load=&#34;YES&#34; if_tap_load=&#34;YES&#34; cloned_interfaces=&#34;bridge0 tap0&#34; ifconfig_bridge0=&#34;DHCP addm re0 addm tap0&#34; ifconfig_bridge0_alias0=&#34;inet 10.0.0.1/24&#34; </code></pre><p>Reboot your machine and then grab a <a href="http://9front.org/releases/">9front ISO</a>.</p> <p>Make a directory where you&rsquo;ll store everything 9front-related. I usually keep all my <a href="https://www.freebsd.org/cgi/man.cgi?query=bhyve&amp;sektion=8">bhyve(8)</a> VMs under a <a href="https://docs.freebsd.org/en/books/handbook/zfs/">ZFS dataset</a>:</p> <pre tabindex="0"><code>$ cd /path/to/vms/ $ mkdir 9front $ mv /path/to/9front_iso 9front.iso </code></pre><p>Create an empty file to be used as the VM&rsquo;s hard drive. 10G will be more than enough:</p> <pre tabindex="0"><code>$ truncate -s 10G disk.img </code></pre><p>Make a startup script. Feel free to tweak the variable values to match your own setup. Obviously, when you&rsquo;re done installing 9front from the ISO, you&rsquo;ll be running the script without the <code>-s 3,...</code> line:</p> <pre tabindex="0"><code>$ cat 9front_start #!/bin/sh name=&#34;9front&#34; cpu=&#34;2&#34; mem=&#34;2G&#34; iso=&#34;9front.iso&#34; disk=&#34;disk.img&#34; tap=&#34;tap0&#34; ifconfig ${tap} up bhyve -c ${cpu} -m ${mem} -wH \ -s 0,hostbridge \ -s 3,ahci-cd,${iso} \ -s 4,ahci-hd,${disk} \ -s 5,virtio-net,${tap} \ -s 29,fbuf,tcp=0.0.0.0:5900,w=800,h=600,wait \ -s 30,xhci,tablet \ -s 31,lpc \ -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \ ${name} </code></pre><p>Make a shutdown script in order for bhyve(8) to shutdown properly:</p> <pre tabindex="0"><code>$ cat 9front_stop #!/bin/sh name=&#34;9front&#34; tap=&#34;tap0&#34; ifconfig ${tap} down bhyvectl --force-poweroff --vm=${name} bhyvectl --destroy --vm=${name} </code></pre><p>Make the scripts executable and start the VM:</p> <pre tabindex="0"><code>$ chmod +x 9front_start 9front_stop # ./9front_start; ./9front_stop </code></pre><p>Run vncviewer(1) to connect to the VNC display:</p> <pre tabindex="0"><code>$ vncviewer 0.0.0.0 </code></pre><p>When prompted for the monitor type during boot, choose <code>xga</code>.</p> Git Web frontend with stagit(1) http://margiolis.net/w/stagit_frontend/ Fri, 05 Nov 2021 00:00:00 +1200 <p><code>stagit(1)</code> reads Git repositories and generates static pages for them with a predefined CSS stylesheet (I said <em>stylesheet</em> twice!), a website logo and a favicon.</p> <p>Required:</p> <ul> <li><a href="http://margiolis.net/w/openbsd_web">A Web server</a></li> <li><a href="http://margiolis.net/w/openbsd_git">A Git Server</a></li> </ul> <p>Install <code>stagit(1)</code>:</p> <pre tabindex="0"><code>$ git clone git://git.codemadness.org/stagit $ cd stagit # doas make install clean </code></pre><p>I like to have everything <code>stagit(1)</code>-related inside <code>/var/www/htdocs/git.example.org</code> along with the actual repositories, but you can choose any other directory.</p> <p>Store the stylesheet, logo and favicon inside. You&rsquo;ll find the stylesheet inside the <code>stagit(1)</code> repository you cloned earlier; it&rsquo;s called <code>style.css</code>. For the logo and favicon, just make your own, but make sure they are in PNG format and have the same names as below.</p> <pre tabindex="0"><code># cp logo.png favicon.png style.css /var/www/htdocs/git.example.org </code></pre><p>You can edit the stylesheet to your likings, but if you&rsquo;re afraid that you&rsquo;ll mess things up, play around with the colors only.</p> <p><a href="http://margiolis.net/w/openbsd_git">Here</a> I created a repository named <code>repo</code> in <code>/var/www/htdocs/git.example.org/repo.git</code>. I will now make a directory to store the files that <code>stagit(1)</code> will produce. I like to give it the same name as the repository but without the <code>.git</code> extension:</p> <pre tabindex="0"><code># mkdir /var/www/htdocs/git.example.org/repo </code></pre><p>Create symlinks for the stylesheet, logo and favicon inside the directory you just created:</p> <pre tabindex="0"><code>$ cd /var/www/htdocs/git.example.org/repo # ln -s ../style.css ../logo.png ../favicon.png . </code></pre><p>Run <code>stagit(1)</code> and generate the static page for your repository. This has to be done inside the directory you just created since <code>stagit(1)</code> stores the files in the working directory. Make sure that you give it the <code>.git</code> directory (i.e the actual repository):</p> <pre tabindex="0"><code># stagit ../repo.git </code></pre><p>Go to the top-level Git directory and generate an <code>index.html</code> file which is your Git server&rsquo;s homepage where all repositories will be listed. Again, you give it the actual repositories:</p> <pre tabindex="0"><code>$ cd /var/www/htdocs/git.example.org # stagit-index repo.git &gt; index.html </code></pre><p>I&rsquo;ve made a simple script to automate the generation process:</p> <pre tabindex="0"><code>#!/bin/sh cd /var/www/htdocs/git.example.org for dir in $(ls | grep &#34;git&#34;); do cd ${dir%.*} &amp;&amp; stagit ../${dir} &amp;&amp; cd .. &amp;&amp; echo &#34;updating ${dir}: ok&#34; done stagit-index $(ls | grep &#34;git&#34;) &gt; index.html &amp;&amp; echo &#34;creating index.html. ok&#34; </code></pre><p>Fire up a browser and go to <code>git.example.org</code> and you should see everything. Success!</p> OpenBSD Git server setup http://margiolis.net/w/openbsd_git/ Wed, 10 Feb 2021 00:00:00 +1200 <h2 id="git-subdomain">Git subdomain</h2> <p>It&rsquo;s good to have your Git server under a subdomain, so that it looks like <code>git.example.org</code>. To do that, add a new CNAME record for the subdomain on your registrar. If you&rsquo;re on Epik, don&rsquo;t forget the trailing period after the domain name.</p> <pre tabindex="0"><code>Host: git | Points to: example.org. </code></pre><h2 id="setting-up-the-git-server">Setting up the Git server</h2> <p>Install <code>git</code>:</p> <pre tabindex="0"><code># pkg_add git </code></pre><p>First create a <code>git</code> user and give it a password. If you don&rsquo;t want the same hosts that exist in your current user&rsquo;s <code>authorized_keys</code> to have access to the Git server, then add their keys manually in the <code>git</code> user&rsquo;s <code>.ssh/authorized_keys</code> file. In my case, it&rsquo;s just me who has access to the server, so I will not edit anything:</p> <pre tabindex="0"><code># adduser git ... # cp -r $HOME/.ssh /home/git/ # chown -R git:git /home/git/.ssh </code></pre><p>Try <code>ssh</code>ing to the server as <code>git</code> from your local computer just to test if it works:</p> <pre tabindex="0"><code>$ ssh git@example.org </code></pre><p>If it worked, exit the session.</p> <p>Set up a Git directory where all repositories will be stored at and <code>cd</code> into it:</p> <pre tabindex="0"><code># mkdir /var/www/htdocs/git.example.org # chown -R git:git /var/www/htdocs/git.example.org # cd /var/www/htdocs/git.example.org </code></pre><p>Say I want to create a repository named <code>repo</code>. The structure your repositories should have is <code>name.git</code>, so in this case it&rsquo;ll be <code>repo.git</code>. Create a bare repository inside it:</p> <pre tabindex="0"><code># mkdir repo.git # git init --bare repo.git </code></pre><p>Replace each text inside double quotes with what you want it to be:</p> <pre tabindex="0"><code># echo &#34;John Doe&#34; &gt; repo.git/.git/owner # echo &#34;This is a repo&#34; &gt; repo.git/.git/description # echo &#34;git://git.example.org/repo.git&#34; &gt; repo.git/.git/url </code></pre><p>To be able to interact with the Git server, the repository&rsquo;s owner needs to be the <code>git</code> user you created earlier:</p> <pre tabindex="0"><code># chown -R git:git repo.git </code></pre><p>Since sometimes the Git server by default might not be listening on port 9418 (the default Git port), which means that you&rsquo;ll not be able to clone repositories from the Git server, you have to set up the daemon yourself using a massive <code>git</code> command. You can (in fact, you should) also use Git over HTTPS or SSH, but for now I&rsquo;ll be using the default Git protocol for cloning and SSH for pushing.</p> <pre tabindex="0"><code># git daemon \ --base-path=/var/www/htdocs/git.example.org/ \ --export-all \ --enable=receive-pack \ --reuseaddr \ --informative-errors \ --verbose \ --detach </code></pre><h2 id="pushing-changes-to-the-server">Pushing changes to the server</h2> <p>On your local machine, make a repository for <code>repo</code> (the repository you created earlier) and add a few files so that you have something to commit and push to the server:</p> <pre tabindex="0"><code>$ mkdir repo &amp;&amp; cd repo $ touch foo bar $ git init $ git add . $ git commit -m &#34;initial commit&#34; </code></pre><p>In order to be able to push you have to edit <code>.git/config</code>. If you&rsquo;ve used GitHub before, you might have noticed that when you want to push, you push to <code>origin</code>; for this server I&rsquo;ll use <code>home</code>, but you can use whatever else you want, it doesn&rsquo;t really matter.</p> <p>Append the following block to your repository&rsquo;s <code>.git/config</code> and of course replace the domain and repository names with your own ones:</p> <pre tabindex="0"><code>[remote &#34;home&#34;] url = git@git.example.org:/var/www/htdocs/git.example.org/repo.git fetch = +refs/heads/*:refs/remotes/home/* </code></pre><p>Push to the Git server:</p> <pre tabindex="0"><code>$ git push home master </code></pre><p>Try also cloning the repository to make sure that everything works:</p> <pre tabindex="0"><code>$ git clone git://git.example.org/repo.git </code></pre><p>If that step was successful too, great, you now have a working Git server. :-)</p> <p><a href="http://margiolis.net/w/stagit_frontend">Create a Web frontend with stagit(1)</a>.</p> OpenBSD Web server setup http://margiolis.net/w/openbsd_web/ Tue, 09 Feb 2021 00:00:00 +1200 <h2 id="https">HTTPS</h2> <p><code>acme-client(1)</code> lets us have HTTPS.</p> <p>Create the certificate directories:</p> <pre tabindex="0"><code># mkdir -p -m 700 /etc/ssl/private # mkdir -p -m 755 /var/www/acme </code></pre><p>Edit <code>/etc/acme-client.conf</code>. Replace <code>example.org</code> with your domain.</p> <pre tabindex="0"><code>authority letsencrypt { api url &#34;https://acme-v02.api.letsencrypt.org/directory&#34; account key &#34;/etc/ssl/private/letsencrypt.key&#34; } domain example.org { domain key &#34;/etc/ssl/private/example.org.key&#34; domain certificate &#34;/etc/ssl/example.org.crt&#34; domain full chain certificate &#34;/etc/ssl/example.org.pem&#34; sign with letsencrypt } </code></pre><h2 id="httpd8">httpd(8)</h2> <p>OpenBSD already ships with a web server: <code>httpd</code>. You need to specify the following things:</p> <ul> <li>The domain name.</li> <li>Which port/s the server will listen to.</li> <li>Where the website&rsquo;s root directory is.</li> </ul> <p>Create the website&rsquo;s root directory. It should normally reside under <code>/var/www/htdocs</code>.</p> <pre tabindex="0"><code># mkdir -p /var/www/htdocs/example.org </code></pre><p>The following configuration will suffice for now. With it you have HTTP and HTTPS for your website and it redirects HTTP to HTTPS automatically. If you want to learn more or see what other options and settings are available, read <a href="https://man.openbsd.org/httpd.conf.5">httpd.conf(5)&rsquo;s man page</a>. Inside <code>/etc/httpd.conf</code> we&rsquo;ll write the following:</p> <pre tabindex="0"><code>server &#34;example.org&#34; { listen on * port 80 root &#34;/htdocs/example.org&#34; location &#34;/.well-known/acme-challenge/*&#34; { root &#34;/acme&#34; request strip 2 } block return 301 &#34;https://example.org$REQUEST_URI&#34; } server &#34;example.org&#34; { listen on * tls port 443 root &#34;/htdocs/example.org&#34; tls { certificate &#34;/etc/ssl/example.org.pem&#34; key &#34;/etc/ssl/private/example.org.key&#34; } location &#34;/.well-known/acme-challenge/*&#34; { root &#34;/acme&#34; request strip 2 } } </code></pre><p>Generate the TLS certificates:</p> <pre tabindex="0"><code># acme-client -v example.org </code></pre><p>Test to see if the configuration is correct:</p> <pre tabindex="0"><code># httpd -n </code></pre><p>(Re)start the web server:</p> <pre tabindex="0"><code># rcctl restart httpd </code></pre><h2 id="certificate-renewal">Certificate renewal</h2> <p>TLS certificates expire after a few months so new certificates need to be generated when they expire. In order to avoid having to remember this and having to manually generate them, a cronjob will do it automatically:</p> <pre tabindex="0"><code># crontab -e </code></pre><p>Append the following line:</p> <pre tabindex="0"><code>0 0 * * * acme-client -v example.org &amp;&amp; rcctl reload httpd </code></pre> Use cases for goto http://margiolis.net/w/goto/ Tue, 19 Jan 2021 00:00:00 +1200 <p>This article is a response to all my university professors who, for some reason, think <code>goto</code> is useless and should be avoided at all costs.</p> <h2 id="some-use-cases">Some use cases</h2> <p>The most important use case there is for <code>goto</code> is by far error handling when there are more than 1 points of failure. In this case, you might want to cleanup some resources while also skipping part of the code that should not be executed, without having to deal with flags, helper functions, and other methods that would make the code ugly, slower and error prone. Try rewriting the following snippet <em>without</em> a <code>goto</code>:</p> <pre tabindex="0"><code>int foo(int *bar, int *baz) { if (!func1()) goto fail; if (!func2()) goto fail; if (!func3()) goto fail; return 0; fail: warn(&#34;foo failed&#34;); if (bar != NULL) free(bar); if (baz != NULL) free(baz); return -1 } </code></pre><p>Another use case is breaking out of deeply nested code. Let&rsquo;s say you&rsquo;ve got 3 <code>for</code> loops and there&rsquo;s a special case in which you really want to break out of all the loops at once. how do you do that? There are multiple ways you can go about doing so but one way would be to set a flag and check it on every nested level.</p> <pre tabindex="0"><code>flag = 0; for (i = 0; i &lt; 10; i++) { for (j = 0; j &lt; 10; j++) { for (k = 0; k &lt; 10; k++) { ... if (flag) break; } if (flag) break; } if (flag) break; } </code></pre><p>Another ugly hack you can use is something another colleague from university showed me, and something I would <em>never</em> use; when the flag is set, manually max out all the loop counters.</p> <p>A pretty straight-forward solution would also be to put the loop into a function and use a <code>return</code> statement to break out of all the loops. That&rsquo;s actually a good solution, and I&rsquo;m aware of it, but I want to provide another solution, which is also quite faster than using a function since it avoids that additional function call.</p> <p>An alternative, and in my opinion, better way of solving this problem would be by using a (<em>don&rsquo;t say it, don&rsquo;t say it</em>) <code>goto</code>:</p> <pre tabindex="0"><code>flag = 0; for (i = 0; i &lt; 10; i++) { for (j = 0; j &lt; 10; j++) { for (k = 0; k &lt; 10; k++) { ... if (flag) goto end; } } } end: ... </code></pre><h2 id="who-cares-anyway">Who cares, anyway?</h2> <p>In the first use case, the code is much more readable and you avoid code duplication. In the second use case the <code>goto</code> solution actually <em>does</em> improve performance. The reason why is simple; we check for <code>flag</code> on every single loop, which means, that in case <code>flag</code> is never set, we&rsquo;ll have done 10 * 10 * 10 = 1000 checks just to see if <code>flag</code> is set. And that&rsquo;s just with 3 <code>for</code> loops going from 0 to 10 each; think how easily this can scale up if you just increase the iterations. The <code>goto</code> solution does only <em>one</em> check in the third loop, which means that, in the above scenario, where <code>flag</code> never gets set, we&rsquo;ll have done only 10 checks - that&rsquo;s 100 times faster than the other solution.</p> <p>Using a function is almost just as fast as using a <code>goto</code> without a function, but not having to call a function is generally faster. Both solutions are great and totally valid, I just want to show an alternative one.</p> <h2 id="final-note">Final note</h2> <p><code>goto</code> <em>does</em> have its place but it should be used carefuly; if you overuse it, your code will either become incomprehensible, or flat out broken. The use cases I showcased in this post are very common and sometimes the code can be vastly improved with just a simple <code>goto</code> if used correctly.</p> <p>Again, thanks to both my colleagues who helped me improve this article with their recommendations.</p> Sync files with rsync(1) http://margiolis.net/w/rsync/ Sun, 22 Nov 2020 00:00:00 +1200 <h2 id="connecting-local-machines">Connecting local machines</h2> <p>In order to connect two machines on the same network, they both need to know each other&rsquo;s network IP address. To get your network IP address execute the following command on each of the local machines you want to connect:</p> <pre tabindex="0"><code>$ ifconfig </code></pre><p>In your running network interface&rsquo;s section, <code>ifconfig</code> will output a line that looks like this:</p> <pre tabindex="0"><code>inet 192.168.x.y </code></pre><p>This is your network IP address which the other machine needs to know. Suppose you want to connect two local machines at home; we&rsquo;ll call the first one <code>host1</code> and the other <code>host2</code>. the ip address for <code>host1</code> is <code>192.168.1.7</code> and for <code>host2</code> it&rsquo;s <code>192.168.1.8</code>. add the following line on <code>host1</code>&rsquo;s <code>/etc/hosts</code> so that it knows who <code>host2</code> is. do the same for <code>host2</code>.</p> <pre tabindex="0"><code>192.168.1.8 host2 </code></pre><p>After completing this step ping each other using both the IP and the hostname to make sure everything works:</p> <pre tabindex="0"><code>$ ping 192.168.1.8 ping 192.168.1.8 (192.168.1.8): 56 data bytes 64 bytes from 192.168.1.8: icmp_seq=0 ttl=64 time=36.931 ms ... $ ping host2 ping host2 (192.168.1.8): 56 data bytes 64 bytes from 192.168.1.8: icmp_seq=0 ttl=64 time=74.056 ms ... </code></pre><p>If you don&rsquo;t already have an <code>ssh</code> key pair, generate one:</p> <pre tabindex="0"><code>$ ssh-keygen </code></pre><p>Copy the <code>ssh</code> public key over to the other machine:</p> <pre tabindex="0"><code>$ eval $(ssh-agent -s) $ ssh-add $ ssh-copy-id user@host2 </code></pre><p>Repeat the same process for <code>host2</code>. to verify that the key was indeed copied over, look at <code>~/.ssh/authorized_keys</code>.</p> <h2 id="using-rsync">Using rsync</h2> <p>You can think of <code>rsync</code> as <code>cp</code> with superpowers; in fact, you could even alias <code>cp</code> to <code>rsync</code>.</p> <p>To just copy a file on your machine you can do exactly the same thing you would do with <code>cp</code>:</p> <pre tabindex="0"><code>$ rsync foo bar/ </code></pre><p>Suppose we want to send that file to <code>host2</code> (read the previous section):</p> <pre tabindex="0"><code>$ rsync foo host2: </code></pre><p>By default, <code>rsync</code> assumes that the target user has the same name as yours. In case it&rsquo;s different, you need to specify it:</p> <p>$ rsync foo user@host2:</p> <p>The file will be copied in the other machine&rsquo;s home directory, if no other directory is specified. To send it to a specific path:</p> <pre tabindex="0"><code>$ rsync foo user@host2:foo/ </code></pre><p><code>rsync</code> has <em>a lot</em> of options, but some of the most common ones, and the ones I use most of the time are the following:</p> <dl> <dt>-r</dt> <dd>Copy directories recursively.</dd> <dt>-u</dt> <dd>Update only the files that have changed (incremental copy).</dd> <dt>-v</dt> <dd>Increase verbosity.</dd> <dt>-a</dt> <dd>Preserve permissions and metadata. does not preserve hard links.</dd> <dt>-p</dt> <dd>In case something goes wrong and the sync breaks, continue the sync from where it stopped.</dd> <dt>--delete-after</dt> <dd>Delete the old files after the transfer.</dd> </dl> <p><strong>Note</strong>: when you want to transfer a directory, you <em>must</em> add a <code>/</code> after its name (e.g <code>foo/</code>), otherwise <code>rsync</code> will skip it.</p> <h2 id="sample-usage">Sample usage</h2> <p>Suppose I want to update a website that is hosted on a remote server. I can make changes locally and just sync it with the server whenever I want using <code>rsync</code>. Let&rsquo;s say the remote directory is located at <code>/var/www/website</code>:</p> <pre tabindex="0"><code>$ rsync -purv --delete-after website/ user@server:/var/www/website </code></pre><p>You can automate this process using a simple shell script. In this case though, you have to pass the absolute paths of the directories so that you can run the script from everywhere:</p> <pre tabindex="0"><code>#!/bin/sh src=&#34;/foo/bar/website/&#34; dst=&#34;user@server:/var/www/website&#34; rsync -purv --delete-after ${src} ${dst} </code></pre> Arduino on FreeBSD http://margiolis.net/w/arduino_freebsd/ Wed, 28 Oct 2020 00:00:00 +1200 <p>This article demonstrates how to develop for Arduino boards using only basic command line utilities, without having to use the Arduino IDE. The article has also been published on the <a href="https://wiki.freebsd.org/Arduino/NativeCLI">FreeBSD Wiki</a>.</p> <p>Tested on FreeBSD 12.2 and above.</p> <h2 id="prerequisites">Prerequisites</h2> <p>Required ports:</p> <pre tabindex="0"><code>devel/arduino-core devel/arduino-bsd-mk devel/avr-gcc devel/avr-libc devel/avrdude comms/uarduno </code></pre><p>With all the software installed, add the following line to <code>/boot/loader.conf</code> in case you want the Arduino kernel module to load automatically on boot. If you want to manually load the module whenever you need it, skip this step:</p> <pre tabindex="0"><code>uarduno_load=&#34;YES&#34; </code></pre><p>Load the kernel module:</p> <pre tabindex="0"><code># kldload uarduno </code></pre><p>Check your <code>~/.arduino/preferences.txt</code> and see if the following lines exist (<a href="https://wiki.freebsd.org/Arduino/NativeIDE">source</a>):</p> <pre tabindex="0"><code>serial.port=/dev/cuaU0 launcher=/usr/local/bin/firefox </code></pre><p>Add your user to the <code>dialer</code> group:</p> <pre tabindex="0"><code># pw group mod dialer -m $USER </code></pre><h2 id="connecting-the-board">Connecting the board</h2> <p>Standard Arduino boards connect as <code>/dev/cuaU0</code> and/or <code>/dev/ttyU0</code> on FreeBSD. In case these serial ports don&rsquo;t show up in <code>/dev</code>, you might need to press your board&rsquo;s reset button. After you&rsquo;ve plugged your board into a USB port, you should get the following output from <code>dmesg</code>. Although the output may vary, the important thing is that your board is connected and detected.</p> <pre tabindex="0"><code>ugen1.5: &lt;Arduino (www.arduino.cc) product 0x0043&gt; at usbus1 uarduno0: &lt;Arduino (www.arduino.cc) product 0x0043, class 2/0, rev 1.10/0.01, addr 5&gt; on usbus1 </code></pre><p>If <code>dmesg</code> returned information about your board, you should also see <code>cuaU0</code> and/or <code>ttyU0</code> in <code>/dev</code>. In case your board is still not detected &mdash; considering it&rsquo;s not a fake one, try using a different USB cable or reset it again and make sure you&rsquo;ve followed the setup steps correctly.</p> <h2 id="the-makefile">The Makefile</h2> <p>The only thing you&rsquo;re going to need in order to get started is just a <code>Makefile</code> that&rsquo;ll be used to compile <em>and upload</em> your Arduino programs. Make a new directory for your Arduino project and a <code>Makefile</code> with the following lines:</p> <pre tabindex="0"><code>ARDUINO_DIR= /usr/local/arduino ARDUINO_MK_DIR= /usr/local/arduino-bsd-mk #ARDUINO_LIBS= AVRDUDE_PORT= your_board_port ARDUINO_BOARD= your_board_name SRCS= your_source_files TARGET= your_program_name include /usr/local/arduino-bsd-mk/bsd.arduino.mk </code></pre><p>In my case my board is an Arduino Uno, so I&rsquo;d have to set <code>ARDUINO_BOARD</code> to <code>uno</code>. You can see which other board types are available in <code>/usr/local/arduino/hardware/arduino/avr/boards.txt</code>. If you want to install new libraries, copy them over to <code>/usr/local/arduino/hardware/arduino/avr/libraries/</code>.</p> <p>Avoid having source files named <code>main</code>.</p> <h2 id="building-and-uploading-a-program">Building and uploading a program</h2> <p>Write some Arduino code, and when you&rsquo;re ready to compile and upload, run the following command:</p> <pre tabindex="0"><code># make install flash clean cleandepend </code></pre><p>If all went well you should see the board executing the new code. If it doesn&rsquo;t, try to see what errors the <code>Makefile</code> produced.</p> <h2 id="monitoring">Monitoring</h2> <p>The Arduino IDE provides a serial monitor feature, but FreeBSD has a builtin monitoring utility which can be accessed directly from the terminal. Run this whenever you want to monitor your board and exit with <code>~!</code> (use the appropriate port):</p> <pre tabindex="0"><code>$ cu -l /dev/cuaU0 </code></pre><h2 id="using-board-types-other-than-the-uno">Using board types other than the Uno</h2> <p>As it&rsquo;s mentioned above, we&rsquo;re using the <code>uarduno</code> kernel module. Even though the module&rsquo;s description is <em>&ldquo;FreeBSD Kernel Driver for the Arduino Uno USB interface&rdquo;</em>, you can, in fact, use different board types other than the Uno. According to <code>uarduno</code>&rsquo;s <a href="http://www.mrp3.com/uarduno.html">website</a>, you can modify <code>/usr/ports/comms/uarduno/files/ids.txt</code> to include more board types; the two fields are Vendor ID and Product ID. Read the comments inside the file for more information.</p> <pre tabindex="0"><code>{ 0x2341, 0x0001 }, // Arduino UNO, vendor 2341H, product 0001H { 0x2341, 0x0042 }, // Arduino MEGA (rev 3), vendor 2341H, product 0042H { 0x2341, 0x0043 }, // Arduino UNO (rev 3), vendor 2341H, product 0043H { 0x2341, 0x0010 }, // Arduino MEGA 2560 R3, vendor 2341H, product 0010H { 0x2341, 0x8037 }, // Arduino Micro </code></pre><p>When you&rsquo;re done, clean and re-build the port.</p> <h2 id="known-issues-and-their-fixes">Known issues and their fixes</h2> <p>Even though you might have plugged your board to your machine, you might notice that there is no device appearing in <code>/dev</code>. Although there is no definite answer as to why this is happening, make sure that the USB cable is connected properly; on some boards, you have to hear a click sound.</p> <p>When trying to use a new library, you might notice that your code doesn&rsquo;t compile. A common issue is that you haven&rsquo;t stored the library in the correct path. As mentioned, libraries are stored in <code>/usr/local/arduino/hardware/arduino/avr/libraries/</code>, so you have to move it there.</p> Simple Brainfuck interpreter in C http://margiolis.net/w/brainfuck/ Wed, 02 Sep 2020 00:00:00 +1200 <h2 id="how-brainfuck-works">How Brainfuck works</h2> <p>There are only 8 symbols supported in Brainfuck:</p> <table border="solid" style="width: auto; table-layout: auto"> <tr> <th>Symbol</th> <th>Function</th> </tr> <tr> <td>&gt;</td> <td>Increase position of pointer</td> </tr> <tr> <td>&lt;</td> <td>Decrease position of pointer</td> </tr> <tr> <td>&#43;</td> <td>Increase value of pointer</td> </tr> <tr> <td>&#45;</td> <td>Decrease value of pointer</td> </tr> <tr> <td>&#91;</td> <td>Beginning of loop</td> </tr> <tr> <td>&#93;</td> <td>End of loop</td> </tr> <tr> <td>&#46;</td> <td>Output ASCII code of pointer</td> </tr> <tr> <td>&#44;</td> <td>Read a character and stores its ASCII value in pointer</td> </tr> </table> <p>It&rsquo;s best to imagine Brainfuck programs as arrays of integers, which the pointer can manipulate. Let&rsquo;s say this is our initial state:</p> <pre tabindex="0"><code>{ 0, 0, 0, 0, 0, 0 } </code></pre><p>We can assign values to each position in the array by moving the pointer around. Using the + and - symbols, we can increment or decrement by 1 each time. If we wanted to move the pointer <em>two times to the right</em> and increment the value there by 3, we would have a program that looks like this:</p> <pre tabindex="0"><code>&gt;&gt;+++ </code></pre><p>The updated version of the array:</p> <pre tabindex="0"><code>{ 0, 0, 3, 0, 0, 0 } </code></pre><p>Following the same logic, we can assign specific values to each cell and make an actual program. If we wanted to print the letter &ldquo;B&rdquo; on the screen, which corresponds to the ASCII value 66, we could write the following program:</p> <pre tabindex="0"><code>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++. </code></pre><p>In order to avoid writing things like this we can use loops. The loop is executed as long as the value inside it is not 0. Essentially, it&rsquo;s going to do the multiplication 10 x 6 = 66 and then print the value:</p> <pre tabindex="0"><code>+++++ +++++ # add 10 to cell #0 [ # beginning of loop &gt; +++ +++ # add 6 to cell #1 &lt; - # subtract 1 from cell #0 ] # end of loop # value at cell 1 is now 66 (10 x 6 = 66) &gt; . # go to cell 1 and print its value </code></pre><p>Or, for compactness:</p> <pre tabindex="0"><code>+++++++++++[&gt;++++++&lt;-]&gt;. </code></pre><p>You can learn more about how Brainfuck works <a href="https://esolangs.org/wiki/Brainfuck">here</a>.</p> <h2 id="building-the-interpreter">Building the interpreter</h2> <p>We&rsquo;ll first read the Brainfuck source from <code>stdin</code> into a static size buffer. 50.000 bytes should be large enough to store any Brainfuck program, since I doubt anyone is mad enough to write actual programs in it:</p> <pre tabindex="0"><code>#define BUFSIZE 50000 . . . size_t len = 0; char buf[BUFSIZE]; while (read(STDIN_FILENO, &amp;buf[len], 1) &gt; 0) len++; buf[len] = &#39;\0&#39;; </code></pre><p>We&rsquo;ll declare the rest of the needed variables:</p> <pre tabindex="0"><code>int closed; /* number of active loops */ int opened; /* number of inactive loops */ int pos = 0; /* position in the program */ unsigned short *pc; /* program counter */ char *src; /* source code */ </code></pre><p>One of the reasons we have a <code>len</code> variable is to allocate just enough memory for <code>src</code>. We&rsquo;ll also empty the buffer because we now want to use it to store the values the Brainfuck program will produce:</p> <pre tabindex="0"><code>if ((src = malloc(len)) == NULL) { perror(&#34;malloc&#34;); exit(1); } strcpy(src, buf); memset(buf, 0, len); </code></pre><p>We can now parse the source code symbol by symbol. <code>pc</code> will act as the &ldquo;pointer&rdquo;, moving back and forth in the array. Each symbol will have its own case inside the following <code>switch</code> statement:</p> <pre tabindex="0"><code>for (pc = (unsigned short *)buf, pos = 0; pos &lt; len; pos++) { switch (src[pos]) { ... } } </code></pre><p>For the <code>&lt;</code> and <code>&gt;</code> symbols we simply move the pointer:</p> <pre tabindex="0"><code>case &#39;&gt;&#39;: pc++; break; case &#39;&lt;&#39;: pc--; break; </code></pre><p>The + and - symbols in/decrement the value of the cell the pointer is currently at:</p> <pre tabindex="0"><code>case &#39;+&#39;: (*pc)++; break; case &#39;-&#39;: (*pc)--; break; </code></pre><p>To implement the <code>.</code> and <code>,</code> symbols we&rsquo;ll use the standard library&rsquo;s <code>putchar()</code> and <code>getchar()</code> functions:</p> <pre tabindex="0"><code>case &#39;.&#39;: putchar(*pc); break; case &#39;,&#39;: *pc = getchar(); break; </code></pre><p>Now comes the last, but harder part, which is to implement loops. The logic behind my implementation is that instead of keeping track of every bracket to know where a loop starts and ends, the program keeps going through the source code and, using a counter, we know that a loop starts or ends when that counter is 0 <em><strong>and</strong></em> and an opposite bracket has been found. Also, in each iteration the <code>pos</code> variable changes accordingly so that we can imitate the looping behavior of Brainfuck.</p> <p>These are the steps the program follows for each of the two symbols:</p> <h2 id="beginning-of-loop-">Beginning of loop: <code>[</code></h2> <ul> <li>If the pointer&rsquo;s value <em>is</em> 0, we have a new loop.</li> <li>Count how many (if any) nested loops we encounter.</li> <li>If we encouter a <code>[</code>, increment the <code>opened</code> variable.</li> <li>If we encouter a <code>]</code>, decrement the <code>opened</code> variable.</li> <li>Keep counting until there are no new active loops (i.e <code>opened</code> is 0).</li> </ul> <pre tabindex="0"><code>case &#39;[&#39;: if (!(*pc)) { for (opened = 0; pos++; pos &lt; len; pos++) { if (src[pos] == &#39;]&#39; &amp;&amp; !opened) break; else if (src[pos] == &#39;[&#39;) opened++; else if (src[pos] == &#39;]&#39;) opened--; } } break; </code></pre><h2 id="end-of-loop-">End of loop: <code>]</code></h2> <ul> <li>If the pointer&rsquo;s value is <em>is not</em> 0, we have an active loop.</li> <li>Start going back and count how many (if any) nested loops there are.</li> <li>If we encouter a <code>]</code>, increment the <code>closed</code> variable.</li> <li>If we encouter a <code>[</code>, decrement the <code>closed</code> variable.</li> <li>Keep counting until there are no active loops (i.e <code>closed</code> is 0).</li> </ul> <pre tabindex="0"><code>case &#39;]&#39;: if ((*pc)) { for (closed = 0; pos--; pos &gt;= 0; pos--) { if (src[pos] == &#39;[&#39; &amp;&amp; !closed) break; else if (src[pos] == &#39;]&#39;) closed++; else if (src[pos] == &#39;[&#39;) closed--; } } break; </code></pre><h2 id="putting-it-all-together">Putting it all together</h2> <p>Below is the full program. You can also find this program <a href="https://git.sr.ht/~crm/random/tree/master/item/bf">here</a></p> <pre tabindex="0"><code>#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; #include &lt;unistd.h&gt; #define BUFSIZE 50000 int main(int argc, char *argv[]) { size_t len = 0; int closed, opened, pos = 0; unsigned short *pc; char buf[BUFSIZE], *src; while (read(STDIN_FILENO, &amp;buf[len], 1) &gt; 0) len++; buf[len] = &#39;\0&#39;; if ((src = malloc(len)) == NULL) { perror(&#34;malloc&#34;); exit(1); } strcpy(src, buf); memset(buf, 0, len); for (pc = (unsigned short *)buf; pos &lt; len; pos++) { switch (src[pos]) { case &#39;&gt;&#39;: pc++; break; case &#39;&lt;&#39;: pc--; break; case &#39;+&#39;: (*pc)++; break; case &#39;-&#39;: (*pc)--; break; case &#39;.&#39;: putchar(*pc); break; case &#39;,&#39;: *pc = getchar(); break; case &#39;[&#39;: if (!(*pc)) { for (opened = 0, pos++; pos &lt; len; pos++) { if (src[pos] == &#39;]&#39; &amp;&amp; !opened) break; else if (src[pos] == &#39;[&#39;) opened++; else if (src[pos] == &#39;]&#39;) opened--; } } break; case &#39;]&#39;: if (*pc) { for (closed = 0, pos--; pos &gt;= 0; pos--) { if (src[pos] == &#39;[&#39; &amp;&amp; !closed) break; else if (src[pos] == &#39;]&#39;) closed++; else if (src[pos] == &#39;[&#39;) closed--; } } break; } } free(src); return (0); } </code></pre>