Tuesday, June 25, 2019

util-linux v2.34 -- what's new?

The code of the popular command lsblk(8) has been completely rewritten. The result is more extendible and readable code. Now lsblk(8) keeps all block devices tree in memory before it's printed. It allows to modify and reorder the tree independently on the way how kernel (/sys filesystem) exports the tree to userspace. The new features based on this change are:
  • devices de-duplication (e.g. lsblk --dedup WWN); this is useful for example on systems with multi-path devices where the same device is accessible by more ways
  • M:N relationships without repeating devices (e.g. lsblk --merge); this feature is implemented by additional tree graph.
For example visualization of really complicated setup; thin provisioning pool with external snapshot (the pool is based on loop0 and it's created by metadata and data devices; the pool and loop1 are used for the snapshot).

$ lsblk --merge
       NAME                 MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
       loop0                  7:0    0 955.7M  0 loop  
   ┌┈▶ ├─test-thin-metadata 253:0    0     2M  0 dm    
   └┬▶ └─test-thin-data     253:1    0 953.7M  0 dm    
┌┈▶ └┈┈test-thin-pool       253:2    0 953.7M  0 dm    
┆      └─test-thin          253:3    0 190.8M  0 dm    
└┬▶    loop1                  7:1    0 190.8M  0 loop  
 └┈┈┈┈┈test-thin-extsnap    253:4    0 190.8M  0 dm    

The same situation with classic lsblk output:

$ lsblk
loop0                     7:0    0 955.7M  0 loop  
├─test-thin-metadata    253:0    0     2M  0 dm    
│ └─test-thin-pool      253:2    0 953.7M  0 dm    
│   ├─test-thin         253:3    0 190.8M  0 dm    
│   └─test-thin-extsnap 253:4    0 190.8M  0 dm    
└─test-thin-data        253:1    0 953.7M  0 dm    
  └─test-thin-pool      253:2    0 953.7M  0 dm    
    ├─test-thin         253:3    0 190.8M  0 dm    
    └─test-thin-extsnap 253:4    0 190.8M  0 dm    
loop1                     7:1    0 190.8M  0 loop  
└─test-thin-extsnap     253:4    0 190.8M  0 dm    

You can see that lsblk repeating test-thin and test-thin-extsnap, and loop1 seems unrelated for the setup at first glance.

The repeating of the block devices in the output is more annoying for multi-path devices. See another example in my previous blog: https://karelzak.blogspot.com/2018/11/lsblk-merge.html

I guess that FUSE user will be very happy with v2.34; finally the command umount(8) supports non-root user unmount for FUSE mounts. The requirement is FUSE specific user_id= in /proc/self/mountinfo for the filesystem. So, you do not have to call fuse specific umount tool, just use umount(8) as usually.

The command mount(8) now allows to use "--all -o remount" to remount all filesystems with specified or fstab options. It's possible to use filters (-t and -O).

The new command hardlink has been merged to util-linux. The command consolidates duplicate files via hardlinks. The current implementation is from Fedora, but in the next versions we will reuse also code from Debian fork to create one widely usable implementation.

The command lscpu(1) now prints more information about your CPU, for example 'Frequency boost' and 'Vulnerability' fields. The caches calculation has been modified to print summary from all system caches rather than per code numbers; and new command line option --caches lists details about CPU caches.

... and many another small changes :-) See complete release notes at kernel.org: https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.34/v2.34-ReleaseNotes

Thanks to Sami Kerola, Stanislav Brabec, Ruediger Meier and others!

Thursday, November 8, 2018

lsblk --merge

For uti-linux version v2.34 (next spring) I'm working on new lsblk. The goal is keep all devices as data structs in memory to have ability to modify or reorder the tree.

Now lsblk blindly follows slaves/holders links in /sys and in case of RAIDs or multi-path devices the output contains duplicate lines.

The new version will provide de-duplication (lsblk --dedup WWN) to completely remove unnecessary lines from output.

