From 294e7375612d4f26257785dabb9a53e8002cb0a9 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Mon, 9 Nov 2020 00:19:51 -0800 Subject: [PATCH] Added FreeBSD install notes to website. --- data/notes/freebsd_10.md | 187 ++++++++++++ data/notes/freebsd_10.metadata | 6 + data/notes/freebsd_11.md | 534 +++++++++++++++++++++++++++++++++ data/notes/freebsd_11.metadata | 6 + 4 files changed, 733 insertions(+) create mode 100644 data/notes/freebsd_10.md create mode 100644 data/notes/freebsd_10.metadata create mode 100644 data/notes/freebsd_11.md create mode 100644 data/notes/freebsd_11.metadata diff --git a/data/notes/freebsd_10.md b/data/notes/freebsd_10.md new file mode 100644 index 0000000..4622101 --- /dev/null +++ b/data/notes/freebsd_10.md @@ -0,0 +1,187 @@ +# Overview # + +Installation notes for personal FreeBSD server. Very incomplete. + +# Basic System # + +## Networking ## + +## SCSI Tape Library ## + +### Recipe: Identify tape library devices ### + +Use `camcontrol` to identify the drive(`sa`), changer(`ch`) and CAM(`pass`) +devices. + + root@talisker:/usr/local/etc/bacula # camcontrol devlist + at scbus0 target 5 lun 0 (sa0,pass0) + at scbus0 target 5 lun 1 (pass1,ch0) + at scbus1 target 0 lun 0 (pass2,da0) + at scbus1 target 1 lun 0 (pass3,da1) + at scbus1 target 2 lun 0 (pass4,da2) + at scbus2 target 0 lun 0 (ada0,pass5) + at scbus2 target 1 lun 0 (ada1,pass6) + at scbus3 target 0 lun 0 (ada2,pass7) + at scbus3 target 1 lun 0 (ada3,pass8) + +### Recipe: Probe SCSI bus ### + +If the tape library isn't powered up and finished with startup routines by the +time you turn on the server, it won't be detected properly. You must reprobe +the SCSI bus with the following steps, where "X" is the SCSI bus number. + + camcontrol devlist -v + camcontrol reset X + camcontrol rescan X + +### Recipe: Manipulate changer ### + +Install `misc/mtx` from ports. Slots marked "Full" without a VolumeTag are +non-barcoded tapes. The `load` and `transfer` commands can be used to move +tapes within the library. + + root@talisker:~ # mtx -f /dev/pass1 status + Storage Changer /dev/pass1:1 Drives, 8 Slots ( 0 Import/Export ) + Data Transfer Element 0:Full (Storage Element 1 Loaded) + Storage Element 1:Empty + Storage Element 2:Full + Storage Element 3:Full + Storage Element 4:Full + Storage Element 5:Full + Storage Element 6:Empty + Storage Element 7:Empty + Storage Element 8:Full :VolumeTag=000106L3 + +## ZFS ## + +### Recipe: Create new ZFS filesystem ### + +There is effectively no overhead to compression, so enable it on all root ZFS +filesystems and allow it to be inherited. + + zfs create zfs_stripe_0/software + zfs set compression=lz4 zfs_stripe_0/software + +### Recipe: Replacing a failed disk ### + +At the OS level, a failing disk may manifest like this: + + root@talisker:~ # dmesg + ... + (da0:mpt1:0:1:0): READ(10). CDB: 28 00 e8 e0 88 ae 00 00 01 00 + (da0:mpt1:0:1:0): CAM status: SCSI Status Error + (da0:mpt1:0:1:0): SCSI status: Check Condition + (da0:mpt1:0:1:0): SCSI sense: RECOVERED ERROR asc:5d,14 (Hardware impending failure too many block reassigns) + (da0:mpt1:0:1:0): Field Replaceable Unit: 5 + ... + +Once the disk has fully failed ZFS reports it like this: + + root@talisker:~ # zpool status + pool: zfs_mirror_0 + state: DEGRADED + status: One or more devices has been removed by the administrator. + Sufficient replicas exist for the pool to continue functioning in a + degraded state. + action: Online the device using 'zpool online' or replace the device with + 'zpool replace'. + scan: scrub repaired 128K in 15h33m with 0 errors on Fri Aug 31 22:08:42 2018 + config: + + NAME STATE READ WRITE CKSUM + zfs_mirror_0 DEGRADED 0 0 0 + mirror-0 DEGRADED 0 0 0 + 16848908995976332824 REMOVED 0 0 0 was /dev/da0 + da0 ONLINE 0 0 0 + da1 ONLINE 0 0 0 + + errors: No known data errors + +Replacing the disk is a single command: + + root@talisker:~ # zpool replace zfs_mirror_0 16848908995976332824 da2 + +Monitor the resilvering process until complete. + + root@talisker:~ # zpool status + pool: zfs_mirror_0 + state: DEGRADED + status: One or more devices is currently being resilvered. The pool will + continue to function, possibly in a degraded state. + action: Wait for the resilver to complete. + scan: resilver in progress since Wed Sep 12 20:20:39 2018 + 4.82M scanned out of 682G at 617K/s, 322h2m to go + 4.62M resilvered, 0.00% done + config: + + NAME STATE READ WRITE CKSUM + zfs_mirror_0 DEGRADED 0 0 0 + mirror-0 DEGRADED 0 0 0 + replacing-0 REMOVED 0 0 0 + 16848908995976332824 REMOVED 0 0 0 was /dev/da0 + da2 ONLINE 0 0 0 (resilvering) + da0 ONLINE 0 0 0 + da1 ONLINE 0 0 0 + + errors: No known data errors + +## Fluxbox ## + +Change Fluxbox clock format in `~/.fluxbox/init`. + + session.screen0.strftimeFormat: %d %b, %a %H:%M:%S + +## Misc Software ## + +Install from ports: `ntp`, `gimp`, `pidgin`, `geeqie`, `deluge`, +`kicad[-devel]`, `wget`, `bind-tools`, `irssi`, `vim` (and with GTK support) + +# Daemons # + +## Samba ## + +## Bacula ## + +Install `sysutils/bacula-server` from ports. + +## PostgreSQL ## + +## MySQL ## + +Install `databases/mysql56-client` and `databases/mysql56-server` from ports. + +Add `mysql_enable="YES"` to `/etc/rc.conf`. + +Set passwords for root and create unprivileged users for relevant databases. + +## Apache (with Python CGI and MySQL capabilities) ## + +Install `www/apache24` and `databases/py-MySQLdb` from ports. + +Add `apache24_enable="YES"` to `/etc/rc.conf`. + +Add an entry for this host to `/etc/hosts` and ensure file based authentication is enabled in `/etc/nsswitch.conf`. + +Edit `/usr/local/etc/apache24/httpd.conf`, setting `ServerName` and `ServerAdmin`, and uncommenting the `cgi-bin` module. + +Install cgi-bin script, setting shebang, execute permissions, etc. + +# Recipes # + +## List of installed ports ## + + pkg query --all '%o %n-%v %R' + +## Show options port was built with ## + + cd $portsdir + make showconfig + +## Reset terminal environment ## + + exec su - + +## Start separate X session ## + + xinit /usr/local/bin/xterm -- :2 + diff --git a/data/notes/freebsd_10.metadata b/data/notes/freebsd_10.metadata new file mode 100644 index 0000000..7695184 --- /dev/null +++ b/data/notes/freebsd_10.metadata @@ -0,0 +1,6 @@ +[DEFAULT] +page_title = FreeBSD 10 Notes +meta_keywords = +meta_description = +menu_text = FreeBSD 10 +menu_priority = 3000 diff --git a/data/notes/freebsd_11.md b/data/notes/freebsd_11.md new file mode 100644 index 0000000..009e557 --- /dev/null +++ b/data/notes/freebsd_11.md @@ -0,0 +1,534 @@ +# Overview # + +Installation notes for a FreeBSD 11 graphical workstation. + + +# X11 & Fluxbox # + +Compile from ports: + + x11/xorg + curl+libssh+smb + gmp+assembly_opts + mesa_libs+wayland + webp+x11 + (xterm+xinerama) + x11/nvidia-driver + x11-wm/fluxbox + +Add the following line to `/etc/rc.conf`, rebooting or using `kldload` before +attempting to start X. + + kld_list="nvidia-modeset" + +Create the following configuration file at +`/usr/local/etc/X11/xorg.conf.d/driver-nvidia.conf`. + + Section "Device" + Identifier "NVIDIA Card" + VendorName "NVIDIA Corporation" + Driver "nvidia" + EndSection + +Create `/home/ataylor/.xinitrc` to load Fluxbox and set +resolution/orientation/etc. + + xrandr --output HDMI-0 --mode 3840x2160 --pos 0x0 --output DVI-D-0 --rotate left --mode 2560x1600 --pos 3840x-150 + /usr/local/bin/fluxbox + +After starting X, use the Fluxbox config menus to set: + +* Focus model = mouse focus +* Auto-raise windows? = no +* Style = Meta + +Overwrite `~/.fluxbox/keys` with my keys file: + + # click on the desktop to get menus + OnDesktop Mouse1 :HideMenus + OnDesktop Mouse2 :WorkspaceMenu + OnDesktop Mouse3 :RootMenu + + # scroll on the desktop to change workspaces + OnDesktop Mouse4 :PrevWorkspace + OnDesktop Mouse5 :NextWorkspace + + # alt + left/right click to move/resize a window + OnWindow Mod1 Mouse1 :MacroCmd {Raise} {Focus} {StartMoving} + OnWindowBorder Move1 :StartMoving + + OnWindow Mod1 Mouse3 :MacroCmd {Raise} {Focus} {StartResizing NearestCorner} + OnLeftGrip Move1 :StartResizing bottomleft + OnRightGrip Move1 :StartResizing bottomright + + # control-click a window's titlebar and drag to attach windows + OnTitlebar Control Mouse1 :StartTabbing + + # double click on the titlebar to shade + OnTitlebar Double Mouse1 :Shade + + # left click on the titlebar to move the window + OnTitlebar Mouse1 :MacroCmd {Raise} {Focus} {ActivateTab} + OnTitlebar Move1 :StartMoving + + # middle click on the titlebar to lower + OnTitlebar Mouse2 :Lower + + # right click on the titlebar for a menu of options + OnTitlebar Mouse3 :WindowMenu + + # open a terminal + Mod1 F1 :Exec xterm + # open a dialog to run programs + Mod1 F2 :Exec fbrun + # Open file manager + #Mod1 F3 :Exec spacefm + # current window commands + Mod1 F4 :Close + + # Take a screenshot + 111 :Exec /home/ataylor/bin/take_screenshot.sh + + # Start screensaver, lock screen + 110 :Exec xscreensaver-command -lock + + # Window sizing commands + Control F1 :Minimize + Control F2 :Shade + Control F3 :Maximize + Control F4 :Fullscreen + + Control F5 :MacroCmd {ResizeTo 1600 1250} {Moveto 0 0 UpperLeft} + Control F6 :MacroCmd {ResizeTo 1600 1250} {Moveto 0 0 LowerLeft} + + Control F7 :MacroCmd {ResizeTo 1905 2160} {Moveto 0 0 UpperLeft} + Control F8 :MacroCmd {ResizeTo 1905 2160} {Moveto 0 0 UpperRight} + + Control F9 :MacroCmd {ResizeTo 1920 1080} {Moveto 0 0 UpperLeft} + Control F10 :MacroCmd {ResizeTo 1920 1080} {Moveto 0 0 UpperRight} + Control F11 :MacroCmd {ResizeTo 1920 1080} {Moveto 0 0 LowerLeft} + Control F12 :MacroCmd {ResizeTo 1920 1080} {Moveto 0 0 LowerRight} + +Set the system clock by modifying `~/.fluxbox/init` to contain: + + session.screen0.strftimeFormat: %a, %b %d %H:%M:%S + +# Ports - Misc # + +To enable searching ports via `make search key=text-string`, execute the +following command. + + cd /usr/ports && make index + +Some packages with auto-updating strings (like version number) fail checksum +tests. Both `print/texinfo` and `security/trousers` are common examples. When +encountering a message about size mismatches during the fetch stage, perform +the following steps. + +1. Download the source file manually with `wget`. +2. Use `make fetch-list` with `grep` to find out where the file should go. + Should be somewhere under `/usr/ports/distfiles`. +3. Copy the file to the proper location. +4. In the folder of the port you are trying to build, run `make makesum` to + update the checksum based on the currently present file. +5. Resume making the port as normal. + +Compile and install the following ports: + + sysutils/screen + ftp/wget + editors/vim + Set EDITOR=vim in ~/.profile + www/firefox + Build failing on ffmpeg dependency + going into ffmpeg folder and building directly solved it. + Also had to run (as root) dbus-uuidgen > /etc/machine-id + Otherwise firefox coredumps with: + "D-Bus library appears to be incorrectly set up; failed to read machine uuid: Failed to open "/etc/machine-id": No such file or directory" + mail/mutt + +sidebar + www/links (start with links -g) + sysutils/nitrogen + net/samba46 + -ads,ad_dc,ldap + mount_smbfs -I 192.168.1.5 //ataylor@talisker/zfs_stripe_0 /mnt/zfs_stripe_0 + graphics/geeqie + multimedia/vlc + +x264/x265 support + sysutils/fusefs-exfat + fuse_load="YES" -> /boot/loader.conf + /dev/nvd0s3 /mnt/windows exfat rw,mountprog=/usr/local/sbin/mount.exfat 0 0 -> /etc/fstab + devel/git + git config --global user.name "Aaron Taylor" + git config --global user.email "ataylor@subgeniuskitty.com" + devel/sdl20 + graphics/ImageMagick-nox11 + graphics/sdl2_image + net-p2p/deluge + irc/irssi + dns/bind-tools + www/apache24 + apache24_enable="yes" -> rc.conf + in httpd.conf set: + ServerName + ServerAdmin + and uncomment the cgi (not cgid) module in the MPMPREFORK IFDEF + net-mgmt/icmpmonitor + emulators/simh + emulators/virtualbox-ose + Add "vboxdrv_load="YES"" to /boot/loader.conf + pw groupmod vboxusers -m ataylor + Add "vboxnet_enable="YES"" to /etc/rc.conf for bridging support + multimedia/quodlibet + devel/arm-none-eabi-gdb + devel/valgrind + devel/arm-none-eabi-gcc + editors/libreoffice + graphics/inkscape + graphics/okular + math/gnuplot + editors/texmaker + devel/openocd + Enable USB for non-root users: + pw groupmod operator -m ataylor + setup the devfs subsystem by adding these lines to the following files: + ***/etc/devfs.rules (FreeBSD >= 8): + [localrules=10] + add path 'ugen*' mode 0660 group operator + add path 'usb/*' mode 0660 group operator + add path 'usb' mode 0770 group operator + ***/etc/rc.conf: + devfs_system_ruleset="localrules" + graphics/gimp + -gutenprint (brings in samba-4.4 and conflicts with installed samba-4.6) + x11-fm/qtfm + x11-fm/twander + TODO: Re-work ~/.twander for FreeBSD + www/py-beautifulsoup + www/py-requests + www/py3-requests + games/linux-dwarffortress + games/openmw + +# Mounting SMB/CIFS at Boot # + +Add to `/etc/fstab`: + + //ataylor@TALISKER/zfs_mirror_0 /mnt/zfs_mirror_0 smbfs rw,late,-N,-I192.168.1.5 0 0 + //ataylor@TALISKER/zfs_stripe_0 /mnt/zfs_stripe_0 smbfs rw,late,-N,-I192.168.1.5 0 0 + +Set ownership to `ataylor:ataylor` on `/mnt/zfs_stripe_0 and /mnt/zfs_mirror_0` +since `mount_smbfs` uses default "owner and group IDs from the directory where +the volume is mounted." (from `mount_smbfs` manpage) + +Add to `/etc/nsmbd.conf` + [TALISKER:ATAYLOR] + password=password-goes-here + + +# Firefox Configuration # + +In `about:config`: + + browser.newtabpage.enabled = false + general.useragent.override = NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m) + gfx.canvas.azure.backends = cairo + gfx.content.azure.backends = cairo + gfx.canvas.azure.accelerated = true + +Note that the `gfx.*` entries enable hardware acceleration since the Nvidia +drivers I am using support cairo but not skia, or is it that cairo supports my +video drives and skia doesn't? + +In preferences: + +* Remember last session on startup +* Download files to `/home/ataylor/downloads` +* Check for updates but do not auto-install +* Default search -> DuckDuckGo +* Do not provide search suggestions +* Location Bar, disable all 'suggest' locations (History, Bookmarks, Open Tabs) + +Install plugins: + + +* uBlock Origin +* NoScript +* Image Zoom +* Save Image in Folder +* Thumbnail Zoom Plus + +Execute `touch ~/.null` and then create `~/.config/user-dirs.dirs` as below. + + XDG_DESKTOP_DIR="$HOME/.null" + XDG_DOWNLOAD_DIR="$HOME/.null" + XDG_TEMPLATES_DIR="$HOME/.null" + XDG_PUBLICSHARE_DIR="$HOME/.null" + XDG_DOCUMENTS_DIR="$HOME/.null" + XDG_MUSIC_DIR="$HOME/.null" + XDG_PICTURES_DIR="$HOME/.null" + XDG_VIDEOS_DIR="$HOME/.null" + +To make scrollbars move one-page-per-click rather than jumping to the cursor, +create the file `~/.config/gtk-3.0/settings.ini` with contents: + + [Settings] + gtk-primary-button-warps-slider=false + + +# X11 Configuration # + +In `~/.Xresources`: + + xterm*faceName: Liberaqtion Mono:size=12:antialias=true + xterm*font: 7x13 + +In `~/.Xmodmap`: + + ! + ! Swap Caps_Lock and Control_L + ! + remove Lock = Caps_Lock + remove Control = Control_L + keycode 9 = Caps_Lock NoSymbol Caps_Lock + keycode 37 = Escape NoSymbol Escape + keycode 66 = Control_L NoSymbol Control_L + add Lock = Caps_Lock + add Control = Control_L + +In `~/.xinitrc`: + + xrdb /home/ataylor/.Xresources + xmodmap /home/ataylor/.Xmodmap + + +# FreeBSD Configuration # + +Add to `/etc/sysctl.conf`: + + kern.corefile=/dev/null + +# Screensaver/Screenlock # + +Compile the `x11/xscreensaver` port and execute `xscreensaver-demo` to +configure the screensaver. + +Add `xscreensaver -nosplash &` to `~/.xinitrc`. + +Add a line like the one below to `~/.fluxbox/keys`. + + 110 :Exec xscreensaver-command -lock + + +# Wallpaper Configuration # + +Create `~/bin/wallpaper_rotation.sh` and add to `~/.xinitrc`. +Remember to make it executable. + + #!/usr/local/bin/python3 + # This script creates a wallpaper slideshow. + + ##### Configuration + + # List of image source directories + # Each sublist is of the form: + # [monitor ID, absolute path to directory containing images for this monitor] + source_dir = [ + [0,"/mnt/zfs_mirror_0/wallpaper/ratio_10_16"], + [1,"/mnt/zfs_mirror_0/wallpaper/ratio_16_9"] + ] + + # This list will be populated during runtime. + # Each sublist is of the form: + # [monitor ID, absolute path to image file to display on this monitor] + current_wallpaper = [ + [0,""], + [1,""] + ] + + # Path to nitrogen configuration file + nitrogen_config = "/home/ataylor/.config/nitrogen/bg-saved.cfg" + + ##### Dependencies + + from subprocess import call + from os import listdir + from os.path import isfile, join + from random import choice + from time import sleep + + ##### Source Code + + def write_config(): + config_file = open(nitrogen_config, 'w') + for monitor in current_wallpaper: + # Indenting the following triple-quoted text inserts leading whitespace in the + # nitrogen config file. However, nitrogen strips leading whitespace before + # processing its config file and the extra whitespace makes this Python script + # more readable. + template = """[xin_{monitor_n}] + file={wallpaper_file} + mode=0 + bgcolor=#000000 + """ + context = { + "monitor_n":monitor[0], + "wallpaper_file":monitor[1] + } + config_file.write(template.format(**context)) + config_file.close() + + def refresh_wallpaper(): + write_config() + call(["/usr/local/bin/nitrogen", "--restore"]) + + def get_rand_from_dir(): + file_choices = [x for x in listdir(source_dir[current_monitor][1]) if isfile(join(source_dir[current_monitor][1], x))] + filename = choice(file_choices) + filename = join(source_dir[current_monitor][1], filename) + current_wallpaper[current_monitor][1] = filename + + # Put something up on all monitors + for i in range(len(source_dir)): + current_monitor = i + get_rand_from_dir() + refresh_wallpaper() + + # Eternal slideshow loop + while 1: + current_monitor = (current_monitor + 1) % len(source_dir) + get_rand_from_dir() + refresh_wallpaper() + sleep(10) + + +# Vim Configuration # + +Create `~/.vimrc` as follows. + + set nocompatible " be iMproved, required + filetype off " required + + set number + syntax on + set tabstop=4 + set expandtab + "set background=dark + "colorscheme solarized + + "Folding + "http://vim.wikia.com/wiki/Folding_for_plain_text_files_based_on_indentation + "set foldmethod=expr + "set foldexpr=(getline(v:lnum)=~'^$')?-1:((indent(v:lnum)'.indent(v:lnum+1)):indent(v:lnum)) + "set foldtext=getline(v:foldstart) + "set fillchars=fold:\ "(there's a space after that \) + "highlight Folded ctermfg=DarkGreen ctermbg=Black + "set foldcolumn=6 + + " Color the 100th column. + set colorcolumn=100 + highlight ColorColumn ctermbg = lightgray + + +# Deluge Configuration # + +Follow instructions at . Add check boxes in Connection Manager: + +* "Automatically connect to selected host on start-up" +* "Do not show this dialog on start-up". + + +# Personal Software # + +Compile and install `wallproc`. Requires the following dependencies from ports: + + devel/sdl20 + graphics/ImageMagick-nox11 + graphics/sdl2_image + + +# Printer Configuration # + +Create a spool directory: + + mkdir -p /var/spool/lpd/lp + chown daemon:daemon /var/spool/lpd/lp + chmod 770 /var/spool/lpd/lp + +Add an entry to `/etc/printcap`. + + # HP LaserJet, M's Office + lp:\ + :lp=:rm=192.168.1.252:rp=raw:\ + :sh:\ + :mx#0:\ + :sd=/var/spool/lpd/lp:\ + :lf=/var/log/lpd-errs: + +Edit `/etc/rc.conf` to add the line `lpd_enable="YES"`. + +Note that plaintext files print in stairstep. See the link below if I want to +fix this. Consider adding a smart filter to only apply this to text and pass PS +straight through. + + + +# TODO # + + After reboot: + Setup virtualbox and verify CAD software runs + Firefox: + Menus/tabs/etc use AA fonts but websites don't. Why not? + YES: wiki.archlinux.org + NO: reddit + https://forums.freebsd.org/threads/firefox-no-gpu-acceleration-since-50-0.59089/ + Check Debian & FreeBSD install notes from previous desktop systems + Add lagavulin to tape backup schedule over network. Where to spool? + sysutils/bacula-client + + +# temp notes from ports compiles # + +=================================================================================================================== + + Virtualbox USB Support + USB Support: + ============ + + For USB support your user needs to be in the operator group and needs read + and write permissions to the USB device. + + % pw groupmod operator -m jerry + + Add the following to /etc/devfs.rules (create if it doesn't exist): + + [system=10] + add path 'usb/*' mode 0660 group operator + + To load these new rule add the following to /etc/rc.conf: + + devfs_system_ruleset="system" + + Then restart devfs to load the new rules: + + % /etc/rc.d/devfs restart + +=================================================================================================================== + + FreeBSD USB NOTE: + To allow an ordinary user to acces any of the the hotplug USB interface + add him/her to the operator group (pw groupmod operator -m username), then + setup the devfs subsystem by adding these lines to the following files: + + ***/etc/devfs.rules (FreeBSD >= 8): + [localrules=10] + add path 'ugen*' mode 0660 group operator + add path 'usb/*' mode 0660 group operator + add path 'usb' mode 0770 group operator + + ***/etc/devfs.rules (FreeBSD <= 7): + [localrules=10] + add path 'ugen*' mode 0660 group operator + + ***/etc/rc.conf: + devfs_system_ruleset="localrules" diff --git a/data/notes/freebsd_11.metadata b/data/notes/freebsd_11.metadata new file mode 100644 index 0000000..4c9c9b9 --- /dev/null +++ b/data/notes/freebsd_11.metadata @@ -0,0 +1,6 @@ +[DEFAULT] +page_title = FreeBSD 11 Notes +meta_keywords = +meta_description = +menu_text = FreeBSD 11 +menu_priority = 3000 -- 2.20.1