Christos Margiolis: bsd Christos Margiolis http://margiolis.net/tags/bsd/ 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> 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> 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> 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> 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> 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> 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> 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> 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>