The another feature is possibility to use libsmartcols to specify group of lines and define children for the group. This allows to describe M:N relationships by chart without duplicate lines in output.

See the next example with one multi-path device and one RAID device.

$ lsblk --merge
      sda           8:0    0 223.6G  0 disk  
      ├─sda1        8:1    0   200M  0 part  /boot/efi
      ├─sda2        8:2    0   200M  0 part  /boot
      ├─sda3        8:3    0 130.3G  0 part  
      ├─sda4        8:4    0    50G  0 part  /
┌┈┄┄▶ └─sda5        8:5    0  42.9G  0 part  
┆     sdb           8:16   0  74.5G  0 disk  
┆     └─sdb1        8:17   0  74.5G  0 part  /home/archive
┆ ┌┈▶ sdc           8:32   0   100M  0 disk  
┆ ├┈▶ sdd           8:48   0   100M  0 disk  
┆ ├┈▶ sde           8:64   0   100M  0 disk  
┆ └┬▶ sdf           8:80   0   100M  0 disk  
┆  └┈┄mpatha      253:0    0   100M  0 mpath 
┆     ├─mpatha1   253:1    0    50M  0 part  
┆     └─mpatha2   253:2    0    49M  0 part  
┆     nvme0n1     259:0    0 223.6G  0 disk  
┆     ├─nvme0n1p1 259:1    0   7.8G  0 part  
┆     ├─nvme0n1p2 259:2    0   200G  0 part  /home
└┬┄┄▶ └─nvme0n1p3 259:3    0  15.8G  0 part  
 └┄┄┈┄md0           9:0    0  15.8G  0 raid1 
      ├─md0p1     259:4    0   100M  0 md    
      └─md0p2     259:5    0  15.7G  0 md    

The original lsblk output:
sda           8:0    0 223.6G  0 disk  
├─sda1        8:1    0   200M  0 part  /boot/efi
├─sda2        8:2    0   200M  0 part  /boot
├─sda3        8:3    0 130.3G  0 part  
├─sda4        8:4    0    50G  0 part  /
└─sda5        8:5    0  42.9G  0 part  
  └─md0       9:0    0  15.8G  0 raid1 
    ├─md0p1 259:4    0   100M  0 md    
    └─md0p2 259:5    0  15.7G  0 md    
sdb           8:16   0  74.5G  0 disk  
└─sdb1        8:17   0  74.5G  0 part  /home/archive
sdc           8:32   0   100M  0 disk  
└─mpatha    253:0    0   100M  0 mpath 
  ├─mpatha1 253:1    0    50M  0 part  
  └─mpatha2 253:2    0    49M  0 part  
sdd           8:48   0   100M  0 disk  
└─mpatha    253:0    0   100M  0 mpath 
  ├─mpatha1 253:1    0    50M  0 part  
  └─mpatha2 253:2    0    49M  0 part  
sde           8:64   0   100M  0 disk  
└─mpatha    253:0    0   100M  0 mpath 
  ├─mpatha1 253:1    0    50M  0 part  
  └─mpatha2 253:2    0    49M  0 part  
sdf           8:80   0   100M  0 disk  
└─mpatha    253:0    0   100M  0 mpath 
  ├─mpatha1 253:1    0    50M  0 part  
  └─mpatha2 253:2    0    49M  0 part  
nvme0n1     259:0    0 223.6G  0 disk  
├─nvme0n1p1 259:1    0   7.8G  0 part  
├─nvme0n1p2 259:2    0   200G  0 part  /home
└─nvme0n1p3 259:3    0  15.8G  0 part  
  └─md0       9:0    0  15.8G  0 raid1 
    ├─md0p1 259:4    0   100M  0 md    
    └─md0p2 259:5    0  15.7G  0 md    

The code is still not ready to merge to the master branch and some cosmetic changes are expected, but the idea is obvious I guess. (If you want to play with it see topic/lsblk branch in util-linux repository.)

Thursday, February 15, 2018

util-linux v2.32 -- what's new?

This release (rc1 now) is without dramatic changes and game-changing improvements.

We have again invested our time and love to make cal(1) more usable. The most visible change is possibility to specify calendar system.

The current (backwardly compatible) default is to use Gregorian calendar and Julian calendar for dates before September 1752 (British Empire calendar reform). Unfortunately, this default is pretty frustrating if you want to use cal(1) for old dates before 1752 and you don't want to follow UK calendar.

The new command line option --reform={Julian,Gregorian,iso,1752,...} allows to specify exclusively calendar system or reform date. The currently supported reform is only UK reform in 1752. In the next versions we will probably add support for another reforms as it's very region specific (for example 1584 in my country, 1873 in Japan and 1926 Turkey, etc.).

Linux kernel supports multi-line log messages. Unfortunately, dmesg(1) support for this feature was insufficient. Now dmesg(1) provides better support and by new command line option --force-prefix allows to to print facility, level or timestamp information to each line of a multi-line message.

The command fallocate(1) --dig-holes has been significantly improved and it's faster more effective now (thanks to Vaclav Dolezal).

The command lscpu(1) provides more details about ARM CPUs now.

The command lsmem(1) supports memory zones now.

The command lsns(8) provides netnsid and nsfs columns now. ip(1) command allows to create network namespace, add logical name and ID for the namespace. Now all is visible by lsns(8). For example copy & past from our regression tests:

        NS TYPE NPROCS   PID USER     NETNSID NSFS                            COMMAND
4026532001 net     281     1 root  unassigned                                 /usr/lib/systemd/systemd --switched-root --system --deserialize 24
4026532400 net       1   795 rtkit unassigned                                 /usr/libexec/rtkit-daemon
4026532590 net       1  6707 root           0 /run/netns/LSNS-TEST-NETNSID-NS dd if=tests/output/lsns/FIFO-NETNSID bs=1 count=2 of=/dev/null

where dd(1) is running in the net namespace, and the namespace is by nsfs mounted on /run/netns/LSNS-TEST-NETNSID-NS. See https://raw.githubusercontent.com/karelzak/util-linux/master/tests/ts/lsns/netnsid for more details how to use ip(8) to setup this namespace.

The command rtcwake(8) has been improved to wait stdin to settle down before entering a system sleep. This is important on systems where wireless USB devices (mouse, keyboard, ...) generate "noise" for fraction of a second after rtcwake(8) execution.

The library libblkid has been extended to support LUKS2, Micron mpool, VDO and Atari partition table.

Thanks to all 43 contributors!

The next release v2.33 is planned for May 2018 (yes, the goal is to have 3-4 releases per year rather than 2 releases like in last years).

Thursday, October 19, 2017

util-linux v2.31 -- what's new?

uuidparse -- this is a new small command to get more information about UUIDs "hash". The command provides info about UUID type, variant and time. For example:

$ (uuidgen; uuidgen -t) | uuidparse
UUID                                  VARIANT TYPE       TIME
8f251893-d33a-40f7-9bb3-36988ec77527  DCE     random
66509634-b404-11e7-aa8e-7824af891670  DCE     time-based 2017-10-18 15:01:04,751570+0200

The command su has been refactored and extended to create pseudo terminal for the session (new option --pty). The reason is CVE-2016-2779, but the issue addressed by this CVE is pretty old and all the problem is silently ignored for for years on many places (on only su(1)). The core of the problem is that unprivileged user (within su(1) session) shares terminal file descriptor with original root's session. The new option --pty forces su(1) to create independent pseudo terminal for the session and than su(1) works as proxy between the terminals. The feature is experimental and not enabled by default (you have to use su --pty).

standard su session (all on pts/0):
24909 pts/0    S      0:02          \_ -bash                  
13607 pts/0    S      0:00              \_ su - kzak          
13608 pts/0    S      0:00                  \_ -bash          
13679 pts/0    R+     0:00                      \_ ps af      

su --pty session (root pts/0; user pts/5):
24909 pts/0    S      0:02          \_ -bash                  
13857 pts/0    S+     0:00              \_ su --pty - kzak    
13858 pts/5    Ss     0:00                  \_ -bash          
13921 pts/5    R+     0:00                      \_ ps af      

rfkill -- this is a new command in util-linux. The command was originally written by Johannes Berg and Marcel Holtmann and maintained for years as standalone package. We believe that it's better to maintain and distribute it with another commands on one place. The util-linux version is backwardly compatible with the original implementations. The command has been also improved (libsmartcols ouotput, etc.), the new default output:
# rfkill       
ID TYPE      DEVICE                   SOFT      HARD          
 0 bluetooth tpacpi_bluetooth_sw unblocked unblocked          
 1 wlan      phy0                unblocked unblocked          
 4 bluetooth hci0                  blocked unblocked

The library libuuid and command uuidgen support hash-based UUIDs v3 (md5) and v5 (sha1) as specified by RFC-4122 now. The library also provides UUID templates for dns, url, oid, or x500. For example:
$ uuidgen --sha1  --namespace @dns --name foobar.com

and it's expected to use v3 and v5 UUIDs as hierarchy, so you can use this UUID (or arbitrary other UUID) as a namespace:
$ uuidgen --sha1  --namespace e361e3ab-32c6-58c4-8f00-01bee1ad27ec --name mystuff

I can imagine system where for example per-user or per-architecture partition UUIDs are based on this system. For example use UUID specific for the system root as --namespace and username as --name, or so. 

wipefs and libblkid have been improved to provide all possible string permutations for a device. It means that wipefs does not return the first detected signature, but it continues and tries another offsets for the signature. This is important for filesystems and partitions tables where the superblock is backuped on multiple places (e.g. GPT) or detectable by multiple independent ways (FATs). This all is possible without a device modification (the old version provides the same, but only in "wipe" mode). 

The libfdisk has been extended to use BLKPG ioctls to inform the kernel about changes. This means that cfdisk and fdisk will not force your kernel to reread all of the partition table, but untouched partitions may remain mounted and used by the system. The typical use-case is resizing the last partition on the system disk. 

You can use cfdisk to resize a partition. Yep, cool.

The hwclock command now significantly reduces system shutdown times by not reading the RTC before setting it (except when the --update-drift option is used). This also mitigates other potential shutdown and RTC setting problems caused by requiring an RTC read.

Friday, June 9, 2017

util-linux v2.30 -- what's new?

The command tailf is dead thing. (RIP ... years ago I had nice time to improve it with inotify:) You have to use "tail -f" from coreutils project.

blkzone -- this new command is excellent example of the open source collaboration. The command has been developed by people from WD, Seagate and SanDisk (thanks to Shaun Tancheff, Damien Le Moal and others). The goal is to have command line interface to run zone commands on block devices that support Zoned Block Commands (ZBC) or Zoned-device ATA Commands (ZAC). For now the supported zone commands are "reset" and "report". See http://www.storagereview.com/methods_of_smr_data_management for more details about zones.

fincore (file in core)-- this is nice useful command to get information about number of memory pages used by file content. For example my fulltext email DB:

# fincore ~/Mail/Maildir/.notmuch/xapian/*.DB
 60.1M  15392  4.6G /home/kzak/Mail/Maildir/.notmuch/xapian/position.DB
687.4M 175982  3.5G /home/kzak/Mail/Maildir/.notmuch/xapian/postlist.DB
  328K     82 18.6M /home/kzak/Mail/Maildir/.notmuch/xapian/record.DB
190.5M  48758  2.1G /home/kzak/Mail/Maildir/.notmuch/xapian/termlist.DB

Fortunately RAM is cheap :) Thanks to Masatake Yamato from Red Hat.

lsmem (list memory) and chmem (change memory) -- another new commands. The commands have been originally implemented in Perl for s390-tools, now re-implemented in C in more generic way and to be usable on another architectures too. (thanks to Clemens von Mann and Heiko Carstens from IBM.)

The command fallocate supports an "insert range" operation now.

We continue on hwclock cleanup, some things in the code have been simplified, dead and useless things removed. (thanks to J William Piggott)

The code behind "column -t|--table" uses libsmartcols now. This change dramatically increased number of available features for table formatting. Now it's possible to define header for columns, truncate text in cells, align text to the right, change order of columns, JSON output or create tree-like output. Now almost all libsmartcols features are available on command line, example:
pstree-like output:

 $ ps -h -o pid,ppid,comm | column --table --tree 3 --tree-id 1 --tree-parent 2 --table-hide 2 --table-right 1
 1799  bash
 2254  bash
28427  └─mutt
 4263    └─vim
 7409  bash
10641  └─man
10657    └─less
16775  bash
11486  ├─ps
11487  └─column

 $ column /proc/diskstats --table --table-columns MAJ,MIN,NAME,READ-COMP,\
      --table-hide MAJ,MIN \
      --table-right 4,5,6,7,8,9,10,11,12,13,14 \
sda     13486466     149085  1288469300    9715620    45556082     7788088  1600182109   150180178        0  12935701  159902109
sda1         463        170       19002        131          91           0         161         331        0       334        462
sda2         778         16       63140        276         434         261      507574       12616        0      2382      12889
sda3    10710224     109592  1052352266    8018950    43983768     7022717  1153182094   126210185        0  11002854  134299501
sda4     1630396      32476    67166050    1039837     1197142      665798   343331344    23264993        0   2148041   24306932
sda5     1140435        241   168747746     655625      225373       73891   102920032      637906        0    627834    1293105
sda6        3703       6590       99512        691        4691       25421      240904        8418        0      6402       9108
sdb          448          0       22506       3088        1887           4         128         275        0      1449       3363
sdb1         404          0       19370       3035          12           4         128          60        0      1187       3095
loop0      22086          0      347311       2025       10738           0      844888        2226        0      1129       4265
loop1        947          0       26940        325        1100           0      133316         734        0       411       1058
md8            0          0           0          0           0           0           0           0        0         0          0

passwd in JSON:
 $ grep -v nologin /etc/passwd | \
     column --separator : --table --table-name passwd --json \
            --table-columns USERNAME,PWD,UID,GID,GECOS,HOME,SHELL \
            --table-hide PWD
   "passwd": [
      {"username": "root", "uid": "0", "gid": "0", "gecos": "root", "home": "/root", "shell": "/bin/bash"},
      {"username": "sync", "uid": "5", "gid": "0", "gecos": "sync", "home": "/sbin", "shell": "/bin/sync"},
      {"username": "shutdown", "uid": "6", "gid": "0", "gecos": "shutdown", "home": "/sbin", "shell": "/sbin/shutdown"},
      {"username": "halt", "uid": "7", "gid": "0", "gecos": "halt", "home": "/sbin", "shell": "/sbin/halt"},
      {"username": "kzak", "uid": "1000", "gid": "1000", "gecos": "Karel Zak,Home,,,", "home": "/home/kzak", "shell": "/bin/bash"},
      {"username": "gamer", "uid": "1001", "gid": "1001", "gecos": null, "home": "/home/gamer", "shell": "/bin/bash"},
      {"username": "test", "uid": "1002", "gid": "1002", "gecos": null, "home": "/home/test", "shell": "/bin/bash"}

findmnt-like output:
 $ column /proc/self/mountinfo \
     --table-order TARGET,SOURCE,TYPE,VFS-OPTS \
     --tree TARGET \
     --tree-id ID \
     --tree-parent PARENT
TARGET                             SOURCE       TYPE         VFS-OPTS
/                                  /dev/sda4    ext4         rw,relatime
├─/sys                             sysfs        sysfs        rw,nosuid,nodev,noexec,relatime
│ ├─/sys/kernel/security           securityfs   securityfs   rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/cgroup                 tmpfs        tmpfs        ro,nosuid,nodev,noexec
│ │ ├─/sys/fs/cgroup/systemd       cgroup       cgroup       rw,nosuid,nodev,noexec,relatime
│ │ ├─/sys/fs/cgroup/blkio         cgroup       cgroup       rw,nosuid,nodev,noexec,relatime
│ │ ├─/sys/fs/cgroup/cpu,cpuacct   cgroup       cgroup       rw,nosuid,nodev,noexec,relatime
│ │ ├─/sys/fs/cgroup/devices       cgroup       cgroup       rw,nosuid,nodev,noexec,relatime
│ │ ├─/sys/fs/cgroup/hugetlb       cgroup       cgroup       rw,nosuid,nodev,noexec,relatime
│ │ ├─/sys/fs/cgroup/pids          cgroup       cgroup       rw,nosuid,nodev,noexec,relatime
│ │ ├─/sys/fs/cgroup/memory        cgroup       cgroup       rw,nosuid,nodev,noexec,relatime
│ │ ├─/sys/fs/cgroup/cpuset        cgroup       cgroup       rw,nosuid,nodev,noexec,relatime
... and so on ... 
Thanks to all contributors. The next version v2.31 is planned for September 2017.

Friday, January 6, 2017

10 years with util-linux project!

I can't believe that it's already 10 years we have active community around basic Linux utils.

Yes, we had util-linux before (and many thanks to Adrian Bunk and Andries E. Brouwer), but I believe that with git and close collaboration between Linux distributions and Linux kernel community it better now :-)
  • ~11000 commits
  • ~460 unique contributors
  • ~630 regression tests
  • ~100 utils, 5 shared libs with public API
  • ~16 major releases (from v2.13 to v2.29)
  • ~26 translated languages
  • ~10000 e-mails on mailing list

  • ported to GNU Hurd, FreeBSD and XOS
  • used by all mainstream Linux distros (as well as by Systemd haters ;-)
  • modern autotools based build system

  • merges from another projects
    • libblkid, libuuid and fsck from e2fsprogs
    • sulogin, last, utmpdump  and mounpoint from sysvinit
    • su from coreutils

  • many new utils (lsblk, findmnt, wipefs, rtcwake, unshare, nsenter, prlimit, blkdiscard, flock, fstrim, ipcmk, ldattach, lscpu, lsipc, lslocks, lslogins, resizepart, setarch, setpriv, switch_root, swaplabel)

  • many rewrites (libblkid probing code, mount, fdisks, etc.)

  • new shared libraries
    • libmount
    • libfdisk
    • libsmartcols

...the first original announce: https://lkml.org/lkml/2006/12/18/12

... gource video with all the ten years https://youtu.be/YcG6hHGD-78 :-)

Thanks to all contributors!

Thursday, October 6, 2016

util-linux v2.29 -- what's new?

The release v2.29 (now rc1) is without dramatical changes, the small exception is libsmartcols where we have many improvements. 

The old good cal(1) is more user-friendly now. It's possible to specify month by name (e.g. "cal January 2017") and use relative placeholders, for example:

        cal now
        cal '1 year ago'
        cal '+2 months'

fdisk(8) allows to wipe newly created partitions -- the feature is possible to control by new command line option --wipe-partitions[==auto|never|default]. 
 The default in the interactive mode is to ask user when a filesystem or RAID signature is detected. The goal is to be sure that new block devices are usable without any collisions and extra wipefs(8) step (because users are lazy and mkfs-like programs are often no smart enough to wipe the device). 

findmnt --verify is probably the most attractive new feature for admins. The command scans /etc/fstab and tries to verify the configuration. The traditional way is to use "mount -a" for this purpose, but it's overkill. The new --verify does not call mount(2), but it checks parsability, LABEL/UUID/etc. translation to paths, mountpoints order, support for specified FS types. The option --verify together with --verbose provides many details. 

For example my ext4 filesystems:

# findmnt --verify --verbose -t ext4
   [ ] target exists
   [ ] LABEL=ROOT translated to /dev/sda4
   [ ] source /dev/sda4 exists
   [ ] FS type is ext4
   [W] recommended root FS passno is 1 (current is 2)
   [ ] target exists 
   [ ] UUID=c5490147-2a6c-4c8a-aa1b-33492034f927 translated to /dev/sda2
   [ ] source /dev/sda2 exists
   [ ] FS type is ext4
   [ ] target exists
   [ ] UUID=196972ad-3b13-4bba-ac54-4cb3f7b409a4 translated to /dev/sda3
   [ ] source /dev/sda3 exists
   [ ] FS type is ext4
   [E] unreachable on boot required target: No such file or directory 
   [ ] UUID=e8ce5375-29d4-4e2f-a688-d3bae4b8d162 translated to /dev/sda5
   [ ] source /dev/sda5 exists
   [ ] FS type is ext4
0 parse errors, 1 error, 1 warning

When you create multiple loop block devices from one backing file then Linux kernel does not care about possible collisions and the same on-disk filesystem is maintained by multiple independent in-memory filesystem instances. The result is obvious -- data lost and filesystem damage.

Now mount(8) rejects requests to create another device and mount filesystem for the same backing file. The command losetup --nooverlap reuse loop device if already exists for the same backing file. All the functionality calculate with offset and sizelimit options of course, so it's fine to have multiple regions (partitions) in the same image file and mount all of them in the same time. The restriction is that the regions should not overlap. Thanks to Stanislav Brabec from Suse! 

Heiko Carstens from IBM (thanks!) has improved lscpu(1) for s390. Now it supports "drawer" topology level, static and dynamic MHz, machine type and a new option --physical. 

The most important libsmartcols change is probably better support for multi-line cells. Now the library supports custom cell wrap functions -- this allows to wrap your text in cells after words, line breaks, etc. See multi-line cells (WRAPNL column) output: 

aaaa            1      0 aaa
├─bbb           2      1 bbbbb
│ ├─ee          5      2 hello
│ │                      baby
│ └─ffff        6      2 aaa
│                        bbb
│                        ccc
│                        ddd
├─ccccc         3      1 cccc
│ │                      CCCC
│ └─gggggg      7      3 eee
│   ├─hhh       8      7 fffff
│   │ └─iiiiii  9      8 g
│   │                    hhhhh
│   └─jj       10      7 ppppppppp
└─dddddd        4      1 dddddddd

The another change is support for user defined padding chars; we use this feature for LIBSMARTCOLS_DEBUG_PADDING=on|off, for example: 


For me really important is that we have regression tests for all libsmartcols table and tree formatting code now :-) 

Igor Gnatenko from Red Hat (thanks!) continues to work on Python binding for libsmartcols, see https://github.com/ignatenkobrain/python-smartcols and see example below.

The idea is to use libsmartcols as output formatter for Fedora/RHEL dnf (package manager for RPM-based Linux distributions, yum replacement). This is also reason why libsmartcols has been massively extended and improved in the last releases. 

That's all. Thanks also to Werner Fink, Sami Kerola, Ruediger Meier and many others contributors! 

import smartcols
tb = smartcols.Table()
name = tb.new_column("NAME")
name.tree = True
age = tb.new_column("AGE")
age.right = True
ggf = tb.new_line()
ggf[name] = "John"
ggf[age] = "70"
gfa = tb.new_line(ggf)
gfa[name] = "Donald"
gfa[age] = "50"
fa = tb.new_line(gfa)
fa[name] = "Benny"
fa[age] = "30"
ln = tb.new_line(fa)
ln[name] = "Arlen"
ln[age] = "5"
ln = tb.new_line(fa)
ln[name] = "Gerge"
ln[age] = "7"
fa = tb.new_line(gfa)
fa[name] = "Berry"
fa[age] = "32"
ln = tb.new_line(ggf)
ln[name] = "Alex"
ln[age] = "44"
NAME        AGE
John         70
├─Donald     50
│ ├─Benny    30
│ │ ├─Arlen   5
│ │ └─Gerge   7
│ └─Berry    32
└─Alex       44