diff --git a/SOURCES/2.17-kill-strtol.patch b/SOURCES/2.17-kill-strtol.patch new file mode 100644 index 0000000..bedd0ab --- /dev/null +++ b/SOURCES/2.17-kill-strtol.patch @@ -0,0 +1,22 @@ +diff -up util-linux-2.23.2/misc-utils/kill.c.kzak util-linux-2.23.2/misc-utils/kill.c +--- util-linux-2.23.2/misc-utils/kill.c.kzak 2013-06-13 09:46:10.448650861 +0200 ++++ util-linux-2.23.2/misc-utils/kill.c 2014-09-25 10:08:27.879359310 +0200 +@@ -48,6 +48,7 @@ + #include /* for isdigit() */ + #include + #include ++#include + + #include "c.h" + #include "nls.h" +@@ -279,8 +280,9 @@ int main (int argc, char *argv[]) + the rest of the arguments should be process ids and names. + kill them. */ + for (errors = 0; (arg = *argv) != NULL; argv++) { ++ errno = 0; + pid = strtol (arg, &ep, 10); +- if (! *ep) ++ if (errno == 0 && ep && *ep == '\0' && arg < ep) + errors += kill_verbose (arg, pid, numsig); + else { + struct proc_processes *ps = proc_open_processes(); diff --git a/SOURCES/2.24-libmount-3.14.patch b/SOURCES/2.24-libmount-3.14.patch new file mode 100644 index 0000000..64de1a9 --- /dev/null +++ b/SOURCES/2.24-libmount-3.14.patch @@ -0,0 +1,80 @@ +diff -up util-linux-2.23.2/libmount/src/tab.c.kzak util-linux-2.23.2/libmount/src/tab.c +--- util-linux-2.23.2/libmount/src/tab.c.kzak 2013-07-30 10:39:26.218738358 +0200 ++++ util-linux-2.23.2/libmount/src/tab.c 2014-09-25 10:53:43.525269554 +0200 +@@ -47,6 +47,8 @@ + #include "strutils.h" + #include "loopdev.h" + ++static int is_mountinfo(struct libmnt_table *tb); ++ + /** + * mnt_new_table: + * +@@ -233,7 +235,7 @@ int mnt_table_get_root_fs(struct libmnt_ + assert(tb); + assert(root); + +- if (!tb || !root) ++ if (!tb || !root || !is_mountinfo(tb)) + return -EINVAL; + + DBG(TAB, mnt_debug_h(tb, "lookup root fs")); +@@ -241,8 +243,6 @@ int mnt_table_get_root_fs(struct libmnt_ + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + int id = mnt_fs_get_parent_id(fs); +- if (!id) +- break; /* @tab is not mountinfo file? */ + + if (!*root || id < root_id) { + *root = fs; +@@ -250,7 +250,7 @@ int mnt_table_get_root_fs(struct libmnt_ + } + } + +- return root_id ? 0 : -EINVAL; ++ return *root ? 0 : -EINVAL; + } + + /** +@@ -271,15 +271,13 @@ int mnt_table_next_child_fs(struct libmn + struct libmnt_fs *fs; + int parent_id, lastchld_id = 0, chld_id = 0; + +- if (!tb || !itr || !parent) ++ if (!tb || !itr || !parent || !is_mountinfo(tb)) + return -EINVAL; + + DBG(TAB, mnt_debug_h(tb, "lookup next child of '%s'", + mnt_fs_get_target(parent))); + + parent_id = mnt_fs_get_id(parent); +- if (!parent_id) +- return -EINVAL; + + /* get ID of the previously returned child */ + if (itr->head && itr->p != itr->head) { +@@ -310,7 +308,7 @@ int mnt_table_next_child_fs(struct libmn + } + } + +- if (!chld_id) ++ if (!*chld) + return 1; /* end of iterator */ + + /* set the iterator to the @chld for the next call */ +diff -up util-linux-2.23.2/misc-utils/findmnt.c.kzak util-linux-2.23.2/misc-utils/findmnt.c +--- util-linux-2.23.2/misc-utils/findmnt.c.kzak 2013-07-30 11:07:35.138395654 +0200 ++++ util-linux-2.23.2/misc-utils/findmnt.c 2014-09-25 10:49:50.953050560 +0200 +@@ -826,8 +826,9 @@ static int tab_is_tree(struct libmnt_tab + if (!itr) + return 0; + +- if (mnt_table_next_fs(tb, itr, &fs) == 0) +- rc = mnt_fs_get_id(fs) > 0 && mnt_fs_get_parent_id(fs) > 0; ++ rc = (mnt_table_next_fs(tb, itr, &fs) == 0 && ++ mnt_fs_is_kernel(fs) && ++ mnt_fs_get_root(fs)); + + mnt_free_iter(itr); + return rc; diff --git a/SOURCES/2.24-losetup-offset.patch b/SOURCES/2.24-losetup-offset.patch new file mode 100644 index 0000000..a93cc55 --- /dev/null +++ b/SOURCES/2.24-losetup-offset.patch @@ -0,0 +1,32 @@ +diff -up util-linux-2.23.2/lib/loopdev.c.kzak util-linux-2.23.2/lib/loopdev.c +--- util-linux-2.23.2/lib/loopdev.c.kzak 2014-09-25 10:16:23.521897462 +0200 ++++ util-linux-2.23.2/lib/loopdev.c 2014-09-25 10:23:38.852050990 +0200 +@@ -1129,6 +1129,12 @@ static int loopcxt_check_size(struct loo + return -errno; + } + ++ /* It's block device, so, align to 512-byte sectors */ ++ if (expected_size % 512) { ++ DBG(lc, loopdev_debug("expected size misaligned to 512-byte sectors")); ++ expected_size = (expected_size >> 9) << 9; ++ } ++ + if (expected_size != size) { + DBG(lc, loopdev_debug("warning: loopdev and expected " + "size dismatch (%ju/%ju)", +diff -up util-linux-2.23.2/sys-utils/losetup.c.kzak util-linux-2.23.2/sys-utils/losetup.c +--- util-linux-2.23.2/sys-utils/losetup.c.kzak 2014-09-25 10:16:23.521897462 +0200 ++++ util-linux-2.23.2/sys-utils/losetup.c 2014-09-25 10:23:38.852050990 +0200 +@@ -632,11 +632,7 @@ int main(int argc, char **argv) + /* errors */ + errpre = hasdev && loopcxt_get_fd(&lc) < 0 ? + loopcxt_get_device(&lc) : file; +- if (errno == ERANGE && offset && offset % 512) +- warnx(_("%s: failed to set up loop device, " +- "offset is not 512-byte aligned."), errpre); +- else +- warn(_("%s: failed to set up loop device"), errpre); ++ warn(_("%s: failed to set up loop device"), errpre); + break; + } while (hasdev == 0); + diff --git a/SOURCES/2.24-partx-update.patch b/SOURCES/2.24-partx-update.patch new file mode 100644 index 0000000..f1dc8e7 --- /dev/null +++ b/SOURCES/2.24-partx-update.patch @@ -0,0 +1,131 @@ +From 5cc378e4cdeb957b405e0264a09295eda7d75ff7 Mon Sep 17 00:00:00 2001 +From: Scott Moser +Date: Mon, 13 Jan 2014 15:32:49 -0500 +Subject: [PATCH] partx: fix --update ranges and out of order tables + +partx --update DEVICE NUMBER +was broken in 2 cases: + * if NUMBER != 1 + * if the partition table was "out of order". + Ie, where sda2 came after sda3. + +References: https://bugs.launchpad.net/ubuntu/+source/cloud-utils/+bug/1244662 +Signed-off-by: Scott Moser +Signed-off-by: Karel Zak +--- + disk-utils/partx.c | 75 ++++++++++++++++++++++++++++++++++++------------------ + 1 file changed, 50 insertions(+), 25 deletions(-) + +diff --git a/disk-utils/partx.c b/disk-utils/partx.c +index 880d779..df03e59 100644 +--- a/disk-utils/partx.c ++++ b/disk-utils/partx.c +@@ -412,10 +412,41 @@ static void upd_parts_warnx(const char *device, int first, int last) + device, first, last); + } + ++/** ++ * get_partition_by_partno: ++ * @ls: partitions list ++ * @n: the partition number (e.g. 'N' from sda'N') ++ * ++ * This does not assume any order of the input blkid_partlist. ++ * And correctly handles "out of order" partition tables. ++ * partition N is located after partition N+1 on the disk. ++ * ++ * Returns: partition object or NULL in case or error. ++ */ ++blkid_partition get_partition_by_partno(blkid_partlist ls, int n) ++{ ++ int i, nparts; ++ blkid_partition par; ++ if (!ls) ++ return NULL; ++ ++ nparts = blkid_partlist_numof_partitions(ls); ++ if (nparts < 0) ++ return NULL; ++ ++ for (i = 0; i < nparts; i++) { ++ par = blkid_partlist_get_partition(ls, i); ++ if (n == blkid_partition_get_partno(par)) { ++ return par; ++ } ++ } ++ return NULL; ++} ++ + static int upd_parts(int fd, const char *device, dev_t devno, + blkid_partlist ls, int lower, int upper) + { +- int i, n, an, nparts, rc = 0, errfirst = 0, errlast = 0, err; ++ int n, nparts, rc = 0, errfirst = 0, errlast = 0, err; + blkid_partition par; + uintmax_t start, size; + +@@ -441,18 +472,16 @@ static int upd_parts(int fd, const char *device, dev_t devno, + return -1; + } + +- for (i = 0, n = lower; n <= upper; n++) { +- par = blkid_partlist_get_partition(ls, i); +- an = blkid_partition_get_partno(par); +- +- if (lower && n < lower) +- continue; +- if (upper && n > upper) ++ for (n = lower; n <= upper; n++) { ++ par = get_partition_by_partno(ls, n); ++ if (!par) { ++ if (verbose) ++ warn(_("%s: no partition #%d"), device, n); + continue; ++ } + + start = blkid_partition_get_start(par); + size = blkid_partition_get_size(par); +- + if (blkid_partition_is_extended(par)) + /* + * Let's follow the Linux kernel and reduce +@@ -463,25 +492,21 @@ static int upd_parts(int fd, const char *device, dev_t devno, + err = partx_del_partition(fd, n); + if (err == -1 && errno == ENXIO) + err = 0; /* good, it already doesn't exist */ +- if (an == n) ++ if (err == -1 && errno == EBUSY) + { +- if (i < nparts) +- i++; +- if (err == -1 && errno == EBUSY) +- { +- /* try to resize */ +- err = partx_resize_partition(fd, n, start, size); +- if (verbose) +- printf(_("%s: partition #%d resized\n"), device, n); +- if (err == 0) +- continue; +- } +- if (err == 0 && partx_add_partition(fd, n, start, size) == 0) { +- if (verbose) +- printf(_("%s: partition #%d added\n"), device, n); ++ /* try to resize */ ++ err = partx_resize_partition(fd, n, start, size); ++ if (verbose) ++ printf(_("%s: partition #%d resized\n"), device, n); ++ if (err == 0) + continue; +- } + } ++ if (err == 0 && partx_add_partition(fd, n, start, size) == 0) { ++ if (verbose) ++ printf(_("%s: partition #%d added\n"), device, n); ++ continue; ++ } ++ + if (err == 0) + continue; + rc = -1; +-- +1.9.3 + diff --git a/SOURCES/2.24-unshare-mount-fork.patch b/SOURCES/2.24-unshare-mount-fork.patch new file mode 100644 index 0000000..1f2d816 --- /dev/null +++ b/SOURCES/2.24-unshare-mount-fork.patch @@ -0,0 +1,248 @@ +diff -up util-linux-2.23.2/sys-utils/Makemodule.am.kzak util-linux-2.23.2/sys-utils/Makemodule.am +--- util-linux-2.23.2/sys-utils/Makemodule.am.kzak 2014-09-25 14:16:33.526384729 +0200 ++++ util-linux-2.23.2/sys-utils/Makemodule.am 2014-09-25 14:15:34.861825005 +0200 +@@ -290,6 +290,7 @@ usrbin_exec_PROGRAMS += unshare + dist_man_MANS += sys-utils/unshare.1 + unshare_SOURCES = sys-utils/unshare.c + unshare_LDADD = $(LDADD) libcommon.la ++unshare_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir) + endif + + if BUILD_NSENTER +diff -up util-linux-2.23.2/sys-utils/unshare.1.kzak util-linux-2.23.2/sys-utils/unshare.1 +--- util-linux-2.23.2/sys-utils/unshare.1.kzak 2014-09-25 14:14:30.194208005 +0200 ++++ util-linux-2.23.2/sys-utils/unshare.1 2014-09-25 14:15:17.617660476 +0200 +@@ -1,63 +1,82 @@ + .\" Process this file with + .\" groff -man -Tascii lscpu.1 + .\" +-.TH UNSHARE 1 "January 2013" "util-linux" "User Commands" ++.TH UNSHARE 1 "July 2013" "util-linux" "User Commands" + .SH NAME + unshare \- run program with some namespaces unshared from parent + .SH SYNOPSIS + .B unshare + .RI [ options ] +-program ++.I program + .RI [ arguments ] + .SH DESCRIPTION +-Unshares specified namespaces from parent process and then executes specified +-program. Unshareable namespaces are: ++Unshares the indicated namespaces from the parent process and then executes ++the specified program. The namespaces to be unshared are indicated via ++options. Unshareable namespaces are: + .TP + .BR "mount namespace" +-mounting and unmounting filesystems will not affect rest of the system ++Mounting and unmounting filesystems will not affect the rest of the system + (\fBCLONE_NEWNS\fP flag), except for filesystems which are explicitly marked as +-shared (by mount --make-shared). See /proc/self/mountinfo for the shared flags. ++shared (with \fBmount --make-shared\fP; see \fI/proc/self/mountinfo\fP for the ++\fBshared\fP flags). ++ ++It's recommended to use \fBmount --make-rprivate\fP or \fBmount --make-rslave\fP ++after \fBunshare --mount\fP to make sure that mountpoints in the new namespace ++are really unshared from parental namespace. + .TP + .BR "UTS namespace" +-setting hostname, domainname will not affect rest of the system +-(\fBCLONE_NEWUTS\fP flag). ++Setting hostname or domainname will not affect the rest of the system. ++(\fBCLONE_NEWUTS\fP flag) + .TP + .BR "IPC namespace" +-process will have independent namespace for System V message queues, semaphore +-sets and shared memory segments (\fBCLONE_NEWIPC\fP flag). ++The process will have an independent namespace for System V message queues, ++semaphore sets and shared memory segments. (\fBCLONE_NEWIPC\fP flag) + .TP + .BR "network namespace" +-process will have independent IPv4 and IPv6 stacks, IP routing tables, firewall +-rules, the \fI/proc/net\fP and \fI/sys/class/net\fP directory trees, sockets +-etc. (\fBCLONE_NEWNET\fP flag). ++The process will have independent IPv4 and IPv6 stacks, IP routing tables, ++firewall rules, the \fI/proc/net\fP and \fI/sys/class/net\fP directory trees, ++sockets, etc. (\fBCLONE_NEWNET\fP flag) + .TP + .BR "pid namespace" +-children will have a distinct set of pid to process mappings than their parent. +-(\fBCLONE_NEWPID\fP flag). ++Children will have a distinct set of PID to process mappings from their parent. ++(\fBCLONE_NEWPID\fP flag) + .PP +-See the \fBclone\fR(2) for exact semantics of the flags. ++See \fBclone\fR(2) for the exact semantics of the flags. + .SH OPTIONS + .TP + .BR \-h , " \-\-help" +-Print a help message, +-.TP +-.BR \-m , " \-\-mount" +-Unshare the mount namespace, +-.TP +-.BR \-u , " \-\-uts" +-Unshare the UTS namespace, ++Display help text and exit. + .TP + .BR \-i , " \-\-ipc" +-Unshare the IPC namespace, ++Unshare the IPC namespace. ++.TP ++.BR \-m , " \-\-mount" ++Unshare the mount namespace. + .TP + .BR \-n , " \-\-net" + Unshare the network namespace. + .TP + .BR \-p , " \-\-pid" + Unshare the pid namespace. ++See also the \fB--fork\fP and \fB--mount-proc\fP options. ++.TP ++.BR \-u , " \-\-uts" ++Unshare the UTS namespace. ++.TP ++.BR \-f , " \-\-fork" ++Fork the specified \fIprogram\fR as a child process of \fBunshare\fR rather than ++running it directly. This is useful when creating a new pid namespace. ++.TP ++.BR \-\-mount-proc "[=\fImountpoint\fP]" ++Just before running the program, mount the proc filesystem at the \fImountpoint\fP ++(default is /proc). This is useful when creating a new pid namespace. It also ++implies creating a new mount namespace since the /proc mount would otherwise ++mess up existing programs on the system. The new proc filesystem is explicitly ++mounted as private (by MS_PRIVATE|MS_REC). + .SH SEE ALSO + .BR unshare (2), +-.BR clone (2) ++.BR clone (2), ++.BR mount (8) + .SH BUGS + None known so far. + .SH AUTHOR +diff -up util-linux-2.23.2/sys-utils/unshare.c.kzak util-linux-2.23.2/sys-utils/unshare.c +--- util-linux-2.23.2/sys-utils/unshare.c.kzak 2014-09-25 14:14:30.194208005 +0200 ++++ util-linux-2.23.2/sys-utils/unshare.c 2014-09-25 14:15:34.861825005 +0200 +@@ -24,12 +24,19 @@ + #include + #include + #include ++#include ++#include ++ ++/* we only need some defines missing in sys/mount.h, no libmount linkage */ ++#include + + #include "nls.h" + #include "c.h" +-#include "closestream.h" + #include "namespace.h" + #include "exec_shell.h" ++#include "xalloc.h" ++#include "pathnames.h" ++ + + static void usage(int status) + { +@@ -40,11 +47,13 @@ static void usage(int status) + _(" %s [options] [args...]\n"), program_invocation_short_name); + + fputs(USAGE_OPTIONS, out); +- fputs(_(" -m, --mount unshare mounts namespace\n"), out); +- fputs(_(" -u, --uts unshare UTS namespace (hostname etc)\n"), out); +- fputs(_(" -i, --ipc unshare System V IPC namespace\n"), out); +- fputs(_(" -n, --net unshare network namespace\n"), out); +- fputs(_(" -p, --pid unshare pid namespace\n"), out); ++ fputs(_(" -m, --mount unshare mounts namespace\n"), out); ++ fputs(_(" -u, --uts unshare UTS namespace (hostname etc)\n"), out); ++ fputs(_(" -i, --ipc unshare System V IPC namespace\n"), out); ++ fputs(_(" -n, --net unshare network namespace\n"), out); ++ fputs(_(" -p, --pid unshare pid namespace\n"), out); ++ fputs(_(" -f, --fork fork before launching \n"), out); ++ fputs(_(" --mount-proc[=] mount proc filesystem first (implies --mount)\n"), out); + + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_HELP, out); +@@ -56,6 +65,9 @@ static void usage(int status) + + int main(int argc, char *argv[]) + { ++ enum { ++ OPT_MOUNTPROC = CHAR_MAX + 1 ++ }; + static const struct option longopts[] = { + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'V'}, +@@ -64,20 +76,24 @@ int main(int argc, char *argv[]) + { "ipc", no_argument, 0, 'i' }, + { "net", no_argument, 0, 'n' }, + { "pid", no_argument, 0, 'p' }, ++ { "fork", no_argument, 0, 'f' }, ++ { "mount-proc", optional_argument, 0, OPT_MOUNTPROC }, + { NULL, 0, 0, 0 } + }; + + int unshare_flags = 0; ++ int c, forkit = 0; ++ const char *procmnt = NULL; + +- int c; +- +- setlocale(LC_MESSAGES, ""); ++ setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +- atexit(close_stdout); + +- while ((c = getopt_long(argc, argv, "hVmuinp", longopts, NULL)) != -1) { ++ while ((c = getopt_long(argc, argv, "+fhVmuinp", longopts, NULL)) != -1) { + switch (c) { ++ case 'f': ++ forkit = 1; ++ break; + case 'h': + usage(EXIT_SUCCESS); + case 'V': +@@ -98,6 +114,10 @@ int main(int argc, char *argv[]) + case 'p': + unshare_flags |= CLONE_NEWPID; + break; ++ case OPT_MOUNTPROC: ++ unshare_flags |= CLONE_NEWNS; ++ procmnt = optarg ? optarg : "/proc"; ++ break; + default: + usage(EXIT_FAILURE); + } +@@ -106,6 +126,31 @@ int main(int argc, char *argv[]) + if (-1 == unshare(unshare_flags)) + err(EXIT_FAILURE, _("unshare failed")); + ++ if (forkit) { ++ int status; ++ pid_t pid = fork(); ++ ++ switch(pid) { ++ case -1: ++ err(EXIT_FAILURE, _("fork failed")); ++ case 0: /* child */ ++ break; ++ default: /* parent */ ++ if (waitpid(pid, &status, 0) == -1) ++ err(EXIT_FAILURE, _("waitpid failed")); ++ if (WIFEXITED(status)) ++ return WEXITSTATUS(status); ++ else if (WIFSIGNALED(status)) ++ kill(getpid(), WTERMSIG(status)); ++ err(EXIT_FAILURE, _("child exit failed")); ++ } ++ } ++ ++ if (procmnt && ++ (mount("none", procmnt, NULL, MS_PRIVATE|MS_REC, NULL) != 0 || ++ mount("proc", procmnt, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)) ++ err(EXIT_FAILURE, _("mount %s failed"), procmnt); ++ + if (optind < argc) { + execvp(argv[optind], argv + optind); + err(EXIT_FAILURE, _("failed to execute %s"), argv[optind]); diff --git a/SOURCES/2.25-dmesg-w.patch b/SOURCES/2.25-dmesg-w.patch new file mode 100644 index 0000000..c01449c --- /dev/null +++ b/SOURCES/2.25-dmesg-w.patch @@ -0,0 +1,12 @@ +diff -up util-linux-2.23.2/sys-utils/dmesg.c.kzak util-linux-2.23.2/sys-utils/dmesg.c +--- util-linux-2.23.2/sys-utils/dmesg.c.kzak 2013-07-30 11:22:47.213494455 +0200 ++++ util-linux-2.23.2/sys-utils/dmesg.c 2014-09-24 10:24:49.179371108 +0200 +@@ -991,6 +991,8 @@ static int init_kmsg(struct dmesg_contro + + if (!ctl->follow) + mode |= O_NONBLOCK; ++ else ++ setlinebuf(stdout); + + ctl->kmsg = open("/dev/kmsg", mode); + if (ctl->kmsg < 0) diff --git a/SOURCES/2.25-hwclock-hang.patch b/SOURCES/2.25-hwclock-hang.patch new file mode 100644 index 0000000..de7df17 --- /dev/null +++ b/SOURCES/2.25-hwclock-hang.patch @@ -0,0 +1,255 @@ +From 4a44a54b3caf77923f0e3f1d5bdf5eda6ef07f62 Mon Sep 17 00:00:00 2001 +From: Chris MacGregor +Date: Thu, 27 Feb 2014 10:40:59 -0800 +Subject: [PATCH] hwclock: fix possible hang and other + set_hardware_clock_exact() issues + +In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the +process gets pre-empted (for more than 100ms) before reaching the time for +which it waits: + +1. The "continue" statement causes execution to skip the final tdiff +assignment at the end of the do...while loop, leading to the while condition +using the wrong value of tdiff, and thus always exiting the loop once +newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below. + +2. The previously-existing bug is that because it starts over waiting for the +desired time whenever two successive calls to gettimeofday() return values > +100ms apart, the loop will never terminate unless the process holds the CPU +(without losing it for more than 100ms) for at least 500ms. This can happen +on a heavily loaded machine or on a virtual machine (or on a heavily loaded +virtual machine). This has been observed to occur, preventing a machine from +completing the shutdown or reboot process due to a "hwclock --systohc" call in +a shutdown script. + +The new implementation presented in this patch takes a somewhat different +approach, intended to accomplish the same goals: + +It computes the desired target system time (at which the requested hardware +clock time will be applied to the hardware clock), and waits for that time to +arrive. If it misses the time (such as due to being pre-empted for too long), +it recalculates the target time, and increases the tolerance (how late it can +be relative to the target time, and still be "close enough". Thus, if all is +well, the time will be set *very* precisely. On a machine where the hwclock +process is repeatedly pre-empted, it will set the time as precisely as is +possible under the conditions present on that particular machine. In any +case, it will always terminate eventually (and pretty quickly); it will never +hang forever. + +[kzak@redhat.com: - tiny coding style changes] + +Signed-off-by: Chris MacGregor +Signed-off-by: Karel Zak +--- + sys-utils/hwclock.c | 170 ++++++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 131 insertions(+), 39 deletions(-) + +diff --git a/sys-utils/hwclock.c b/sys-utils/hwclock.c +index 30660d4..395b5c3 100644 +--- a/sys-utils/hwclock.c ++++ b/sys-utils/hwclock.c +@@ -125,7 +125,7 @@ struct adjtime { + * We are running in debug mode, wherein we put a lot of information about + * what we're doing to standard output. + */ +-bool debug; ++int debug; + + /* Workaround for Award 4.50g BIOS bug: keep the year in a file. */ + bool badyear; +@@ -526,43 +526,141 @@ set_hardware_clock_exact(const time_t sethwtime, + const struct timeval refsystime, + const bool universal, const bool testing) + { +- time_t newhwtime = sethwtime; +- struct timeval beginsystime, nowsystime; +- double tdiff; +- int time_resync = 1; +- + /* +- * Now delay some more until Hardware Clock time newhwtime arrives. +- * The 0.5 s is because the Hardware Clock always sets to your set +- * time plus 500 ms (because it is designed to update to the next +- * second precisely 500 ms after you finish the setting). ++ * The Hardware Clock can only be set to any integer time plus one ++ * half second. The integer time is required because there is no ++ * interface to set or get a fractional second. The additional half ++ * second is because the Hardware Clock updates to the following ++ * second precisely 500 ms (not 1 second!) after you release the ++ * divider reset (after setting the new time) - see description of ++ * DV2, DV1, DV0 in Register A in the MC146818A data sheet (and note ++ * that although that document doesn't say so, real-world code seems ++ * to expect that the SET bit in Register B functions the same way). ++ * That means that, e.g., when you set the clock to 1:02:03, it ++ * effectively really sets it to 1:02:03.5, because it will update to ++ * 1:02:04 only half a second later. Our caller passes the desired ++ * integer Hardware Clock time in sethwtime, and the corresponding ++ * system time (which may have a fractional part, and which may or may ++ * not be the same!) in refsystime. In an ideal situation, we would ++ * then apply sethwtime to the Hardware Clock at refsystime+500ms, so ++ * that when the Hardware Clock ticks forward to sethwtime+1s half a ++ * second later at refsystime+1000ms, everything is in sync. So we ++ * spin, waiting for gettimeofday() to return a time at or after that ++ * time (refsystime+500ms) up to a tolerance value, initially 1ms. If ++ * we miss that time due to being preempted for some other process, ++ * then we increase the margin a little bit (initially 1ms, doubling ++ * each time), add 1 second (or more, if needed to get a time that is ++ * in the future) to both the time for which we are waiting and the ++ * time that we will apply to the Hardware Clock, and start waiting ++ * again. ++ * ++ * For example, the caller requests that we set the Hardware Clock to ++ * 1:02:03, with reference time (current system time) = 6:07:08.250. ++ * We want the Hardware Clock to update to 1:02:04 at 6:07:09.250 on ++ * the system clock, and the first such update will occur 0.500 ++ * seconds after we write to the Hardware Clock, so we spin until the ++ * system clock reads 6:07:08.750. If we get there, great, but let's ++ * imagine the system is so heavily loaded that our process is ++ * preempted and by the time we get to run again, the system clock ++ * reads 6:07:11.990. We now want to wait until the next xx:xx:xx.750 ++ * time, which is 6:07:12.750 (4.5 seconds after the reference time), ++ * at which point we will set the Hardware Clock to 1:02:07 (4 seconds ++ * after the originally requested time). If we do that successfully, ++ * then at 6:07:13.250 (5 seconds after the reference time), the ++ * Hardware Clock will update to 1:02:08 (5 seconds after the ++ * originally requested time), and all is well thereafter. + */ +- do { +- if (time_resync) { +- gettimeofday(&beginsystime, NULL); +- tdiff = time_diff(beginsystime, refsystime); +- newhwtime = sethwtime + (int)(tdiff + 0.5); +- if (debug) +- printf(_ +- ("Time elapsed since reference time has been %.6f seconds.\n" +- "Delaying further to reach the new time.\n"), +- tdiff); +- time_resync = 0; ++ ++ time_t newhwtime = sethwtime; ++ double target_time_tolerance_secs = 0.001; /* initial value */ ++ double tolerance_incr_secs = 0.001; /* initial value */ ++ const double RTC_SET_DELAY_SECS = 0.5; /* 500 ms */ ++ const struct timeval RTC_SET_DELAY_TV = { 0, RTC_SET_DELAY_SECS * 1E6 }; ++ ++ struct timeval targetsystime; ++ struct timeval nowsystime; ++ struct timeval prevsystime = refsystime; ++ double deltavstarget; ++ ++ timeradd(&refsystime, &RTC_SET_DELAY_TV, &targetsystime); ++ ++ while (1) { ++ double ticksize; ++ ++ /* FOR TESTING ONLY: inject random delays of up to 1000ms */ ++ if (debug >= 10) { ++ int usec = random() % 1000000; ++ printf(_("sleeping ~%d usec\n"), usec); ++ usleep(usec); + } + + gettimeofday(&nowsystime, NULL); +- tdiff = time_diff(nowsystime, beginsystime); +- if (tdiff < 0) { +- time_resync = 1; /* probably backward time reset */ +- continue; +- } +- if (tdiff > 0.1) { +- time_resync = 1; /* probably forward time reset */ +- continue; ++ deltavstarget = time_diff(nowsystime, targetsystime); ++ ticksize = time_diff(nowsystime, prevsystime); ++ prevsystime = nowsystime; ++ ++ if (ticksize < 0) { ++ if (debug) ++ printf(_("time jumped backward %.6f seconds " ++ "to %ld.%06d - retargeting\n"), ++ ticksize, (long)nowsystime.tv_sec, ++ (int)nowsystime.tv_usec); ++ /* The retarget is handled at the end of the loop. */ ++ } else if (deltavstarget < 0) { ++ /* deltavstarget < 0 if current time < target time */ ++ if (debug >= 2) ++ printf(_("%ld.%06d < %ld.%06d (%.6f)\n"), ++ (long)nowsystime.tv_sec, ++ (int)nowsystime.tv_usec, ++ (long)targetsystime.tv_sec, ++ (int)targetsystime.tv_usec, ++ deltavstarget); ++ continue; /* not there yet - keep spinning */ ++ } else if (deltavstarget <= target_time_tolerance_secs) { ++ /* Close enough to the target time; done waiting. */ ++ break; ++ } else /* (deltavstarget > target_time_tolerance_secs) */ { ++ /* ++ * We missed our window. Increase the tolerance and ++ * aim for the next opportunity. ++ */ ++ if (debug) ++ printf(_("missed it - %ld.%06d is too far " ++ "past %ld.%06d (%.6f > %.6f)\n"), ++ (long)nowsystime.tv_sec, ++ (int)nowsystime.tv_usec, ++ (long)targetsystime.tv_sec, ++ (int)targetsystime.tv_usec, ++ deltavstarget, ++ target_time_tolerance_secs); ++ target_time_tolerance_secs += tolerance_incr_secs; ++ tolerance_incr_secs *= 2; + } +- beginsystime = nowsystime; +- tdiff = time_diff(nowsystime, refsystime); +- } while (newhwtime == sethwtime + (int)(tdiff + 0.5)); ++ ++ /* ++ * Aim for the same offset (tv_usec) within the second in ++ * either the current second (if that offset hasn't arrived ++ * yet), or the next second. ++ */ ++ if (nowsystime.tv_usec < targetsystime.tv_usec) ++ targetsystime.tv_sec = nowsystime.tv_sec; ++ else ++ targetsystime.tv_sec = nowsystime.tv_sec + 1; ++ } ++ ++ newhwtime = sethwtime ++ + (int)(time_diff(nowsystime, refsystime) ++ - RTC_SET_DELAY_SECS /* don't count this */ ++ + 0.5 /* for rounding */); ++ if (debug) ++ printf(_("%ld.%06d is close enough to %ld.%06d (%.6f < %.6f)\n" ++ "Set RTC to %ld (%ld + %d; refsystime = %ld.%06d)\n"), ++ (long)nowsystime.tv_sec, (int)nowsystime.tv_usec, ++ (long)targetsystime.tv_sec, (int)targetsystime.tv_usec, ++ deltavstarget, target_time_tolerance_secs, ++ (long)newhwtime, (long)sethwtime, ++ (int)(newhwtime - sethwtime), ++ (long)refsystime.tv_sec, (int)refsystime.tv_usec); + + set_hardware_clock(newhwtime, universal, testing); + } +@@ -1636,7 +1734,7 @@ int main(int argc, char **argv) + + switch (c) { + case 'D': +- debug = TRUE; ++ ++debug; + break; + case 'a': + adjust = TRUE; +@@ -1953,10 +2051,4 @@ void __attribute__((__noreturn__)) hwaudit_exit(int status) + * + * hwclock uses this method, and considers the Hardware Clock to have + * infinite precision. +- * +- * TODO: Enhancements needed: +- * +- * - When waiting for whole second boundary in set_hardware_clock_exact, +- * fail if we miss the goal by more than .1 second, as could happen if we +- * get pre-empted (by the kernel dispatcher). + */ +-- +1.9.3 + diff --git a/SOURCES/2.25-libblkid-gpt-512.patch b/SOURCES/2.25-libblkid-gpt-512.patch new file mode 100644 index 0000000..3e711ce --- /dev/null +++ b/SOURCES/2.25-libblkid-gpt-512.patch @@ -0,0 +1,22 @@ +diff -up util-linux-2.23.2/libblkid/src/partitions/gpt.c.kzak util-linux-2.23.2/libblkid/src/partitions/gpt.c +--- util-linux-2.23.2/libblkid/src/partitions/gpt.c.kzak 2014-09-25 10:36:26.761377688 +0200 ++++ util-linux-2.23.2/libblkid/src/partitions/gpt.c 2014-09-25 10:36:56.912665364 +0200 +@@ -332,7 +332,7 @@ static int probe_gpt_pt(blkid_probe pr, + + blkid_probe_use_wiper(pr, lba * blkid_probe_get_size(pr), 8); + +- if (blkid_probe_set_magic(pr, lba << 9, ++ if (blkid_probe_set_magic(pr, blkid_probe_get_sectorsize(pr) * lba, + sizeof(GPT_HEADER_SIGNATURE_STR) - 1, + (unsigned char *) GPT_HEADER_SIGNATURE_STR)) + goto err; +@@ -345,7 +345,8 @@ static int probe_gpt_pt(blkid_probe pr, + if (!ls) + goto err; + +- tab = blkid_partlist_new_parttable(ls, "gpt", lba << 9); ++ tab = blkid_partlist_new_parttable(ls, "gpt", ++ blkid_probe_get_sectorsize(pr) * lba); + if (!tab) + goto err; + diff --git a/SOURCES/2.25-libblkid-xfs.patch b/SOURCES/2.25-libblkid-xfs.patch new file mode 100644 index 0000000..adc717b --- /dev/null +++ b/SOURCES/2.25-libblkid-xfs.patch @@ -0,0 +1,177 @@ +diff -up util-linux-2.23.2/libblkid/src/superblocks/xfs.c.kzak util-linux-2.23.2/libblkid/src/superblocks/xfs.c +--- util-linux-2.23.2/libblkid/src/superblocks/xfs.c.kzak 2014-09-24 10:59:39.548315524 +0200 ++++ util-linux-2.23.2/libblkid/src/superblocks/xfs.c 2014-09-24 11:02:55.595186026 +0200 +@@ -20,20 +20,143 @@ + #include "superblocks.h" + + struct xfs_super_block { +- unsigned char xs_magic[4]; +- uint32_t xs_blocksize; +- uint64_t xs_dblocks; +- uint64_t xs_rblocks; +- uint32_t xs_dummy1[2]; +- unsigned char xs_uuid[16]; +- uint32_t xs_dummy2[15]; +- char xs_fname[12]; +- uint32_t xs_dummy3[2]; +- uint64_t xs_icount; +- uint64_t xs_ifree; +- uint64_t xs_fdblocks; ++ uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */ ++ uint32_t sb_blocksize; /* logical block size, bytes */ ++ uint64_t sb_dblocks; /* number of data blocks */ ++ uint64_t sb_rblocks; /* number of realtime blocks */ ++ uint64_t sb_rextents; /* number of realtime extents */ ++ unsigned char sb_uuid[16]; /* file system unique id */ ++ uint64_t sb_logstart; /* starting block of log if internal */ ++ uint64_t sb_rootino; /* root inode number */ ++ uint64_t sb_rbmino; /* bitmap inode for realtime extents */ ++ uint64_t sb_rsumino; /* summary inode for rt bitmap */ ++ uint32_t sb_rextsize; /* realtime extent size, blocks */ ++ uint32_t sb_agblocks; /* size of an allocation group */ ++ uint32_t sb_agcount; /* number of allocation groups */ ++ uint32_t sb_rbmblocks; /* number of rt bitmap blocks */ ++ uint32_t sb_logblocks; /* number of log blocks */ ++ ++ uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */ ++ uint16_t sb_sectsize; /* volume sector size, bytes */ ++ uint16_t sb_inodesize; /* inode size, bytes */ ++ uint16_t sb_inopblock; /* inodes per block */ ++ char sb_fname[12]; /* file system name */ ++ uint8_t sb_blocklog; /* log2 of sb_blocksize */ ++ uint8_t sb_sectlog; /* log2 of sb_sectsize */ ++ uint8_t sb_inodelog; /* log2 of sb_inodesize */ ++ uint8_t sb_inopblog; /* log2 of sb_inopblock */ ++ uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */ ++ uint8_t sb_rextslog; /* log2 of sb_rextents */ ++ uint8_t sb_inprogress; /* mkfs is in progress, don't mount */ ++ uint8_t sb_imax_pct; /* max % of fs for inode space */ ++ /* statistics */ ++ uint64_t sb_icount; /* allocated inodes */ ++ uint64_t sb_ifree; /* free inodes */ ++ uint64_t sb_fdblocks; /* free data blocks */ ++ uint64_t sb_frextents; /* free realtime extents */ ++ ++ /* this is not all... but enough for libblkid */ ++ + } __attribute__((packed)); + ++#define XFS_MIN_BLOCKSIZE_LOG 9 /* i.e. 512 bytes */ ++#define XFS_MAX_BLOCKSIZE_LOG 16 /* i.e. 65536 bytes */ ++#define XFS_MIN_BLOCKSIZE (1 << XFS_MIN_BLOCKSIZE_LOG) ++#define XFS_MAX_BLOCKSIZE (1 << XFS_MAX_BLOCKSIZE_LOG) ++#define XFS_MIN_SECTORSIZE_LOG 9 /* i.e. 512 bytes */ ++#define XFS_MAX_SECTORSIZE_LOG 15 /* i.e. 32768 bytes */ ++#define XFS_MIN_SECTORSIZE (1 << XFS_MIN_SECTORSIZE_LOG) ++#define XFS_MAX_SECTORSIZE (1 << XFS_MAX_SECTORSIZE_LOG) ++ ++#define XFS_DINODE_MIN_LOG 8 ++#define XFS_DINODE_MAX_LOG 11 ++#define XFS_DINODE_MIN_SIZE (1 << XFS_DINODE_MIN_LOG) ++#define XFS_DINODE_MAX_SIZE (1 << XFS_DINODE_MAX_LOG) ++ ++#define XFS_MAX_RTEXTSIZE (1024 * 1024 * 1024) /* 1GB */ ++#define XFS_DFL_RTEXTSIZE (64 * 1024) /* 64kB */ ++#define XFS_MIN_RTEXTSIZE (4 * 1024) /* 4kB */ ++ ++#define XFS_MIN_AG_BLOCKS 64 ++#define XFS_MAX_DBLOCKS(s) ((uint64_t)(s)->sb_agcount * (s)->sb_agblocks) ++#define XFS_MIN_DBLOCKS(s) ((uint64_t)((s)->sb_agcount - 1) * \ ++ (s)->sb_agblocks + XFS_MIN_AG_BLOCKS) ++ ++ ++static void sb_from_disk(struct xfs_super_block *from, ++ struct xfs_super_block *to) ++{ ++ ++ to->sb_magicnum = be32_to_cpu(from->sb_magicnum); ++ to->sb_blocksize = be32_to_cpu(from->sb_blocksize); ++ to->sb_dblocks = be64_to_cpu(from->sb_dblocks); ++ to->sb_rblocks = be64_to_cpu(from->sb_rblocks); ++ to->sb_rextents = be64_to_cpu(from->sb_rextents); ++ to->sb_logstart = be64_to_cpu(from->sb_logstart); ++ to->sb_rootino = be64_to_cpu(from->sb_rootino); ++ to->sb_rbmino = be64_to_cpu(from->sb_rbmino); ++ to->sb_rsumino = be64_to_cpu(from->sb_rsumino); ++ to->sb_rextsize = be32_to_cpu(from->sb_rextsize); ++ to->sb_agblocks = be32_to_cpu(from->sb_agblocks); ++ to->sb_agcount = be32_to_cpu(from->sb_agcount); ++ to->sb_rbmblocks = be32_to_cpu(from->sb_rbmblocks); ++ to->sb_logblocks = be32_to_cpu(from->sb_logblocks); ++ to->sb_versionnum = be16_to_cpu(from->sb_versionnum); ++ to->sb_sectsize = be16_to_cpu(from->sb_sectsize); ++ to->sb_inodesize = be16_to_cpu(from->sb_inodesize); ++ to->sb_inopblock = be16_to_cpu(from->sb_inopblock); ++ to->sb_blocklog = from->sb_blocklog; ++ to->sb_sectlog = from->sb_sectlog; ++ to->sb_inodelog = from->sb_inodelog; ++ to->sb_inopblog = from->sb_inopblog; ++ to->sb_agblklog = from->sb_agblklog; ++ to->sb_rextslog = from->sb_rextslog; ++ to->sb_inprogress = from->sb_inprogress; ++ to->sb_imax_pct = from->sb_imax_pct; ++ to->sb_icount = be64_to_cpu(from->sb_icount); ++ to->sb_ifree = be64_to_cpu(from->sb_ifree); ++ to->sb_fdblocks = be64_to_cpu(from->sb_fdblocks); ++ to->sb_frextents = be64_to_cpu(from->sb_frextents); ++} ++ ++static int xfs_verify_sb(struct xfs_super_block *ondisk) ++{ ++ struct xfs_super_block sb, *sbp = &sb; ++ ++ /* beXX_to_cpu(), but don't convert UUID and fsname! */ ++ sb_from_disk(ondisk, sbp); ++ ++ /* sanity checks, we don't want to rely on magic string only */ ++ if (sbp->sb_agcount <= 0 || ++ sbp->sb_sectsize < XFS_MIN_SECTORSIZE || ++ sbp->sb_sectsize > XFS_MAX_SECTORSIZE || ++ sbp->sb_sectlog < XFS_MIN_SECTORSIZE_LOG || ++ sbp->sb_sectlog > XFS_MAX_SECTORSIZE_LOG || ++ sbp->sb_sectsize != (1 << sbp->sb_sectlog) || ++ sbp->sb_blocksize < XFS_MIN_BLOCKSIZE || ++ sbp->sb_blocksize > XFS_MAX_BLOCKSIZE || ++ sbp->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG || ++ sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG || ++ sbp->sb_blocksize != (1 << sbp->sb_blocklog) || ++ sbp->sb_inodesize < XFS_DINODE_MIN_SIZE || ++ sbp->sb_inodesize > XFS_DINODE_MAX_SIZE || ++ sbp->sb_inodelog < XFS_DINODE_MIN_LOG || ++ sbp->sb_inodelog > XFS_DINODE_MAX_LOG || ++ sbp->sb_inodesize != (1 << sbp->sb_inodelog) || ++ (sbp->sb_blocklog - sbp->sb_inodelog != sbp->sb_inopblog) || ++ (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE) || ++ (sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE) || ++ (sbp->sb_imax_pct > 100 /* zero sb_imax_pct is valid */) || ++ sbp->sb_dblocks == 0 || ++ sbp->sb_dblocks > XFS_MAX_DBLOCKS(sbp) || ++ sbp->sb_dblocks < XFS_MIN_DBLOCKS(sbp)) ++ return 0; ++ ++ /* TODO: version 5 has also checksum CRC32, maybe we can check it too */ ++ ++ return 1; ++} ++ + static int probe_xfs(blkid_probe pr, const struct blkid_idmag *mag) + { + struct xfs_super_block *xs; +@@ -42,10 +165,13 @@ static int probe_xfs(blkid_probe pr, con + if (!xs) + return errno ? -errno : 1; + +- if (strlen(xs->xs_fname)) +- blkid_probe_set_label(pr, (unsigned char *) xs->xs_fname, +- sizeof(xs->xs_fname)); +- blkid_probe_set_uuid(pr, xs->xs_uuid); ++ if (!xfs_verify_sb(xs)) ++ return 1; ++ ++ if (strlen(xs->sb_fname)) ++ blkid_probe_set_label(pr, (unsigned char *) xs->sb_fname, ++ sizeof(xs->sb_fname)); ++ blkid_probe_set_uuid(pr, xs->sb_uuid); + return 0; + } + diff --git a/SOURCES/2.25-lscpu-d_type.patch b/SOURCES/2.25-lscpu-d_type.patch new file mode 100644 index 0000000..b6055af --- /dev/null +++ b/SOURCES/2.25-lscpu-d_type.patch @@ -0,0 +1,12 @@ +diff -up util-linux-2.23.2/sys-utils/lscpu.c.kzak util-linux-2.23.2/sys-utils/lscpu.c +--- util-linux-2.23.2/sys-utils/lscpu.c.kzak 2014-09-24 10:27:29.410899893 +0200 ++++ util-linux-2.23.2/sys-utils/lscpu.c 2014-09-24 10:33:20.960254060 +0200 +@@ -809,7 +809,7 @@ static inline int is_node_dirent(struct + return + d && + #ifdef _DIRENT_HAVE_D_TYPE +- d->d_type == DT_DIR && ++ (d->d_type == DT_DIR || d->d_type == DT_UNKNOWN) && + #endif + strncmp(d->d_name, "node", 4) == 0 && + isdigit_string(d->d_name + 4); diff --git a/SOURCES/2.25-swapon-discard.patch b/SOURCES/2.25-swapon-discard.patch new file mode 100644 index 0000000..15277ff --- /dev/null +++ b/SOURCES/2.25-swapon-discard.patch @@ -0,0 +1,200 @@ +diff -up util-linux-2.23.2/sys-utils/swapon.8.kzak util-linux-2.23.2/sys-utils/swapon.8 +--- util-linux-2.23.2/sys-utils/swapon.8.kzak 2013-06-13 09:46:10.544651682 +0200 ++++ util-linux-2.23.2/sys-utils/swapon.8 2014-09-24 10:57:45.855230767 +0200 +@@ -112,15 +112,25 @@ All devices marked as ``swap'' in + are made available, except for those with the ``noauto'' option. + Devices that are already being used as swap are silently skipped. + .TP +-.B "\-d, \-\-discard" +-Discard freed swap pages before they are reused, if the swap +-device supports the discard or trim operation. This may improve +-performance on some Solid State Devices, but often it does not. ++.B "\-d, \-\-discard\fR [=\fIpolicy\fR]" ++Enable swap discards, if the swap backing device supports the discard or ++trim operation. This may improve performance on some Solid State Devices, ++but often it does not. The option allows one to select between two ++available swap discard policies: ++.BI \-\-discard=once ++to perform a single-time discard operation for the whole swap area at swapon; ++or ++.BI \-\-discard=pages ++to discard freed swap pages before they are reused, while swapping. ++If no policy is selected, the default behavior is to enable both discard types. + The + .I /etc/fstab +-mount option +-.BI discard +-may be also used to enable discard flag. ++mount options ++.BI discard, ++.BI discard=once, ++or ++.BI discard=pages ++may be also used to enable discard flags. + .TP + .B "\-e, \-\-ifexists" + Silently skip devices that do not exist. +diff -up util-linux-2.23.2/sys-utils/swapon.c.kzak util-linux-2.23.2/sys-utils/swapon.c +--- util-linux-2.23.2/sys-utils/swapon.c.kzak 2013-07-30 10:39:26.348739643 +0200 ++++ util-linux-2.23.2/sys-utils/swapon.c 2014-09-24 10:57:45.855230767 +0200 +@@ -34,9 +34,20 @@ + #endif + + #ifndef SWAP_FLAG_DISCARD +-# define SWAP_FLAG_DISCARD 0x10000 /* discard swap cluster after use */ ++# define SWAP_FLAG_DISCARD 0x10000 /* enable discard for swap */ + #endif + ++#ifndef SWAP_FLAG_DISCARD_ONCE ++# define SWAP_FLAG_DISCARD_ONCE 0x20000 /* discard swap area at swapon-time */ ++#endif ++ ++#ifndef SWAP_FLAG_DISCARD_PAGES ++# define SWAP_FLAG_DISCARD_PAGES 0x40000 /* discard page-clusters after use */ ++#endif ++ ++#define SWAP_FLAGS_DISCARD_VALID (SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE | \ ++ SWAP_FLAG_DISCARD_PAGES) ++ + #ifndef SWAP_FLAG_PREFER + # define SWAP_FLAG_PREFER 0x8000 /* set if swap priority specified */ + #endif +@@ -70,7 +81,7 @@ enum { + + static int all; + static int priority = -1; /* non-prioritized swap by default */ +-static int discard; ++static int discard; /* don't send swap discards by default */ + + /* If true, don't complain if the device/file doesn't exist */ + static int ifexists; +@@ -567,8 +578,22 @@ static int do_swapon(const char *orig_sp + << SWAP_FLAG_PRIO_SHIFT); + } + #endif +- if (fl_discard) +- flags |= SWAP_FLAG_DISCARD; ++ /* ++ * Validate the discard flags passed and set them ++ * accordingly before calling sys_swapon. ++ */ ++ if (fl_discard && !(fl_discard & ~SWAP_FLAGS_DISCARD_VALID)) { ++ /* ++ * If we get here with both discard policy flags set, ++ * we just need to tell the kernel to enable discards ++ * and it will do correctly, just as we expect. ++ */ ++ if ((fl_discard & SWAP_FLAG_DISCARD_ONCE) && ++ (fl_discard & SWAP_FLAG_DISCARD_PAGES)) ++ flags |= SWAP_FLAG_DISCARD; ++ else ++ flags |= fl_discard; ++ } + + status = swapon(special, flags); + if (status < 0) +@@ -608,12 +633,22 @@ static int swapon_all(void) + while (mnt_table_find_next_fs(tb, itr, match_swap, NULL, &fs) == 0) { + /* defaults */ + int pri = priority, dsc = discard, nofail = ifexists; +- char *p, *src; ++ char *p, *src, *dscarg; + + if (mnt_fs_get_option(fs, "noauto", NULL, NULL) == 0) + continue; +- if (mnt_fs_get_option(fs, "discard", NULL, NULL) == 0) +- dsc = 1; ++ if (mnt_fs_get_option(fs, "discard", &dscarg, NULL) == 0) { ++ dsc |= SWAP_FLAG_DISCARD; ++ if (dscarg) { ++ /* only single-time discards are wanted */ ++ if (strcmp(dscarg, "once") == 0) ++ dsc |= SWAP_FLAG_DISCARD_ONCE; ++ ++ /* do discard for every released swap page */ ++ if (strcmp(dscarg, "pages") == 0) ++ dsc |= SWAP_FLAG_DISCARD_PAGES; ++ } ++ } + if (mnt_fs_get_option(fs, "nofail", NULL, NULL) == 0) + nofail = 1; + if (mnt_fs_get_option(fs, "pri", &p, NULL) == 0 && p) +@@ -643,17 +678,17 @@ static void __attribute__ ((__noreturn__ + fprintf(out, _(" %s [options] []\n"), program_invocation_short_name); + + fputs(USAGE_OPTIONS, out); +- fputs(_(" -a, --all enable all swaps from /etc/fstab\n" +- " -d, --discard discard freed pages before they are reused\n" +- " -e, --ifexists silently skip devices that do not exist\n" +- " -f, --fixpgsz reinitialize the swap space if necessary\n" +- " -p, --priority specify the priority of the swap device\n" +- " -s, --summary display summary about used swap devices\n" +- " --show[=] display summary in definable table\n" +- " --noheadings don't print headings, use with --show\n" +- " --raw use the raw output format, use with --show\n" +- " --bytes display swap size in bytes in --show output\n" +- " -v, --verbose verbose mode\n"), out); ++ fputs(_(" -a, --all enable all swaps from /etc/fstab\n" ++ " -d, --discard[=] enable swap discards, if supported by device\n" ++ " -e, --ifexists silently skip devices that do not exist\n" ++ " -f, --fixpgsz reinitialize the swap space if necessary\n" ++ " -p, --priority specify the priority of the swap device\n" ++ " -s, --summary display summary about used swap devices\n" ++ " --show[=] display summary in definable table\n" ++ " --noheadings don't print headings, use with --show\n" ++ " --raw use the raw output format, use with --show\n" ++ " --bytes display swap size in bytes in --show output\n" ++ " -v, --verbose verbose mode\n"), out); + + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_HELP, out); +@@ -669,6 +704,11 @@ static void __attribute__ ((__noreturn__ + " name of device to be used\n" + " name of file to be used\n"), out); + ++ fputs(_("\nAvailable discard policy types (for --discard):\n" ++ " once : only single-time area discards are issued. (swapon)\n" ++ " pages : discard freed pages before they are reused.\n" ++ " * if no policy is selected both discard types are enabled. (default)\n"), out); ++ + fputs(_("\nAvailable columns (for --show):\n"), out); + for (i = 0; i < NCOLS; i++) + fprintf(out, " %4s %s\n", infos[i].name, _(infos[i].help)); +@@ -693,7 +733,7 @@ int main(int argc, char *argv[]) + + static const struct option long_opts[] = { + { "priority", 1, 0, 'p' }, +- { "discard", 0, 0, 'd' }, ++ { "discard", 2, 0, 'd' }, + { "ifexists", 0, 0, 'e' }, + { "summary", 0, 0, 's' }, + { "fixpgsz", 0, 0, 'f' }, +@@ -716,7 +756,7 @@ int main(int argc, char *argv[]) + mnt_init_debug(0); + mntcache = mnt_new_cache(); + +- while ((c = getopt_long(argc, argv, "ahdefp:svVL:U:", ++ while ((c = getopt_long(argc, argv, "ahd::efp:svVL:U:", + long_opts, NULL)) != -1) { + switch (c) { + case 'a': /* all */ +@@ -736,7 +776,18 @@ int main(int argc, char *argv[]) + add_uuid(optarg); + break; + case 'd': +- discard = 1; ++ discard |= SWAP_FLAG_DISCARD; ++ if (optarg) { ++ if (*optarg == '=') ++ optarg++; ++ ++ if (strcmp(optarg, "once") == 0) ++ discard |= SWAP_FLAG_DISCARD_ONCE; ++ else if (strcmp(optarg, "pages") == 0) ++ discard |= SWAP_FLAG_DISCARD_PAGES; ++ else ++ errx(EXIT_FAILURE, _("unsupported discard policy: %s"), optarg); ++ } + break; + case 'e': /* ifexists */ + ifexists = 1; diff --git a/SOURCES/2.25-wipefs-nested-pt.patch b/SOURCES/2.25-wipefs-nested-pt.patch new file mode 100644 index 0000000..a9b97dd --- /dev/null +++ b/SOURCES/2.25-wipefs-nested-pt.patch @@ -0,0 +1,53 @@ +diff -up util-linux-2.23.2/misc-utils/wipefs.8.kzak util-linux-2.23.2/misc-utils/wipefs.8 +--- util-linux-2.23.2/misc-utils/wipefs.8.kzak 2014-09-24 10:41:31.061930168 +0200 ++++ util-linux-2.23.2/misc-utils/wipefs.8 2014-09-24 10:46:30.142783728 +0200 +@@ -37,6 +37,11 @@ table will still be visible by another m + When used with option \fB-a\fR, all magic strings that are visible for libblkid are + erased. + ++Note that by default ++.B wipefs ++does not erase nested partition tables on non-whole disk devices. The option ++\-\-force is required. ++ + .SH OPTIONS + .TP + .BR \-a , " \-\-all" +diff -up util-linux-2.23.2/misc-utils/wipefs.c.kzak util-linux-2.23.2/misc-utils/wipefs.c +--- util-linux-2.23.2/misc-utils/wipefs.c.kzak 2014-09-24 10:41:31.061930168 +0200 ++++ util-linux-2.23.2/misc-utils/wipefs.c 2014-09-24 10:50:07.728859738 +0200 +@@ -332,7 +332,7 @@ static void rereadpt(int fd, const char + static struct wipe_desc * + do_wipe(struct wipe_desc *wp, const char *devname, int noact, int all, int quiet, int force) + { +- int flags, reread = 0; ++ int flags, reread = 0, need_force = 0; + blkid_probe pr; + struct wipe_desc *w, *wp0; + int zap = all ? 1 : wp->zap; +@@ -365,6 +365,15 @@ do_wipe(struct wipe_desc *wp, const char + if (!wp->on_disk) + continue; + ++ if (!force ++ && wp->is_parttable ++ && !blkid_probe_is_wholedisk(pr)) { ++ warnx(_("%s: ignore nested \"%s\" partition " ++ "table on non-whole disk device."), devname, wp->type); ++ need_force = 1; ++ continue; ++ } ++ + if (zap) { + do_wipe_real(pr, devname, wp, noact, quiet); + if (wp->is_parttable) +@@ -377,6 +386,9 @@ do_wipe(struct wipe_desc *wp, const char + warnx(_("%s: offset 0x%jx not found"), devname, w->offset); + } + ++ if (need_force) ++ warnx(_("Use the --force option to force erase.")); ++ + fsync(blkid_probe_get_fd(pr)); + + if (reread) diff --git a/SOURCES/2.26-blkdiscard.patch b/SOURCES/2.26-blkdiscard.patch new file mode 100644 index 0000000..91e88d2 --- /dev/null +++ b/SOURCES/2.26-blkdiscard.patch @@ -0,0 +1,259 @@ +diff -up util-linux-2.23.2/sys-utils/blkdiscard.8.kzak util-linux-2.23.2/sys-utils/blkdiscard.8 +--- util-linux-2.23.2/sys-utils/blkdiscard.8.kzak 2013-06-13 09:46:10.532651579 +0200 ++++ util-linux-2.23.2/sys-utils/blkdiscard.8 2014-10-27 10:03:13.650011708 +0100 +@@ -1,5 +1,5 @@ + .\" -*- nroff -*- +-.TH BLKDISCARD 8 "October 2012" "util-linux" "System Administration" ++.TH BLKDISCARD 8 "July 2014" "util-linux" "System Administration" + .SH NAME + blkdiscard \- discard sectors on a device + .SH SYNOPSIS +@@ -15,7 +15,7 @@ blkdiscard \- discard sectors on a devic + .B blkdiscard + is used to discard device sectors. This is useful for solid-state + drivers (SSDs) and thinly-provisioned storage. Unlike +-.BR fstrim (8) ++.BR fstrim (8) , + this command is used directly on the block device. + .PP + By default, +@@ -33,32 +33,44 @@ The + .I offset + and + .I length +-arguments may be followed by the multiplicative suffixes KiB=1024, +-MiB=1024*1024, and so on for GiB, TiB, PiB, EiB, ZiB and YiB (the "iB" is ++arguments may be followed by the multiplicative suffixes KiB (=1024), ++MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB and YiB (the "iB" is + optional, e.g., "K" has the same meaning as "KiB") or the suffixes +-KB=1000, MB=1000*1000, and so on for GB, TB, PB, EB, ZB and YB. +-.IP "\fB\-h, \-\-help\fP" +-Print help and exit. +-.IP "\fB\-o, \-\-offset\fP \fIoffset\fP" +-Byte offset in the device from which to discard. Provided value will be +-aligned to the device sector size. Default value is zero. +-.IP "\fB\-l, \-\-length\fP \fIlength\fP" +-Number of bytes after starting point to discard. Provided value will be +-aligned to the device sector size. If the specified value extends past ++KB (=1000), MB (=1000*1000), and so on for GB, TB, PB, EB, ZB and YB. ++.TP ++.BR \-o , " \-\-offset \fIoffset" ++Byte offset into the device from which to start discarding. The provided value ++will be aligned to the device sector size. The default value is zero. ++.TP ++.BR \-l , " \-\-length \fIlength" ++The number of bytes to discard (counting from the starting point). The provided value ++will be aligned to the device sector size. If the specified value extends past + the end of the device, + .B blkdiscard +-will stop at the device size boundary. Default value extends to the end ++will stop at the device size boundary. The default value extends to the end + of the device. +-.IP "\fB\-s, \-\-secure\fP" +-Perform secure discard. Secure discard is the same as regular discard +-except all copies of the discarded blocks possibly created by garbage +-collection must also be erased. It has to be supported by the device. +-.IP "\fB\-v, \-\-verbose\fP" +-Print aligned ++.TP ++.BR \-p , " \-\-step \fIlength" ++The number of bytes to discard within one iteration. The default is to discard ++all by one ioctl call. ++.TP ++.BR \-s , " \-\-secure" ++Perform a secure discard. A secure discard is the same as a regular discard ++except that all copies of the discarded blocks that were possibly created by ++garbage collection must also be erased. This requires support from the device. ++.TP ++.BR \-v , " \-\-verbose" ++Display the aligned values of + .I offset + and +-.I length +-arguments. ++.IR length . ++If the option \fB\-\-step\fR specified than it prints discard progress every second. ++.TP ++.BR \-V , " \-\-version" ++Display version information and exit. ++.TP ++.BR \-h , " \-\-help" ++Display help text and exit. + .SH AUTHOR + .MT lczerner@redhat.com + Lukas Czerner +diff -up util-linux-2.23.2/sys-utils/blkdiscard.c.kzak util-linux-2.23.2/sys-utils/blkdiscard.c +--- util-linux-2.23.2/sys-utils/blkdiscard.c.kzak 2013-07-30 10:39:26.337739534 +0200 ++++ util-linux-2.23.2/sys-utils/blkdiscard.c 2014-10-27 10:03:20.981088614 +0100 +@@ -31,9 +31,11 @@ + #include + #include + #include ++#include + + #include + #include ++#include + #include + + #include "nls.h" +@@ -49,6 +51,10 @@ + #define BLKSECDISCARD _IO(0x12,125) + #endif + ++#define print_stats(path, stats) \ ++ printf(_("%s: Discarded %" PRIu64 " bytes from the " \ ++ "offset %" PRIu64"\n"), path, stats[1], stats[0]); ++ + static void __attribute__((__noreturn__)) usage(FILE *out) + { + fputs(USAGE_HEADER, out); +@@ -57,6 +63,7 @@ static void __attribute__((__noreturn__) + fputs(USAGE_OPTIONS, out); + fputs(_(" -o, --offset offset in bytes to discard from\n" + " -l, --length length of bytes to discard from the offset\n" ++ " -p, --step size of the discard iterations within the offset\n" + " -s, --secure perform secure discard\n" + " -v, --verbose print aligned length and offset\n"), + out); +@@ -70,15 +77,17 @@ static void __attribute__((__noreturn__) + int main(int argc, char **argv) + { + char *path; +- int c, fd, verbose = 0, secure = 0; +- uint64_t end, blksize, secsize, range[2]; ++ int c, fd, verbose = 0, secure = 0, secsize; ++ uint64_t end, blksize, step, range[2], stats[2]; + struct stat sb; ++ struct timespec now, last; + + static const struct option longopts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "offset", 1, 0, 'o' }, + { "length", 1, 0, 'l' }, ++ { "step", 1, 0, 'p' }, + { "secure", 0, 0, 's' }, + { "verbose", 0, 0, 'v' }, + { NULL, 0, 0, 0 } +@@ -91,8 +100,9 @@ int main(int argc, char **argv) + + range[0] = 0; + range[1] = ULLONG_MAX; ++ step = 0; + +- while ((c = getopt_long(argc, argv, "hVsvo:l:", longopts, NULL)) != -1) { ++ while ((c = getopt_long(argc, argv, "hVsvo:l:p:", longopts, NULL)) != -1) { + switch(c) { + case 'h': + usage(stdout); +@@ -108,6 +118,10 @@ int main(int argc, char **argv) + range[0] = strtosize_or_err(optarg, + _("failed to parse offset")); + break; ++ case 'p': ++ step = strtosize_or_err(optarg, ++ _("failed to parse step")); ++ break; + case 's': + secure = 1; + break; +@@ -121,7 +135,7 @@ int main(int argc, char **argv) + } + + if (optind == argc) +- errx(EXIT_FAILURE, _("no device specified.")); ++ errx(EXIT_FAILURE, _("no device specified")); + + path = argv[optind++]; + +@@ -130,43 +144,69 @@ int main(int argc, char **argv) + usage(stderr); + } + +- if (stat(path, &sb) == -1) +- err(EXIT_FAILURE, _("stat failed %s"), path); +- if (!S_ISBLK(sb.st_mode)) +- errx(EXIT_FAILURE, _("%s: not a block device"), path); +- + fd = open(path, O_WRONLY); + if (fd < 0) + err(EXIT_FAILURE, _("cannot open %s"), path); + ++ if (fstat(fd, &sb) == -1) ++ err(EXIT_FAILURE, _("stat failed %s"), path); ++ if (!S_ISBLK(sb.st_mode)) ++ errx(EXIT_FAILURE, _("%s: not a block device"), path); ++ + if (ioctl(fd, BLKGETSIZE64, &blksize)) + err(EXIT_FAILURE, _("%s: BLKGETSIZE64 ioctl failed"), path); +- + if (ioctl(fd, BLKSSZGET, &secsize)) + err(EXIT_FAILURE, _("%s: BLKSSZGET ioctl failed"), path); + +- /* align range to the sector size */ +- range[0] = (range[0] + secsize - 1) & ~(secsize - 1); +- range[1] &= ~(secsize - 1); ++ /* check offset alignment to the sector size */ ++ if (range[0] % secsize) ++ errx(EXIT_FAILURE, _("%s: offset %" PRIu64 " is not aligned " ++ "to sector size %i"), path, range[0], secsize); + + /* is the range end behind the end of the device ?*/ ++ if (range[0] > blksize) ++ errx(EXIT_FAILURE, _("%s: offset is greater than device size"), path); + end = range[0] + range[1]; + if (end < range[0] || end > blksize) +- range[1] = blksize - range[0]; ++ end = blksize; ++ ++ range[1] = (step > 0) ? step : end - range[0]; ++ ++ /* check length alignment to the sector size */ ++ if (range[1] % secsize) ++ errx(EXIT_FAILURE, _("%s: length %" PRIu64 " is not aligned " ++ "to sector size %i"), path, range[1], secsize); ++ ++ stats[0] = range[0], stats[1] = 0; ++ clock_gettime(CLOCK_MONOTONIC, &last); ++ ++ for (range[0] = range[0]; range[0] < end; range[0] += range[1]) { ++ if (range[0] + range[1] > end) ++ range[1] = end - range[0]; ++ ++ if (secure) { ++ if (ioctl(fd, BLKSECDISCARD, &range)) ++ err(EXIT_FAILURE, _("%s: BLKSECDISCARD ioctl failed"), path); ++ } else { ++ if (ioctl(fd, BLKDISCARD, &range)) ++ err(EXIT_FAILURE, _("%s: BLKDISCARD ioctl failed"), path); ++ } ++ ++ /* reporting progress */ ++ if (verbose && step) { ++ clock_gettime(CLOCK_MONOTONIC, &now); ++ if (last.tv_sec < now.tv_sec) { ++ print_stats(path, stats); ++ stats[0] = range[0], stats[1] = 0; ++ last = now; ++ } ++ } + +- if (secure) { +- if (ioctl(fd, BLKSECDISCARD, &range)) +- err(EXIT_FAILURE, _("%s: BLKSECDISCARD ioctl failed"), path); +- } else { +- if (ioctl(fd, BLKDISCARD, &range)) +- err(EXIT_FAILURE, _("%s: BLKDISCARD ioctl failed"), path); ++ stats[1] += range[1]; + } + + if (verbose) +- /* TRANSLATORS: The standard value here is a very large number. */ +- printf(_("%s: Discarded %" PRIu64 " bytes from the " +- "offset %" PRIu64"\n"), path, +- (uint64_t) range[1], (uint64_t) range[0]); ++ print_stats(path, stats); + + close(fd); + return EXIT_SUCCESS; diff --git a/SOURCES/2.26-libsmartcols.patch b/SOURCES/2.26-libsmartcols.patch new file mode 100644 index 0000000..edad5fb --- /dev/null +++ b/SOURCES/2.26-libsmartcols.patch @@ -0,0 +1,5242 @@ +diff -up util-linux-2.23.2/configure.ac.kzak util-linux-2.23.2/configure.ac +--- util-linux-2.23.2/configure.ac.kzak 2013-07-30 10:39:26.188738061 +0200 ++++ util-linux-2.23.2/configure.ac 2014-09-25 14:41:48.980843829 +0200 +@@ -46,6 +46,13 @@ LIBMOUNT_LT_MINOR=1 + LIBMOUNT_LT_MICRO=0 + LIBMOUNT_VERSION_INFO=`expr $LIBMOUNT_LT_MAJOR + $LIBMOUNT_LT_MINOR`:$LIBMOUNT_LT_MICRO:$LIBMOUNT_LT_MINOR + ++dnl libsmartcols version ++LIBSMARTCOLS_VERSION="$PACKAGE_VERSION_MAJOR.$PACKAGE_VERSION_MINOR.$PACKAGE_VERSION_RELEASE" ++LIBSMARTCOLS_LT_MAJOR=1 ++LIBSMARTCOLS_LT_MINOR=1 ++LIBSMARTCOLS_LT_MICRO=0 ++LIBSMARTCOLS_VERSION_INFO=`expr $LIBSMARTCOLS_LT_MAJOR + $LIBSMARTCOLS_LT_MINOR`:$LIBSMARTCOLS_LT_MICRO:$LIBSMARTCOLS_LT_MINOR ++ + # Check whether exec_prefix=/usr: + case $exec_prefix:$prefix in + NONE:NONE | NONE:/usr | /usr:*) +@@ -765,6 +772,18 @@ AC_DEFINE_UNQUOTED(LIBMOUNT_VERSION, "$L + + + dnl ++dnl libsmartcols ++dnl ++UL_BUILD_INIT([libsmartcols], [yes]) ++AM_CONDITIONAL([BUILD_LIBSMARTCOLS], [test "x$build_libsmartcols" = xyes]) ++AM_CONDITIONAL([BUILD_LIBSMARTCOLS_TESTS], [test "x$build_libsmartcols" = xyes -a "x$enable_static" = xyes]) ++ ++AC_SUBST([LIBSMARTCOLS_VERSION]) ++AC_SUBST([LIBSMARTCOLS_VERSION_INFO]) ++AC_DEFINE_UNQUOTED([LIBSMARTCOLS_VERSION], ["$LIBSMARTCOLS_VERSION"], [libsmartcols version string]) ++ ++ ++dnl + dnl libfdisk is enabled all time if possible + dnl + UL_BUILD_INIT([libfdisk], [check]) +@@ -1500,6 +1519,9 @@ libblkid/src/blkid.h + libmount/docs/Makefile + libmount/docs/version.xml + libmount/src/libmount.h ++libsmartcols/docs/Makefile ++libsmartcols/docs/version.xml ++libsmartcols/src/libsmartcols.h + po/Makefile.in + ]) + +diff -up util-linux-2.23.2/include/carefulputc.h.kzak util-linux-2.23.2/include/carefulputc.h +--- util-linux-2.23.2/include/carefulputc.h.kzak 2012-11-29 16:18:33.956147961 +0100 ++++ util-linux-2.23.2/include/carefulputc.h 2014-09-25 14:41:48.980843829 +0200 +@@ -26,4 +26,39 @@ static inline int carefulputc(int c, FIL + return (ret < 0) ? EOF : 0; + } + ++static inline void fputs_quoted(const char *data, FILE *out) ++{ ++ const char *p; ++ ++ fputc('"', out); ++ for (p = data; p && *p; p++) { ++ if ((unsigned char) *p == 0x22 || /* " */ ++ (unsigned char) *p == 0x5c || /* \ */ ++ !isprint((unsigned char) *p) || ++ iscntrl((unsigned char) *p)) { ++ ++ fprintf(out, "\\x%02x", (unsigned char) *p); ++ } else ++ fputc(*p, out); ++ } ++ fputc('"', out); ++} ++ ++static inline void fputs_nonblank(const char *data, FILE *out) ++{ ++ const char *p; ++ ++ for (p = data; p && *p; p++) { ++ if (isblank((unsigned char) *p) || ++ (unsigned char) *p == 0x5c || /* \ */ ++ !isprint((unsigned char) *p) || ++ iscntrl((unsigned char) *p)) { ++ ++ fprintf(out, "\\x%02x", (unsigned char) *p); ++ ++ } else ++ fputc(*p, out); ++ } ++} ++ + #endif /* _CAREFUULPUTC_H */ +diff -up util-linux-2.23.2/include/debug.h.kzak util-linux-2.23.2/include/debug.h +--- util-linux-2.23.2/include/debug.h.kzak 2014-09-25 14:41:48.981843839 +0200 ++++ util-linux-2.23.2/include/debug.h 2014-09-25 14:41:48.981843839 +0200 +@@ -0,0 +1,126 @@ ++/* ++ * Copyright (C) 2014 Ondrej Oprala ++ * ++ * This file may be distributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++#ifndef UTIL_LINUX_DEBUG_H ++#define UTIL_LINUX_DEBUG_H ++ ++#include ++#include ++ ++struct dbg_mask { char *mname; int val; }; ++#define UL_DEBUG_EMPTY_MASKNAMES {{ NULL, 0 }} ++ ++#define UL_DEBUG_DEFINE_MASK(m) int m ## _debug_mask ++#define UL_DEBUG_DECLARE_MASK(m) extern UL_DEBUG_DEFINE_MASK(m) ++#define UL_DEBUG_DEFINE_MASKANEMS(m) static const struct dbg_mask m ## _masknames[] ++ ++/* p - flag prefix, m - flag postfix */ ++#define UL_DEBUG_DEFINE_FLAG(p, m) p ## m ++ ++/* l - library name, p - flag prefix, m - flag postfix, x - function */ ++#define __UL_DBG(l, p, m, x) \ ++ do { \ ++ if ((p ## m) & l ## _debug_mask) { \ ++ fprintf(stderr, "%d: %s: %8s: ", getpid(), # l, # m); \ ++ x; \ ++ } \ ++ } while (0) ++ ++#define __UL_DBG_CALL(l, p, m, x) \ ++ do { \ ++ if ((p ## m) & l ## _debug_mask) { \ ++ x; \ ++ } \ ++ } while (0) ++ ++#define __UL_DBG_FLUSH(l, p) \ ++ do { \ ++ if (l ## _debug_mask && \ ++ l ## _debug_mask != p ## INIT) { \ ++ fflush(stderr); \ ++ } \ ++ } while (0) ++ ++ ++#define __UL_INIT_DEBUG(lib, pref, mask, env) \ ++ do { \ ++ if (lib ## _debug_mask & pref ## INIT) \ ++ ; \ ++ else if (!mask) { \ ++ char *str = getenv(# env); \ ++ if (str) \ ++ lib ## _debug_mask = parse_envmask(lib ## _masknames, str); \ ++ } else \ ++ lib ## _debug_mask = mask; \ ++ lib ## _debug_mask |= pref ## INIT; \ ++ if (lib ## _debug_mask != pref ## INIT) { \ ++ __UL_DBG(lib, pref, INIT, ul_debug("library debug mask: 0x%04x", \ ++ lib ## _debug_mask)); \ ++ } \ ++ } while (0) ++ ++ ++static inline void __attribute__ ((__format__ (__printf__, 1, 2))) ++ul_debug(const char *mesg, ...) ++{ ++ va_list ap; ++ va_start(ap, mesg); ++ vfprintf(stderr, mesg, ap); ++ va_end(ap); ++ fputc('\n', stderr); ++} ++ ++static inline void __attribute__ ((__format__ (__printf__, 2, 3))) ++ul_debugobj(void *handler, const char *mesg, ...) ++{ ++ va_list ap; ++ ++ if (handler) ++ fprintf(stderr, "[%p]: ", handler); ++ va_start(ap, mesg); ++ vfprintf(stderr, mesg, ap); ++ va_end(ap); ++ fputc('\n', stderr); ++} ++ ++static inline int parse_envmask(const struct dbg_mask const flagnames[], ++ const char *mask) ++{ ++ int res; ++ char *ptr; ++ ++ /* let's check for a numeric mask first */ ++ res = strtoul(mask, &ptr, 0); ++ ++ /* perhaps it's a comma-separated string? */ ++ if (*ptr != '\0' && flagnames) { ++ char *msbuf, *ms, *name; ++ res = 0; ++ ++ ms = msbuf = strdup(mask); ++ if (!ms) ++ return res; ++ ++ while ((name = strtok_r(ms, ",", &ptr))) { ++ size_t i = 0; ++ ms = ptr; ++ ++ while (flagnames[i].mname) { ++ if (!strcmp(name, flagnames[i].mname)) { ++ res |= flagnames[i].val; ++ break; ++ } ++ ++i; ++ } ++ /* nothing else we can do by OR-ing the mask */ ++ if (res == 0xffff) ++ break; ++ } ++ free(msbuf); ++ } ++ return res; ++} ++#endif /* UTIL_LINUX_DEBUG_H */ +diff -up util-linux-2.23.2/include/list.h.kzak util-linux-2.23.2/include/list.h +--- util-linux-2.23.2/include/list.h.kzak 2013-06-13 09:46:10.396650417 +0200 ++++ util-linux-2.23.2/include/list.h 2014-09-25 14:41:48.981843839 +0200 +@@ -212,14 +212,16 @@ _INLINE_ void list_splice(struct list_he + * sentinel head node, "prev" links not maintained. + */ + _INLINE_ struct list_head *merge(int (*cmp)(struct list_head *a, +- struct list_head *b), ++ struct list_head *b, ++ void *data), ++ void *data, + struct list_head *a, struct list_head *b) + { + struct list_head head, *tail = &head; + + while (a && b) { + /* if equal, take 'a' -- important for sort stability */ +- if ((*cmp)(a, b) <= 0) { ++ if ((*cmp)(a, b, data) <= 0) { + tail->next = a; + a = a->next; + } else { +@@ -240,7 +242,9 @@ _INLINE_ struct list_head *merge(int (*c + * throughout. + */ + _INLINE_ void merge_and_restore_back_links(int (*cmp)(struct list_head *a, +- struct list_head *b), ++ struct list_head *b, ++ void *data), ++ void *data, + struct list_head *head, + struct list_head *a, struct list_head *b) + { +@@ -248,7 +252,7 @@ _INLINE_ void merge_and_restore_back_lin + + while (a && b) { + /* if equal, take 'a' -- important for sort stability */ +- if ((*cmp)(a, b) <= 0) { ++ if ((*cmp)(a, b, data) <= 0) { + tail->next = a; + a->prev = tail; + a = a->next; +@@ -268,7 +272,7 @@ _INLINE_ void merge_and_restore_back_lin + * element comparison is needed, so the client's cmp() + * routine can invoke cond_resched() periodically. + */ +- (*cmp)(tail->next, tail->next); ++ (*cmp)(tail->next, tail->next, data); + + tail->next->prev = tail; + tail = tail->next; +@@ -294,7 +298,9 @@ _INLINE_ void merge_and_restore_back_lin + */ + _INLINE_ void list_sort(struct list_head *head, + int (*cmp)(struct list_head *a, +- struct list_head *b)) ++ struct list_head *b, ++ void *data), ++ void *data) + { + struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists + -- last slot is a sentinel */ +@@ -316,7 +322,7 @@ _INLINE_ void list_sort(struct list_head + cur->next = NULL; + + for (lev = 0; part[lev]; lev++) { +- cur = merge(cmp, part[lev], cur); ++ cur = merge(cmp, data, part[lev], cur); + part[lev] = NULL; + } + if (lev > max_lev) { +@@ -330,11 +336,12 @@ _INLINE_ void list_sort(struct list_head + + for (lev = 0; lev < max_lev; lev++) + if (part[lev]) +- list = merge(cmp, part[lev], list); ++ list = merge(cmp, data, part[lev], list); + +- merge_and_restore_back_links(cmp, head, part[max_lev], list); ++ merge_and_restore_back_links(cmp, data, head, part[max_lev], list); + } + ++ + #undef _INLINE_ + + #endif /* UTIL_LINUX_LIST_H */ +diff -up util-linux-2.23.2/include/mbsalign.h.kzak util-linux-2.23.2/include/mbsalign.h +--- util-linux-2.23.2/include/mbsalign.h.kzak 2013-06-13 09:46:10.397650425 +0200 ++++ util-linux-2.23.2/include/mbsalign.h 2014-09-25 14:41:48.981843839 +0200 +@@ -1,5 +1,6 @@ + /* Align/Truncate a string in a given screen width + Copyright (C) 2009-2010 Free Software Foundation, Inc. ++ Copyright (C) 2010-2013 Karel Zak + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by +@@ -13,8 +14,9 @@ + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ +- +-#include ++#ifndef UTIL_LINUX_MBSALIGN_H ++# define UTIL_LINUX_MBSALIGN_H ++# include + + typedef enum { MBS_ALIGN_LEFT, MBS_ALIGN_RIGHT, MBS_ALIGN_CENTER } mbs_align_t; + +@@ -43,3 +45,12 @@ extern size_t mbs_truncate(char *str, si + extern size_t mbsalign (const char *src, char *dest, + size_t dest_size, size_t *width, + mbs_align_t align, int flags); ++ ++extern size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz); ++extern size_t mbs_safe_width(const char *s); ++ ++extern char *mbs_safe_encode(const char *s, size_t *width); ++extern char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf); ++extern size_t mbs_safe_encode_size(size_t bytes); ++ ++#endif /* UTIL_LINUX_MBSALIGN_H */ +diff -up util-linux-2.23.2/lib/mbsalign.c.kzak util-linux-2.23.2/lib/mbsalign.c +--- util-linux-2.23.2/lib/mbsalign.c.kzak 2013-07-30 10:39:26.203738210 +0200 ++++ util-linux-2.23.2/lib/mbsalign.c 2014-09-25 14:41:48.982843848 +0200 +@@ -23,17 +23,193 @@ + #include + #include + #include ++#include + + #include "c.h" + #include "mbsalign.h" + #include "widechar.h" + +- + #ifdef HAVE_WIDECHAR + /* Replace non printable chars. + Note \t and \n etc. are non printable. + Return 1 if replacement made, 0 otherwise. */ + ++/* ++ * Counts number of cells in multibyte string. For all control and ++ * non-printable chars is the result width enlarged to store \x?? hex ++ * sequence. See mbs_safe_encode(). ++ * ++ * Returns: number of cells, @sz returns number of bytes. ++ */ ++size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz) ++{ ++ mbstate_t st; ++ const char *p = buf, *last = buf; ++ size_t width = 0, bytes = 0; ++ ++ memset(&st, 0, sizeof(st)); ++ ++ if (p && *p && bufsz) ++ last = p + (bufsz - 1); ++ ++ while (p && *p && p <= last) { ++ if (iscntrl((unsigned char) *p)) { ++ width += 4, bytes += 4; /* *p encoded to \x?? */ ++ p++; ++ } ++#ifdef HAVE_WIDECHAR ++ else { ++ wchar_t wc; ++ size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st); ++ ++ if (len == 0) ++ break; ++ ++ if (len == (size_t) -1 || len == (size_t) -2) { ++ len = 1; ++ if (isprint((unsigned char) *p)) ++ width += 1, bytes += 1; ++ else ++ width += 4, bytes += 4; ++ ++ } else if (!iswprint(wc)) { ++ width += len * 4; /* hex encode whole sequence */ ++ bytes += len * 4; ++ } else { ++ width += wcwidth(wc); /* number of cells */ ++ bytes += len; /* number of bytes */ ++ } ++ p += len; ++ } ++#else ++ else if (!isprint((unsigned char) *p)) { ++ width += 4, bytes += 4; /* *p encoded to \x?? */ ++ p++; ++ } else { ++ width++, bytes++; ++ p++; ++ } ++#endif ++ } ++ ++ if (sz) ++ *sz = bytes; ++ return width; ++} ++ ++size_t mbs_safe_width(const char *s) ++{ ++ if (!s || !*s) ++ return 0; ++ return mbs_safe_nwidth(s, strlen(s), NULL); ++} ++ ++/* ++ * Copy @s to @buf and replace control and non-printable chars with ++ * \x?? hex sequence. The @width returns number of cells. ++ * ++ * The @buf has to be big enough to store mbs_safe_encode_size(strlen(s))) ++ * bytes. ++ */ ++char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf) ++{ ++ mbstate_t st; ++ const char *p = s; ++ char *r; ++ size_t sz = s ? strlen(s) : 0; ++ ++ if (!sz || !buf) ++ return NULL; ++ ++ memset(&st, 0, sizeof(st)); ++ ++ r = buf; ++ *width = 0; ++ ++ while (p && *p) { ++ if (iscntrl((unsigned char) *p)) { ++ sprintf(r, "\\x%02x", (unsigned char) *p); ++ r += 4; ++ *width += 4; ++ p++; ++ } ++#ifdef HAVE_WIDECHAR ++ else { ++ wchar_t wc; ++ size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st); ++ ++ if (len == 0) ++ break; /* end of string */ ++ ++ if (len == (size_t) -1 || len == (size_t) -2) { ++ len = 1; ++ /* ++ * Not valid multibyte sequence -- maybe it's ++ * printable char according to the current locales. ++ */ ++ if (!isprint((unsigned char) *p)) { ++ sprintf(r, "\\x%02x", (unsigned char) *p); ++ r += 4; ++ *width += 4; ++ } else { ++ width++; ++ *r++ = *p; ++ } ++ } else if (!iswprint(wc)) { ++ size_t i; ++ for (i = 0; i < len; i++) { ++ sprintf(r, "\\x%02x", (unsigned char) *p); ++ r += 4; ++ *width += 4; ++ } ++ } else { ++ memcpy(r, p, len); ++ r += len; ++ *width += wcwidth(wc); ++ } ++ p += len; ++ } ++#else ++ else if (!isprint((unsigned char) *p)) { ++ sprintf(r, "\\x%02x", (unsigned char) *p); ++ p++; ++ r += 4; ++ *width += 4; ++ } else { ++ *r++ = *p++; ++ *width++; ++ } ++#endif ++ } ++ ++ *r = '\0'; ++ ++ return buf; ++} ++ ++size_t mbs_safe_encode_size(size_t bytes) ++{ ++ return (bytes * 4) + 1; ++} ++ ++/* ++ * Returns allocated string where all control and non-printable chars are ++ * replaced with \x?? hex sequence. ++ */ ++char *mbs_safe_encode(const char *s, size_t *width) ++{ ++ size_t sz = s ? strlen(s) : 0; ++ char *buf; ++ ++ if (!sz) ++ return NULL; ++ buf = malloc(mbs_safe_encode_size(sz)); ++ if (!buf) ++ return NULL; ++ ++ return mbs_safe_encode_to_buffer(s, width, buf); ++} ++ + static bool + wc_ensure_printable (wchar_t *wchars) + { +@@ -254,8 +430,8 @@ mbsalign_unibyte: + if (dest_size != 0) + { + char *dest_end = dest + dest_size - 1; +- size_t start_spaces = n_spaces / 2 + n_spaces % 2; +- size_t end_spaces = n_spaces / 2; ++ size_t start_spaces; ++ size_t end_spaces; + + switch (align) + { +diff -up util-linux-2.23.2/libsmartcols/COPYING.kzak util-linux-2.23.2/libsmartcols/COPYING +--- util-linux-2.23.2/libsmartcols/COPYING.kzak 2014-09-25 14:41:48.983843858 +0200 ++++ util-linux-2.23.2/libsmartcols/COPYING 2014-09-25 14:41:48.983843858 +0200 +@@ -0,0 +1,8 @@ ++This library is free software; you can redistribute it and/or ++modify it under the terms of the GNU Lesser General Public ++License as published by the Free Software Foundation; either ++version 2.1 of the License, or (at your option) any later ++version. ++ ++The complete text of the license is available in the ++../Documentation/licenses/COPYING.LGPLv2.1 file. +diff -up util-linux-2.23.2/libsmartcols/docs/.gitignore.kzak util-linux-2.23.2/libsmartcols/docs/.gitignore +--- util-linux-2.23.2/libsmartcols/docs/.gitignore.kzak 2014-09-25 14:41:48.983843858 +0200 ++++ util-linux-2.23.2/libsmartcols/docs/.gitignore 2014-09-25 14:41:48.983843858 +0200 +@@ -0,0 +1,18 @@ ++*-decl-list.txt ++*-decl.txt ++*-overrides.txt ++*-undeclared.txt ++*-undocumented.txt ++*-unused.txt ++*.args ++*.bak ++*.hierarchy ++*.interfaces ++*.prerequisites ++*.signals ++*.stamp ++*.types ++html/* ++tmpl/* ++version.xml ++xml/* +diff -up util-linux-2.23.2/libsmartcols/docs/libsmartcols-docs.xml.kzak util-linux-2.23.2/libsmartcols/docs/libsmartcols-docs.xml +--- util-linux-2.23.2/libsmartcols/docs/libsmartcols-docs.xml.kzak 2014-09-25 14:41:48.984843867 +0200 ++++ util-linux-2.23.2/libsmartcols/docs/libsmartcols-docs.xml 2014-09-25 14:41:48.984843867 +0200 +@@ -0,0 +1,52 @@ ++ ++ ++]> ++ ++ ++ libsmartcols Reference Manual ++ for libsmartcols version &version; ++ ++ 2014 ++ Karel Zak <kzak@redhat.com> ++ ++ ++ ++ ++ libsmartcols Overview ++ ++ ++The libsmartcols library is used for smart adaptive formatting of tabular data. ++ ++ ++The library is part of the util-linux package since version 2.25 and is ++available from ftp://ftp.kernel.org/pub/linux/utils/util-linux/. ++ ++ ++ ++ ++ ++ Data manipulation ++ ++ ++ ++ ++ ++ ++ ++ Printing ++ ++ ++ ++ Misc ++ ++ ++ ++ ++ ++ API Index ++ ++ ++ +diff -up util-linux-2.23.2/libsmartcols/docs/libsmartcols-sections.txt.kzak util-linux-2.23.2/libsmartcols/docs/libsmartcols-sections.txt +--- util-linux-2.23.2/libsmartcols/docs/libsmartcols-sections.txt.kzak 2014-09-25 14:41:48.984843867 +0200 ++++ util-linux-2.23.2/libsmartcols/docs/libsmartcols-sections.txt 2014-09-25 14:41:48.984843867 +0200 +@@ -0,0 +1,146 @@ ++
++cell ++libscols_cell ++scols_cell_copy_content ++scols_cell_get_color ++scols_cell_get_data ++scols_cell_get_userdata ++scols_cell_refer_data ++scols_cell_set_color ++scols_cell_set_data ++scols_cell_set_userdata ++scols_cmpstr_cells ++scols_reset_cell ++
++ ++
++column ++libscols_column ++scols_column_get_color ++scols_column_get_flags ++scols_column_get_header ++scols_column_get_whint ++scols_column_is_noextremes ++scols_column_is_right ++scols_column_is_strict_width ++scols_column_is_tree ++scols_column_is_trunc ++scols_column_set_cmpfunc ++scols_column_set_color ++scols_column_set_flags ++scols_column_set_whint ++scols_copy_column ++scols_new_column ++scols_ref_column ++scols_unref_column ++
++ ++
++iter ++libscols_iter ++scols_free_iter ++scols_iter_get_direction ++scols_new_iter ++scols_reset_iter ++
++ ++
++line ++libscols_line ++scols_copy_line ++scols_line_add_child ++scols_line_alloc_cells ++scols_line_free_cells ++scols_line_get_cell ++scols_line_get_color ++scols_line_get_column_cell ++scols_line_get_ncells ++scols_line_get_parent ++scols_line_get_userdata ++scols_line_has_children ++scols_line_next_child ++scols_line_refer_data ++scols_line_remove_child ++scols_line_set_color ++scols_line_set_data ++scols_line_set_userdata ++scols_new_line ++scols_ref_line ++scols_unref_line ++
++ ++
++symbols ++libscols_symbols ++scols_copy_symbols ++scols_new_symbols ++scols_ref_symbols ++scols_symbols_set_branch ++scols_symbols_set_right ++scols_symbols_set_vertical ++scols_unref_symbols ++
++ ++
++table ++libscols_table ++scols_copy_table ++scols_new_table ++scols_ref_table ++scols_table_add_column ++scols_table_add_line ++scols_table_colors_wanted ++scols_table_enable_ascii ++scols_table_enable_colors ++scols_table_enable_export ++scols_table_enable_maxout ++scols_table_enable_noheadings ++scols_table_enable_raw ++scols_table_get_column ++scols_table_get_column_separator ++scols_table_get_line ++scols_table_get_line_separator ++scols_table_get_ncols ++scols_table_get_nlines ++scols_table_get_stream ++scols_table_is_ascii ++scols_table_is_empty ++scols_table_is_export ++scols_table_is_maxout ++scols_table_is_noheadings ++scols_table_is_raw ++scols_table_is_tree ++scols_table_new_column ++scols_table_new_line ++scols_table_next_column ++scols_table_next_line ++scols_table_reduce_termwidth ++scols_table_remove_column ++scols_table_remove_columns ++scols_table_remove_line ++scols_table_remove_lines ++scols_table_set_column_separator ++scols_table_set_line_separator ++scols_table_set_stream ++scols_table_set_symbols ++scols_sort_table ++scols_unref_table ++
++ ++
++table_print ++scols_print_table ++scols_print_table_to_string ++
++ ++
++version-utils ++scols_get_library_version ++scols_parse_version_string ++LIBSMARTCOLS_VERSION ++
++ ++
++init ++scols_init_debug ++
+diff -up util-linux-2.23.2/libsmartcols/docs/Makefile.am.kzak util-linux-2.23.2/libsmartcols/docs/Makefile.am +--- util-linux-2.23.2/libsmartcols/docs/Makefile.am.kzak 2014-09-25 14:41:48.984843867 +0200 ++++ util-linux-2.23.2/libsmartcols/docs/Makefile.am 2014-09-25 14:41:48.984843867 +0200 +@@ -0,0 +1,93 @@ ++## Process this file with automake to produce Makefile.in ++ ++# We require automake 1.10 at least. ++AUTOMAKE_OPTIONS = 1.10 ++ ++# This is a blank Makefile.am for using gtk-doc. ++# Copy this to your project's API docs directory and modify the variables to ++# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples ++# of using the various options. ++ ++# The name of the module, e.g. 'glib'. ++DOC_MODULE=libsmartcols ++ ++# Uncomment for versioned docs and specify the version of the module, e.g. '2'. ++#DOC_MODULE_VERSION=2 ++ ++# The top-level SGML file. You can change this if you want to. ++DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml ++ ++# The directory containing the source code. Relative to $(srcdir). ++# gtk-doc will search all .c & .h files beneath here for inline comments ++# documenting the functions and macros. ++# e.g. DOC_SOURCE_DIR=../../../gtk ++DOC_SOURCE_DIR=../src ++ ++# Extra options to pass to gtkdoc-scangobj. Not normally needed. ++SCANGOBJ_OPTIONS= ++ ++# Extra options to supply to gtkdoc-scan. ++# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" ++SCAN_OPTIONS= ++ ++# Extra options to supply to gtkdoc-mkdb. ++# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml ++MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space mnt ++ ++# Extra options to supply to gtkdoc-mktmpl ++# e.g. MKTMPL_OPTIONS=--only-section-tmpl ++MKTMPL_OPTIONS= ++ ++# Extra options to supply to gtkdoc-mkhtml ++MKHTML_OPTIONS= ++ ++# Extra options to supply to gtkdoc-fixref. Not normally needed. ++# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html ++FIXXREF_OPTIONS= ++ ++# Used for dependencies. The docs will be rebuilt if any of these change. ++# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h ++# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c ++HFILE_GLOB=$(top_builddir)/libsmartcols/src/libsmartcols.h ++CFILE_GLOB=$(top_srcdir)/libsmartcols/src/*.c ++ ++# Extra header to include when scanning, which are not under DOC_SOURCE_DIR ++# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h ++EXTRA_HFILES= ++ ++# Header files to ignore when scanning. Use base file name, no paths ++# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h ++IGNORE_HFILES=smartcolsP.h ++ ++# Images to copy into HTML directory. ++# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png ++HTML_IMAGES= ++ ++# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). ++# e.g. content_files=running.sgml building.sgml changes-2.0.sgml ++content_files = $(builddir)/version.xml ++ ++# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded ++# These files must be listed here *and* in content_files ++# e.g. expand_content_files=running.sgml ++expand_content_files= ++ ++# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. ++# Only needed if you are using gtkdoc-scangobj to dynamically query widget ++# signals and properties. ++# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) ++# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) ++GTKDOC_CFLAGS= ++GTKDOC_LIBS= ++ ++# This includes the standard gtk-doc make rules, copied by gtkdocize. ++include $(top_srcdir)/config/gtk-doc.make ++ ++# Other files to distribute ++# e.g. EXTRA_DIST += version.xml.in ++EXTRA_DIST += version.xml.in ++ ++# Files not to distribute ++# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types ++# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt ++DISTCLEANFILES += version.xml +diff -up util-linux-2.23.2/libsmartcols/docs/version.xml.in.kzak util-linux-2.23.2/libsmartcols/docs/version.xml.in +--- util-linux-2.23.2/libsmartcols/docs/version.xml.in.kzak 2014-09-25 14:41:48.985843877 +0200 ++++ util-linux-2.23.2/libsmartcols/docs/version.xml.in 2014-09-25 14:41:48.984843867 +0200 +@@ -0,0 +1 @@ ++@VERSION@ +diff -up util-linux-2.23.2/libsmartcols/Makemodule.am.kzak util-linux-2.23.2/libsmartcols/Makemodule.am +--- util-linux-2.23.2/libsmartcols/Makemodule.am.kzak 2014-09-25 14:41:48.983843858 +0200 ++++ util-linux-2.23.2/libsmartcols/Makemodule.am 2014-09-25 14:41:48.983843858 +0200 +@@ -0,0 +1,14 @@ ++if BUILD_LIBSMARTCOLS ++ ++include libsmartcols/src/Makemodule.am ++ ++if ENABLE_GTK_DOC ++# Docs uses separate Makefiles ++SUBDIRS += libsmartcols/docs ++endif ++ ++# noinst for RHEL7: pkgconfig_DATA += libsmartcols/smartcols.pc ++PATHFILES += libsmartcols/smartcols.pc ++EXTRA_DIST += libsmartcols/COPYING ++ ++endif # BUILD_LIBSMARTCOLS +diff -up util-linux-2.23.2/libsmartcols/smartcols.pc.in.kzak util-linux-2.23.2/libsmartcols/smartcols.pc.in +--- util-linux-2.23.2/libsmartcols/smartcols.pc.in.kzak 2014-09-25 14:41:48.985843877 +0200 ++++ util-linux-2.23.2/libsmartcols/smartcols.pc.in 2014-09-25 14:41:48.985843877 +0200 +@@ -0,0 +1,10 @@ ++prefix=@prefix@ ++exec_prefix=@exec_prefix@ ++libdir=@usrlib_execdir@ ++includedir=@includedir@ ++ ++Name: smartcols ++Description: table or tree library ++Version: @LIBSMARTCOLS_VERSION@ ++Cflags: -I${includedir}/libsmartcols ++Libs: -L${libdir} -lsmartcols +diff -up util-linux-2.23.2/libsmartcols/src/cell.c.kzak util-linux-2.23.2/libsmartcols/src/cell.c +--- util-linux-2.23.2/libsmartcols/src/cell.c.kzak 2014-09-25 14:41:48.986843886 +0200 ++++ util-linux-2.23.2/libsmartcols/src/cell.c 2014-09-25 14:41:48.986843886 +0200 +@@ -0,0 +1,242 @@ ++/* ++ * cell.c - functions for table handling at the cell level ++ * ++ * Copyright (C) 2014 Ondrej Oprala ++ * Copyright (C) 2014 Karel Zak ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++ ++/** ++ * SECTION: cell ++ * @title: Cell ++ * @short_description: cell API ++ * ++ * An API to access and modify per-cell data and information. Note that cell is ++ * always part of the line. If you destroy (un-reference) a line than it ++ * destroys all line cells too. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++ ++#include "smartcolsP.h" ++ ++/* ++ * The cell has no ref-counting, free() and new() functions. All is ++ * handled by libscols_line. ++ */ ++ ++/** ++ * scols_reset_cell: ++ * @ce: pointer to a struct libscols_cell instance ++ * ++ * Frees the cell's internal data and resets its status. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_reset_cell(struct libscols_cell *ce) ++{ ++ assert(ce); ++ ++ if (!ce) ++ return -EINVAL; ++ ++ /*DBG(CELL, ul_debugobj(ce, "reset"));*/ ++ free(ce->data); ++ free(ce->color); ++ memset(ce, 0, sizeof(*ce)); ++ return 0; ++} ++ ++/** ++ * scols_cell_set_data: ++ * @ce: a pointer to a struct libscols_cell instance ++ * @str: data (used for scols_printtable()) ++ * ++ * Stores a copy of the @str in @ce. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_cell_set_data(struct libscols_cell *ce, const char *str) ++{ ++ char *p = NULL; ++ ++ assert(ce); ++ ++ if (!ce) ++ return -EINVAL; ++ if (str) { ++ p = strdup(str); ++ if (!p) ++ return -ENOMEM; ++ } ++ free(ce->data); ++ ce->data = p; ++ return 0; ++} ++ ++/** ++ * scols_cell_refer_data: ++ * @ce: a pointer to a struct libscols_cell instance ++ * @str: data (used for scols_printtable()) ++ * ++ * Adds a reference to @str to @ce. The pointer is deallocated by ++ * scols_reset_cell() or scols_unref_line(). This function is mostly designed ++ * for situations when the data for the cell are already composed in allocated ++ * memory (e.g. asprintf()) to avoid extra unnecessary strdup(). ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_cell_refer_data(struct libscols_cell *ce, char *str) ++{ ++ assert(ce); ++ ++ if (!ce) ++ return -EINVAL; ++ free(ce->data); ++ ce->data = str; ++ return 0; ++} ++ ++/** ++ * scols_cell_get_data: ++ * @ce: a pointer to a struct libscols_cell instance ++ * ++ * Returns: data in @ce or NULL. ++ */ ++const char *scols_cell_get_data(const struct libscols_cell *ce) ++{ ++ assert(ce); ++ return ce ? ce->data : NULL; ++} ++ ++/** ++ * scols_cell_set_userdata: ++ * @ce: a pointer to a struct libscols_cell instance ++ * @data: private user data ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_cell_set_userdata(struct libscols_cell *ce, void *data) ++{ ++ assert(ce); ++ ++ if (!ce) ++ return -EINVAL; ++ ce->userdata = data; ++ return 0; ++} ++ ++/** ++ * scols_cell_get_userdata ++ * @ce: a pointer to a struct libscols_cell instance ++ * ++ * Returns: user data ++ */ ++void *scols_cell_get_userdata(struct libscols_cell *ce) ++{ ++ return ce ? ce->userdata : NULL; ++} ++ ++/** ++ * scols_cmpstr_cells: ++ * @a: pointer to cell ++ * @b: pointer to cell ++ * @data: unused pointer to private data (defined by API) ++ * ++ * Compares cells data by strcmp(). The function is designed for ++ * scols_column_set_cmpfunc() and scols_sort_table(). ++ * ++ * Returns: follows strcmp() return values. ++ */ ++int scols_cmpstr_cells(struct libscols_cell *a, ++ struct libscols_cell *b, ++ __attribute__((__unused__)) void *data) ++{ ++ const char *adata, *bdata; ++ ++ if (a == b) ++ return 0; ++ ++ adata = scols_cell_get_data(a); ++ bdata = scols_cell_get_data(b); ++ ++ if (adata == NULL && bdata == NULL) ++ return 0; ++ if (adata == NULL) ++ return -1; ++ if (bdata == NULL) ++ return 1; ++ return strcmp(adata, bdata); ++} ++ ++/** ++ * scols_cell_set_color: ++ * @ce: a pointer to a struct libscols_cell instance ++ * @color: ESC sequence ++ * ++ * Set the color of @ce to @color. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_cell_set_color(struct libscols_cell *ce, const char *color) ++{ ++ char *p = NULL; ++ ++ assert(ce); ++ ++ if (!ce) ++ return -EINVAL; ++ if (color) { ++ p = strdup(color); ++ if (!p) ++ return -ENOMEM; ++ } ++ free(ce->color); ++ ce->color = p; ++ return 0; ++} ++ ++/** ++ * scols_cell_get_color: ++ * @ce: a pointer to a struct libscols_cell instance ++ * ++ * Returns: the current color of @ce. ++ */ ++const char *scols_cell_get_color(const struct libscols_cell *ce) ++{ ++ assert(ce); ++ return ce ? ce->color : NULL; ++} ++ ++/** ++ * scols_cell_copy_content: ++ * @dest: a pointer to a struct libscols_cell instance ++ * @src: a pointer to an immutable struct libscols_cell instance ++ * ++ * Copy the contents of @src into @dest. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_cell_copy_content(struct libscols_cell *dest, ++ const struct libscols_cell *src) ++{ ++ int rc; ++ ++ assert(dest); ++ assert(src); ++ ++ rc = scols_cell_set_data(dest, scols_cell_get_data(src)); ++ if (!rc) ++ rc = scols_cell_set_color(dest, scols_cell_get_color(src)); ++ if (!rc) ++ dest->userdata = src->userdata; ++ ++ DBG(CELL, ul_debugobj((void *) src, "copy into %p", dest)); ++ return rc; ++} +diff -up util-linux-2.23.2/libsmartcols/src/column.c.kzak util-linux-2.23.2/libsmartcols/src/column.c +--- util-linux-2.23.2/libsmartcols/src/column.c.kzak 2014-09-25 14:41:48.986843886 +0200 ++++ util-linux-2.23.2/libsmartcols/src/column.c 2014-09-25 14:41:48.986843886 +0200 +@@ -0,0 +1,337 @@ ++/* ++ * column.c - functions for table handling at the column level ++ * ++ * Copyright (C) 2014 Ondrej Oprala ++ * Copyright (C) 2014 Karel Zak ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++ ++/** ++ * SECTION: column ++ * @title: Column ++ * @short_description: column API ++ * ++ * An API to access and modify per-column data and information. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++ ++#include "smartcolsP.h" ++ ++/** ++ * scols_new_column: ++ * ++ * Allocates space for a new column. ++ * ++ * Returns: a pointer to a new struct libscols_cell instance, NULL in case of an ENOMEM error. ++ */ ++struct libscols_column *scols_new_column(void) ++{ ++ struct libscols_column *cl; ++ ++ cl = calloc(1, sizeof(*cl)); ++ if (!cl) ++ return NULL; ++ DBG(COL, ul_debugobj(cl, "alloc")); ++ cl->refcount = 1; ++ INIT_LIST_HEAD(&cl->cl_columns); ++ return cl; ++} ++ ++/** ++ * scols_ref_column: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Increases the refcount of @cl. ++ */ ++void scols_ref_column(struct libscols_column *cl) ++{ ++ if (cl) ++ cl->refcount++; ++} ++ ++/** ++ * scols_unref_column: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Decreases the refcount of @cl. When the count falls to zero, the instance ++ * is automatically deallocated. ++ */ ++void scols_unref_column(struct libscols_column *cl) ++{ ++ if (cl && --cl->refcount <= 0) { ++ DBG(COL, ul_debugobj(cl, "dealloc")); ++ list_del(&cl->cl_columns); ++ scols_reset_cell(&cl->header); ++ free(cl->color); ++ free(cl); ++ } ++} ++ ++/** ++ * scols_copy_column: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Creates a new column and copies @cl's data over to it. ++ * ++ * Returns: a pointer to a new struct libscols_column instance. ++ */ ++struct libscols_column *scols_copy_column(const struct libscols_column *cl) ++{ ++ struct libscols_column *ret; ++ ++ assert (cl); ++ if (!cl) ++ return NULL; ++ ret = scols_new_column(); ++ if (!ret) ++ return NULL; ++ ++ DBG(COL, ul_debugobj((void *) cl, "copy to %p", ret)); ++ ++ if (scols_column_set_color(ret, cl->color)) ++ goto err; ++ if (scols_cell_copy_content(&ret->header, &cl->header)) ++ goto err; ++ ++ ret->width = cl->width; ++ ret->width_min = cl->width_min; ++ ret->width_max = cl->width_max; ++ ret->width_avg = cl->width_avg; ++ ret->width_hint = cl->width_hint; ++ ret->flags = cl->flags; ++ ret->is_extreme = cl->is_extreme; ++ ++ return ret; ++err: ++ scols_unref_column(ret); ++ return NULL; ++} ++ ++/** ++ * scols_column_set_whint: ++ * @cl: a pointer to a struct libscols_column instance ++ * @whint: a width hint ++ * ++ * Sets the width hint of column @cl to @whint. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_column_set_whint(struct libscols_column *cl, double whint) ++{ ++ assert(cl); ++ ++ if (!cl) ++ return -EINVAL; ++ ++ cl->width_hint = whint; ++ return 0; ++} ++ ++/** ++ * scols_column_get_whint: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Returns: The width hint of column @cl, a negative value in case of an error. ++ */ ++double scols_column_get_whint(struct libscols_column *cl) ++{ ++ assert(cl); ++ return cl ? cl->width_hint : -EINVAL; ++} ++ ++/** ++ * scols_column_set_flags: ++ * @cl: a pointer to a struct libscols_column instance ++ * @flags: a flag mask ++ * ++ * Sets the flags of @cl to @flags. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_column_set_flags(struct libscols_column *cl, int flags) ++{ ++ assert(cl); ++ ++ if (!cl) ++ return -EINVAL; ++ ++ cl->flags = flags; ++ return 0; ++} ++ ++/** ++ * scols_column_get_flags: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Returns: The flag mask of @cl, a negative value in case of an error. ++ */ ++int scols_column_get_flags(struct libscols_column *cl) ++{ ++ assert(cl); ++ return cl ? cl->flags : -EINVAL; ++} ++ ++/** ++ * scols_column_get_header: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Returns: A pointer to a struct libscols_cell instance, representing the ++ * header info of column @cl or NULL in case of an error. ++ */ ++struct libscols_cell *scols_column_get_header(struct libscols_column *cl) ++{ ++ assert(cl); ++ return cl ? &cl->header : NULL; ++} ++ ++/** ++ * scols_column_set_color: ++ * @cl: a pointer to a struct libscols_column instance ++ * @color: ESC sequence ++ * ++ * The default color for data cells and column header. ++ * ++ * If you want to set header specific color then use scols_column_get_header() ++ * and scols_cell_set_color(). ++ * ++ * If you want to set data cell specific color the use scols_line_get_cell() + ++ * scols_cell_set_color(). ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_column_set_color(struct libscols_column *cl, const char *color) ++{ ++ char *p = NULL; ++ ++ assert(cl); ++ if (!cl) ++ return -EINVAL; ++ if (color) { ++ p = strdup(color); ++ if (!p) ++ return -ENOMEM; ++ } ++ ++ free(cl->color); ++ cl->color = p; ++ return 0; ++} ++ ++/** ++ * scols_column_get_color: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Returns: The current color setting of the column @cl. ++ */ ++const char *scols_column_get_color(struct libscols_column *cl) ++{ ++ assert(cl); ++ return cl ? cl->color : NULL; ++} ++ ++ ++/** ++ * scols_column_set_cmpfunc: ++ * @cl: column ++ * @cmp: pointer to compare function ++ * @data: private data for cmp function ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_column_set_cmpfunc(struct libscols_column *cl, ++ int (*cmp)(struct libscols_cell *, ++ struct libscols_cell *, ++ void *), ++ void *data) ++{ ++ assert(cl); ++ if (!cl) ++ return -EINVAL; ++ ++ cl->cmpfunc = cmp; ++ cl->cmpfunc_data = data; ++ return 0; ++} ++ ++/** ++ * scols_column_is_trunc: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Gets the value of @cl's flag trunc. ++ * ++ * Returns: trunc flag value, negative value in case of an error. ++ */ ++int scols_column_is_trunc(struct libscols_column *cl) ++{ ++ assert(cl); ++ if (!cl) ++ return -EINVAL; ++ return cl->flags & SCOLS_FL_TRUNC; ++} ++/** ++ * scols_column_is_tree: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Gets the value of @cl's flag tree. ++ * ++ * Returns: tree flag value, negative value in case of an error. ++ */ ++int scols_column_is_tree(struct libscols_column *cl) ++{ ++ assert(cl); ++ if (!cl) ++ return -EINVAL; ++ return cl->flags & SCOLS_FL_TREE; ++} ++/** ++ * scols_column_is_right: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Gets the value of @cl's flag right. ++ * ++ * Returns: right flag value, negative value in case of an error. ++ */ ++int scols_column_is_right(struct libscols_column *cl) ++{ ++ assert(cl); ++ if (!cl) ++ return -EINVAL; ++ return cl->flags & SCOLS_FL_RIGHT; ++} ++/** ++ * scols_column_is_strict_width: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Gets the value of @cl's flag strict_width. ++ * ++ * Returns: strict_width flag value, negative value in case of an error. ++ */ ++int scols_column_is_strict_width(struct libscols_column *cl) ++{ ++ assert(cl); ++ if (!cl) ++ return -EINVAL; ++ return cl->flags & SCOLS_FL_STRICTWIDTH; ++} ++/** ++ * scols_column_is_noextremes: ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Gets the value of @cl's flag no_extremes. ++ * ++ * Returns: no_extremes flag value, negative value in case of an error. ++ */ ++int scols_column_is_noextremes(struct libscols_column *cl) ++{ ++ assert(cl); ++ if (!cl) ++ return -EINVAL; ++ return cl->flags & SCOLS_FL_NOEXTREMES; ++} +diff -up util-linux-2.23.2/libsmartcols/src/.gitignore.kzak util-linux-2.23.2/libsmartcols/src/.gitignore +--- util-linux-2.23.2/libsmartcols/src/.gitignore.kzak 2014-09-25 14:41:48.985843877 +0200 ++++ util-linux-2.23.2/libsmartcols/src/.gitignore 2014-09-25 14:41:48.985843877 +0200 +@@ -0,0 +1 @@ ++libsmartcols.h +diff -up util-linux-2.23.2/libsmartcols/src/init.c.kzak util-linux-2.23.2/libsmartcols/src/init.c +--- util-linux-2.23.2/libsmartcols/src/init.c.kzak 2014-09-25 14:41:48.987843896 +0200 ++++ util-linux-2.23.2/libsmartcols/src/init.c 2014-09-25 14:41:48.987843896 +0200 +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (C) 2014 Karel Zak ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++ ++/** ++ * SECTION: init ++ * @title: Library initialization ++ * @short_description: initialize debugging ++ * ++ * The library debug stuff. ++ */ ++ ++#include ++ ++#include "smartcolsP.h" ++ ++UL_DEBUG_DEFINE_MASK(libsmartcols); ++ ++static const struct dbg_mask libsmartcols_masknames [] = { ++ { "all", SCOLS_DEBUG_ALL }, ++ { "cell", SCOLS_DEBUG_CELL }, ++ { "line", SCOLS_DEBUG_LINE }, ++ { "tab", SCOLS_DEBUG_TAB }, ++ { "col", SCOLS_DEBUG_COL }, ++ { "buff", SCOLS_DEBUG_BUFF }, ++ { NULL, 0 } ++}; ++/** ++ * scols_init_debug: ++ * @mask: debug mask (0xffff to enable full debugging) ++ * ++ * If the @mask is not specified, then this function reads ++ * the LIBSMARTCOLS_DEBUG environment variable to get the mask. ++ * ++ * Already initialized debugging stuff cannot be changed. Calling ++ * this function twice has no effect. ++ */ ++void scols_init_debug(int mask) ++{ ++ __UL_INIT_DEBUG(libsmartcols, SCOLS_DEBUG_, mask, LIBSMARTCOLS_DEBUG); ++ ++ if (libsmartcols_debug_mask != SCOLS_DEBUG_INIT) { ++ const char *ver = NULL; ++ ++ scols_get_library_version(&ver); ++ ++ DBG(INIT, ul_debug("library version: %s", ver)); ++ } ++} +diff -up util-linux-2.23.2/libsmartcols/src/iter.c.kzak util-linux-2.23.2/libsmartcols/src/iter.c +--- util-linux-2.23.2/libsmartcols/src/iter.c.kzak 2014-09-25 14:41:48.987843896 +0200 ++++ util-linux-2.23.2/libsmartcols/src/iter.c 2014-09-25 14:41:48.987843896 +0200 +@@ -0,0 +1,74 @@ ++/* ++ * Copyright (C) 2009-2014 Karel Zak ++ * Copyright (C) 2014 Ondrej Oprala ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++ ++/** ++ * SECTION: iter ++ * @title: Iterator ++ * @short_description: unified iterator ++ * ++ * The iterator keeps the direction and the last position ++ * for access to the internal library tables/lists. ++ */ ++ ++#include ++#include ++ ++#include "smartcolsP.h" ++ ++/** ++ * scols_new_iter: ++ * @direction: SCOLS_INTER_{FOR,BACK}WARD direction ++ * ++ * Returns: newly allocated generic libmount iterator. ++ */ ++struct libscols_iter *scols_new_iter(int direction) ++{ ++ struct libscols_iter *itr = calloc(1, sizeof(*itr)); ++ if (!itr) ++ return NULL; ++ itr->direction = direction; ++ return itr; ++} ++ ++/** ++ * scols_free_iter: ++ * @itr: iterator pointer ++ * ++ * Deallocates the iterator. ++ */ ++void scols_free_iter(struct libscols_iter *itr) ++{ ++ free(itr); ++} ++ ++/** ++ * scols_reset_iter: ++ * @itr: iterator pointer ++ * @direction: SCOLS_INTER_{FOR,BACK}WARD or -1 to keep the direction unchanged ++ * ++ * Resets the iterator. ++ */ ++void scols_reset_iter(struct libscols_iter *itr, int direction) ++{ ++ if (direction == -1) ++ direction = itr->direction; ++ ++ memset(itr, 0, sizeof(*itr)); ++ itr->direction = direction; ++} ++ ++/** ++ * scols_iter_get_direction: ++ * @itr: iterator pointer ++ * ++ * Returns: SCOLS_INTER_{FOR,BACK}WARD ++ */ ++int scols_iter_get_direction(struct libscols_iter *itr) ++{ ++ return itr->direction; ++} +diff -up util-linux-2.23.2/libsmartcols/src/libsmartcols.h.in.kzak util-linux-2.23.2/libsmartcols/src/libsmartcols.h.in +--- util-linux-2.23.2/libsmartcols/src/libsmartcols.h.in.kzak 2014-09-25 14:41:48.988843905 +0200 ++++ util-linux-2.23.2/libsmartcols/src/libsmartcols.h.in 2014-09-25 14:41:48.988843905 +0200 +@@ -0,0 +1,229 @@ ++/* ++ * Prints table or tree. ++ * ++ * Copyright (C) 2014 Ondrej Oprala ++ * Copyright (C) 2014 Karel Zak ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++#ifndef _LIBSMARTCOLS_H ++#define _LIBSMARTCOLS_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include ++#include ++#include ++ ++/** ++ * LIBSMARTCOLS_VERSION: ++ * ++ * Library version string ++ */ ++#define LIBSMARTCOLS_VERSION "@LIBSMARTCOLS_VERSION@" ++ ++/** ++ * libscols_iter: ++ * ++ * Generic iterator ++ */ ++struct libscols_iter; ++ ++/** ++ * libscols_symbols: ++ * ++ * Symbol groups for printing tree hierarchies ++ */ ++struct libscols_symbols; ++ ++/** ++ * libscols_cell: ++ * ++ * A cell - the smallest library object ++ */ ++struct libscols_cell; ++ ++/** ++ * libscols_line: ++ * ++ * A line - an array of cells ++ */ ++struct libscols_line; ++ ++/** ++ * libscols_table: ++ * ++ * A table - The most abstract object, encapsulating lines, columns, symbols and cells ++ */ ++struct libscols_table; ++ ++/** ++ * libscols_column: ++ * ++ * A column - defines the number of columns and column names ++ */ ++struct libscols_column; ++ ++/* iter.c */ ++enum { ++ ++ SCOLS_ITER_FORWARD = 0, ++ SCOLS_ITER_BACKWARD ++}; ++ ++/* ++ * Column flags ++ */ ++enum { ++ SCOLS_FL_TRUNC = (1 << 0), /* truncate fields data if necessary */ ++ SCOLS_FL_TREE = (1 << 1), /* use tree "ascii art" */ ++ SCOLS_FL_RIGHT = (1 << 2), /* align to the right */ ++ SCOLS_FL_STRICTWIDTH = (1 << 3), /* don't reduce width if column is empty */ ++ SCOLS_FL_NOEXTREMES = (1 << 4), /* ignore extreme fields when count column width*/ ++}; ++ ++extern struct libscols_iter *scols_new_iter(int direction); ++extern void scols_free_iter(struct libscols_iter *itr); ++extern void scols_reset_iter(struct libscols_iter *itr, int direction); ++extern int scols_iter_get_direction(struct libscols_iter *itr); ++ ++/* init.c */ ++extern void scols_init_debug(int mask); ++ ++/* version.c */ ++extern int scols_parse_version_string(const char *ver_string); ++extern int scols_get_library_version(const char **ver_string); ++ ++/* symbols.c */ ++extern struct libscols_symbols *scols_new_symbols(void); ++extern void scols_ref_symbols(struct libscols_symbols *sy); ++extern void scols_unref_symbols(struct libscols_symbols *sy); ++extern struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sb); ++extern int scols_symbols_set_branch(struct libscols_symbols *sb, const char *str); ++extern int scols_symbols_set_vertical(struct libscols_symbols *sb, const char *str); ++extern int scols_symbols_set_right(struct libscols_symbols *sb, const char *str); ++ ++/* cell.c */ ++extern int scols_reset_cell(struct libscols_cell *ce); ++extern int scols_cell_copy_content(struct libscols_cell *dest, ++ const struct libscols_cell *src); ++extern int scols_cell_set_data(struct libscols_cell *ce, const char *str); ++extern int scols_cell_refer_data(struct libscols_cell *ce, char *str); ++extern const char *scols_cell_get_data(const struct libscols_cell *ce); ++extern int scols_cell_set_color(struct libscols_cell *ce, const char *color); ++extern const char *scols_cell_get_color(const struct libscols_cell *ce); ++ ++extern void *scols_cell_get_userdata(struct libscols_cell *ce); ++extern int scols_cell_set_userdata(struct libscols_cell *ce, void *data); ++ ++extern int scols_cmpstr_cells(struct libscols_cell *a, ++ struct libscols_cell *b, void *data); ++/* column.c */ ++extern int scols_column_is_tree(struct libscols_column *cl); ++extern int scols_column_is_trunc(struct libscols_column *cl); ++extern int scols_column_is_right(struct libscols_column *cl); ++extern int scols_column_is_strict_width(struct libscols_column *cl); ++extern int scols_column_is_noextremes(struct libscols_column *cl); ++ ++extern int scols_column_set_flags(struct libscols_column *cl, int flags); ++extern int scols_column_get_flags(struct libscols_column *cl); ++extern struct libscols_column *scols_new_column(void); ++extern void scols_ref_column(struct libscols_column *cl); ++extern void scols_unref_column(struct libscols_column *cl); ++extern struct libscols_column *scols_copy_column(const struct libscols_column *cl); ++extern int scols_column_set_whint(struct libscols_column *cl, double whint); ++extern double scols_column_get_whint(struct libscols_column *cl); ++extern struct libscols_cell *scols_column_get_header(struct libscols_column *cl); ++extern int scols_column_set_color(struct libscols_column *cl, const char *color); ++extern const char *scols_column_get_color(struct libscols_column *cl); ++ ++extern int scols_column_set_cmpfunc(struct libscols_column *cl, ++ int (*cmp)(struct libscols_cell *a, ++ struct libscols_cell *b, void *), ++ void *data); ++ ++/* line.c */ ++extern struct libscols_line *scols_new_line(void); ++extern void scols_ref_line(struct libscols_line *ln); ++extern void scols_unref_line(struct libscols_line *ln); ++extern int scols_line_alloc_cells(struct libscols_line *ln, size_t n); ++extern void scols_line_free_cells(struct libscols_line *ln); ++extern int scols_line_set_userdata(struct libscols_line *ln, void *data); ++extern void *scols_line_get_userdata(struct libscols_line *ln); ++extern int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *child); ++extern int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child); ++extern int scols_line_has_children(struct libscols_line *ln); ++extern int scols_line_next_child(struct libscols_line *ln, ++ struct libscols_iter *itr, struct libscols_line **chld); ++extern struct libscols_line *scols_line_get_parent(struct libscols_line *ln); ++extern int scols_line_set_color(struct libscols_line *ln, const char *color); ++extern const char *scols_line_get_color(struct libscols_line *ln); ++extern size_t scols_line_get_ncells(struct libscols_line *ln); ++extern struct libscols_cell *scols_line_get_cell(struct libscols_line *ln, size_t n); ++extern struct libscols_cell *scols_line_get_column_cell( ++ struct libscols_line *ln, ++ struct libscols_column *cl); ++extern int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data); ++extern int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data); ++extern struct libscols_line *scols_copy_line(struct libscols_line *ln); ++ ++/* table */ ++extern int scols_table_colors_wanted(struct libscols_table *tb); ++extern int scols_table_is_raw(struct libscols_table *tb); ++extern int scols_table_is_ascii(struct libscols_table *tb); ++extern int scols_table_is_noheadings(struct libscols_table *tb); ++extern int scols_table_is_empty(struct libscols_table *tb); ++extern int scols_table_is_export(struct libscols_table *tb); ++extern int scols_table_is_maxout(struct libscols_table *tb); ++extern int scols_table_is_tree(struct libscols_table *tb); ++ ++extern int scols_table_enable_colors(struct libscols_table *tb, int enable); ++extern int scols_table_enable_raw(struct libscols_table *tb, int enable); ++extern int scols_table_enable_ascii(struct libscols_table *tb, int enable); ++extern int scols_table_enable_noheadings(struct libscols_table *tb, int enable); ++extern int scols_table_enable_export(struct libscols_table *tb, int enable); ++extern int scols_table_enable_maxout(struct libscols_table *tb, int enable); ++ ++extern int scols_table_set_column_separator(struct libscols_table *tb, const char *sep); ++extern int scols_table_set_line_separator(struct libscols_table *tb, const char *sep); ++ ++extern struct libscols_table *scols_new_table(void); ++extern void scols_ref_table(struct libscols_table *tb); ++extern void scols_unref_table(struct libscols_table *tb); ++extern int scols_table_add_column(struct libscols_table *tb, struct libscols_column *cl); ++extern int scols_table_remove_column(struct libscols_table *tb, struct libscols_column *cl); ++extern int scols_table_remove_columns(struct libscols_table *tb); ++extern struct libscols_column *scols_table_new_column(struct libscols_table *tb, const char *name, double whint, int flags); ++extern int scols_table_next_column(struct libscols_table *tb, struct libscols_iter *itr, struct libscols_column **cl); ++extern char *scols_table_get_column_separator(struct libscols_table *tb); ++extern char *scols_table_get_line_separator(struct libscols_table *tb); ++extern int scols_table_get_ncols(struct libscols_table *tb); ++extern int scols_table_get_nlines(struct libscols_table *tb); ++extern struct libscols_column *scols_table_get_column(struct libscols_table *tb, size_t n); ++extern int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln); ++extern int scols_table_remove_line(struct libscols_table *tb, struct libscols_line *ln); ++extern void scols_table_remove_lines(struct libscols_table *tb); ++extern int scols_table_next_line(struct libscols_table *tb, struct libscols_iter *itr, struct libscols_line **ln); ++extern struct libscols_line *scols_table_new_line(struct libscols_table *tb, struct libscols_line *parent); ++extern struct libscols_line *scols_table_get_line(struct libscols_table *tb, size_t n); ++extern struct libscols_table *scols_copy_table(struct libscols_table *tb); ++extern int scols_table_set_symbols(struct libscols_table *tb, struct libscols_symbols *sy); ++ ++extern int scols_table_set_stream(struct libscols_table *tb, FILE *stream); ++extern FILE *scols_table_get_stream(struct libscols_table *tb); ++extern int scols_table_reduce_termwidth(struct libscols_table *tb, size_t reduce); ++ ++extern int scols_sort_table(struct libscols_table *tb, struct libscols_column *cl); ++ ++/* table_print.c */ ++extern int scols_print_table(struct libscols_table *tb); ++extern int scols_print_table_to_string(struct libscols_table *tb, char **data); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _LIBSMARTCOLS_H */ +diff -up util-linux-2.23.2/libsmartcols/src/libsmartcols.sym.kzak util-linux-2.23.2/libsmartcols/src/libsmartcols.sym +--- util-linux-2.23.2/libsmartcols/src/libsmartcols.sym.kzak 2014-09-25 14:41:48.988843905 +0200 ++++ util-linux-2.23.2/libsmartcols/src/libsmartcols.sym 2014-09-25 14:41:48.988843905 +0200 +@@ -0,0 +1,112 @@ ++/* ++ * symbols since util-linux 2.25 ++ */ ++SMARTCOLS_2.25 { ++global: ++ scols_cell_copy_content; ++ scols_cell_get_color; ++ scols_cell_get_data; ++ scols_cell_get_userdata; ++ scols_cell_refer_data; ++ scols_cell_set_color; ++ scols_cell_set_data; ++ scols_cell_set_userdata; ++ scols_cmpstr_cells; ++ scols_column_get_color; ++ scols_column_get_flags; ++ scols_column_get_header; ++ scols_column_get_whint; ++ scols_column_is_noextremes; ++ scols_column_is_right; ++ scols_column_is_strict_width; ++ scols_column_is_tree; ++ scols_column_is_trunc; ++ scols_column_set_cmpfunc; ++ scols_column_set_color; ++ scols_column_set_flags; ++ scols_column_set_whint; ++ scols_copy_column; ++ scols_copy_line; ++ scols_copy_symbols; ++ scols_copy_table; ++ scols_free_iter; ++ scols_get_library_version; ++ scols_init_debug; ++ scols_iter_get_direction; ++ scols_line_add_child; ++ scols_line_alloc_cells; ++ scols_line_free_cells; ++ scols_line_get_cell; ++ scols_line_get_color; ++ scols_line_get_column_cell; ++ scols_line_get_ncells; ++ scols_line_get_parent; ++ scols_line_get_userdata; ++ scols_line_has_children; ++ scols_line_next_child; ++ scols_line_refer_data; ++ scols_line_remove_child; ++ scols_line_set_color; ++ scols_line_set_data; ++ scols_line_set_userdata; ++ scols_new_column; ++ scols_new_iter; ++ scols_new_line; ++ scols_new_symbols; ++ scols_new_table; ++ scols_parse_version_string; ++ scols_print_table; ++ scols_print_table_to_string; ++ scols_ref_column; ++ scols_ref_line; ++ scols_ref_symbols; ++ scols_ref_table; ++ scols_reset_cell; ++ scols_reset_iter; ++ scols_sort_table; ++ scols_symbols_set_branch; ++ scols_symbols_set_right; ++ scols_symbols_set_vertical; ++ scols_table_add_column; ++ scols_table_add_line; ++ scols_table_colors_wanted; ++ scols_table_enable_ascii; ++ scols_table_enable_colors; ++ scols_table_enable_export; ++ scols_table_enable_maxout; ++ scols_table_enable_noheadings; ++ scols_table_enable_raw; ++ scols_table_get_column; ++ scols_table_get_column_separator; ++ scols_table_get_line; ++ scols_table_get_line_separator; ++ scols_table_get_ncols; ++ scols_table_get_nlines; ++ scols_table_get_stream; ++ scols_table_is_ascii; ++ scols_table_is_empty; ++ scols_table_is_export; ++ scols_table_is_maxout; ++ scols_table_is_noheadings; ++ scols_table_is_raw; ++ scols_table_is_tree; ++ scols_table_new_column; ++ scols_table_new_line; ++ scols_table_next_column; ++ scols_table_next_line; ++ scols_table_reduce_termwidth; ++ scols_table_remove_column; ++ scols_table_remove_columns; ++ scols_table_remove_line; ++ scols_table_remove_lines; ++ scols_table_set_column_separator; ++ scols_table_set_line_separator; ++ scols_table_set_stream; ++ scols_table_set_symbols; ++ scols_unref_column; ++ scols_unref_line; ++ scols_unref_symbols; ++ scols_unref_table; ++local: ++ *; ++}; +diff -up util-linux-2.23.2/libsmartcols/src/line.c.kzak util-linux-2.23.2/libsmartcols/src/line.c +--- util-linux-2.23.2/libsmartcols/src/line.c.kzak 2014-09-25 14:41:48.989843915 +0200 ++++ util-linux-2.23.2/libsmartcols/src/line.c 2014-09-25 14:41:48.989843915 +0200 +@@ -0,0 +1,456 @@ ++/* ++ * line.c - functions for table handling at the line level ++ * ++ * Copyright (C) 2014 Karel Zak ++ * Copyright (C) 2014 Ondrej Oprala ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++ ++/** ++ * SECTION: line ++ * @title: Line ++ * @short_description: line API ++ * ++ * An API to access and modify per-line data and information. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++ ++#include "smartcolsP.h" ++ ++/** ++ * scols_new_line: ++ * ++ * Note that the line is allocated without cells, the cells will be allocated ++ * later when you add the line to the table. If you want to use the line ++ * without table then you have to explicitly allocate the cells by ++ * scols_line_alloc_cells(). ++ * ++ * Returns: a pointer to a new struct libscols_line instance. ++ */ ++struct libscols_line *scols_new_line(void) ++{ ++ struct libscols_line *ln; ++ ++ ln = calloc(1, sizeof(*ln)); ++ if (!ln) ++ return NULL; ++ ++ DBG(LINE, ul_debugobj(ln, "alloc")); ++ ln->refcount = 1; ++ INIT_LIST_HEAD(&ln->ln_lines); ++ INIT_LIST_HEAD(&ln->ln_children); ++ INIT_LIST_HEAD(&ln->ln_branch); ++ return ln; ++} ++ ++/** ++ * scols_ref_line: ++ * @ln: a pointer to a struct libscols_line instance ++ * ++ * Increases the refcount of @ln. ++ */ ++void scols_ref_line(struct libscols_line *ln) ++{ ++ if (ln) ++ ln->refcount++; ++} ++ ++/** ++ * scols_unref_line: ++ * @ln: a pointer to a struct libscols_line instance ++ * ++ * Decreases the refcount of @ln. When the count falls to zero, the instance ++ * is automatically deallocated. ++ */ ++void scols_unref_line(struct libscols_line *ln) ++{ ++ ++ if (ln && --ln->refcount <= 0) { ++ DBG(CELL, ul_debugobj(ln, "dealloc")); ++ list_del(&ln->ln_lines); ++ list_del(&ln->ln_children); ++ scols_line_free_cells(ln); ++ free(ln->color); ++ free(ln); ++ return; ++ } ++} ++ ++/** ++ * scols_line_free_cells: ++ * @ln: a pointer to a struct libscols_line instance ++ * ++ * Frees the allocated cells referenced to by @ln. ++ */ ++void scols_line_free_cells(struct libscols_line *ln) ++{ ++ size_t i; ++ ++ if (!ln || !ln->cells) ++ return; ++ ++ DBG(LINE, ul_debugobj(ln, "free cells")); ++ ++ for (i = 0; i < ln->ncells; i++) ++ scols_reset_cell(&ln->cells[i]); ++ ++ free(ln->cells); ++ ln->ncells = 0; ++ ln->cells = NULL; ++} ++ ++/** ++ * scols_line_alloc_cells: ++ * @ln: a pointer to a struct libscols_line instance ++ * @n: the number of elements ++ * ++ * Allocates space for @n cells. This function is optional, ++ * and libsmartcols automatically allocates necessary cells ++ * according to number of columns in the table when you add ++ * the line to the table. See scols_table_add_line(). ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_line_alloc_cells(struct libscols_line *ln, size_t n) ++{ ++ struct libscols_cell *ce; ++ ++ assert(ln); ++ ++ if (!ln) ++ return -EINVAL; ++ if (ln->ncells == n) ++ return 0; ++ ++ if (!n) { ++ scols_line_free_cells(ln); ++ return 0; ++ } ++ ++ DBG(LINE, ul_debugobj(ln, "alloc %zu cells", n)); ++ ++ ce = realloc(ln->cells, n * sizeof(struct libscols_cell)); ++ if (!ce) ++ return -errno; ++ ++ if (n > ln->ncells) ++ memset(ce + ln->ncells, 0, ++ (n - ln->ncells) * sizeof(struct libscols_cell)); ++ ++ ln->cells = ce; ++ ln->ncells = n; ++ return 0; ++} ++ ++/** ++ * scols_line_set_userdata: ++ * @ln: a pointer to a struct libscols_line instance ++ * @data: user data ++ * ++ * Binds @data to @ln. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_line_set_userdata(struct libscols_line *ln, void *data) ++{ ++ assert(ln); ++ if (!ln) ++ return -EINVAL; ++ ln->userdata = data; ++ return 0; ++} ++ ++/** ++ * scols_line_get_userdata: ++ * @ln: a pointer to a struct libscols_line instance ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++void *scols_line_get_userdata(struct libscols_line *ln) ++{ ++ assert(ln); ++ return ln ? ln->userdata : NULL; ++} ++ ++/** ++ * scols_line_remove_child: ++ * @ln: a pointer to a struct libscols_line instance ++ * @child: a pointer to a struct libscols_line instance ++ * ++ * Removes @child as a child of @ln. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *child) ++{ ++ assert(ln); ++ assert(child); ++ ++ if (!ln || !child) ++ return -EINVAL; ++ ++ DBG(LINE, ul_debugobj(ln, "remove child %p", child)); ++ ++ list_del_init(&child->ln_children); ++ child->parent = NULL; ++ scols_unref_line(child); ++ ++ scols_unref_line(ln); ++ return 0; ++} ++ ++/** ++ * scols_line_add_child: ++ * @ln: a pointer to a struct libscols_line instance ++ * @child: a pointer to a struct libscols_line instance ++ * ++ * Sets @child as a child of @ln. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child) ++{ ++ assert(ln); ++ assert(child); ++ ++ if (!ln || !child) ++ return -EINVAL; ++ ++ /* unref old<->parent */ ++ if (child->parent) ++ scols_line_remove_child(child->parent, child); ++ ++ DBG(LINE, ul_debugobj(ln, "add child %p", child)); ++ ++ /* new reference from parent to child */ ++ list_add_tail(&child->ln_children, &ln->ln_branch); ++ scols_ref_line(child); ++ ++ /* new reference from child to parent */ ++ child->parent = ln; ++ scols_ref_line(ln); ++ ++ return 0; ++} ++ ++/** ++ * scols_line_get_parent: ++ * @ln: a pointer to a struct libscols_line instance ++ * ++ * Returns: a pointer to @ln's parent, NULL in case it has no parent or if there was an error. ++ */ ++struct libscols_line *scols_line_get_parent(struct libscols_line *ln) ++{ ++ assert(ln); ++ return ln ? ln->parent : NULL; ++} ++ ++/** ++ * scols_line_has_children: ++ * @ln: a pointer to a struct libscols_line instance ++ * ++ * Returns: 1 if @ln has any children, otherwise 0. ++ */ ++int scols_line_has_children(struct libscols_line *ln) ++{ ++ assert(ln); ++ return ln ? !list_empty(&ln->ln_branch) : 0; ++} ++ ++/** ++ * scols_line_next_child: ++ * @ln: a pointer to a struct libscols_line instance ++ * @itr: a pointer to a struct libscols_iter instance ++ * @chld: a pointer to a pointer to a struct libscols_line instance ++ * ++ * Finds the next child and returns a pointer to it via @chld. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_line_next_child(struct libscols_line *ln, ++ struct libscols_iter *itr, ++ struct libscols_line **chld) ++{ ++ int rc = 1; ++ ++ if (!ln || !itr || !chld) ++ return -EINVAL; ++ *chld = NULL; ++ ++ if (!itr->head) ++ SCOLS_ITER_INIT(itr, &ln->ln_branch); ++ if (itr->p != itr->head) { ++ SCOLS_ITER_ITERATE(itr, *chld, struct libscols_line, ln_children); ++ rc = 0; ++ } ++ ++ return rc; ++} ++ ++/** ++ * scols_line_set_color: ++ * @ln: a pointer to a struct libscols_line instance ++ * @color: ESC sequence ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_line_set_color(struct libscols_line *ln, const char *color) ++{ ++ char *p = NULL; ++ ++ assert(ln); ++ if (!ln) ++ return -EINVAL; ++ if (color) { ++ p = strdup(color); ++ if (!p) ++ return -ENOMEM; ++ } ++ ++ free(ln->color); ++ ln->color = p; ++ return 0; ++} ++ ++/** ++ * scols_line_get_color: ++ * @ln: a pointer to a struct libscols_line instance ++ * ++ * Returns: @ln's color string, NULL in case of an error. ++ */ ++const char *scols_line_get_color(struct libscols_line *ln) ++{ ++ assert(ln); ++ return ln ? ln->color : NULL; ++} ++ ++/** ++ * scols_line_get_ncells: ++ * @ln: a pointer to a struct libscols_line instance ++ * ++ * Returns: @ln's number of cells ++ */ ++size_t scols_line_get_ncells(struct libscols_line *ln) ++{ ++ assert(ln); ++ return ln ? ln->ncells : 0; ++} ++ ++/** ++ * scols_line_get_cell: ++ * @ln: a pointer to a struct libscols_line instance ++ * @n: cell number to retrieve ++ * ++ * Returns: the @n-th cell in @ln, NULL in case of an error. ++ */ ++struct libscols_cell *scols_line_get_cell(struct libscols_line *ln, ++ size_t n) ++{ ++ assert(ln); ++ ++ if (!ln || n >= ln->ncells) ++ return NULL; ++ return &ln->cells[n]; ++} ++ ++/** ++ * scols_line_get_column_cell: ++ * @ln: a pointer to a struct libscols_line instance ++ * @cl: pointer to cell ++ * ++ * Like scols_line_get_cell() by cell is referenced by column. ++ * ++ * Returns: the @n-th cell in @ln, NULL in case of an error. ++ */ ++struct libscols_cell *scols_line_get_column_cell( ++ struct libscols_line *ln, ++ struct libscols_column *cl) ++{ ++ assert(ln); ++ assert(cl); ++ ++ return scols_line_get_cell(ln, cl->seqnum); ++} ++ ++/** ++ * scols_line_set_data: ++ * @ln: a pointer to a struct libscols_cell instance ++ * @n: number of the cell, whose data is to be set ++ * @data: actual data to set ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data) ++{ ++ struct libscols_cell *ce = scols_line_get_cell(ln, n); ++ ++ if (!ce) ++ return -EINVAL; ++ return scols_cell_set_data(ce, data); ++} ++ ++/** ++ * scols_line_refer_data: ++ * @ln: a pointer to a struct libscols_cell instance ++ * @n: number of the cell which will refer to @data ++ * @data: actual data to refer to ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data) ++{ ++ struct libscols_cell *ce = scols_line_get_cell(ln, n); ++ ++ if (!ce) ++ return -EINVAL; ++ return scols_cell_refer_data(ce, data); ++} ++ ++/** ++ * scols_copy_line: ++ * @ln: a pointer to a struct libscols_cell instance ++ * ++ * Returns: A newly allocated copy of @ln, NULL in case of an error. ++ */ ++struct libscols_line *scols_copy_line(struct libscols_line *ln) ++{ ++ struct libscols_line *ret; ++ size_t i; ++ ++ assert (ln); ++ if (!ln) ++ return NULL; ++ ++ ret = scols_new_line(); ++ if (!ret) ++ return NULL; ++ if (scols_line_set_color(ret, ln->color)) ++ goto err; ++ if (scols_line_alloc_cells(ret, ln->ncells)) ++ goto err; ++ ++ ret->userdata = ln->userdata; ++ ret->ncells = ln->ncells; ++ ret->seqnum = ln->seqnum; ++ ++ DBG(LINE, ul_debugobj(ln, "copy to %p", ret)); ++ ++ for (i = 0; i < ret->ncells; ++i) { ++ if (scols_cell_copy_content(&ret->cells[i], &ln->cells[i])) ++ goto err; ++ } ++ ++ return ret; ++err: ++ scols_unref_line(ret); ++ return NULL; ++} ++ ++ +diff -up util-linux-2.23.2/libsmartcols/src/Makemodule.am.kzak util-linux-2.23.2/libsmartcols/src/Makemodule.am +--- util-linux-2.23.2/libsmartcols/src/Makemodule.am.kzak 2014-09-25 14:41:48.985843877 +0200 ++++ util-linux-2.23.2/libsmartcols/src/Makemodule.am 2014-09-25 14:42:10.471048869 +0200 +@@ -0,0 +1,75 @@ ++ ++ ++## smartcols.h is generated, so it's stored in builddir! (no distribute in RHEL7) ++#smartcolsincdir = $(includedir)/libsmartcols ++#nodist_smartcolsinc_HEADERS = $(top_builddir)/libsmartcols/src/libsmartcols.h ++ ++noinst_LTLIBRARIES += libsmartcols.la ++libsmartcols_la_SOURCES= \ ++ include/list.h \ ++ \ ++ libsmartcols/src/smartcolsP.h \ ++ libsmartcols/src/iter.c \ ++ libsmartcols/src/symbols.c \ ++ libsmartcols/src/cell.c \ ++ libsmartcols/src/column.c \ ++ libsmartcols/src/line.c \ ++ libsmartcols/src/table.c \ ++ libsmartcols/src/table_print.c \ ++ libsmartcols/src/version.c \ ++ libsmartcols/src/init.c \ ++ $(nodist_smartcolsinc_HEADERS) ++ ++nodist_libsmartcols_la_SOURCES = libsmartcols/src/smartcolsP.h ++ ++libsmartcols_la_LIBADD = libcommon.la ++ ++libsmartcols_la_CFLAGS = \ ++ $(SOLIB_CFLAGS) \ ++ -I$(ul_libsmartcols_incdir) \ ++ -I$(top_srcdir)/libsmartcols/src ++ ++libsmartcols_la_DEPENDENCIES = \ ++ libcommon.la \ ++ libsmartcols/src/libsmartcols.sym \ ++ libsmartcols/src/libsmartcols.h.in ++ ++libsmartcols_la_LDFLAGS = \ ++ $(SOLIB_LDFLAGS) \ ++ -Wl,--version-script=$(top_srcdir)/libsmartcols/src/libsmartcols.sym \ ++ -version-info $(LIBSMARTCOLS_VERSION_INFO) ++ ++EXTRA_DIST += \ ++ libsmartcols/src/libsmartcols.sym \ ++ libsmartcols/src/libsmartcols.h.in ++ ++ ++if BUILD_LIBSMARTCOLS_TESTS ++check_PROGRAMS += test_smartcols ++ ++libsmartcols_tests_cflags = $(libsmartcols_la_CFLAGS) ++libsmartcols_tests_ldadd = libsmartcols.la libcommon.la ++ ++test_smartcols_SOURCES = libsmartcols/src/test.c ++test_smartcols_CFLAGS = $(libsmartcols_tests_cflags) ++test_smartcols_LDADD = $(libsmartcols_tests_ldadd) ++endif # BUILD_LIBSMARTCOLS_TESTS ++ ++ ++# move lib from $(usrlib_execdir) to $(libdir) if needed ++install-exec-hook-libsmartcols: ++ if test "$(usrlib_execdir)" != "$(libdir)" -a -f "$(DESTDIR)$(usrlib_execdir)/libsmartcols.so"; then \ ++ mkdir -p $(DESTDIR)$(libdir); \ ++ mv $(DESTDIR)$(usrlib_execdir)/libsmartcols.so.* $(DESTDIR)$(libdir); \ ++ so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libsmartcols.so); \ ++ so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \ ++ (cd $(DESTDIR)$(usrlib_execdir) && \ ++ rm -f libsmartcols.so && \ ++ $(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name libsmartcols.so); \ ++ fi ++ ++uninstall-hook-libsmartcols: ++ rm -f $(DESTDIR)$(libdir)/libsmartcols.so* ++ ++INSTALL_EXEC_HOOKS += install-exec-hook-libsmartcols ++UNINSTALL_HOOKS += uninstall-hook-libsmartcols +diff -up util-linux-2.23.2/libsmartcols/src/smartcolsP.h.kzak util-linux-2.23.2/libsmartcols/src/smartcolsP.h +--- util-linux-2.23.2/libsmartcols/src/smartcolsP.h.kzak 2014-09-25 14:41:48.989843915 +0200 ++++ util-linux-2.23.2/libsmartcols/src/smartcolsP.h 2014-09-25 14:41:48.989843915 +0200 +@@ -0,0 +1,173 @@ ++/* ++ * smartcolsP.h - private library header file ++ * ++ * Copyright (C) 2014 Ondrej Oprala ++ * Copyright (C) 2014 Karel Zak ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++ ++#ifndef _LIBSMARTCOLS_PRIVATE_H ++#define _LIBSMARTCOLS_PRIVATE_H ++ ++#include "c.h" ++#include "list.h" ++#include "colors.h" ++#include "debug.h" ++ ++#include "libsmartcols.h" ++ ++/* features */ ++#define CONFIG_LIBSMARTCOLS_ASSERT ++ ++#ifdef CONFIG_LIBSMARTCOLS_ASSERT ++# include ++#else ++# define assert(x) ++#endif ++ ++/* ++ * Debug ++ */ ++#define SCOLS_DEBUG_INIT (1 << 1) ++#define SCOLS_DEBUG_CELL (1 << 2) ++#define SCOLS_DEBUG_LINE (1 << 3) ++#define SCOLS_DEBUG_TAB (1 << 4) ++#define SCOLS_DEBUG_COL (1 << 5) ++#define SCOLS_DEBUG_BUFF (1 << 6) ++#define SCOLS_DEBUG_ALL 0xFFFF ++ ++UL_DEBUG_DECLARE_MASK(libsmartcols); ++#define DBG(m, x) __UL_DBG(libsmartcols, SCOLS_DEBUG_, m, x) ++#define ON_DBG(m, x) __UL_DBG_CALL(libsmartcols, SCOLS_DEBUG_, m, x) ++#define DBG_FLUSH __UL_DBG_FLUSH(libsmartcols, SCOLS_DEBUG_) ++ ++/* ++ * Generic iterator ++ */ ++struct libscols_iter { ++ struct list_head *p; /* current position */ ++ struct list_head *head; /* start position */ ++ int direction; /* SCOLS_ITER_{FOR,BACK}WARD */ ++}; ++ ++/* ++ * Tree symbols ++ */ ++struct libscols_symbols { ++ int refcount; ++ char *branch; ++ char *vert; ++ char *right; ++}; ++ ++/* ++ * Table cells ++ */ ++struct libscols_cell { ++ char *data; ++ char *color; ++ void *userdata; ++}; ++ ++ ++/* ++ * Table column ++ */ ++struct libscols_column { ++ int refcount; /* reference counter */ ++ size_t seqnum; /* column index */ ++ ++ size_t width; /* real column width */ ++ size_t width_min; /* minimal width (usually header width) */ ++ size_t width_max; /* maximal width */ ++ size_t width_avg; /* average width, used to detect extreme fields */ ++ double width_hint; /* hint (N < 1 is in percent of termwidth) */ ++ ++ int flags; ++ int is_extreme; ++ char *color; /* default column color */ ++ ++ int (*cmpfunc)(struct libscols_cell *, ++ struct libscols_cell *, ++ void *); /* cells comparison function */ ++ void *cmpfunc_data; ++ ++ struct libscols_cell header; ++ struct list_head cl_columns; ++}; ++ ++/* ++ * Table line ++ */ ++struct libscols_line { ++ int refcount; ++ size_t seqnum; ++ ++ void *userdata; ++ char *color; /* default line color */ ++ ++ struct libscols_cell *cells; /* array with data */ ++ size_t ncells; /* number of cells */ ++ ++ struct list_head ln_lines; /* table lines */ ++ struct list_head ln_branch; /* begin of branch (head of ln_children) */ ++ struct list_head ln_children; ++ ++ struct libscols_line *parent; ++}; ++ ++enum { ++ SCOLS_FMT_HUMAN = 0, /* default, human readable */ ++ SCOLS_FMT_RAW, /* space separated */ ++ SCOLS_FMT_EXPORT /* COLNAME="data" ... */ ++}; ++ ++/* ++ * The table ++ */ ++struct libscols_table { ++ int refcount; ++ size_t ncols; /* number of columns */ ++ size_t ntreecols; /* number of columns with SCOLS_FL_TREE */ ++ size_t nlines; /* number of lines */ ++ size_t termwidth; /* terminal width */ ++ size_t termreduce; /* extra blank space */ ++ FILE *out; /* output stream */ ++ ++ char *colsep; /* column separator */ ++ char *linesep; /* line separator */ ++ ++ struct list_head tb_columns; ++ struct list_head tb_lines; ++ struct libscols_symbols *symbols; ++ ++ int format; /* SCOLS_FMT_* */ ++ ++ /* flags */ ++ unsigned int ascii :1, /* don't use unicode */ ++ colors_wanted :1, /* enable colors */ ++ is_term :1, /* isatty() */ ++ maxout :1, /* maximalize output */ ++ no_headings :1; /* don't print header */ ++}; ++ ++#define IS_ITER_FORWARD(_i) ((_i)->direction == SCOLS_ITER_FORWARD) ++#define IS_ITER_BACKWARD(_i) ((_i)->direction == SCOLS_ITER_BACKWARD) ++ ++#define SCOLS_ITER_INIT(itr, list) \ ++ do { \ ++ (itr)->p = IS_ITER_FORWARD(itr) ? \ ++ (list)->next : (list)->prev; \ ++ (itr)->head = (list); \ ++ } while(0) ++ ++#define SCOLS_ITER_ITERATE(itr, res, restype, member) \ ++ do { \ ++ res = list_entry((itr)->p, restype, member); \ ++ (itr)->p = IS_ITER_FORWARD(itr) ? \ ++ (itr)->p->next : (itr)->p->prev; \ ++ } while(0) ++ ++#endif /* _LIBSMARTCOLS_PRIVATE_H */ +diff -up util-linux-2.23.2/libsmartcols/src/symbols.c.kzak util-linux-2.23.2/libsmartcols/src/symbols.c +--- util-linux-2.23.2/libsmartcols/src/symbols.c.kzak 2014-09-25 14:41:48.989843915 +0200 ++++ util-linux-2.23.2/libsmartcols/src/symbols.c 2014-09-25 14:41:48.989843915 +0200 +@@ -0,0 +1,175 @@ ++/* ++ * symbols.c - routines for symbol handling ++ * ++ * Copyright (C) 2014 Ondrej Oprala ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++ ++/** ++ * SECTION: symbols ++ * @title: Symbols ++ * @short_description: symbols API ++ * ++ * An API to access and modify data and information per symbol/symbol group. ++ */ ++ ++ ++#include ++#include ++#include ++ ++#include "smartcolsP.h" ++ ++/** ++ * scols_new_symbols: ++ * ++ * Returns: a pointer to a newly allocated struct libscols_symbols instance. ++ */ ++struct libscols_symbols *scols_new_symbols(void) ++{ ++ struct libscols_symbols *sy = calloc(1, sizeof(struct libscols_symbols)); ++ ++ if (!sy) ++ return NULL; ++ sy->refcount = 1; ++ return sy; ++} ++ ++/** ++ * scols_ref_symbols: ++ * @sy: a pointer to a struct libscols_symbols instance ++ * ++ * Increases the refcount of @sy. ++ */ ++void scols_ref_symbols(struct libscols_symbols *sy) ++{ ++ if (sy) ++ sy->refcount++; ++} ++ ++/** ++ * scols_unref_symbols: ++ * @sy: a pointer to a struct libscols_symbols instance ++ * ++ * Decreases the refcount of @sy. ++ */ ++void scols_unref_symbols(struct libscols_symbols *sy) ++{ ++ if (sy && --sy->refcount <= 0) { ++ free(sy->branch); ++ free(sy->vert); ++ free(sy->right); ++ free(sy); ++ } ++} ++ ++/** ++ * scols_symbols_set_branch: ++ * @sb: a pointer to a struct libscols_symbols instance ++ * @str: a string which will represent the branch part of a tree output ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_symbols_set_branch(struct libscols_symbols *sb, const char *str) ++{ ++ char *p = NULL; ++ ++ assert(sb); ++ ++ if (!sb) ++ return -EINVAL; ++ if (str) { ++ p = strdup(str); ++ if (!p) ++ return -ENOMEM; ++ } ++ free(sb->branch); ++ sb->branch = p; ++ return 0; ++} ++ ++/** ++ * scols_symbols_set_vertical: ++ * @sb: a pointer to a struct libscols_symbols instance ++ * @str: a string which will represent the vertical part of a tree output ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_symbols_set_vertical(struct libscols_symbols *sb, const char *str) ++{ ++ char *p = NULL; ++ ++ assert(sb); ++ ++ if (!sb) ++ return -EINVAL; ++ if (str) { ++ p = strdup(str); ++ if (!p) ++ return -ENOMEM; ++ } ++ free(sb->vert); ++ sb->vert = p; ++ return 0; ++} ++ ++/** ++ * scols_symbols_set_right: ++ * @sb: a pointer to a struct libscols_symbols instance ++ * @str: a string which will represent the right part of a tree output ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_symbols_set_right(struct libscols_symbols *sb, const char *str) ++{ ++ char *p = NULL; ++ ++ assert(sb); ++ ++ if (!sb) ++ return -EINVAL; ++ if (str) { ++ p = strdup(str); ++ if (!p) ++ return -ENOMEM; ++ } ++ free(sb->right); ++ sb->right = p; ++ return 0; ++} ++ ++/** ++ * scols_copy_symbols: ++ * @sb: a pointer to a struct libscols_symbols instance ++ * ++ * Returns: a newly allocated copy of the @sb symbol group or NULL in caes of an error. ++ */ ++struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sb) ++{ ++ struct libscols_symbols *ret; ++ int rc; ++ ++ assert(sb); ++ if (!sb) ++ return NULL; ++ ++ ret = scols_new_symbols(); ++ if (!ret) ++ return NULL; ++ ++ rc = scols_symbols_set_branch(ret, sb->branch); ++ if (!rc) ++ rc = scols_symbols_set_vertical(ret, sb->vert); ++ if (!rc) ++ rc = scols_symbols_set_right(ret, sb->right); ++ if (!rc) ++ return ret; ++ ++ scols_unref_symbols(ret); ++ return NULL; ++ ++} ++ ++ +diff -up util-linux-2.23.2/libsmartcols/src/table.c.kzak util-linux-2.23.2/libsmartcols/src/table.c +--- util-linux-2.23.2/libsmartcols/src/table.c.kzak 2014-09-25 14:41:48.991843934 +0200 ++++ util-linux-2.23.2/libsmartcols/src/table.c 2014-09-25 14:41:48.991843934 +0200 +@@ -0,0 +1,1049 @@ ++/* ++ * table.c - functions handling the data at the table level ++ * ++ * Copyright (C) 2010-2014 Karel Zak ++ * Copyright (C) 2014 Ondrej Oprala ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++ ++/** ++ * SECTION: table ++ * @title: Table ++ * @short_description: table data API ++ * ++ * Table data manipulation API. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "nls.h" ++#include "widechar.h" ++#include "smartcolsP.h" ++ ++#ifdef HAVE_WIDECHAR ++#define UTF_V "\342\224\202" /* U+2502, Vertical line drawing char */ ++#define UTF_VR "\342\224\234" /* U+251C, Vertical and right */ ++#define UTF_H "\342\224\200" /* U+2500, Horizontal */ ++#define UTF_UR "\342\224\224" /* U+2514, Up and right */ ++#endif /* !HAVE_WIDECHAR */ ++ ++#define is_last_column(_tb, _cl) \ ++ list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns) ++ ++ ++/** ++ * scols_new_table: ++ * ++ * Returns: A newly allocated table. ++ */ ++struct libscols_table *scols_new_table(void) ++{ ++ struct libscols_table *tb; ++ ++ tb = calloc(1, sizeof(struct libscols_table)); ++ if (!tb) ++ return NULL; ++ ++ tb->refcount = 1; ++ tb->out = stdout; ++ ++ INIT_LIST_HEAD(&tb->tb_lines); ++ INIT_LIST_HEAD(&tb->tb_columns); ++ ++ DBG(TAB, ul_debugobj(tb, "alloc")); ++ return tb; ++} ++ ++/** ++ * scols_ref_table: ++ * @tb: a pointer to a struct libscols_table instance ++ * ++ * Increases the refcount of @tb. ++ */ ++void scols_ref_table(struct libscols_table *tb) ++{ ++ if (tb) ++ tb->refcount++; ++} ++ ++/** ++ * scols_unref_table: ++ * @tb: a pointer to a struct libscols_table instance ++ * ++ * Decreases the refcount of @tb. When the count falls to zero, the instance ++ * is automatically deallocated. ++ */ ++void scols_unref_table(struct libscols_table *tb) ++{ ++ if (tb && (--tb->refcount <= 0)) { ++ DBG(TAB, ul_debugobj(tb, "dealloc")); ++ scols_table_remove_lines(tb); ++ scols_table_remove_columns(tb); ++ scols_unref_symbols(tb->symbols); ++ free(tb->linesep); ++ free(tb->colsep); ++ free(tb); ++ } ++} ++ ++/** ++ * scols_table_add_column: ++ * @tb: a pointer to a struct libscols_table instance ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Adds @cl to @tb's column list. ++ * ++ * Returns: 0, a negative number in case of an error. ++ */ ++int scols_table_add_column(struct libscols_table *tb, struct libscols_column *cl) ++{ ++ assert(tb); ++ assert(cl); ++ ++ if (!tb || !cl || !list_empty(&tb->tb_lines)) ++ return -EINVAL; ++ ++ if (cl->flags & SCOLS_FL_TREE) ++ tb->ntreecols++; ++ ++ DBG(TAB, ul_debugobj(tb, "add column %p", cl)); ++ list_add_tail(&cl->cl_columns, &tb->tb_columns); ++ cl->seqnum = tb->ncols++; ++ scols_ref_column(cl); ++ ++ /* TODO: ++ * ++ * Currently it's possible to add/remove columns only if the table is ++ * empty (see list_empty(tb->tb_lines) above). It would be nice to ++ * enlarge/reduce lines cells[] always when we add/remove a new column. ++ */ ++ return 0; ++} ++ ++/** ++ * scols_table_remove_column: ++ * @tb: a pointer to a struct libscols_table instance ++ * @cl: a pointer to a struct libscols_column instance ++ * ++ * Removes @cl from @tb. ++ * ++ * Returns: 0, a negative number in case of an error. ++ */ ++int scols_table_remove_column(struct libscols_table *tb, ++ struct libscols_column *cl) ++{ ++ assert(tb); ++ assert(cl); ++ ++ if (!tb || !cl || !list_empty(&tb->tb_lines)) ++ return -EINVAL; ++ ++ if (cl->flags & SCOLS_FL_TREE) ++ tb->ntreecols--; ++ ++ DBG(TAB, ul_debugobj(tb, "remove column %p", cl)); ++ list_del_init(&cl->cl_columns); ++ tb->ncols--; ++ scols_unref_column(cl); ++ return 0; ++} ++ ++/** ++ * scols_table_remove_columns: ++ * @tb: a pointer to a struct libscols_table instance ++ * ++ * Removes all of @tb's columns. ++ * ++ * Returns: 0, a negative number in case of an error. ++ */ ++int scols_table_remove_columns(struct libscols_table *tb) ++{ ++ assert(tb); ++ ++ if (!tb || !list_empty(&tb->tb_lines)) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "remove all columns")); ++ while (!list_empty(&tb->tb_columns)) { ++ struct libscols_column *cl = list_entry(tb->tb_columns.next, ++ struct libscols_column, cl_columns); ++ scols_table_remove_column(tb, cl); ++ } ++ return 0; ++} ++ ++ ++/** ++ * scols_table_new_column: ++ * @tb: table ++ * @name: column header ++ * @whint: column width hint (absolute width: N > 1; relative width: N < 1) ++ * @flags: flags integer ++ * ++ * This is shortcut for ++ * ++ * cl = scols_new_column(); ++ * scols_column_set_....(cl, ...); ++ * scols_table_add_column(tb, cl); ++ * ++ * The column width is possible to define by three ways: ++ * ++ * @whint = 0..1 : relative width, percent of terminal width ++ * ++ * @whint = 1..N : absolute width, empty colum will be truncated to ++ * the column header width ++ * ++ * @whint = 1..N ++ * ++ * The column is necessary to address by ++ * sequential number. The first defined column has the colnum = 0. For example: ++ * ++ * scols_table_new_column(tab, "FOO", 0.5, 0); // colnum = 0 ++ * scols_table_new_column(tab, "BAR", 0.5, 0); // colnum = 1 ++ * . ++ * . ++ * scols_line_get_cell(line, 0); // FOO column ++ * scols_line_get_cell(line, 1); // BAR column ++ * ++ * Returns: newly allocated column ++ */ ++struct libscols_column *scols_table_new_column(struct libscols_table *tb, ++ const char *name, ++ double whint, ++ int flags) ++{ ++ struct libscols_column *cl; ++ struct libscols_cell *hr; ++ ++ assert (tb); ++ if (!tb) ++ return NULL; ++ ++ DBG(TAB, ul_debugobj(tb, "new column name=%s, whint=%g, flags=%d", ++ name, whint, flags)); ++ cl = scols_new_column(); ++ if (!cl) ++ return NULL; ++ ++ /* set column name */ ++ hr = scols_column_get_header(cl); ++ if (!hr) ++ goto err; ++ if (scols_cell_set_data(hr, name)) ++ goto err; ++ ++ scols_column_set_whint(cl, whint); ++ scols_column_set_flags(cl, flags); ++ ++ if (scols_table_add_column(tb, cl)) /* this increments column ref-counter */ ++ goto err; ++ ++ scols_unref_column(cl); ++ return cl; ++err: ++ scols_unref_column(cl); ++ return NULL; ++} ++ ++/** ++ * scols_table_next_column: ++ * @tb: a pointer to a struct libscols_table instance ++ * @itr: a pointer to a struct libscols_iter instance ++ * @cl: a pointer to a pointer to a struct libscols_column instance ++ * ++ * Returns the next column of @tb via @cl. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_table_next_column(struct libscols_table *tb, ++ struct libscols_iter *itr, ++ struct libscols_column **cl) ++{ ++ int rc = 1; ++ ++ if (!tb || !itr || !cl) ++ return -EINVAL; ++ *cl = NULL; ++ ++ if (!itr->head) ++ SCOLS_ITER_INIT(itr, &tb->tb_columns); ++ if (itr->p != itr->head) { ++ SCOLS_ITER_ITERATE(itr, *cl, struct libscols_column, cl_columns); ++ rc = 0; ++ } ++ ++ return rc; ++} ++ ++ ++/** ++ * scols_table_get_ncols: ++ * @tb: table ++ * ++ * Returns: the ncols table member, a negative number in case of an error. ++ */ ++int scols_table_get_ncols(struct libscols_table *tb) ++{ ++ assert(tb); ++ return tb ? tb->ncols : -EINVAL; ++} ++ ++/** ++ * scols_table_get_nlines: ++ * @tb: table ++ * ++ * Returns: the nlines table member, a negative number in case of an error. ++ */ ++int scols_table_get_nlines(struct libscols_table *tb) ++{ ++ assert(tb); ++ return tb ? tb->nlines : -EINVAL; ++} ++ ++/** ++ * scols_table_set_stream: ++ * @tb: table ++ * @stream: output stream ++ * ++ * Sets the output stream for table @tb. ++ * ++ * Returns: 0, a negative number in case of an error. ++ */ ++int scols_table_set_stream(struct libscols_table *tb, FILE *stream) ++{ ++ assert(tb); ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "setting alternative stream")); ++ tb->out = stream; ++ return 0; ++} ++ ++/** ++ * scols_table_get_stream: ++ * @tb: table ++ * ++ * Gets the output stream for table @tb. ++ * ++ * Returns: stream pointer, NULL in case of an error or an unset stream. ++ */ ++FILE *scols_table_get_stream(struct libscols_table *tb) ++{ ++ assert(tb); ++ return tb ? tb->out: NULL; ++} ++ ++/** ++ * scols_table_reduce_termwidth: ++ * @tb: table ++ * @reduce: width ++ * ++ * Reduce the output width to @reduce. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_table_reduce_termwidth(struct libscols_table *tb, size_t reduce) ++{ ++ assert(tb); ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "reduce terminal width: %zu", reduce)); ++ tb->termreduce = reduce; ++ return 0; ++} ++ ++/** ++ * scols_table_get_column: ++ * @tb: table ++ * @n: number of column (0..N) ++ * ++ * Returns: pointer to column or NULL ++ */ ++struct libscols_column *scols_table_get_column(struct libscols_table *tb, ++ size_t n) ++{ ++ struct libscols_iter itr; ++ struct libscols_column *cl; ++ ++ assert(tb); ++ if (!tb) ++ return NULL; ++ if (n >= tb->ncols) ++ return NULL; ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ if (cl->seqnum == n) ++ return cl; ++ } ++ return NULL; ++} ++ ++/** ++ * scols_table_add_line: ++ * @tb: table ++ * @ln: line ++ * ++ * Note that this function calls scols_line_alloc_cells() if number ++ * of the cells in the line is too small for @tb. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln) ++{ ++ ++ assert(tb); ++ assert(ln); ++ ++ if (!tb || !ln) ++ return -EINVAL; ++ ++ if (tb->ncols > ln->ncells) { ++ int rc = scols_line_alloc_cells(ln, tb->ncols); ++ if (rc) ++ return rc; ++ } ++ ++ DBG(TAB, ul_debugobj(tb, "add line %p", ln)); ++ list_add_tail(&ln->ln_lines, &tb->tb_lines); ++ ln->seqnum = tb->nlines++; ++ scols_ref_line(ln); ++ return 0; ++} ++ ++/** ++ * scols_table_remove_line: ++ * @tb: table ++ * @ln: line ++ * ++ * Note that this function does not destroy the parent<->child relationship between lines. ++ * You have to call scols_line_remove_child() ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_table_remove_line(struct libscols_table *tb, ++ struct libscols_line *ln) ++{ ++ assert(tb); ++ assert(ln); ++ ++ if (!tb || !ln) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "remove line %p", ln)); ++ list_del_init(&ln->ln_lines); ++ tb->nlines--; ++ scols_unref_line(ln); ++ return 0; ++} ++ ++/** ++ * scols_table_remove_lines: ++ * @tb: table ++ * ++ * This empties the table and also destroys all the parent<->child relationships. ++ */ ++void scols_table_remove_lines(struct libscols_table *tb) ++{ ++ assert(tb); ++ if (!tb) ++ return; ++ ++ DBG(TAB, ul_debugobj(tb, "remove all lines")); ++ while (!list_empty(&tb->tb_lines)) { ++ struct libscols_line *ln = list_entry(tb->tb_lines.next, ++ struct libscols_line, ln_lines); ++ if (ln->parent) ++ scols_line_remove_child(ln->parent, ln); ++ scols_table_remove_line(tb, ln); ++ } ++} ++ ++/** ++ * scols_table_next_line: ++ * @tb: a pointer to a struct libscols_table instance ++ * @itr: a pointer to a struct libscols_iter instance ++ * @ln: a pointer to a pointer to a struct libscols_line instance ++ * ++ * Finds the next line and returns a pointer to it via @ln. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_table_next_line(struct libscols_table *tb, ++ struct libscols_iter *itr, ++ struct libscols_line **ln) ++{ ++ int rc = 1; ++ ++ if (!tb || !itr || !ln) ++ return -EINVAL; ++ *ln = NULL; ++ ++ if (!itr->head) ++ SCOLS_ITER_INIT(itr, &tb->tb_lines); ++ if (itr->p != itr->head) { ++ SCOLS_ITER_ITERATE(itr, *ln, struct libscols_line, ln_lines); ++ rc = 0; ++ } ++ ++ return rc; ++} ++ ++/** ++ * scols_table_new_line: ++ * @tb: table ++ * @parent: parental line or NULL ++ * ++ * This is shortcut for ++ * ++ * ln = scols_new_line(); ++ * scols_table_add_line(tb, ln); ++ * scols_line_add_child(parent, ln); ++ * ++ * ++ * Returns: newly allocate line ++ */ ++struct libscols_line *scols_table_new_line(struct libscols_table *tb, ++ struct libscols_line *parent) ++{ ++ struct libscols_line *ln; ++ ++ assert(tb); ++ assert(tb->ncols); ++ ++ if (!tb || !tb->ncols) ++ return NULL; ++ ++ ln = scols_new_line(); ++ if (!ln) ++ return NULL; ++ ++ if (scols_table_add_line(tb, ln)) ++ goto err; ++ if (parent) ++ scols_line_add_child(parent, ln); ++ ++ scols_unref_line(ln); /* ref-counter incremented by scols_table_add_line() */ ++ return ln; ++err: ++ scols_unref_line(ln); ++ return NULL; ++} ++ ++/** ++ * scols_table_get_line: ++ * @tb: table ++ * @n: column number (0..N) ++ * ++ * This is a shortcut for ++ * ++ * ln = scols_new_line(); ++ * scols_line_set_....(cl, ...); ++ * scols_table_add_line(tb, ln); ++ * ++ * Returns: a newly allocate line ++ */ ++struct libscols_line *scols_table_get_line(struct libscols_table *tb, ++ size_t n) ++{ ++ struct libscols_iter itr; ++ struct libscols_line *ln; ++ ++ assert(tb); ++ if (!tb) ++ return NULL; ++ if (n >= tb->nlines) ++ return NULL; ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_line(tb, &itr, &ln) == 0) { ++ if (ln->seqnum == n) ++ return ln; ++ } ++ return NULL; ++} ++ ++/** ++ * scols_copy_table: ++ * @tb: table ++ * ++ * Creates a new independent table copy, except struct libscols_symbols that ++ * are shared between the tables. ++ * ++ * Returns: a newly allocated copy of @tb ++ */ ++struct libscols_table *scols_copy_table(struct libscols_table *tb) ++{ ++ struct libscols_table *ret; ++ struct libscols_line *ln; ++ struct libscols_column *cl; ++ struct libscols_iter itr; ++ ++ assert(tb); ++ if (!tb) ++ return NULL; ++ ret = scols_new_table(); ++ if (!ret) ++ return NULL; ++ ++ DBG(TAB, ul_debugobj(tb, "copy into %p", ret)); ++ ++ if (tb->symbols) ++ scols_table_set_symbols(ret, tb->symbols); ++ ++ /* columns */ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ cl = scols_copy_column(cl); ++ if (!cl) ++ goto err; ++ if (scols_table_add_column(ret, cl)) ++ goto err; ++ scols_unref_column(cl); ++ } ++ ++ /* lines */ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_line(tb, &itr, &ln) == 0) { ++ struct libscols_line *newln = scols_copy_line(ln); ++ if (!newln) ++ goto err; ++ if (scols_table_add_line(ret, newln)) ++ goto err; ++ if (ln->parent) { ++ struct libscols_line *p = ++ scols_table_get_line(ret, ln->parent->seqnum); ++ if (p) ++ scols_line_add_child(p, newln); ++ } ++ scols_unref_line(newln); ++ } ++ ++ /* separators */ ++ if (scols_table_set_column_separator(ret, tb->colsep) || ++ scols_table_set_line_separator(ret, tb->linesep)) ++ goto err; ++ ++ return ret; ++err: ++ scols_unref_table(ret); ++ return NULL; ++} ++ ++/** ++ * scols_table_set_symbols: ++ * @tb: table ++ * @sy: symbols or NULL ++ * ++ * Add a reference to @sy from the table. The symbols are used by library to ++ * draw tree output. If no symbols are specified then library checks the ++ * current environment to select ASCII or UTF8 symbols. This default behavior ++ * could be controlled by scols_table_enable_ascii(). ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_table_set_symbols(struct libscols_table *tb, ++ struct libscols_symbols *sy) ++{ ++ assert(tb); ++ ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "setting alternative symbols %p", sy)); ++ ++ if (tb->symbols) /* unref old */ ++ scols_unref_symbols(tb->symbols); ++ if (sy) { /* ref user defined */ ++ tb->symbols = sy; ++ scols_ref_symbols(sy); ++ } else { /* default symbols */ ++ tb->symbols = scols_new_symbols(); ++ if (!tb->symbols) ++ return -ENOMEM; ++#if defined(HAVE_WIDECHAR) ++ if (!scols_table_is_ascii(tb) && ++ !strcmp(nl_langinfo(CODESET), "UTF-8")) { ++ scols_symbols_set_branch(tb->symbols, UTF_VR UTF_H); ++ scols_symbols_set_vertical(tb->symbols, UTF_V " "); ++ scols_symbols_set_right(tb->symbols, UTF_UR UTF_H); ++ } else ++#endif ++ { ++ scols_symbols_set_branch(tb->symbols, "|-"); ++ scols_symbols_set_vertical(tb->symbols, "| "); ++ scols_symbols_set_right(tb->symbols, "`-"); ++ } ++ } ++ ++ return 0; ++} ++/** ++ * scols_table_enable_colors: ++ * @tb: table ++ * @enable: 1 or 0 ++ * ++ * Enable/disable colors. ++ * ++ * Returns: 0 on success, negative number in case of an error. ++ */ ++int scols_table_enable_colors(struct libscols_table *tb, int enable) ++{ ++ assert(tb); ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "colors: %s", enable ? "ENABLE" : "DISABLE")); ++ tb->colors_wanted = enable; ++ return 0; ++} ++/** ++ * scols_table_enable_raw: ++ * @tb: table ++ * @enable: 1 or 0 ++ * ++ * Enable/disable raw output format. The parsable output formats ++ * (export and raw) are mutually exclusive. ++ * ++ * Returns: 0 on success, negative number in case of an error. ++ */ ++int scols_table_enable_raw(struct libscols_table *tb, int enable) ++{ ++ assert(tb); ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "raw: %s", enable ? "ENABLE" : "DISABLE")); ++ if (enable) ++ tb->format = SCOLS_FMT_RAW; ++ else if (tb->format == SCOLS_FMT_RAW) ++ tb->format = 0; ++ return 0; ++} ++ ++/** ++ * scols_table_enable_export: ++ * @tb: table ++ * @enable: 1 or 0 ++ * ++ * Enable/disable export output format (COLUMNAME="value" ...). ++ * The parsable output formats (export and raw) are mutually exclusive. ++ * ++ * Returns: 0 on success, negative number in case of an error. ++ */ ++int scols_table_enable_export(struct libscols_table *tb, int enable) ++{ ++ assert(tb); ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "export: %s", enable ? "ENABLE" : "DISABLE")); ++ if (enable) ++ tb->format = SCOLS_FMT_EXPORT; ++ else if (tb->format == SCOLS_FMT_EXPORT) ++ tb->format = 0; ++ return 0; ++} ++ ++/** ++ * scols_table_enable_ascii: ++ * @tb: table ++ * @enable: 1 or 0 ++ * ++ * The ASCII-only output is relevant for tree-like outputs. The library ++ * checks if the current environment is UTF8 compatible by default. This ++ * function overrides this check and force the library to use ASCII chars ++ * for the tree. ++ * ++ * If a custom libcols_symbols are specified (see scols_table_set_symbols() ++ * then ASCII flag setting is ignored. ++ * ++ * Returns: 0 on success, negative number in case of an error. ++ */ ++int scols_table_enable_ascii(struct libscols_table *tb, int enable) ++{ ++ assert(tb); ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "ascii: %s", enable ? "ENABLE" : "DISABLE")); ++ tb->ascii = enable ? 1 : 0; ++ return 0; ++} ++ ++/** ++ * scols_table_enable_noheadings: ++ * @tb: table ++ * @enable: 1 or 0 ++ * ++ * Enable/disable header line. ++ * ++ * Returns: 0 on success, negative number in case of an error. ++ */ ++int scols_table_enable_noheadings(struct libscols_table *tb, int enable) ++{ ++ assert(tb); ++ if (!tb) ++ return -EINVAL; ++ DBG(TAB, ul_debugobj(tb, "noheading: %s", enable ? "ENABLE" : "DISABLE")); ++ tb->no_headings = enable ? 1 : 0; ++ return 0; ++} ++ ++/** ++ * scols_table_enable_maxout: ++ * @tb: table ++ * @enable: 1 or 0 ++ * ++ * The extra space after last column is ignored by default. The output ++ * maximization use the extra space for all columns. ++ * ++ * Returns: 0 on success, negative number in case of an error. ++ */ ++int scols_table_enable_maxout(struct libscols_table *tb, int enable) ++{ ++ assert(tb); ++ if (!tb) ++ return -EINVAL; ++ DBG(TAB, ul_debugobj(tb, "maxout: %s", enable ? "ENABLE" : "DISABLE")); ++ tb->maxout = enable ? 1 : 0; ++ return 0; ++} ++ ++/** ++ * scols_table_colors_wanted: ++ * @tb: table ++ * ++ * Returns: 1 if colors are enabled. ++ */ ++int scols_table_colors_wanted(struct libscols_table *tb) ++{ ++ assert(tb); ++ return tb && tb->colors_wanted; ++} ++ ++/** ++ * scols_table_is_empty: ++ * @tb: table ++ * ++ * Returns: 1 if the table is empty. ++ */ ++int scols_table_is_empty(struct libscols_table *tb) ++{ ++ assert(tb); ++ return !tb || !tb->nlines; ++} ++ ++/** ++ * scols_table_is_ascii: ++ * @tb: table ++ * ++ * Returns: 1 if ASCII tree is enabled. ++ */ ++int scols_table_is_ascii(struct libscols_table *tb) ++{ ++ assert(tb); ++ return tb && tb->ascii; ++} ++ ++/** ++ * scols_table_is_noheadings: ++ * @tb: table ++ * ++ * Returns: 1 if header output is disabled. ++ */ ++int scols_table_is_noheadings(struct libscols_table *tb) ++{ ++ assert(tb); ++ return tb && tb->no_headings; ++} ++ ++/** ++ * scols_table_is_export: ++ * @tb: table ++ * ++ * Returns: 1 if export output format is enabled. ++ */ ++int scols_table_is_export(struct libscols_table *tb) ++{ ++ assert(tb); ++ return tb && tb->format == SCOLS_FMT_EXPORT; ++} ++ ++/** ++ * scols_table_is_raw: ++ * @tb: table ++ * ++ * Returns: 1 if raw output format is enabled. ++ */ ++int scols_table_is_raw(struct libscols_table *tb) ++{ ++ assert(tb); ++ return tb && tb->format == SCOLS_FMT_RAW; ++} ++ ++ ++/** ++ * scols_table_is_maxout ++ * @tb: table ++ * ++ * Returns: 1 if output maximization is enabled, negative value in case of an error. ++ */ ++int scols_table_is_maxout(struct libscols_table *tb) ++{ ++ assert(tb); ++ return tb && tb->maxout; ++} ++ ++/** ++ * scols_table_is_tree: ++ * @tb: table ++ * ++ * Returns: returns 1 tree-like output is expected. ++ */ ++int scols_table_is_tree(struct libscols_table *tb) ++{ ++ assert(tb); ++ return tb && tb->ntreecols > 0; ++} ++ ++/** ++ * scols_table_set_column_separator: ++ * @tb: table ++ * @sep: separator ++ * ++ * Sets the column separator of @tb to @sep. ++ * Please note that @sep should always take up a single cell in the output. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_table_set_column_separator(struct libscols_table *tb, const char *sep) ++{ ++ char *p = NULL; ++ ++ assert (tb); ++ ++ if (!tb) ++ return -EINVAL; ++ ++ if (sep) { ++ p = strdup(sep); ++ if (!p) ++ return -ENOMEM; ++ } ++ ++ DBG(TAB, ul_debugobj(tb, "new columns separator: %s", sep)); ++ free(tb->colsep); ++ tb->colsep = p; ++ return 0; ++} ++ ++/** ++ * scols_table_set_line_separator: ++ * @tb: table ++ * @sep: separator ++ * ++ * Sets the line separator of @tb to @sep. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_table_set_line_separator(struct libscols_table *tb, const char *sep) ++{ ++ char *p = NULL; ++ ++ assert (tb); ++ ++ if (!tb) ++ return -EINVAL; ++ ++ if (sep) { ++ p = strdup(sep); ++ if (!p) ++ return -ENOMEM; ++ } ++ ++ DBG(TAB, ul_debugobj(tb, "new lines separator: %s", sep)); ++ free(tb->linesep); ++ tb->linesep = p; ++ return 0; ++} ++ ++/** ++ * scols_table_get_column_separator: ++ * @tb: table ++ * ++ * Returns: @tb column separator, NULL in case of an error ++ */ ++char *scols_table_get_column_separator(struct libscols_table *tb) ++{ ++ assert (tb); ++ ++ if (!tb) ++ return NULL; ++ return tb->colsep; ++} ++ ++/** ++ * scols_table_get_line_separator: ++ * @tb: table ++ * ++ * Returns: @tb line separator, NULL in case of an error ++ */ ++char *scols_table_get_line_separator(struct libscols_table *tb) ++{ ++ assert (tb); ++ ++ if (!tb) ++ return NULL; ++ return tb->linesep; ++ ++} ++ ++static int cells_cmp_wrapper(struct list_head *a, struct list_head *b, void *data) ++{ ++ struct libscols_column *cl = (struct libscols_column *) data; ++ struct libscols_line *ra, *rb; ++ struct libscols_cell *ca, *cb; ++ ++ assert(a); ++ assert(b); ++ assert(cl); ++ ++ ra = list_entry(a, struct libscols_line, ln_lines); ++ rb = list_entry(b, struct libscols_line, ln_lines); ++ ca = scols_line_get_cell(ra, cl->seqnum); ++ cb = scols_line_get_cell(rb, cl->seqnum); ++ ++ return cl->cmpfunc(ca, cb, cl->cmpfunc_data); ++} ++ ++/** ++ * scols_sort_table: ++ * @tb: table ++ * @cl: order by this column ++ * ++ * Orders the table by the column. See also scols_column_set_cmpfunc(). ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_sort_table(struct libscols_table *tb, struct libscols_column *cl) ++{ ++ assert(tb); ++ assert(cl); ++ ++ if (!tb || !cl) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "sorting table")); ++ list_sort(&tb->tb_lines, cells_cmp_wrapper, cl); ++ return 0; ++} +diff -up util-linux-2.23.2/libsmartcols/src/table_print.c.kzak util-linux-2.23.2/libsmartcols/src/table_print.c +--- util-linux-2.23.2/libsmartcols/src/table_print.c.kzak 2014-09-25 14:41:48.992843944 +0200 ++++ util-linux-2.23.2/libsmartcols/src/table_print.c 2014-09-25 14:41:48.992843944 +0200 +@@ -0,0 +1,841 @@ ++/* ++ * table.c - functions handling the data at the table level ++ * ++ * Copyright (C) 2010-2014 Karel Zak ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++ ++/** ++ * SECTION: table_print ++ * @title: Table print ++ * @short_description: table print API ++ * ++ * Table output API. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "nls.h" ++#include "mbsalign.h" ++#include "widechar.h" ++#include "ttyutils.h" ++#include "carefulputc.h" ++#include "smartcolsP.h" ++ ++/* This is private struct to work with output data */ ++struct libscols_buffer { ++ char *begin; /* begin of the buffer */ ++ char *cur; /* current end of the buffer */ ++ char *encdata; /* encoded buffer mbs_safe_encode() */ ++ ++ size_t bufsz; /* size of the buffer */ ++ size_t art_idx; /* begin of the tree ascii art or zero */ ++}; ++ ++static struct libscols_buffer *new_buffer(size_t sz) ++{ ++ struct libscols_buffer *buf = malloc(sz + sizeof(struct libscols_buffer)); ++ ++ if (!buf) ++ return NULL; ++ ++ buf->cur = buf->begin = ((char *) buf) + sizeof(struct libscols_buffer); ++ buf->encdata = NULL; ++ buf->bufsz = sz; ++ ++ DBG(BUFF, ul_debugobj(buf, "alloc (size=%zu)", sz)); ++ return buf; ++} ++ ++static void free_buffer(struct libscols_buffer *buf) ++{ ++ if (!buf) ++ return; ++ DBG(BUFF, ul_debugobj(buf, "dealloc")); ++ free(buf->encdata); ++ free(buf); ++} ++ ++static int buffer_reset_data(struct libscols_buffer *buf) ++{ ++ if (!buf) ++ return -EINVAL; ++ ++ /*DBG(BUFF, ul_debugobj(buf, "reset data"));*/ ++ buf->begin[0] = '\0'; ++ buf->cur = buf->begin; ++ buf->art_idx = 0; ++ return 0; ++} ++ ++static int buffer_append_data(struct libscols_buffer *buf, const char *str) ++{ ++ size_t maxsz, sz; ++ ++ if (!buf) ++ return -EINVAL; ++ if (!str || !*str) ++ return 0; ++ ++ sz = strlen(str); ++ maxsz = buf->bufsz - (buf->cur - buf->begin); ++ ++ if (maxsz <= sz) ++ return -EINVAL; ++ ++ memcpy(buf->cur, str, sz + 1); ++ buf->cur += sz; ++ return 0; ++} ++ ++static int buffer_set_data(struct libscols_buffer *buf, const char *str) ++{ ++ int rc = buffer_reset_data(buf); ++ return rc ? rc : buffer_append_data(buf, str); ++} ++ ++/* save the current buffer possition to art_idx */ ++static void buffer_set_art_index(struct libscols_buffer *buf) ++{ ++ if (buf) { ++ buf->art_idx = buf->cur - buf->begin; ++ /*DBG(BUFF, ul_debugobj(buf, "art index: %zu", buf->art_idx));*/ ++ } ++} ++ ++static char *buffer_get_data(struct libscols_buffer *buf) ++{ ++ return buf ? buf->begin : NULL; ++} ++ ++/* encode data by mbs_safe_encode() to avoid control and non-printable chars */ ++static char *buffer_get_safe_data(struct libscols_buffer *buf, size_t *cells) ++{ ++ char *data = buffer_get_data(buf); ++ char *res = NULL; ++ ++ if (!data) ++ goto nothing; ++ ++ if (!buf->encdata) { ++ buf->encdata = malloc(mbs_safe_encode_size(buf->bufsz) + 1); ++ if (!buf->encdata) ++ goto nothing; ++ } ++ ++ res = mbs_safe_encode_to_buffer(data, cells, buf->encdata); ++ if (!res || !*cells || *cells == (size_t) -1) ++ goto nothing; ++ return res; ++nothing: ++ *cells = 0; ++ return NULL; ++} ++ ++/* returns size in bytes of the ascii art (according to art_idx) in safe encoding */ ++static size_t buffer_get_safe_art_size(struct libscols_buffer *buf) ++{ ++ char *data = buffer_get_data(buf); ++ size_t bytes = 0; ++ ++ if (!data || !buf->art_idx) ++ return 0; ++ ++ mbs_safe_nwidth(data, buf->art_idx, &bytes); ++ return bytes; ++} ++ ++#define is_last_column(_tb, _cl) \ ++ list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns) ++ ++#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ") ++#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n") ++ ++static int print_data(struct libscols_table *tb, ++ struct libscols_column *cl, ++ struct libscols_line *ln, /* optional */ ++ struct libscols_cell *ce, /* optional */ ++ struct libscols_buffer *buf) ++{ ++ size_t len = 0, i, width, bytes; ++ const char *color = NULL; ++ char *data; ++ ++ assert(tb); ++ assert(cl); ++ ++ DBG(TAB, ul_debugobj(tb, ++ " -> data, column=%p, line=%p, cell=%p, buff=%p", ++ cl, ln, ce, buf)); ++ ++ data = buffer_get_data(buf); ++ if (!data) ++ data = ""; ++ ++ /* raw mode */ ++ if (scols_table_is_raw(tb)) { ++ fputs_nonblank(data, tb->out); ++ if (!is_last_column(tb, cl)) ++ fputs(colsep(tb), tb->out); ++ return 0; ++ } ++ ++ /* NAME=value mode */ ++ if (scols_table_is_export(tb)) { ++ fprintf(tb->out, "%s=", scols_cell_get_data(&cl->header)); ++ fputs_quoted(data, tb->out); ++ if (!is_last_column(tb, cl)) ++ fputs(colsep(tb), tb->out); ++ return 0; ++ } ++ ++ if (tb->colors_wanted) { ++ if (ce && !color) ++ color = ce->color; ++ if (ln && !color) ++ color = ln->color; ++ if (!color) ++ color = cl->color; ++ } ++ ++ /* encode, note that 'len' and 'width' are number of cells, not bytes */ ++ data = buffer_get_safe_data(buf, &len); ++ if (!data) ++ data = ""; ++ width = cl->width; ++ bytes = strlen(data); ++ ++ if (is_last_column(tb, cl) && len < width && !scols_table_is_maxout(tb)) ++ width = len; ++ ++ /* truncate data */ ++ if (len > width && scols_column_is_trunc(cl)) { ++ len = width; ++ bytes = mbs_truncate(data, &len); /* updates 'len' */ ++ ++ if (!data || bytes == (size_t) -1) { ++ bytes = len = 0; ++ data = NULL; ++ } ++ } ++ ++ if (data) { ++ if (scols_column_is_right(cl)) { ++ size_t xw = cl->width; ++ if (color) ++ fputs(color, tb->out); ++ fprintf(tb->out, "%*s", (int) xw, data); ++ if (color) ++ fputs(UL_COLOR_RESET, tb->out); ++ if (len < xw) ++ len = xw; ++ } else if (color) { ++ char *p = data; ++ size_t art = buffer_get_safe_art_size(buf); ++ ++ /* we don't want to colorize tree ascii art */ ++ if (scols_column_is_tree(cl) && art && art < bytes) { ++ fwrite(p, 1, art, tb->out); ++ p += art; ++ } ++ ++ fputs(color, tb->out); ++ fputs(p, tb->out); ++ fputs(UL_COLOR_RESET, tb->out); ++ } else ++ fputs(data, tb->out); ++ } ++ for (i = len; i < width; i++) ++ fputs(" ", tb->out); /* padding */ ++ ++ if (!is_last_column(tb, cl)) { ++ if (len > width && !scols_column_is_trunc(cl)) { ++ fputs(linesep(tb), tb->out); ++ for (i = 0; i <= (size_t) cl->seqnum; i++) { ++ struct libscols_column *x = scols_table_get_column(tb, i); ++ fprintf(tb->out, "%*s ", -((int)x->width), " "); ++ } ++ } else ++ fputs(colsep(tb), tb->out); /* columns separator */ ++ } ++ ++ return 0; ++} ++ ++/* returns pointer to the end of used data */ ++static int line_ascii_art_to_buffer(struct libscols_table *tb, ++ struct libscols_line *ln, ++ struct libscols_buffer *buf) ++{ ++ const char *art; ++ int rc; ++ ++ assert(ln); ++ assert(buf); ++ ++ if (!ln->parent) ++ return 0; ++ ++ rc = line_ascii_art_to_buffer(tb, ln->parent, buf); ++ if (rc) ++ return rc; ++ ++ if (list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch)) ++ art = " "; ++ else ++ art = tb->symbols->vert; ++ ++ return buffer_append_data(buf, art); ++} ++ ++static int cell_to_buffer(struct libscols_table *tb, ++ struct libscols_line *ln, ++ struct libscols_column *cl, ++ struct libscols_buffer *buf) ++{ ++ const char *data; ++ struct libscols_cell *ce; ++ int rc = 0; ++ ++ assert(tb); ++ assert(ln); ++ assert(cl); ++ assert(buf); ++ assert(cl->seqnum <= tb->ncols); ++ ++ buffer_reset_data(buf); ++ ++ ce = scols_line_get_cell(ln, cl->seqnum); ++ data = ce ? scols_cell_get_data(ce) : NULL; ++ if (!data) ++ return 0; ++ ++ if (!scols_column_is_tree(cl)) ++ return buffer_set_data(buf, data); ++ ++ /* ++ * Tree stuff ++ */ ++ if (ln->parent) { ++ rc = line_ascii_art_to_buffer(tb, ln->parent, buf); ++ ++ if (!rc && list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch)) ++ rc = buffer_append_data(buf, tb->symbols->right); ++ else if (!rc) ++ rc = buffer_append_data(buf, tb->symbols->branch); ++ if (!rc) ++ buffer_set_art_index(buf); ++ } ++ ++ if (!rc) ++ rc = buffer_append_data(buf, data); ++ return rc; ++} ++ ++/* ++ * Prints data, data maybe be printed in more formats (raw, NAME=xxx pairs) and ++ * control and non-printable chars maybe encoded in \x?? hex encoding. ++ */ ++static int print_line(struct libscols_table *tb, ++ struct libscols_line *ln, ++ struct libscols_buffer *buf) ++{ ++ int rc = 0; ++ struct libscols_column *cl; ++ struct libscols_iter itr; ++ ++ assert(ln); ++ ++ DBG(TAB, ul_debugobj(tb, "printing line, line=%p, buff=%p", ln, buf)); ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) { ++ rc = cell_to_buffer(tb, ln, cl, buf); ++ if (!rc) ++ rc = print_data(tb, cl, ln, ++ scols_line_get_cell(ln, cl->seqnum), ++ buf); ++ } ++ ++ if (rc == 0) ++ fputs(linesep(tb), tb->out); ++ return 0; ++} ++ ++static int print_header(struct libscols_table *tb, struct libscols_buffer *buf) ++{ ++ int rc = 0; ++ struct libscols_column *cl; ++ struct libscols_iter itr; ++ ++ assert(tb); ++ ++ if (scols_table_is_noheadings(tb) || ++ scols_table_is_export(tb) || ++ list_empty(&tb->tb_lines)) ++ return 0; ++ ++ DBG(TAB, ul_debugobj(tb, "printing header")); ++ ++ /* set width according to the size of data ++ */ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) { ++ rc = buffer_set_data(buf, scols_cell_get_data(&cl->header)); ++ if (!rc) ++ rc = print_data(tb, cl, NULL, &cl->header, buf); ++ } ++ ++ if (rc == 0) ++ fputs(linesep(tb), tb->out); ++ return rc; ++} ++ ++static int print_table(struct libscols_table *tb, struct libscols_buffer *buf) ++{ ++ int rc; ++ struct libscols_line *ln; ++ struct libscols_iter itr; ++ ++ assert(tb); ++ ++ rc = print_header(tb, buf); ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) ++ rc = print_line(tb, ln, buf); ++ ++ return rc; ++} ++ ++static int print_tree_line(struct libscols_table *tb, ++ struct libscols_line *ln, ++ struct libscols_buffer *buf) ++{ ++ int rc; ++ struct list_head *p; ++ ++ rc = print_line(tb, ln, buf); ++ if (rc) ++ return rc; ++ if (list_empty(&ln->ln_branch)) ++ return 0; ++ ++ /* print all children */ ++ list_for_each(p, &ln->ln_branch) { ++ struct libscols_line *chld = ++ list_entry(p, struct libscols_line, ln_children); ++ rc = print_tree_line(tb, chld, buf); ++ if (rc) ++ break; ++ } ++ ++ return rc; ++} ++ ++static int print_tree(struct libscols_table *tb, struct libscols_buffer *buf) ++{ ++ int rc; ++ struct libscols_line *ln; ++ struct libscols_iter itr; ++ ++ assert(tb); ++ ++ DBG(TAB, ul_debugobj(tb, "printing tree")); ++ ++ rc = print_header(tb, buf); ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) { ++ if (ln->parent) ++ continue; ++ rc = print_tree_line(tb, ln, buf); ++ } ++ ++ return rc; ++} ++ ++static void dbg_column(struct libscols_table *tb, struct libscols_column *cl) ++{ ++ DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, " ++ "hint=%d, avg=%zu, max=%zu, min=%zu, " ++ "extreme=%s", ++ ++ cl->header.data, cl->seqnum, cl->width, ++ cl->width_hint > 1 ? (int) cl->width_hint : ++ (int) (cl->width_hint * tb->termwidth), ++ cl->width_avg, ++ cl->width_max, ++ cl->width_min, ++ cl->is_extreme ? "yes" : "not")); ++} ++ ++static void dbg_columns(struct libscols_table *tb) ++{ ++ struct libscols_iter itr; ++ struct libscols_column *cl; ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_column(tb, &itr, &cl) == 0) ++ dbg_column(tb, cl); ++} ++ ++/* ++ * This function counts column width. ++ * ++ * For the SCOLS_FL_NOEXTREMES columns is possible to call this function two ++ * times. The first pass counts width and average width. If the column ++ * contains too large fields (width greater than 2 * average) then the column ++ * is marked as "extreme". In the second pass all extreme fields are ignored ++ * and column width is counted from non-extreme fields only. ++ */ ++static int count_column_width(struct libscols_table *tb, ++ struct libscols_column *cl, ++ struct libscols_buffer *buf) ++{ ++ struct libscols_line *ln; ++ struct libscols_iter itr; ++ int count = 0, rc = 0; ++ size_t sum = 0; ++ ++ assert(tb); ++ assert(cl); ++ ++ cl->width = 0; ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_line(tb, &itr, &ln) == 0) { ++ size_t len; ++ char *data; ++ ++ rc = cell_to_buffer(tb, ln, cl, buf); ++ if (rc) ++ return rc; ++ ++ data = buffer_get_data(buf); ++ len = data ? mbs_safe_width(data) : 0; ++ ++ if (len == (size_t) -1) /* ignore broken multibyte strings */ ++ len = 0; ++ if (len > cl->width_max) ++ cl->width_max = len; ++ ++ if (cl->is_extreme && len > cl->width_avg * 2) ++ continue; ++ else if (scols_column_is_noextremes(cl)) { ++ sum += len; ++ count++; ++ } ++ if (len > cl->width) ++ cl->width = len; ++ } ++ ++ if (count && cl->width_avg == 0) { ++ cl->width_avg = sum / count; ++ ++ if (cl->width_max > cl->width_avg * 2) ++ cl->is_extreme = 1; ++ } ++ ++ /* check and set minimal column width */ ++ if (scols_cell_get_data(&cl->header)) ++ cl->width_min = mbs_safe_width(scols_cell_get_data(&cl->header)); ++ ++ /* enlarge to minimal width */ ++ if (cl->width < cl->width_min && !scols_column_is_strict_width(cl)) ++ cl->width = cl->width_min; ++ ++ /* use relative size for large columns */ ++ else if (cl->width_hint >= 1 && cl->width < (size_t) cl->width_hint ++ && cl->width_min < (size_t) cl->width_hint) ++ ++ cl->width = (size_t) cl->width_hint; ++ ++ ON_DBG(COL, dbg_column(tb, cl)); ++ return rc; ++} ++ ++ ++/* ++ * This is core of the scols_* voodo... ++ */ ++static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf) ++{ ++ struct libscols_column *cl; ++ struct libscols_iter itr; ++ size_t width = 0; /* output width */ ++ int trunc_only, rc = 0; ++ int extremes = 0; ++ ++ ++ DBG(TAB, ul_debugobj(tb, "recounting widths (termwidth=%zu)", tb->termwidth)); ++ ++ /* set basic columns width ++ */ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ rc = count_column_width(tb, cl, buf); ++ if (rc) ++ return rc; ++ ++ width += cl->width + (is_last_column(tb, cl) ? 0 : 1); ++ extremes += cl->is_extreme; ++ } ++ ++ if (!tb->is_term) ++ return 0; ++ ++ /* reduce columns with extreme fields ++ */ ++ if (width > tb->termwidth && extremes) { ++ DBG(TAB, ul_debugobj(tb, " reduce width (extreme columns)")); ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ size_t org_width; ++ ++ if (!cl->is_extreme) ++ continue; ++ ++ org_width = cl->width; ++ rc = count_column_width(tb, cl, buf); ++ if (rc) ++ return rc; ++ ++ if (org_width > cl->width) ++ width -= org_width - cl->width; ++ else ++ extremes--; /* hmm... nothing reduced */ ++ } ++ } ++ ++ if (width < tb->termwidth) { ++ if (extremes) { ++ DBG(TAB, ul_debugobj(tb, " enlarge width (extreme columns)")); ++ ++ /* enlarge the first extreme column */ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ size_t add; ++ ++ if (!cl->is_extreme) ++ continue; ++ ++ /* this column is tooo large, ignore? ++ if (cl->width_max - cl->width > ++ (tb->termwidth - width)) ++ continue; ++ */ ++ ++ add = tb->termwidth - width; ++ if (add && cl->width + add > cl->width_max) ++ add = cl->width_max - cl->width; ++ ++ cl->width += add; ++ width += add; ++ ++ if (width == tb->termwidth) ++ break; ++ } ++ } ++ ++ if (width < tb->termwidth && scols_table_is_maxout(tb)) { ++ DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)")); ++ ++ /* try enlarge all columns */ ++ while (width < tb->termwidth) { ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ cl->width++; ++ width++; ++ if (width == tb->termwidth) ++ break; ++ } ++ } ++ } else if (width < tb->termwidth) { ++ /* enlarge the last column */ ++ struct libscols_column *cl = list_entry( ++ tb->tb_columns.prev, struct libscols_column, cl_columns); ++ ++ DBG(TAB, ul_debugobj(tb, " enlarge width (last column)")); ++ ++ if (!scols_column_is_right(cl) && tb->termwidth - width > 0) { ++ cl->width += tb->termwidth - width; ++ width = tb->termwidth; ++ } ++ } ++ } ++ ++ /* bad, we have to reduce output width, this is done in two steps: ++ * 1/ reduce columns with a relative width and with truncate flag ++ * 2) reduce columns with a relative width without truncate flag ++ */ ++ trunc_only = 1; ++ while (width > tb->termwidth) { ++ size_t org = width; ++ ++ DBG(TAB, ul_debugobj(tb, " reduce width (current=%zu, " ++ "wanted=%zu, mode=%s)", ++ width, tb->termwidth, ++ trunc_only ? "trunc-only" : "all-relative")); ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_column(tb, &itr, &cl) == 0) { ++ if (width <= tb->termwidth) ++ break; ++ if (cl->width_hint > 1 && !scols_column_is_trunc(cl)) ++ continue; /* never truncate columns with absolute sizes */ ++ if (scols_column_is_tree(cl)) ++ continue; /* never truncate the tree */ ++ if (trunc_only && !scols_column_is_trunc(cl)) ++ continue; ++ if (cl->width == cl->width_min) ++ continue; ++ ++ /* truncate column with relative sizes */ ++ if (cl->width_hint < 1 && cl->width > 0 && width > 0 && ++ cl->width > cl->width_hint * tb->termwidth) { ++ cl->width--; ++ width--; ++ } ++ /* truncate column with absolute size */ ++ if (cl->width_hint > 1 && cl->width > 0 && width > 0 && ++ !trunc_only) { ++ cl->width--; ++ width--; ++ } ++ ++ } ++ if (org == width) { ++ if (trunc_only) ++ trunc_only = 0; ++ else ++ break; ++ } ++ } ++ ++ DBG(TAB, ul_debugobj(tb, " result: %zu", width)); ++ ON_DBG(TAB, dbg_columns(tb)); ++ ++ return rc; ++} ++ ++static size_t strlen_line(struct libscols_line *ln) ++{ ++ size_t i, sz = 0; ++ ++ assert(ln); ++ ++ for (i = 0; i < ln->ncells; i++) { ++ struct libscols_cell *ce = scols_line_get_cell(ln, i); ++ const char *data = ce ? scols_cell_get_data(ce) : NULL; ++ ++ sz += data ? strlen(data) : 0; ++ } ++ ++ return sz; ++} ++ ++ ++ ++/** ++ * scols_print_table: ++ * @tb: table ++ * ++ * Prints the table to the output stream. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_print_table(struct libscols_table *tb) ++{ ++ int rc = 0; ++ size_t bufsz; ++ struct libscols_line *ln; ++ struct libscols_iter itr; ++ struct libscols_buffer *buf; ++ ++ assert(tb); ++ if (!tb) ++ return -1; ++ ++ DBG(TAB, ul_debugobj(tb, "printing")); ++ if (!tb->symbols) ++ scols_table_set_symbols(tb, NULL); /* use default */ ++ ++ tb->is_term = isatty(STDOUT_FILENO) ? 1 : 0; ++ tb->termwidth = tb->is_term ? get_terminal_width() : 0; ++ if (tb->termwidth <= 0) ++ tb->termwidth = 80; ++ tb->termwidth -= tb->termreduce; ++ ++ bufsz = tb->termwidth; ++ ++ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); ++ while (scols_table_next_line(tb, &itr, &ln) == 0) { ++ size_t sz = strlen_line(ln); ++ if (sz > bufsz) ++ bufsz = sz; ++ } ++ ++ buf = new_buffer(bufsz + 1); /* data + space for \0 */ ++ if (!buf) ++ return -ENOMEM; ++ ++ if (!(scols_table_is_raw(tb) || scols_table_is_export(tb))) { ++ rc = recount_widths(tb, buf); ++ if (rc != 0) ++ goto done; ++ } ++ ++ if (scols_table_is_tree(tb)) ++ rc = print_tree(tb, buf); ++ else ++ rc = print_table(tb, buf); ++ ++done: ++ free_buffer(buf); ++ return rc; ++} ++ ++/** ++ * scols_print_table_to_string: ++ * @tb: table ++ * @data: pointer to the beginning of a memory area to print to ++ * ++ * Prints the table to @data. ++ * ++ * Returns: 0, a negative value in case of an error. ++ */ ++int scols_print_table_to_string(struct libscols_table *tb, char **data) ++{ ++#ifdef HAVE_OPEN_MEMSTREAM ++ FILE *stream; ++ size_t sz; ++ int rc; ++ ++ if (!tb) ++ return -EINVAL; ++ ++ DBG(TAB, ul_debugobj(tb, "printing to string")); ++ ++ /* create a stream for output */ ++ stream = open_memstream(data, &sz); ++ if (!stream) ++ return -ENOMEM; ++ ++ scols_table_set_stream(tb, stream); ++ rc = scols_print_table(tb); ++ fclose(stream); ++ ++ return rc; ++#else ++ return -ENOSYS; ++#endif ++} ++ +diff -up util-linux-2.23.2/libsmartcols/src/test.c.kzak util-linux-2.23.2/libsmartcols/src/test.c +--- util-linux-2.23.2/libsmartcols/src/test.c.kzak 2014-09-25 14:41:48.993843953 +0200 ++++ util-linux-2.23.2/libsmartcols/src/test.c 2014-09-25 14:41:48.993843953 +0200 +@@ -0,0 +1,218 @@ ++/* ++ * Copyright (C) 2010-2014 Karel Zak ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "c.h" ++#include "nls.h" ++#include "strutils.h" ++ ++#include "libsmartcols.h" ++ ++static int add_children(struct libscols_table *tb, ++ struct libscols_line *ln, int fd); ++ ++ ++enum { COL_MODE, COL_SIZE, COL_NAME }; ++ ++/* add columns to the @tb */ ++static void setup_columns(struct libscols_table *tb, int notree) ++{ ++ if (!scols_table_new_column(tb, "MODE", 0.3, 0)) ++ goto fail; ++ if (!scols_table_new_column(tb, "SIZE", 5, SCOLS_FL_RIGHT)) ++ goto fail; ++ if (!scols_table_new_column(tb, "NAME", 0.5, ++ (notree ? 0 : SCOLS_FL_TREE) | SCOLS_FL_NOEXTREMES)) ++ goto fail; ++ ++ return; ++fail: ++ scols_unref_table(tb); ++ err(EXIT_FAILURE, "faild to create output columns"); ++} ++ ++/* add a new line to @tb, the content is based on @st */ ++static int add_line_from_stat(struct libscols_table *tb, ++ struct libscols_line *parent, ++ int parent_fd, ++ struct stat *st, ++ const char *name) ++{ ++ struct libscols_line *ln; ++ char modbuf[11], *p; ++ mode_t mode = st->st_mode; ++ int rc = 0; ++ ++ ln = scols_table_new_line(tb, parent); ++ if (!ln) ++ err(EXIT_FAILURE, "failed to create output line"); ++ ++ /* MODE; local buffer, use scols_line_set_data() that calls strdup() */ ++ strmode(mode, modbuf); ++ if (scols_line_set_data(ln, COL_MODE, modbuf)) ++ goto fail; ++ ++ /* SIZE; already allocated string, use scols_line_refer_data() */ ++ p = size_to_human_string(0, st->st_size); ++ if (!p || scols_line_refer_data(ln, COL_SIZE, p)) ++ goto fail; ++ ++ /* NAME */ ++ if (scols_line_set_data(ln, COL_NAME, name)) ++ goto fail; ++ ++ /* colors */ ++ if (scols_table_colors_wanted(tb)) { ++ struct libscols_cell *ce = scols_line_get_cell(ln, COL_NAME); ++ ++ if (S_ISDIR(mode)) ++ scols_cell_set_color(ce, "blue"); ++ else if (S_ISLNK(mode)) ++ scols_cell_set_color(ce, "cyan"); ++ else if (S_ISBLK(mode)) ++ scols_cell_set_color(ce, "magenta"); ++ else if ((mode & S_IXOTH) || (mode & S_IXGRP) || (mode & S_IXUSR)) ++ scols_cell_set_color(ce, "green"); ++ } ++ ++ if (S_ISDIR(st->st_mode)) { ++ int fd; ++ ++ if (parent_fd >= 0) ++ fd = openat(parent_fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC); ++ else ++ fd = open(name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC); ++ if (fd >= 0) { ++ rc = add_children(tb, ln, fd); ++ close(fd); ++ } ++ } ++ return rc; ++fail: ++ err(EXIT_FAILURE, "failed to create cell data"); ++ return -1; ++} ++ ++/* read all entrines from directory addressed by @fd */ ++static int add_children(struct libscols_table *tb, ++ struct libscols_line *ln, ++ int fd) ++{ ++ DIR *dir; ++ struct dirent *d; ++ ++ dir = fdopendir(fd); ++ if (!dir) ++ return -errno; ++ ++ while ((d = readdir(dir))) { ++ struct stat st; ++ ++ if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) ++ continue; ++ if (fstatat(fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) ++ continue; ++ add_line_from_stat(tb, ln, fd, &st, d->d_name); ++ } ++ closedir(dir); ++ return 0; ++} ++ ++static void add_lines(struct libscols_table *tb, const char *dirname) ++{ ++ struct stat st; ++ ++ if (lstat(dirname, &st)) ++ err(EXIT_FAILURE, "%s", dirname); ++ ++ add_line_from_stat(tb, NULL, -1, &st, dirname); ++} ++ ++static void __attribute__((__noreturn__)) usage(FILE *out) ++{ ++ fprintf(out, " %s [options] [ ...]\n\n", program_invocation_short_name); ++ fputs(" -c, --csv display a csv-like output\n", out); ++ fputs(" -i, --ascii use ascii characters only\n", out); ++ fputs(" -l, --list use list format output\n", out); ++ fputs(" -n, --noheadings don't print headings\n", out); ++ fputs(" -p, --pairs use key=\"value\" output format\n", out); ++ fputs(" -r, --raw use raw output format\n", out); ++ ++ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ struct libscols_table *tb; ++ int c, notree = 0; ++ ++ static const struct option longopts[] = { ++ { "ascii", 0, 0, 'i' }, ++ { "csv", 0, 0, 'c' }, ++ { "list", 0, 0, 'l' }, ++ { "noheadings", 0, 0, 'n' }, ++ { "pairs", 0, 0, 'p' }, ++ { "raw", 0, 0, 'r' }, ++ ++ { NULL, 0, 0, 0 }, ++ }; ++ ++ setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */ ++ ++ scols_init_debug(0); ++ ++ tb = scols_new_table(); ++ if (!tb) ++ err(EXIT_FAILURE, "faild to create output table"); ++ ++ while((c = getopt_long(argc, argv, "cilnpr", longopts, NULL)) != -1) { ++ switch(c) { ++ case 'c': ++ scols_table_set_column_separator(tb, ","); ++ scols_table_enable_raw(tb, 1); ++ notree = 1; ++ break; ++ case 'i': ++ scols_table_enable_ascii(tb, 1); ++ break; ++ case 'l': ++ notree = 1; ++ break; ++ case 'n': ++ scols_table_enable_noheadings(tb, 1); ++ break; ++ case 'p': ++ scols_table_enable_export(tb, 1); ++ notree = 1; ++ break; ++ case 'r': ++ scols_table_enable_raw(tb, 1); ++ notree = 1; ++ break; ++ default: ++ usage(stderr); ++ } ++ } ++ ++ scols_table_enable_colors(tb, 1); ++ setup_columns(tb, notree); ++ ++ while (optind < argc) ++ add_lines(tb, argv[optind++]); ++ ++ scols_print_table(tb); ++ scols_unref_table(tb); ++ ++ return EXIT_SUCCESS; ++} +diff -up util-linux-2.23.2/libsmartcols/src/version.c.kzak util-linux-2.23.2/libsmartcols/src/version.c +--- util-linux-2.23.2/libsmartcols/src/version.c.kzak 2014-09-25 14:41:48.993843953 +0200 ++++ util-linux-2.23.2/libsmartcols/src/version.c 2014-09-25 14:41:48.993843953 +0200 +@@ -0,0 +1,62 @@ ++/* ++ * version.c - Return the version of the library ++ * ++ * Copyright (C) 2014 Karel Zak ++ * ++ * See COPYING.libmount for the License of this software. ++ */ ++ ++/** ++ * SECTION: version-utils ++ * @title: Version functions ++ * @short_description: functions to get the library version. ++ * ++ * Note that library version is not the same thing as SONAME version. The ++ * libsmarcols uses symbols versioning and SONAME is not modified for releases. ++ * ++ * The library version and symbols version follow util-linux package versioning. ++ */ ++ ++#include ++ ++#include "smartcolsP.h" ++ ++static const char *lib_version = LIBSMARTCOLS_VERSION; ++ ++/** ++ * scols_parse_version_string: ++ * @ver_string: version string (e.g "2.18.0") ++ * ++ * Returns: release version code. ++ */ ++int scols_parse_version_string(const char *ver_string) ++{ ++ const char *cp; ++ int version = 0; ++ ++ assert(ver_string); ++ ++ for (cp = ver_string; *cp; cp++) { ++ if (*cp == '.') ++ continue; ++ if (!isdigit(*cp)) ++ break; ++ version = (version * 10) + (*cp - '0'); ++ } ++ return version; ++} ++ ++/** ++ * scols_get_library_version: ++ * @ver_string: return pointer to the static library version string if not NULL ++ * ++ * Returns: release version number. ++ */ ++int scols_get_library_version(const char **ver_string) ++{ ++ if (ver_string) ++ *ver_string = lib_version; ++ ++ return scols_parse_version_string(lib_version); ++} ++ +diff -up util-linux-2.23.2/lib/tt.c.kzak util-linux-2.23.2/lib/tt.c +--- util-linux-2.23.2/lib/tt.c.kzak 2013-07-15 10:25:46.280049032 +0200 ++++ util-linux-2.23.2/lib/tt.c 2014-09-25 14:41:48.982843848 +0200 +@@ -52,140 +52,6 @@ static const struct tt_symbols utf8_tt_s + #define is_last_column(_tb, _cl) \ + list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns) + +-/* +- * Counts number of cells in multibyte string. For all control and +- * non-printable chars is the result width enlarged to store \x?? hex +- * sequence. See mbs_safe_encode(). +- */ +-static size_t mbs_safe_width(const char *s) +-{ +- mbstate_t st; +- const char *p = s; +- size_t width = 0; +- +- memset(&st, 0, sizeof(st)); +- +- while (p && *p) { +- if (iscntrl((unsigned char) *p)) { +- width += 4; /* *p encoded to \x?? */ +- p++; +- } +-#ifdef HAVE_WIDECHAR +- else { +- wchar_t wc; +- size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st); +- +- if (len == 0) +- break; +- +- if (len == (size_t) -1 || len == (size_t) -2) { +- len = 1; +- width += (isprint((unsigned char) *p) ? 1 : 4); +- +- } if (!iswprint(wc)) +- width += len * 4; /* hex encode whole sequence */ +- else +- width += wcwidth(wc); /* number of cells */ +- p += len; +- } +-#else +- else if (!isprint((unsigned char) *p)) { +- width += 4; /* *p encoded to \x?? */ +- p++; +- } else { +- width++; +- p++; +- } +-#endif +- } +- +- return width; +-} +- +-/* +- * Returns allocated string where all control and non-printable chars are +- * replaced with \x?? hex sequence. +- */ +-static char *mbs_safe_encode(const char *s, size_t *width) +-{ +- mbstate_t st; +- const char *p = s; +- char *res, *r; +- size_t sz = s ? strlen(s) : 0; +- +- +- if (!sz) +- return NULL; +- +- memset(&st, 0, sizeof(st)); +- +- res = malloc((sz * 4) + 1); +- if (!res) +- return NULL; +- +- r = res; +- *width = 0; +- +- while (p && *p) { +- if (iscntrl((unsigned char) *p)) { +- sprintf(r, "\\x%02x", (unsigned char) *p); +- r += 4; +- *width += 4; +- p++; +- } +-#ifdef HAVE_WIDECHAR +- else { +- wchar_t wc; +- size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st); +- +- if (len == 0) +- break; /* end of string */ +- +- if (len == (size_t) -1 || len == (size_t) -2) { +- len = 1; +- /* +- * Not valid multibyte sequence -- maybe it's +- * printable char according to the current locales. +- */ +- if (!isprint((unsigned char) *p)) { +- sprintf(r, "\\x%02x", (unsigned char) *p); +- r += 4; +- *width += 4; +- } else { +- width++; +- *r++ = *p; +- } +- } else if (!iswprint(wc)) { +- size_t i; +- for (i = 0; i < len; i++) { +- sprintf(r, "\\x%02x", (unsigned char) *p); +- r += 4; +- *width += 4; +- } +- } else { +- memcpy(r, p, len); +- r += len; +- *width += wcwidth(wc); +- } +- p += len; +- } +-#else +- else if (!isprint((unsigned char) *p)) { +- sprintf(r, "\\x%02x", (unsigned char) *p); +- p++; +- r += 4; +- *width += 4; +- } else { +- *r++ = *p++; +- *width++; +- } +-#endif +- } +- +- *r = '\0'; +- +- return res; +-} + + /* + * @flags: TT_FL_* flags (usually TT_FL_{ASCII,RAW}) +diff -up util-linux-2.23.2/Makefile.am.kzak util-linux-2.23.2/Makefile.am +--- util-linux-2.23.2/Makefile.am.kzak 2013-06-13 09:46:10.334649886 +0200 ++++ util-linux-2.23.2/Makefile.am 2014-09-25 14:41:48.979843819 +0200 +@@ -22,6 +22,7 @@ dist_noinst_DATA = $(dist_man_MANS) + # + ul_libblkid_incdir = $(top_builddir)/libblkid/src + ul_libmount_incdir = $(top_builddir)/libmount/src ++ul_libsmartcols_incdir = $(top_builddir)/libsmartcols/src + ul_libuuid_incdir = $(top_srcdir)/libuuid/src + ul_libfdisk_incdir = $(top_srcdir)/libfdisk/src + +@@ -77,6 +78,7 @@ include lib/Makemodule.am + include libuuid/Makemodule.am + include libblkid/Makemodule.am + include libmount/Makemodule.am ++include libsmartcols/Makemodule.am + include libfdisk/Makemodule.am + + include schedutils/Makemodule.am diff --git a/SOURCES/2.26-lslogins.patch b/SOURCES/2.26-lslogins.patch new file mode 100644 index 0000000..4693263 --- /dev/null +++ b/SOURCES/2.26-lslogins.patch @@ -0,0 +1,2131 @@ +diff -up util-linux-2.23.2/configure.ac.kzak util-linux-2.23.2/configure.ac +--- util-linux-2.23.2/configure.ac.kzak 2014-12-12 15:27:43.505631342 +0100 ++++ util-linux-2.23.2/configure.ac 2014-12-12 15:28:30.571177081 +0100 +@@ -1027,6 +1027,11 @@ UL_REQUIRES_HAVE([lscpu], [cpu_set_t], [ + AM_CONDITIONAL(BUILD_LSCPU, test "x$build_lscpu" = xyes) + + ++UL_BUILD_INIT([lslogins], [check]) ++UL_REQUIRES_BUILD([lslogins], [libsmartcols]) ++AM_CONDITIONAL([BUILD_LSLOGINS], [test "x$build_lslogins" = xyes]) ++ ++ + UL_BUILD_INIT([chcpu], [check]) + UL_REQUIRES_LINUX([chcpu]) + UL_REQUIRES_HAVE([chcpu], [cpu_set_t], [cpu_set_t type]) +@@ -1404,6 +1409,37 @@ fi + AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != "xno" ]) + + ++# ++# Backport from upstrem to RHEL7.1 ++# ++AC_ARG_WITH([systemd], ++ AS_HELP_STRING([--with-systemd], [build with support for systemd]), ++ [], [with_systemd=check] ++) ++ ++have_systemd=no ++AS_IF([test "x$with_systemd" != xno], [ ++ # new version -- all libsystemd-* libs merged into libsystemd ++ PKG_CHECK_MODULES([SYSTEMD], [libsystemd], [have_systemd=yes], [have_systemd=no]) ++ # old versions ++ AS_IF([test "x$have_systemd" != "xyes"], [ ++ PKG_CHECK_MODULES([SYSTEMD_DAEMON], [libsystemd-daemon], ++ [have_systemd_daemon=yes], [have_systemd_daemon=no]) ++ PKG_CHECK_MODULES([SYSTEMD_JOURNAL], [libsystemd-journal], ++ [have_systemd_journal=yes], [have_systemd_journal=no]) ++ AS_IF([test "x$have_systemd_daemon" = "xyes" -a "x$have_systemd_journal" = "xyes" ],[ ++ have_systemd=yes]) ++ ]) ++ AS_CASE([$with_systemd:$have_systemd], ++ [yes:no], ++ [AC_MSG_ERROR([systemd expected but libsystemd not found])], ++ [*:yes], ++ AC_DEFINE([HAVE_LIBSYSTEMD], [1], [Define if libsystemd is available]) ++ ) ++]) ++ ++ ++ + AC_ARG_WITH([bashcompletiondir], + AS_HELP_STRING([--with-bashcompletiondir=DIR], [Bash completions directory]), + [], +diff -up util-linux-2.23.2/include/Makemodule.am.kzak util-linux-2.23.2/include/Makemodule.am +--- util-linux-2.23.2/include/Makemodule.am.kzak 2013-07-15 10:25:46.277049008 +0200 ++++ util-linux-2.23.2/include/Makemodule.am 2014-12-12 15:28:30.571177081 +0100 +@@ -35,6 +35,7 @@ dist_noinst_HEADERS += \ + include/procutils.h \ + include/randutils.h \ + include/rpmatch.h \ ++ include/readutmp.h \ + include/setproctitle.h \ + include/strutils.h \ + include/swapheader.h \ +diff -up util-linux-2.23.2/include/readutmp.h.kzak util-linux-2.23.2/include/readutmp.h +--- util-linux-2.23.2/include/readutmp.h.kzak 2014-12-12 15:28:30.571177081 +0100 ++++ util-linux-2.23.2/include/readutmp.h 2014-12-12 15:28:30.571177081 +0100 +@@ -0,0 +1,28 @@ ++/* Declarations for GNU's read utmp module. ++ ++ Copyright (C) 1992-2007, 2009-2014 Free Software Foundation, Inc. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++/* Written by jla; revised by djm */ ++ ++#ifndef READUTMP_H ++#define READUTMP_H ++ ++#include ++#include ++ ++int read_utmp (char const *file, size_t *n_entries, struct utmp **utmp_buf); ++ ++#endif /* READUTMP_H */ +diff -up util-linux-2.23.2/lib/Makemodule.am.kzak util-linux-2.23.2/lib/Makemodule.am +--- util-linux-2.23.2/lib/Makemodule.am.kzak 2013-07-30 10:39:26.202738200 +0200 ++++ util-linux-2.23.2/lib/Makemodule.am 2014-12-12 15:28:30.572177092 +0100 +@@ -25,7 +25,8 @@ libcommon_la_SOURCES = \ + lib/wholedisk.c \ + lib/ttyutils.c \ + lib/xgetpass.c \ +- lib/exec_shell.c ++ lib/exec_shell.c \ ++ lib/readutmp.c + + if LINUX + libcommon_la_SOURCES += \ +diff -up util-linux-2.23.2/lib/readutmp.c.kzak util-linux-2.23.2/lib/readutmp.c +--- util-linux-2.23.2/lib/readutmp.c.kzak 2014-12-12 15:28:30.572177092 +0100 ++++ util-linux-2.23.2/lib/readutmp.c 2014-12-12 15:28:30.572177092 +0100 +@@ -0,0 +1,76 @@ ++/* GNU's read utmp module. ++ ++ Copyright (C) 1992-2001, 2003-2006, 2009-2014 Free Software Foundation, Inc. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++/* Written by jla; revised by djm */ ++/* extracted for util-linux by ooprala */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "xalloc.h" ++#include "readutmp.h" ++ ++/* Read the utmp entries corresponding to file FILE into freshly- ++ malloc'd storage, set *UTMP_BUF to that pointer, set *N_ENTRIES to ++ the number of entries, and return zero. If there is any error, ++ return -1, setting errno, and don't modify the parameters. ++ If OPTIONS & READ_UTMP_CHECK_PIDS is nonzero, omit entries whose ++ process-IDs do not currently exist. */ ++int ++read_utmp (char const *file, size_t *n_entries, struct utmp **utmp_buf) ++{ ++ size_t n_read = 0; ++ size_t n_alloc = 0; ++ struct utmp *utmp = NULL; ++ struct utmp *u; ++ ++ /* Ignore the return value for now. ++ Solaris' utmpname returns 1 upon success -- which is contrary ++ to what the GNU libc version does. In addition, older GNU libc ++ versions are actually void. */ ++ utmpname(file); ++ ++ setutent(); ++ ++ errno = 0; ++ while ((u = getutent()) != NULL) { ++ if (n_read == n_alloc) { ++ n_alloc += 32; ++ utmp = xrealloc(utmp, n_alloc * sizeof (struct utmp)); ++ if (!utmp) ++ return -1; ++ } ++ utmp[n_read++] = *u; ++ } ++ if (!u && errno) ++ return -1; ++ ++ endutent(); ++ ++ *n_entries = n_read; ++ *utmp_buf = utmp; ++ ++ return 0; ++} +diff -up util-linux-2.23.2/login-utils/login.c.kzak util-linux-2.23.2/login-utils/login.c +--- util-linux-2.23.2/login-utils/login.c.kzak 2014-12-12 15:27:43.436630542 +0100 ++++ util-linux-2.23.2/login-utils/login.c 2014-12-12 15:28:30.573177104 +0100 +@@ -923,124 +923,6 @@ static void loginpam_session(struct logi + } + + /* +- * We need to check effective UID/GID. For example $HOME could be on root +- * squashed NFS or on NFS with UID mapping and access(2) uses real UID/GID. +- * The open(2) seems as the surest solution. +- * -- kzak@redhat.com (10-Apr-2009) +- */ +-static int effective_access(const char *path, int mode) +-{ +- int fd = open(path, mode); +- if (fd != -1) +- close(fd); +- return fd == -1 ? -1 : 0; +-} +- +-/* +- * Check per accout or global hush-login setting. +- * +- * Hushed mode is enabled: +- * +- * a) if global (e.g. /etc/hushlogins) hush file exists: +- * 1) for ALL ACCOUNTS if the file is empty +- * 2) for the current user if the username or shell are found in the file +- * +- * b) if ~/.hushlogin file exists +- * +- * The ~/.hushlogin is ignored if the global hush file exists. +- * +- * The HUSHLOGIN_FILE login.def variable overwrites the default hush filename. +- * +- * Note that shadow-utils login(1) does not support "a1)". The "a1)" is +- * necessary if you want to use PAM for "Last login" message. +- * +- * -- Karel Zak (26-Aug-2011) +- * +- * +- * Per-account check requires some explanation: As root we may not be able to +- * read the directory of the user if it is on an NFS mounted filesystem. We +- * temporarily set our effective uid to the user-uid making sure that we keep +- * root privs. in the real uid. +- * +- * A portable solution would require a fork(), but we rely on Linux having the +- * BSD setreuid() +- */ +-static int get_hushlogin_status(struct passwd *pwd) +-{ +- const char *files[] = { _PATH_HUSHLOGINS, _PATH_HUSHLOGIN, NULL }; +- const char *file; +- char buf[BUFSIZ]; +- int i; +- +- file = getlogindefs_str("HUSHLOGIN_FILE", NULL); +- if (file) { +- if (!*file) +- return 0; /* empty HUSHLOGIN_FILE defined */ +- +- files[0] = file; +- files[1] = NULL; +- } +- +- for (i = 0; files[i]; i++) { +- int ok = 0; +- +- file = files[i]; +- +- /* Global hush-file*/ +- if (*file == '/') { +- struct stat st; +- FILE *f; +- +- if (stat(file, &st) != 0) +- continue; /* file does not exist */ +- +- if (st.st_size == 0) +- return 1; /* for all accounts */ +- +- f = fopen(file, "r"); +- if (!f) +- continue; /* ignore errors... */ +- +- while (ok == 0 && fgets(buf, sizeof(buf), f)) { +- buf[strlen(buf) - 1] = '\0'; +- ok = !strcmp(buf, *buf == '/' ? pwd->pw_shell : +- pwd->pw_name); +- } +- fclose(f); +- if (ok) +- return 1; /* found username/shell */ +- +- return 0; /* ignore per-account files */ +- } +- +- /* Per-account setting */ +- if (strlen(pwd->pw_dir) + sizeof(file) + 2 > sizeof(buf)) +- continue; +- else { +- uid_t ruid = getuid(); +- gid_t egid = getegid(); +- +- sprintf(buf, "%s/%s", pwd->pw_dir, file); +- +- if (setregid(-1, pwd->pw_gid) == 0 && +- setreuid(0, pwd->pw_uid) == 0) +- ok = effective_access(buf, O_RDONLY) == 0; +- +- if (setuid(0) != 0 || +- setreuid(ruid, 0) != 0 || +- setregid(-1, egid) != 0) { +- syslog(LOG_ALERT, _("hush login status: restore original IDs failed")); +- exit(EXIT_FAILURE); +- } +- if (ok) +- return 1; /* enabled by user */ +- } +- } +- +- return 0; +-} +- +-/* + * Detach the controlling terminal, fork, restore syslog stuff and create a new + * session. + */ +@@ -1372,7 +1254,7 @@ int main(int argc, char **argv) + + endpwent(); + +- cxt.quiet = get_hushlogin_status(pwd); ++ cxt.quiet = get_hushlogin_status(pwd, 1); + + log_utmp(&cxt); + log_audit(&cxt, 1); +diff -up util-linux-2.23.2/login-utils/logindefs.c.kzak util-linux-2.23.2/login-utils/logindefs.c +--- util-linux-2.23.2/login-utils/logindefs.c.kzak 2013-06-13 09:46:10.442650810 +0200 ++++ util-linux-2.23.2/login-utils/logindefs.c 2014-12-12 15:28:30.573177104 +0100 +@@ -27,6 +27,9 @@ + #include + #include + #include ++#include ++#include ++#include + + #include "c.h" + #include "closestream.h" +@@ -259,6 +262,135 @@ int logindefs_setenv(const char *name, c + return val ? setenv(name, val, 1) : -1; + } + ++/* ++ * We need to check the effective UID/GID. For example, $HOME could be on a ++ * root-squashed NFS or on an NFS with UID mapping, and access(2) uses the ++ * real UID/GID. Then open(2) seems as the surest solution. ++ * -- kzak@redhat.com (10-Apr-2009) ++ */ ++int effective_access(const char *path, int mode) ++{ ++ int fd = open(path, mode); ++ if (fd != -1) ++ close(fd); ++ return fd == -1 ? -1 : 0; ++} ++ ++ ++/* ++ * Check the per-account or the global hush-login setting. ++ * ++ * Hushed mode is enabled: ++ * ++ * a) if a global (e.g. /etc/hushlogins) hush file exists: ++ * 1) for ALL ACCOUNTS if the file is empty ++ * 2) for the current user if the username or shell is found in the file ++ * ++ * b) if a ~/.hushlogin file exists ++ * ++ * The ~/.hushlogin file is ignored if the global hush file exists. ++ * ++ * The HUSHLOGIN_FILE login.def variable overrides the default hush filename. ++ * ++ * Note that shadow-utils login(1) does not support "a1)". The "a1)" is ++ * necessary if you want to use PAM for "Last login" message. ++ * ++ * -- Karel Zak (26-Aug-2011) ++ * ++ * ++ * The per-account check requires some explanation: As root we may not be able ++ * to read the directory of the user if it is on an NFS-mounted filesystem. We ++ * temporarily set our effective uid to the user-uid, making sure that we keep ++ * root privileges in the real uid. ++ * ++ * A portable solution would require a fork(), but we rely on Linux having the ++ * BSD setreuid(). ++ */ ++ ++int get_hushlogin_status(struct passwd *pwd, int force_check) ++{ ++ const char *files[] = { _PATH_HUSHLOGINS, _PATH_HUSHLOGIN, NULL }; ++ const char *file; ++ char buf[BUFSIZ]; ++ int i; ++ ++ file = getlogindefs_str("HUSHLOGIN_FILE", NULL); ++ if (file) { ++ if (!*file) ++ return 0; /* empty HUSHLOGIN_FILE defined */ ++ ++ files[0] = file; ++ files[1] = NULL; ++ } ++ ++ for (i = 0; files[i]; i++) { ++ int ok = 0; ++ ++ file = files[i]; ++ ++ /* global hush-file */ ++ if (*file == '/') { ++ struct stat st; ++ FILE *f; ++ ++ if (stat(file, &st) != 0) ++ continue; /* file does not exist */ ++ ++ if (st.st_size == 0) ++ return 1; /* for all accounts */ ++ ++ f = fopen(file, "r"); ++ if (!f) ++ continue; /* ignore errors... */ ++ ++ while (ok == 0 && fgets(buf, sizeof(buf), f)) { ++ buf[strlen(buf) - 1] = '\0'; ++ ok = !strcmp(buf, *buf == '/' ? pwd->pw_shell : ++ pwd->pw_name); ++ } ++ fclose(f); ++ if (ok) ++ return 1; /* found username/shell */ ++ ++ return 0; /* ignore per-account files */ ++ } ++ ++ /* per-account setting */ ++ if (strlen(pwd->pw_dir) + sizeof(file) + 2 > sizeof(buf)) ++ continue; ++ ++ sprintf(buf, "%s/%s", pwd->pw_dir, file); ++ ++ if (force_check) { ++ uid_t ruid = getuid(); ++ gid_t egid = getegid(); ++ ++ if (setregid(-1, pwd->pw_gid) == 0 && ++ setreuid(0, pwd->pw_uid) == 0) ++ ok = effective_access(buf, O_RDONLY) == 0; ++ ++ if (setuid(0) != 0 || ++ setreuid(ruid, 0) != 0 || ++ setregid(-1, egid) != 0) { ++ syslog(LOG_ALERT, _("hush login status: restore original IDs failed")); ++ exit(EXIT_FAILURE); ++ } ++ if (ok) ++ return 1; /* enabled by user */ ++ } ++ else { ++ int rc; ++ rc = effective_access(buf, O_RDONLY); ++ if (rc == 0) ++ return 1; ++ else if (rc == -1 && errno == EACCES) ++ return -1; ++ } ++ ++ } ++ ++ return 0; ++} + #ifdef TEST_PROGRAM + int main(int argc, char *argv[]) + { +diff -up util-linux-2.23.2/login-utils/logindefs.h.kzak util-linux-2.23.2/login-utils/logindefs.h +--- util-linux-2.23.2/login-utils/logindefs.h.kzak 2013-02-27 17:46:29.887020770 +0100 ++++ util-linux-2.23.2/login-utils/logindefs.h 2014-12-12 15:28:30.573177104 +0100 +@@ -8,5 +8,7 @@ extern unsigned long getlogindefs_num(co + extern const char *getlogindefs_str(const char *name, const char *dflt); + extern void free_getlogindefs_data(void); + extern int logindefs_setenv(const char *name, const char *conf, const char *dflt); ++extern int effective_access(const char *path, int mode); ++extern int get_hushlogin_status(struct passwd *pwd, int force_check); + + #endif /* UTIL_LINUX_LOGINDEFS_H */ +diff -up util-linux-2.23.2/login-utils/lslogins.1.kzak util-linux-2.23.2/login-utils/lslogins.1 +--- util-linux-2.23.2/login-utils/lslogins.1.kzak 2014-12-12 15:28:30.574177115 +0100 ++++ util-linux-2.23.2/login-utils/lslogins.1 2014-12-12 15:28:30.574177115 +0100 +@@ -0,0 +1,132 @@ ++.\" Copyright 2014 Ondrej Oprala (ondrej.oprala@gmail.com) ++.\" May be distributed under the GNU General Public License ++.TH LSLOGINS "1" "April 2014" "util-linux" "User Commands" ++.SH NAME ++lslogins \- display information about known users in the system ++.SH SYNOPSIS ++.B lslogins ++[\fIoptions\fR] [\fB-s\fR|\fB-u\fR[=\fIUID\fR]] [\fB-g \fIgroups\fR] [\fB-l \fIlogins\fR] ++.SH DESCRIPTION ++.PP ++Examine the wtmp and btmp logs, /etc/shadow (if necessary) and /etc/passwd ++and output the desired data. ++.PP ++The default action is to list info about all the users in the system. ++.SH OPTIONS ++Mandatory arguments to long options are mandatory for short options too. ++.TP ++\fB\-a\fR, \fB\-\-acc\-expiration\fR ++Display data about the date of last password change and the account expiration ++date (see \fBshadow\fR(5) for more info). (Requires root priviliges.) ++.TP ++\fB\-\-btmp\-file \fIpath\fP ++Alternate path for btmp. ++.TP ++\fB\-c\fR, \fB\-\-colon\-separate\fR ++Separate info about each user with a colon instead of a newline. ++.TP ++\fB\-e\fR, \fB\-\-export\fR ++Output data in the format of NAME=VALUE. ++.TP ++\fB\-f\fR, \fB\-\-failed\fR ++Display data about the users' last failed login attempts. ++.TP ++\fB\-G\fR, \fB\-\-groups\-info\fR ++Show information about groups. ++.TP ++\fB\-g\fR, \fB\-\-groups\fR=\fIgroups\fR ++Only show data of users belonging to \fIgroups\fR. More than one group ++may be specified; the list has to be comma-separated. ++.TP ++\fB\-h\fR, \fB\-\-help\fR ++Display help information and exit. ++.TP ++\fB\-L\fR, \fB\-\-last\fR ++Display data containing information about the users' last login sessions. ++.TP ++\fB\-l\fR, \fB\-\-logins\fR=\fIlogins\fR ++Only show data of users with a login specified in \fIlogins\fR (user names or user ++IDS). More than one login may be specified; the list has to be comma-separated. ++.TP ++\fB\-m\fR, \fB\-\-supp\-groups\fR ++Show supplementary groups. ++.TP ++\fB\-n\fR, \fB\-\-newline\fR ++Display each piece of information on a separate line. ++.TP ++\fB\-\-noheadings\fR ++Do not print a header line. ++.TP ++\fB\-\-notruncate\fR ++Don't truncate output. ++.TP ++\fB\-o\fR, \fB\-\-output \fIlist\fP ++Specify which output columns to print. Use ++.B \-\-help ++to get a list of all supported columns. ++.TP ++\fB\-p\fR, \fB\-\-pwd\fR ++Display information related to login by password (see also \fB\-afL). ++.TP ++\fB\-r\fR, \fB\-\-raw\fR ++Raw output (no columnation). ++.TP ++\fB\-s\fR, \fB\-\-system\-accs\fR[=\fIthreshold\fR] ++Show system accounts. These are by default all accounts with a UID below 1000 ++(non-inclusive), with the exception of either nobody or nfsnobody (UID 65534). The UID ++threshold can also be specified explicitly (necessary for some distributions that ++allocate UIDs starting from 100, 500 - or an entirely different value - rather than 1000). ++.TP ++\fB\-\-time-format\fR \fItype\fP ++Display dates in short, full or iso format. The default is short, this time ++format is designed to be space efficient and human readable. ++.TP ++\fB\-u\fR, \fB\-\-user\-accs\fR[=\fIthreshold\fR] ++Show user accounts. These are by default all accounts with UID above 1000 ++(inclusive), with the exception of either nobody or nfsnobody (UID 65534). The UID ++threshold can also be specified explicitly (necessary for some distributions that ++allocate UIDs starting from 100, 500 - or an entirely different value - rather than 1000). ++.TP ++\fB\-V\fR, \fB\-\-version\fR ++Display version information and exit. ++.TP ++\fB\-\-wtmp\-file \fIpath\fP ++Alternate path for wtmp. ++.TP ++\fB\-Z\fR, \fB\-\-context\fR ++Display the users' security context. ++.TP ++\fB\-z\fR, \fB\-\-print0\fR ++Delimit user entries with a nul character, instead of a newline. ++ ++.SH NOTES ++The default UID thresholds are read from /etc/login.defs. ++ ++.SH EXIT STATUS ++.TP ++0 ++if OK, ++.TP ++1 ++if incorrect arguments specified, ++.TP ++2 ++if a serious error occurs (e.g. a corrupt log). ++.SH SEE ALSO ++\fBgroup\fP(5), \fBpasswd\fP(5), \fBshadow\fP(5), \fButmp\fP(5) ++.SH HISTORY ++The \fBlslogins\fP utility is inspired by the \fBlogins\fP utility, which first appeared in FreeBSD 4.10. ++.SH AUTHORS ++.MT ooprala@redhat.com ++Ondrej Oprala ++.ME ++.br ++.MT kzak@redhat.com ++Karel Zak ++.ME ++ ++.SH AVAILABILITY ++The lslogins command is part of the util-linux package and is available from ++.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/ ++Linux Kernel Archive ++.UE . +diff -up util-linux-2.23.2/login-utils/lslogins.c.kzak util-linux-2.23.2/login-utils/lslogins.c +--- util-linux-2.23.2/login-utils/lslogins.c.kzak 2014-12-12 15:28:30.575177127 +0100 ++++ util-linux-2.23.2/login-utils/lslogins.c 2014-12-12 15:29:19.084739609 +0100 +@@ -0,0 +1,1476 @@ ++/* ++ * lslogins - List information about users on the system ++ * ++ * Copyright (C) 2014 Ondrej Oprala ++ * Copyright (C) 2014 Karel Zak ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it would be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#ifdef HAVE_LIBSELINUX ++# include ++#endif ++ ++#ifdef HAVE_LIBSYSTEMD ++# include ++#endif ++ ++#include "c.h" ++#include "nls.h" ++#include "closestream.h" ++#include "xalloc.h" ++#include "list.h" ++#include "strutils.h" ++#include "optutils.h" ++#include "pathnames.h" ++#include "logindefs.h" ++#include "readutmp.h" ++#include "procutils.h" ++ ++/* ++ * column description ++ */ ++struct lslogins_coldesc { ++ const char *name; ++ const char *help; ++ const char *pretty_name; ++ ++ double whint; /* width hint */ ++ long flag; ++}; ++ ++static int lslogins_flag; ++ ++#define UL_UID_MIN 1000 ++#define UL_UID_MAX 60000 ++#define UL_SYS_UID_MIN 201 ++#define UL_SYS_UID_MAX 999 ++ ++/* we use the value of outmode to determine ++ * appropriate flags for the libsmartcols table ++ * (e.g., a value of out_newline would imply a raw ++ * table with the column separator set to '\n'). ++ */ ++static int outmode; ++/* ++ * output modes ++ */ ++enum { ++ OUT_COLON = 1, ++ OUT_EXPORT, ++ OUT_NEWLINE, ++ OUT_RAW, ++ OUT_NUL, ++ OUT_PRETTY ++}; ++ ++struct lslogins_user { ++ char *login; ++ uid_t uid; ++ char *group; ++ gid_t gid; ++ char *gecos; ++ ++ int pwd_empty; ++ int nologin; ++ int pwd_lock; ++ int pwd_deny; ++ ++ gid_t *sgroups; ++ size_t nsgroups; ++ ++ char *pwd_ctime; ++ char *pwd_warn; ++ char *pwd_expire; ++ char *pwd_ctime_min; ++ char *pwd_ctime_max; ++ ++ char *last_login; ++ char *last_tty; ++ char *last_hostname; ++ ++ char *failed_login; ++ char *failed_tty; ++ ++#ifdef HAVE_LIBSELINUX ++ security_context_t context; ++#endif ++ char *homedir; ++ char *shell; ++ char *pwd_status; ++ int hushed; ++ char *nprocs; ++ ++}; ++ ++/* ++ * time modes ++ * */ ++enum { ++ TIME_INVALID = 0, ++ TIME_SHORT, ++ TIME_FULL, ++ TIME_ISO, ++}; ++ ++/* ++ * flags ++ */ ++enum { ++ F_SYSAC = (1 << 3), ++ F_USRAC = (1 << 4), ++}; ++ ++/* ++ * IDs ++ */ ++enum { ++ COL_USER = 0, ++ COL_UID, ++ COL_GECOS, ++ COL_HOME, ++ COL_SHELL, ++ COL_NOLOGIN, ++ COL_PWDLOCK, ++ COL_PWDEMPTY, ++ COL_PWDDENY, ++ COL_GROUP, ++ COL_GID, ++ COL_SGROUPS, ++ COL_SGIDS, ++ COL_LAST_LOGIN, ++ COL_LAST_TTY, ++ COL_LAST_HOSTNAME, ++ COL_FAILED_LOGIN, ++ COL_FAILED_TTY, ++ COL_HUSH_STATUS, ++ COL_PWD_WARN, ++ COL_PWD_CTIME, ++ COL_PWD_CTIME_MIN, ++ COL_PWD_CTIME_MAX, ++ COL_PWD_EXPIR, ++ COL_SELINUX, ++ COL_NPROCS, ++}; ++ ++#define is_wtmp_col(x) ((x) == COL_LAST_LOGIN || \ ++ (x) == COL_LAST_TTY || \ ++ (x) == COL_LAST_HOSTNAME) ++ ++#define is_btmp_col(x) ((x) == COL_FAILED_LOGIN || \ ++ (x) == COL_FAILED_TTY) ++ ++enum { ++ STATUS_FALSE = 0, ++ STATUS_TRUE, ++ STATUS_UNKNOWN ++}; ++ ++static const char *const status[] = { ++ [STATUS_FALSE] = "0", ++ [STATUS_TRUE] = "1", ++ [STATUS_UNKNOWN]= NULL ++}; ++ ++static const char *const pretty_status[] = { ++ [STATUS_FALSE] = N_("no"), ++ [STATUS_TRUE] = N_("yes"), ++ [STATUS_UNKNOWN]= NULL ++}; ++ ++#define get_status(x) (outmode == OUT_PRETTY ? pretty_status[(x)] : status[(x)]) ++ ++static const struct lslogins_coldesc coldescs[] = ++{ ++ [COL_USER] = { "USER", N_("user name"), N_("Username"), 0.1, SCOLS_FL_NOEXTREMES }, ++ [COL_UID] = { "UID", N_("user ID"), "UID", 1, SCOLS_FL_RIGHT}, ++ [COL_PWDEMPTY] = { "PWD-EMPTY", N_("password not required"), N_("Password not required"), 1, SCOLS_FL_RIGHT }, ++ [COL_PWDDENY] = { "PWD-DENY", N_("login by password disabled"), N_("Login by password disabled"), 1, SCOLS_FL_RIGHT }, ++ [COL_PWDLOCK] = { "PWD-LOCK", N_("password defined, but locked"), N_("Password is locked"), 1, SCOLS_FL_RIGHT }, ++ [COL_NOLOGIN] = { "NOLOGIN", N_("log in disabled by nologin(8) or pam_nologin(8)"), N_("No login"), 1, SCOLS_FL_RIGHT }, ++ [COL_GROUP] = { "GROUP", N_("primary group name"), N_("Primary group"), 0.1 }, ++ [COL_GID] = { "GID", N_("primary group ID"), "GID", 1, SCOLS_FL_RIGHT }, ++ [COL_SGROUPS] = { "SUPP-GROUPS", N_("supplementary group names"), N_("Supplementary groups"), 0.1 }, ++ [COL_SGIDS] = { "SUPP-GIDS", N_("supplementary group IDs"), N_("Supplementary group IDs"), 0.1 }, ++ [COL_HOME] = { "HOMEDIR", N_("home directory"), N_("Home directory"), 0.1 }, ++ [COL_SHELL] = { "SHELL", N_("login shell"), N_("Shell"), 0.1 }, ++ [COL_GECOS] = { "GECOS", N_("full user name"), N_("Gecos field"), 0.1, SCOLS_FL_TRUNC }, ++ [COL_LAST_LOGIN] = { "LAST-LOGIN", N_("date of last login"), N_("Last login"), 0.1, SCOLS_FL_RIGHT }, ++ [COL_LAST_TTY] = { "LAST-TTY", N_("last tty used"), N_("Last terminal"), 0.05 }, ++ [COL_LAST_HOSTNAME] = { "LAST-HOSTNAME",N_("hostname during the last session"), N_("Last hostname"), 0.1}, ++ [COL_FAILED_LOGIN] = { "FAILED-LOGIN", N_("date of last failed login"), N_("Failed login"), 0.1 }, ++ [COL_FAILED_TTY] = { "FAILED-TTY", N_("where did the login fail?"), N_("Failed login terminal"), 0.05 }, ++ [COL_HUSH_STATUS] = { "HUSHED", N_("user's hush settings"), N_("Hushed"), 1, SCOLS_FL_RIGHT }, ++ [COL_PWD_WARN] = { "PWD-WARN", N_("days user is warned of password expiration"), N_("Password expiration warn interval"), 0.1, SCOLS_FL_RIGHT }, ++ [COL_PWD_EXPIR] = { "PWD-EXPIR", N_("password expiration date"), N_("Password expiration"), 0.1, SCOLS_FL_RIGHT }, ++ [COL_PWD_CTIME] = { "PWD-CHANGE", N_("date of last password change"), N_("Password changed"), 0.1, SCOLS_FL_RIGHT}, ++ [COL_PWD_CTIME_MIN] = { "PWD-MIN", N_("number of days required between changes"), N_("Minimum change time"), 0.1, SCOLS_FL_RIGHT }, ++ [COL_PWD_CTIME_MAX] = { "PWD-MAX", N_("max number of days a password may remain unchanged"), N_("Maximum change time"), 0.1, SCOLS_FL_RIGHT }, ++ [COL_SELINUX] = { "CONTEXT", N_("the user's security context"), N_("Selinux context"), 0.1 }, ++ [COL_NPROCS] = { "PROC", N_("number of processes run by the user"), N_("Running processes"), 1, SCOLS_FL_RIGHT }, ++}; ++ ++struct lslogins_control { ++ struct utmp *wtmp; ++ size_t wtmp_size; ++ ++ struct utmp *btmp; ++ size_t btmp_size; ++ ++ void *usertree; ++ ++ uid_t uid; ++ uid_t UID_MIN; ++ uid_t UID_MAX; ++ ++ uid_t SYS_UID_MIN; ++ uid_t SYS_UID_MAX; ++ ++ char **ulist; ++ size_t ulsiz; ++ ++ unsigned int time_mode; ++ ++ const char *journal_path; ++ ++ unsigned int selinux_enabled : 1, ++ ulist_on : 1, ++ noheadings : 1, ++ notrunc : 1; ++}; ++ ++/* these have to remain global since there's no other reasonable way to pass ++ * them for each call of fill_table() via twalk() */ ++static struct libscols_table *tb; ++ ++/* columns[] array specifies all currently wanted output column. The columns ++ * are defined by coldescs[] array and you can specify (on command line) each ++ * column twice. That's enough, dynamically allocated array of the columns is ++ * unnecessary overkill and over-engineering in this case */ ++static int columns[ARRAY_SIZE(coldescs) * 2]; ++static int ncolumns; ++ ++static inline size_t err_columns_index(size_t arysz, size_t idx) ++{ ++ if (idx >= arysz) ++ errx(EXIT_FAILURE, _("too many columns specified, " ++ "the limit is %zu columns"), ++ arysz - 1); ++ return idx; ++} ++ ++#define add_column(ary, n, id) \ ++ ((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id)) ++ ++static struct timeval now; ++ ++static int date_is_today(time_t t) ++{ ++ if (now.tv_sec == 0) ++ gettimeofday(&now, NULL); ++ return t / (3600 * 24) == now.tv_sec / (3600 * 24); ++} ++ ++static int date_is_thisyear(time_t t) ++{ ++ if (now.tv_sec == 0) ++ gettimeofday(&now, NULL); ++ return t / (3600 * 24 * 365) == now.tv_sec / (3600 * 24 * 365); ++} ++ ++static int column_name_to_id(const char *name, size_t namesz) ++{ ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(coldescs); i++) { ++ const char *cn = coldescs[i].name; ++ ++ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) ++ return i; ++ } ++ warnx(_("unknown column: %s"), name); ++ return -1; ++} ++ ++static char *make_time(int mode, time_t time) ++{ ++ char *s; ++ struct tm tm; ++ char buf[64] = {0}; ++ ++ localtime_r(&time, &tm); ++ ++ switch(mode) { ++ case TIME_FULL: ++ asctime_r(&tm, buf); ++ if (*(s = buf + strlen(buf) - 1) == '\n') ++ *s = '\0'; ++ break; ++ case TIME_SHORT: ++ if (date_is_today(time)) ++ strftime(buf, sizeof(buf), "%H:%M:%S", &tm); ++ else if (date_is_thisyear(time)) ++ strftime(buf, sizeof(buf), "%b%d/%H:%M", &tm); ++ else ++ strftime(buf, sizeof(buf), "%Y-%b%d", &tm); ++ break; ++ case TIME_ISO: ++ strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", &tm); ++ break; ++ default: ++ errx(EXIT_FAILURE, _("unsupported time type")); ++ } ++ return xstrdup(buf); ++} ++ ++ ++static char *uidtostr(uid_t uid) ++{ ++ char *str_uid = NULL; ++ xasprintf(&str_uid, "%u", uid); ++ return str_uid; ++} ++ ++static char *gidtostr(gid_t gid) ++{ ++ char *str_gid = NULL; ++ xasprintf(&str_gid, "%u", gid); ++ return str_gid; ++} ++ ++static char *build_sgroups_string(gid_t *sgroups, size_t nsgroups, int want_names) ++{ ++ size_t n = 0, maxlen, len; ++ char *res, *p; ++ ++ if (!nsgroups) ++ return NULL; ++ ++ len = maxlen = nsgroups * 10; ++ res = p = xmalloc(maxlen); ++ ++ while (n < nsgroups) { ++ int x; ++again: ++ if (!want_names) ++ x = snprintf(p, len, "%u,", sgroups[n]); ++ else { ++ struct group *grp = getgrgid(sgroups[n]); ++ if (!grp) { ++ free(res); ++ return NULL; ++ } ++ x = snprintf(p, len, "%s,", grp->gr_name); ++ } ++ ++ if (x < 0 || (size_t) x + 1 > len) { ++ size_t cur = p - res; ++ ++ maxlen *= 2; ++ res = xrealloc(res, maxlen); ++ p = res + cur; ++ len = maxlen - cur; ++ goto again; ++ } ++ ++ len -= x; ++ p += x; ++ ++n; ++ } ++ ++ if (p > res) ++ *(p - 1) = '\0'; ++ ++ return res; ++} ++ ++static struct utmp *get_last_wtmp(struct lslogins_control *ctl, const char *username) ++{ ++ size_t n = 0; ++ size_t len; ++ ++ if (!username) ++ return NULL; ++ ++ len = strlen(username); ++ n = ctl->wtmp_size - 1; ++ do { ++ if (!strncmp(username, ctl->wtmp[n].ut_user, ++ len < UT_NAMESIZE ? len : UT_NAMESIZE)) ++ return ctl->wtmp + n; ++ } while (n--); ++ return NULL; ++ ++} ++ ++static int require_wtmp(void) ++{ ++ size_t i; ++ for (i = 0; i < (size_t) ncolumns; i++) ++ if (is_wtmp_col(columns[i])) ++ return 1; ++ return 0; ++} ++ ++static int require_btmp(void) ++{ ++ size_t i; ++ for (i = 0; i < (size_t) ncolumns; i++) ++ if (is_btmp_col(columns[i])) ++ return 1; ++ return 0; ++} ++ ++static struct utmp *get_last_btmp(struct lslogins_control *ctl, const char *username) ++{ ++ size_t n = 0; ++ size_t len; ++ ++ if (!username) ++ return NULL; ++ ++ len = strlen(username); ++ n = ctl->btmp_size - 1; ++ do { ++ if (!strncmp(username, ctl->btmp[n].ut_user, ++ len < UT_NAMESIZE ? len : UT_NAMESIZE)) ++ return ctl->btmp + n; ++ }while (n--); ++ return NULL; ++ ++} ++ ++static int parse_wtmp(struct lslogins_control *ctl, char *path) ++{ ++ int rc = 0; ++ ++ rc = read_utmp(path, &ctl->wtmp_size, &ctl->wtmp); ++ if (rc < 0 && errno != EACCES) ++ err(EXIT_FAILURE, "%s", path); ++ return rc; ++} ++ ++static int parse_btmp(struct lslogins_control *ctl, char *path) ++{ ++ int rc = 0; ++ ++ rc = read_utmp(path, &ctl->btmp_size, &ctl->btmp); ++ if (rc < 0 && errno != EACCES) ++ err(EXIT_FAILURE, "%s", path); ++ return rc; ++} ++ ++static int get_sgroups(gid_t **list, size_t *len, struct passwd *pwd) ++{ ++ size_t n = 0; ++ ++ *len = 0; ++ *list = NULL; ++ ++ /* first let's get a supp. group count */ ++ getgrouplist(pwd->pw_name, pwd->pw_gid, *list, (int *) len); ++ if (!*len) ++ return -1; ++ ++ *list = xcalloc(1, *len * sizeof(gid_t)); ++ ++ /* now for the actual list of GIDs */ ++ if (-1 == getgrouplist(pwd->pw_name, pwd->pw_gid, *list, (int *) len)) ++ return -1; ++ ++ /* getgroups also returns the user's primary GID - dispose of it */ ++ while (n < *len) { ++ if ((*list)[n] == pwd->pw_gid) ++ break; ++ ++n; ++ } ++ ++ if (*len) ++ (*list)[n] = (*list)[--(*len)]; ++ return 0; ++} ++ ++static int get_nprocs(const uid_t uid) ++{ ++ int nprocs = 0; ++ pid_t pid; ++ struct proc_processes *proc = proc_open_processes(); ++ ++ proc_processes_filter_by_uid(proc, uid); ++ ++ while (!proc_next_pid(proc, &pid)) ++ ++nprocs; ++ ++ proc_close_processes(proc); ++ return nprocs; ++} ++ ++static int valid_pwd(const char *str) ++{ ++ const char *p; ++ ++ for (p = str; p && *p; p++) ++ if (!isalnum((unsigned int) *p)) ++ return 0; ++ return p > str ? 1 : 0; ++} ++ ++static struct lslogins_user *get_user_info(struct lslogins_control *ctl, const char *username) ++{ ++ struct lslogins_user *user; ++ struct passwd *pwd; ++ struct group *grp; ++ struct spwd *shadow; ++ struct utmp *user_wtmp = NULL, *user_btmp = NULL; ++ int n = 0; ++ time_t time; ++ uid_t uid; ++ errno = 0; ++ ++ pwd = username ? getpwnam(username) : getpwent(); ++ if (!pwd) ++ return NULL; ++ ++ ctl->uid = uid = pwd->pw_uid; ++ ++ /* nfsnobody is an exception to the UID_MAX limit. This is "nobody" on ++ * some systems; the decisive point is the UID - 65534 */ ++ if ((lslogins_flag & F_USRAC) && ++ strcmp("nfsnobody", pwd->pw_name) != 0 && ++ uid != 0) { ++ if (uid < ctl->UID_MIN || uid > ctl->UID_MAX) { ++ errno = EAGAIN; ++ return NULL; ++ } ++ ++ } else if ((lslogins_flag & F_SYSAC) && ++ (uid < ctl->SYS_UID_MIN || uid > ctl->SYS_UID_MAX)) { ++ errno = EAGAIN; ++ return NULL; ++ } ++ ++ user = xcalloc(1, sizeof(struct lslogins_user)); ++ ++ grp = getgrgid(pwd->pw_gid); ++ if (!grp) ++ return NULL; ++ ++ if (ctl->wtmp) ++ user_wtmp = get_last_wtmp(ctl, pwd->pw_name); ++ if (ctl->btmp) ++ user_btmp = get_last_btmp(ctl, pwd->pw_name); ++ ++ lckpwdf(); ++ shadow = getspnam(pwd->pw_name); ++ ulckpwdf(); ++ ++ /* required by tseach() stuff */ ++ user->uid = pwd->pw_uid; ++ ++ while (n < ncolumns) { ++ switch (columns[n++]) { ++ case COL_USER: ++ user->login = xstrdup(pwd->pw_name); ++ break; ++ case COL_UID: ++ user->uid = pwd->pw_uid; ++ break; ++ case COL_GROUP: ++ user->group = xstrdup(grp->gr_name); ++ break; ++ case COL_GID: ++ user->gid = pwd->pw_gid; ++ break; ++ case COL_SGROUPS: ++ case COL_SGIDS: ++ if (get_sgroups(&user->sgroups, &user->nsgroups, pwd)) ++ err(EXIT_FAILURE, _("failed to get supplementary groups")); ++ break; ++ case COL_HOME: ++ user->homedir = xstrdup(pwd->pw_dir); ++ break; ++ case COL_SHELL: ++ user->shell = xstrdup(pwd->pw_shell); ++ break; ++ case COL_GECOS: ++ user->gecos = xstrdup(pwd->pw_gecos); ++ break; ++ case COL_LAST_LOGIN: ++ if (user_wtmp) { ++ time = user_wtmp->ut_tv.tv_sec; ++ user->last_login = make_time(ctl->time_mode, time); ++ } ++ break; ++ case COL_LAST_TTY: ++ if (user_wtmp) ++ user->last_tty = xstrdup(user_wtmp->ut_line); ++ break; ++ case COL_LAST_HOSTNAME: ++ if (user_wtmp) ++ user->last_hostname = xstrdup(user_wtmp->ut_host); ++ break; ++ case COL_FAILED_LOGIN: ++ if (user_btmp) { ++ time = user_btmp->ut_tv.tv_sec; ++ user->failed_login = make_time(ctl->time_mode, time); ++ } ++ break; ++ case COL_FAILED_TTY: ++ if (user_btmp) ++ user->failed_tty = xstrdup(user_btmp->ut_line); ++ break; ++ case COL_HUSH_STATUS: ++ user->hushed = get_hushlogin_status(pwd, 0); ++ if (user->hushed == -1) ++ user->hushed = STATUS_UNKNOWN; ++ break; ++ case COL_PWDEMPTY: ++ if (shadow) { ++ if (!*shadow->sp_pwdp) /* '\0' */ ++ user->pwd_empty = STATUS_TRUE; ++ } else ++ user->pwd_empty = STATUS_UNKNOWN; ++ break; ++ case COL_PWDDENY: ++ if (shadow) { ++ if ((*shadow->sp_pwdp == '!' || ++ *shadow->sp_pwdp == '*') && ++ !valid_pwd(shadow->sp_pwdp + 1)) ++ user->pwd_deny = STATUS_TRUE; ++ } else ++ user->pwd_deny = STATUS_UNKNOWN; ++ break; ++ ++ case COL_PWDLOCK: ++ if (shadow) { ++ if (*shadow->sp_pwdp == '!' && valid_pwd(shadow->sp_pwdp + 1)) ++ user->pwd_lock = STATUS_TRUE; ++ } else ++ user->pwd_lock = STATUS_UNKNOWN; ++ break; ++ case COL_NOLOGIN: ++ if (strstr(pwd->pw_shell, "nologin")) ++ user->nologin = 1; ++ else if (pwd->pw_uid) ++ user->nologin = access("/etc/nologin", F_OK) == 0 || ++ access("/var/run/nologin", F_OK) == 0; ++ break; ++ case COL_PWD_WARN: ++ if (shadow && shadow->sp_warn >= 0) ++ xasprintf(&user->pwd_warn, "%ld", shadow->sp_warn); ++ break; ++ case COL_PWD_EXPIR: ++ if (shadow && shadow->sp_expire >= 0) ++ user->pwd_expire = make_time(TIME_SHORT, ++ shadow->sp_expire * 86400); ++ break; ++ case COL_PWD_CTIME: ++ /* sp_lstchg is specified in days, showing hours ++ * (especially in non-GMT timezones) would only serve ++ * to confuse */ ++ if (shadow) ++ user->pwd_ctime = make_time(TIME_SHORT, ++ shadow->sp_lstchg * 86400); ++ break; ++ case COL_PWD_CTIME_MIN: ++ if (shadow && shadow->sp_min > 0) ++ xasprintf(&user->pwd_ctime_min, "%ld", shadow->sp_min); ++ break; ++ case COL_PWD_CTIME_MAX: ++ if (shadow && shadow->sp_max > 0) ++ xasprintf(&user->pwd_ctime_max, "%ld", shadow->sp_max); ++ break; ++ case COL_SELINUX: ++#ifdef HAVE_LIBSELINUX ++ if (ctl->selinux_enabled) { ++ /* typedefs and pointers are pure evil */ ++ security_context_t con = NULL; ++ if (getcon(&con) == 0) ++ user->context = con; ++ } ++#endif ++ break; ++ case COL_NPROCS: ++ xasprintf(&user->nprocs, "%d", get_nprocs(pwd->pw_uid)); ++ break; ++ default: ++ /* something went very wrong here */ ++ err(EXIT_FAILURE, "fatal: unknown error"); ++ break; ++ } ++ } ++ ++ return user; ++} ++ ++/* some UNIX implementations set errno iff a passwd/grp/... ++ * entry was not found. The original UNIX logins(1) utility always ++ * ignores invalid login/group names, so we're going to as well.*/ ++#define IS_REAL_ERRNO(e) !((e) == ENOENT || (e) == ESRCH || \ ++ (e) == EBADF || (e) == EPERM || (e) == EAGAIN) ++ ++/* get a definitive list of users we want info about... */ ++ ++static int str_to_uint(char *s, unsigned int *ul) ++{ ++ char *end; ++ if (!s || !*s) ++ return -1; ++ *ul = strtoul(s, &end, 0); ++ if (!*end) ++ return 0; ++ return 1; ++} ++ ++static int get_ulist(struct lslogins_control *ctl, char *logins, char *groups) ++{ ++ char *u, *g; ++ size_t i = 0, n = 0, *arsiz; ++ struct group *grp; ++ struct passwd *pwd; ++ char ***ar; ++ uid_t uid; ++ gid_t gid; ++ ++ ar = &ctl->ulist; ++ arsiz = &ctl->ulsiz; ++ ++ /* an arbitrary starting value */ ++ *arsiz = 32; ++ *ar = xcalloc(1, sizeof(char *) * (*arsiz)); ++ ++ if (logins) { ++ while ((u = strtok(logins, ","))) { ++ logins = NULL; ++ ++ /* user specified by UID? */ ++ if (!str_to_uint(u, &uid)) { ++ pwd = getpwuid(uid); ++ if (!pwd) ++ continue; ++ u = pwd->pw_name; ++ } ++ (*ar)[i++] = xstrdup(u); ++ ++ if (i == *arsiz) ++ *ar = xrealloc(*ar, sizeof(char *) * (*arsiz += 32)); ++ } ++ ctl->ulist_on = 1; ++ } ++ ++ if (groups) { ++ /* FIXME: this might lead to duplicit entries, although not visible ++ * in output, crunching a user's info multiple times is very redundant */ ++ while ((g = strtok(groups, ","))) { ++ n = 0; ++ groups = NULL; ++ ++ /* user specified by GID? */ ++ if (!str_to_uint(g, &gid)) ++ grp = getgrgid(gid); ++ else ++ grp = getgrnam(g); ++ ++ if (!grp) ++ continue; ++ ++ while ((u = grp->gr_mem[n++])) { ++ (*ar)[i++] = xstrdup(u); ++ ++ if (i == *arsiz) ++ *ar = xrealloc(*ar, sizeof(char *) * (*arsiz += 32)); ++ } ++ } ++ ctl->ulist_on = 1; ++ } ++ *arsiz = i; ++ return 0; ++} ++ ++static void free_ctl(struct lslogins_control *ctl) ++{ ++ size_t n = 0; ++ ++ free(ctl->wtmp); ++ free(ctl->btmp); ++ ++ while (n < ctl->ulsiz) ++ free(ctl->ulist[n++]); ++ ++ free(ctl->ulist); ++ free(ctl); ++} ++ ++static struct lslogins_user *get_next_user(struct lslogins_control *ctl) ++{ ++ struct lslogins_user *u; ++ errno = 0; ++ while (!(u = get_user_info(ctl, NULL))) { ++ /* no "false" errno-s here, iff we're unable to ++ * get a valid user entry for any reason, quit */ ++ if (errno == EAGAIN) ++ continue; ++ return NULL; ++ } ++ return u; ++} ++ ++static int get_user(struct lslogins_control *ctl, struct lslogins_user **user, ++ const char *username) ++{ ++ *user = get_user_info(ctl, username); ++ if (!*user && errno) ++ if (IS_REAL_ERRNO(errno)) ++ return -1; ++ return 0; ++} ++ ++static int cmp_uid(const void *a, const void *b) ++{ ++ uid_t x = ((struct lslogins_user *)a)->uid; ++ uid_t z = ((struct lslogins_user *)b)->uid; ++ return x > z ? 1 : (x < z ? -1 : 0); ++} ++ ++static int create_usertree(struct lslogins_control *ctl) ++{ ++ struct lslogins_user *user = NULL; ++ size_t n = 0; ++ ++ if (ctl->ulist_on) { ++ while (n < ctl->ulsiz) { ++ if (get_user(ctl, &user, ctl->ulist[n])) ++ return -1; ++ if (user) /* otherwise an invalid user name has probably been given */ ++ tsearch(user, &ctl->usertree, cmp_uid); ++ ++n; ++ } ++ } else { ++ while ((user = get_next_user(ctl))) ++ tsearch(user, &ctl->usertree, cmp_uid); ++ } ++ return 0; ++} ++ ++static struct libscols_table *setup_table(struct lslogins_control *ctl) ++{ ++ struct libscols_table *tb = scols_new_table(); ++ int n = 0; ++ ++ if (!tb) ++ errx(EXIT_FAILURE, _("failed to initialize output table")); ++ if (ctl->noheadings) ++ scols_table_enable_noheadings(tb, 1); ++ ++ switch(outmode) { ++ case OUT_COLON: ++ scols_table_enable_raw(tb, 1); ++ scols_table_set_column_separator(tb, ":"); ++ break; ++ case OUT_NEWLINE: ++ scols_table_set_column_separator(tb, "\n"); ++ /* fallthrough */ ++ case OUT_EXPORT: ++ scols_table_enable_export(tb, 1); ++ break; ++ case OUT_NUL: ++ scols_table_set_line_separator(tb, "\0"); ++ /* fallthrough */ ++ case OUT_RAW: ++ scols_table_enable_raw(tb, 1); ++ break; ++ case OUT_PRETTY: ++ scols_table_enable_noheadings(tb, 1); ++ default: ++ break; ++ } ++ ++ while (n < ncolumns) { ++ int flags = coldescs[columns[n]].flag; ++ ++ if (ctl->notrunc) ++ flags &= ~SCOLS_FL_TRUNC; ++ ++ if (!scols_table_new_column(tb, ++ coldescs[columns[n]].name, ++ coldescs[columns[n]].whint, ++ flags)) ++ goto fail; ++ ++n; ++ } ++ ++ return tb; ++fail: ++ scols_unref_table(tb); ++ return NULL; ++} ++ ++static void fill_table(const void *u, const VISIT which, const int depth __attribute__((unused))) ++{ ++ struct libscols_line *ln; ++ struct lslogins_user *user = *(struct lslogins_user **)u; ++ int n = 0; ++ ++ if (which == preorder || which == endorder) ++ return; ++ ++ ln = scols_table_new_line(tb, NULL); ++ while (n < ncolumns) { ++ int rc = 0; ++ ++ switch (columns[n]) { ++ case COL_USER: ++ rc = scols_line_set_data(ln, n, user->login); ++ break; ++ case COL_UID: ++ rc = scols_line_refer_data(ln, n, uidtostr(user->uid)); ++ break; ++ case COL_PWDEMPTY: ++ rc = scols_line_set_data(ln, n, get_status(user->pwd_empty)); ++ break; ++ case COL_NOLOGIN: ++ rc = scols_line_set_data(ln, n, get_status(user->nologin)); ++ break; ++ case COL_PWDLOCK: ++ rc = scols_line_set_data(ln, n, get_status(user->pwd_lock)); ++ break; ++ case COL_PWDDENY: ++ rc = scols_line_set_data(ln, n, get_status(user->pwd_deny)); ++ break; ++ case COL_GROUP: ++ rc = scols_line_set_data(ln, n, user->group); ++ break; ++ case COL_GID: ++ rc = scols_line_refer_data(ln, n, gidtostr(user->gid)); ++ break; ++ case COL_SGROUPS: ++ rc = scols_line_refer_data(ln, n, ++ build_sgroups_string(user->sgroups, ++ user->nsgroups, ++ TRUE)); ++ break; ++ case COL_SGIDS: ++ rc = scols_line_refer_data(ln, n, ++ build_sgroups_string(user->sgroups, ++ user->nsgroups, ++ FALSE)); ++ break; ++ case COL_HOME: ++ rc = scols_line_set_data(ln, n, user->homedir); ++ break; ++ case COL_SHELL: ++ rc = scols_line_set_data(ln, n, user->shell); ++ break; ++ case COL_GECOS: ++ rc = scols_line_set_data(ln, n, user->gecos); ++ break; ++ case COL_LAST_LOGIN: ++ rc = scols_line_set_data(ln, n, user->last_login); ++ break; ++ case COL_LAST_TTY: ++ rc = scols_line_set_data(ln, n, user->last_tty); ++ break; ++ case COL_LAST_HOSTNAME: ++ rc = scols_line_set_data(ln, n, user->last_hostname); ++ break; ++ case COL_FAILED_LOGIN: ++ rc = scols_line_set_data(ln, n, user->failed_login); ++ break; ++ case COL_FAILED_TTY: ++ rc = scols_line_set_data(ln, n, user->failed_tty); ++ break; ++ case COL_HUSH_STATUS: ++ rc = scols_line_set_data(ln, n, get_status(user->hushed)); ++ break; ++ case COL_PWD_WARN: ++ rc = scols_line_set_data(ln, n, user->pwd_warn); ++ break; ++ case COL_PWD_EXPIR: ++ rc = scols_line_set_data(ln, n, user->pwd_expire); ++ break; ++ case COL_PWD_CTIME: ++ rc = scols_line_set_data(ln, n, user->pwd_ctime); ++ break; ++ case COL_PWD_CTIME_MIN: ++ rc = scols_line_set_data(ln, n, user->pwd_ctime_min); ++ break; ++ case COL_PWD_CTIME_MAX: ++ rc = scols_line_set_data(ln, n, user->pwd_ctime_max); ++ break; ++ case COL_SELINUX: ++#ifdef HAVE_LIBSELINUX ++ rc = scols_line_set_data(ln, n, user->context); ++#endif ++ break; ++ case COL_NPROCS: ++ rc = scols_line_set_data(ln, n, user->nprocs); ++ break; ++ default: ++ /* something went very wrong here */ ++ err(EXIT_FAILURE, _("internal error: unknown column")); ++ } ++ ++ if (rc != 0) ++ err(EXIT_FAILURE, _("failed to set data")); ++ ++n; ++ } ++ return; ++} ++#ifdef HAVE_LIBSYSTEMD ++static void print_journal_tail(const char *journal_path, uid_t uid, size_t len) ++{ ++ sd_journal *j; ++ char *match, *buf; ++ uint64_t x; ++ time_t t; ++ const char *identifier, *pid, *message; ++ size_t identifier_len, pid_len, message_len; ++ ++ if (journal_path) ++ sd_journal_open_directory(&j, journal_path, 0); ++ else ++ sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); ++ ++ buf = xmalloc(sizeof(char) * 16); ++ xasprintf(&match, "_UID=%d", uid); ++ ++ sd_journal_add_match(j, match, 0); ++ sd_journal_seek_tail(j); ++ sd_journal_previous_skip(j, len); ++ ++ do { ++ if (0 > sd_journal_get_data(j, "SYSLOG_IDENTIFIER", ++ (const void **) &identifier, &identifier_len)) ++ return; ++ if (0 > sd_journal_get_data(j, "_PID", ++ (const void **) &pid, &pid_len)) ++ return; ++ if (0 > sd_journal_get_data(j, "MESSAGE", ++ (const void **) &message, &message_len)) ++ return; ++ ++ sd_journal_get_realtime_usec(j, &x); ++ t = x / 1000000; ++ strftime(buf, 16, "%b %d %H:%M:%S", localtime(&t)); ++ ++ fprintf(stdout, "%s", buf); ++ ++ identifier = strchr(identifier, '=') + 1; ++ pid = strchr(pid, '=') + 1 ; ++ message = strchr(message, '=') + 1; ++ ++ fprintf(stdout, " %s", identifier); ++ fprintf(stdout, "[%s]:", pid); ++ fprintf(stdout, "%s\n", message); ++ } while (sd_journal_next(j)); ++ ++ free(buf); ++ free(match); ++ sd_journal_flush_matches(j); ++ sd_journal_close(j); ++} ++#endif ++ ++static int print_pretty(struct libscols_table *tb) ++{ ++ struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD); ++ struct libscols_column *col; ++ struct libscols_cell *data; ++ struct libscols_line *ln; ++ const char *hstr, *dstr; ++ int n = 0; ++ ++ ln = scols_table_get_line(tb, 0); ++ while (!scols_table_next_column(tb, itr, &col)) { ++ ++ data = scols_line_get_cell(ln, n); ++ ++ hstr = _(coldescs[columns[n]].pretty_name); ++ dstr = scols_cell_get_data(data); ++ ++ if (dstr) ++ printf("%s:%*c%-36s\n", hstr, 35 - (int)strlen(hstr), ' ', dstr); ++ ++n; ++ } ++ ++ scols_free_iter(itr); ++ return 0; ++ ++} ++ ++static int print_user_table(struct lslogins_control *ctl) ++{ ++ tb = setup_table(ctl); ++ if (!tb) ++ return -1; ++ ++ twalk(ctl->usertree, fill_table); ++ if (outmode == OUT_PRETTY) { ++ print_pretty(tb); ++#ifdef HAVE_LIBSYSTEMD ++ fprintf(stdout, _("\nLast logs:\n")); ++ print_journal_tail(ctl->journal_path, ctl->uid, 3); ++ fputc('\n', stdout); ++#endif ++ } else ++ scols_print_table(tb); ++ return 0; ++} ++ ++static void free_user(void *f) ++{ ++ struct lslogins_user *u = f; ++ free(u->login); ++ free(u->group); ++ free(u->gecos); ++ free(u->sgroups); ++ free(u->pwd_ctime); ++ free(u->pwd_warn); ++ free(u->pwd_ctime_min); ++ free(u->pwd_ctime_max); ++ free(u->last_login); ++ free(u->last_tty); ++ free(u->last_hostname); ++ free(u->failed_login); ++ free(u->failed_tty); ++ free(u->homedir); ++ free(u->shell); ++ free(u->pwd_status); ++#ifdef HAVE_LIBSELINUX ++ freecon(u->context); ++#endif ++ free(u); ++} ++ ++struct lslogins_timefmt { ++ const char *name; ++ int val; ++}; ++ ++static struct lslogins_timefmt timefmts[] = { ++ { "short", TIME_SHORT }, ++ { "full", TIME_FULL }, ++ { "iso", TIME_ISO }, ++}; ++ ++static void __attribute__((__noreturn__)) usage(FILE *out) ++{ ++ size_t i; ++ ++ fputs(USAGE_HEADER, out); ++ fprintf(out, _(" %s [options]\n"), program_invocation_short_name); ++ ++ fputs(USAGE_OPTIONS, out); ++ fputs(_(" -a, --acc-expiration display info about passwords expiration\n"), out); ++ fputs(_(" -c, --colon-separate display data in a format similar to /etc/passwd\n"), out); ++ fputs(_(" -e, --export display in an export-able output format\n"), out); ++ fputs(_(" -f, --failed display data about the users' last failed logins\n"), out); ++ fputs(_(" -G, --groups-info display information about groups\n"), out); ++ fputs(_(" -g, --groups= display users belonging to a group in \n"), out); ++ fputs(_(" -L, --last show info about the users' last login sessions\n"), out); ++ fputs(_(" -l, --logins= display only users from \n"), out); ++ fputs(_(" -m, --supp-groups display supplementary groups as well\n"), out); ++ fputs(_(" -n, --newline display each piece of information on a new line\n"), out); ++ fputs(_(" --noheadings don't print headings\n"), out); ++ fputs(_(" --notruncate don't truncate output\n"), out); ++ fputs(_(" -o, --output[=] define the columns to output\n"), out); ++ fputs(_(" -p, --pwd display information related to login by password.\n"), out); ++ fputs(_(" -r, --raw display in raw mode\n"), out); ++ fputs(_(" -s, --system-accs display system accounts\n"), out); ++ fputs(_(" --time-format= display dates in short, full or iso format\n"), out); ++ fputs(_(" -u, --user-accs display user accounts\n"), out); ++ fputs(_(" -Z, --context display SELinux contexts\n"), out); ++ fputs(_(" -z, --print0 delimit user entries with a nul character\n"), out); ++ fputs(_(" --wtmp-file set an alternate path for wtmp\n"), out); ++ fputs(_(" --btmp-file set an alternate path for btmp\n"), out); ++ fputs(USAGE_SEPARATOR, out); ++ fputs(USAGE_HELP, out); ++ fputs(USAGE_VERSION, out); ++ ++ fprintf(out, _("\nAvailable columns:\n")); ++ ++ for (i = 0; i < ARRAY_SIZE(coldescs); i++) ++ fprintf(out, " %14s %s\n", coldescs[i].name, ++ _(coldescs[i].help)); ++ ++ fprintf(out, _("\nFor more details see lslogins(1).\n")); ++ ++ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int c, opt_o = 0; ++ char *logins = NULL, *groups = NULL; ++ char *path_wtmp = _PATH_WTMP, *path_btmp = _PATH_BTMP; ++ struct lslogins_control *ctl = xcalloc(1, sizeof(struct lslogins_control)); ++ size_t i; ++ ++ /* long only options. */ ++ enum { ++ OPT_VER = CHAR_MAX + 1, ++ OPT_WTMP, ++ OPT_BTMP, ++ OPT_NOTRUNC, ++ OPT_NOHEAD, ++ OPT_TIME_FMT, ++ }; ++ ++ static const struct option longopts[] = { ++ { "acc-expiration", no_argument, 0, 'a' }, ++ { "colon-separate", no_argument, 0, 'c' }, ++ { "export", no_argument, 0, 'e' }, ++ { "failed", no_argument, 0, 'f' }, ++ { "groups", required_argument, 0, 'g' }, ++ { "help", no_argument, 0, 'h' }, ++ { "logins", required_argument, 0, 'l' }, ++ { "supp-groups", no_argument, 0, 'G' }, ++ { "newline", no_argument, 0, 'n' }, ++ { "notruncate", no_argument, 0, OPT_NOTRUNC }, ++ { "noheadings", no_argument, 0, OPT_NOHEAD }, ++ { "output", required_argument, 0, 'o' }, ++ { "last", no_argument, 0, 'L', }, ++ { "raw", no_argument, 0, 'r' }, ++ { "system-accs", no_argument, 0, 's' }, ++ { "time-format", required_argument, 0, OPT_TIME_FMT }, ++ { "user-accs", no_argument, 0, 'u' }, ++ { "version", no_argument, 0, 'V' }, ++ { "pwd", no_argument, 0, 'p' }, ++ { "print0", no_argument, 0, 'z' }, ++ { "wtmp-file", required_argument, 0, OPT_WTMP }, ++ { "btmp-file", required_argument, 0, OPT_BTMP }, ++#ifdef HAVE_LIBSELINUX ++ { "context", no_argument, 0, 'Z' }, ++#endif ++ { NULL, 0, 0, 0 } ++ }; ++ ++ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ ++ { 'G', 'o' }, ++ { 'L', 'o' }, ++ { 'Z', 'o' }, ++ { 'a', 'o' }, ++ { 'c','n','r','z' }, ++ { 'o', 'p' }, ++ { 0 } ++ }; ++ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; ++ ++ setlocale(LC_ALL, ""); ++ bindtextdomain(PACKAGE, LOCALEDIR); ++ textdomain(PACKAGE); ++ atexit(close_stdout); ++ ++ ctl->time_mode = TIME_SHORT; ++ ++ /* very basic default */ ++ add_column(columns, ncolumns++, COL_UID); ++ add_column(columns, ncolumns++, COL_USER); ++ ++ while ((c = getopt_long(argc, argv, "acfGg:hLl:no:prsuVxzZ", ++ longopts, NULL)) != -1) { ++ ++ err_exclusive_options(c, longopts, excl, excl_st); ++ ++ switch (c) { ++ case 'a': ++ add_column(columns, ncolumns++, COL_PWD_WARN); ++ add_column(columns, ncolumns++, COL_PWD_CTIME_MIN); ++ add_column(columns, ncolumns++, COL_PWD_CTIME_MAX); ++ add_column(columns, ncolumns++, COL_PWD_CTIME); ++ add_column(columns, ncolumns++, COL_PWD_EXPIR); ++ break; ++ case 'c': ++ outmode = OUT_COLON; ++ break; ++ case 'e': ++ outmode = OUT_EXPORT; ++ break; ++ case 'f': ++ add_column(columns, ncolumns++, COL_FAILED_LOGIN); ++ add_column(columns, ncolumns++, COL_FAILED_TTY); ++ break; ++ case 'G': ++ add_column(columns, ncolumns++, COL_GID); ++ add_column(columns, ncolumns++, COL_GROUP); ++ add_column(columns, ncolumns++, COL_SGIDS); ++ add_column(columns, ncolumns++, COL_SGROUPS); ++ break; ++ case 'g': ++ groups = optarg; ++ break; ++ case 'h': ++ usage(stdout); ++ break; ++ case 'L': ++ add_column(columns, ncolumns++, COL_LAST_TTY); ++ add_column(columns, ncolumns++, COL_LAST_HOSTNAME); ++ add_column(columns, ncolumns++, COL_LAST_LOGIN); ++ break; ++ case 'l': ++ logins = optarg; ++ break; ++ case 'n': ++ outmode = OUT_NEWLINE; ++ break; ++ case 'o': ++ if (optarg) { ++ if (*optarg == '=') ++ optarg++; ++ ncolumns = string_to_idarray(optarg, ++ columns, ARRAY_SIZE(columns), ++ column_name_to_id); ++ if (ncolumns < 0) ++ return EXIT_FAILURE; ++ } ++ opt_o = 1; ++ break; ++ case 'r': ++ outmode = OUT_RAW; ++ break; ++ case 's': ++ ctl->SYS_UID_MIN = getlogindefs_num("SYS_UID_MIN", UL_SYS_UID_MIN); ++ ctl->SYS_UID_MAX = getlogindefs_num("SYS_UID_MAX", UL_SYS_UID_MAX); ++ lslogins_flag |= F_SYSAC; ++ break; ++ case 'u': ++ ctl->UID_MIN = getlogindefs_num("UID_MIN", UL_UID_MIN); ++ ctl->UID_MAX = getlogindefs_num("UID_MAX", UL_UID_MAX); ++ lslogins_flag |= F_USRAC; ++ break; ++ case 'p': ++ add_column(columns, ncolumns++, COL_PWDEMPTY); ++ add_column(columns, ncolumns++, COL_PWDLOCK); ++ add_column(columns, ncolumns++, COL_PWDDENY); ++ add_column(columns, ncolumns++, COL_NOLOGIN); ++ add_column(columns, ncolumns++, COL_HUSH_STATUS); ++ break; ++ case 'z': ++ outmode = OUT_NUL; ++ break; ++ case OPT_WTMP: ++ path_wtmp = optarg; ++ break; ++ case OPT_BTMP: ++ path_btmp = optarg; ++ break; ++ case OPT_NOTRUNC: ++ ctl->notrunc = 1; ++ break; ++ case OPT_NOHEAD: ++ ctl->noheadings = 1; ++ break; ++ case OPT_TIME_FMT: ++ { ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(timefmts); i++) { ++ if (strcmp(timefmts[i].name, optarg) == 0) { ++ ctl->time_mode = timefmts[i].val; ++ break; ++ } ++ } ++ if (ctl->time_mode == TIME_INVALID) ++ usage(stderr); ++ } ++ break; ++ case 'V': ++ printf(UTIL_LINUX_VERSION); ++ return EXIT_SUCCESS; ++ case 'Z': ++ { ++#ifdef HAVE_LIBSELINUX ++ int sl = is_selinux_enabled(); ++ if (sl < 0) ++ warn(_("failed to request selinux state")); ++ else ++ ctl->selinux_enabled = sl == 1; ++#endif ++ add_column(columns, ncolumns++, COL_SELINUX); ++ break; ++ } ++ default: ++ usage(stderr); ++ } ++ } ++ ++ if (argc - optind == 1) { ++ if (strchr(argv[optind], ',')) ++ errx(EXIT_FAILURE, _("Only one user may be specified. Use -l for multiple users.")); ++ logins = argv[optind]; ++ outmode = OUT_PRETTY; ++ } else if (argc != optind) ++ usage(stderr); ++ ++ scols_init_debug(0); ++ ++ /* lslogins -u -s == lslogins */ ++ if (lslogins_flag & F_USRAC && lslogins_flag & F_SYSAC) ++ lslogins_flag &= ~(F_USRAC | F_SYSAC); ++ ++ if (outmode == OUT_PRETTY && !opt_o) { ++ /* all columns for lslogins */ ++ for (ncolumns = 0, i = 0; i < ARRAY_SIZE(coldescs); i++) ++ columns[ncolumns++] = i; ++ ++ } else if (ncolumns == 2 && !opt_o) { ++ /* default colummns */ ++ add_column(columns, ncolumns++, COL_NPROCS); ++ add_column(columns, ncolumns++, COL_PWDLOCK); ++ add_column(columns, ncolumns++, COL_PWDDENY); ++ add_column(columns, ncolumns++, COL_LAST_LOGIN); ++ add_column(columns, ncolumns++, COL_GECOS); ++ } ++ ++ if (require_wtmp()) ++ parse_wtmp(ctl, path_wtmp); ++ if (require_btmp()) ++ parse_btmp(ctl, path_btmp); ++ ++ if (logins || groups) ++ get_ulist(ctl, logins, groups); ++ ++ if (create_usertree(ctl)) ++ return EXIT_FAILURE; ++ ++ print_user_table(ctl); ++ ++ scols_unref_table(tb); ++ tdestroy(ctl->usertree, free_user); ++ free_ctl(ctl); ++ ++ return EXIT_SUCCESS; ++} +diff -up util-linux-2.23.2/login-utils/Makemodule.am.kzak util-linux-2.23.2/login-utils/Makemodule.am +--- util-linux-2.23.2/login-utils/Makemodule.am.kzak 2013-06-13 09:46:10.441650801 +0200 ++++ util-linux-2.23.2/login-utils/Makemodule.am 2014-12-12 15:28:30.576177139 +0100 +@@ -145,6 +145,25 @@ endif + endif # BUILD_NEWGRP + + ++if BUILD_LSLOGINS ++usrbin_exec_PROGRAMS += lslogins ++dist_man_MANS += login-utils/lslogins.1 ++lslogins_SOURCES = \ ++ login-utils/lslogins.c \ ++ login-utils/logindefs.c \ ++ login-utils/logindefs.h ++lslogins_LDADD = $(LDADD) libcommon.la libsmartcols.la ++lslogins_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir) ++if HAVE_SELINUX ++lslogins_LDADD += -lselinux ++endif ++if HAVE_SYSTEMD ++lslogins_LDADD += $(SYSTEMD_LIBS) $(SYSTEMD_JOURNAL_LIBS) ++lslogins_CFLAGS += $(SYSTEMD_CFLAGS) $(SYSTEMD_JOURNAL_CFLAGS) ++endif ++endif # BUILD_LSLOGINS ++ ++ + if BUILD_VIPW + usrsbin_exec_PROGRAMS += vipw + dist_man_MANS += \ diff --git a/SOURCES/2.26-mount-man-move.patch b/SOURCES/2.26-mount-man-move.patch new file mode 100644 index 0000000..1a290a4 --- /dev/null +++ b/SOURCES/2.26-mount-man-move.patch @@ -0,0 +1,12 @@ +diff -up util-linux-2.23.2/sys-utils/mount.8.kzak util-linux-2.23.2/sys-utils/mount.8 +--- util-linux-2.23.2/sys-utils/mount.8.kzak 2014-09-25 11:03:25.492822164 +0200 ++++ util-linux-2.23.2/sys-utils/mount.8 2014-09-25 11:04:00.102152375 +0200 +@@ -451,7 +451,7 @@ has to be a mountpoint. + + Note that moving a mount residing under a shared mount is invalid and + unsupported. Use +-.B findmnt -o TARGET,PROPAGATION /dir ++.B findmnt -o TARGET,PROPAGATION + to see the current propagation flags. + .RE + diff --git a/SOURCES/2.26-raw-stat.patch b/SOURCES/2.26-raw-stat.patch new file mode 100644 index 0000000..1ca1bee --- /dev/null +++ b/SOURCES/2.26-raw-stat.patch @@ -0,0 +1,12 @@ +diff -up util-linux-2.23.2/disk-utils/raw.c.kzak util-linux-2.23.2/disk-utils/raw.c +--- util-linux-2.23.2/disk-utils/raw.c.kzak 2013-06-13 09:46:10.382650297 +0200 ++++ util-linux-2.23.2/disk-utils/raw.c 2015-01-13 14:51:24.877755962 +0100 +@@ -220,7 +220,7 @@ static int query(int minor_raw, const ch + if (raw_name) { + struct stat statbuf; + +- if (!stat(raw_name, &statbuf)) ++ if (stat(raw_name, &statbuf) != 0) + err(EXIT_RAW_ACCESS, + _("Cannot locate raw device '%s'"), raw_name); + if (!S_ISCHR(statbuf.st_mode)) diff --git a/SPECS/util-linux.spec b/SPECS/util-linux.spec index 7a1ca6b..9d3479d 100644 --- a/SPECS/util-linux.spec +++ b/SPECS/util-linux.spec @@ -2,7 +2,7 @@ Summary: A collection of basic system utilities Name: util-linux Version: 2.23.2 -Release: 16%{?dist} +Release: 21%{?dist} License: GPLv2 and GPLv2+ and LGPLv2+ and BSD with advertising and Public Domain Group: System Environment/Base URL: http://en.wikipedia.org/wiki/Util-linux @@ -25,6 +25,9 @@ BuildRequires: libutempter-devel Buildrequires: systemd-devel Buildrequires: libuser-devel BuildRequires: libcap-ng-devel +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: libtool ### Sources Source0: ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.23/util-linux-%{upstream_version}.tar.xz @@ -126,6 +129,43 @@ Patch24: 2.25-libblkid-io-errors.patch Patch25: 2.24-fsck-fstab.patch Patch26: 2.25-fsck-nohelper.patch +# +# RHEL 7.1 +# +# 1067354 - dmesg -w output not line-buffered +Patch27: 2.25-dmesg-w.patch +# 1072298 - util-linux/lscpu: '--sysroot' broken on XFS +Patch28: 2.25-lscpu-d_type.patch +# 1072930 - hwclock --systohc can hang on busy or virtual machine +Patch29: 2.25-hwclock-hang.patch +# 1077310 - wipefs problem with some live .isos +Patch30: 2.25-wipefs-nested-pt.patch +# 1077864 - Backport swapon: allow a more flexible swap discard policy +Patch31: 2.25-swapon-discard.patch +# 1080407 - libblkid XFS detection is too fragile +Patch32: 2.25-libblkid-xfs.patch +# RHEL6.6 port #1115442 - kill(1) doesn't check errno after calling strtol() +Patch33: 2.17-kill-strtol.patch +# 1127823 - losetup does not accept offset +Patch34: 2.24-losetup-offset.patch +# 1127891 - wipefs 4k disks and gpt partitions +Patch35: 2.25-libblkid-gpt-512.patch +# 1136111 - "findmnt" returns incomplete output on kernel-3.14 +Patch36: 2.24-libmount-3.14.patch +# backport from upstream: #1140591 - improve mount --move documentation +Patch37: 2.26-mount-man-move.patch +# RHEL6.6 port: #1131522 - backport --fork and --mount-proc to unshare(1) +Patch38: 2.24-unshare-mount-fork.patch +# Backport from upstream (v2.25-214-g8a4c64e): #1113043 - backport lslogins(1) +Patch39: 2.26-libsmartcols.patch +Patch40: 2.26-lslogins.patch +# 1149278 - partx fails to update a specific partition other than the first +Patch41: 2.24-partx-update.patch +# 1156352 - blkdiscard progress report and interruptibility of the process +Patch42: 2.26-blkdiscard.patch +# 1181444 - raw command fails with "Cannot locate raw device". +Patch43: 2.26-raw-stat.patch + %description The util-linux package contains a large variety of low-level system utilities that are necessary for a Linux system to function. Among @@ -243,6 +283,9 @@ cp %{SOURCE8} %{SOURCE9} . %build unset LINGUAS || : +# unfortunately, we did changes to build-system +./autogen.sh + export CFLAGS="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 $RPM_OPT_FLAGS" export SUID_CFLAGS="-fpie" export SUID_LDFLAGS="-pie -Wl,-z,relro -Wl,-z,now" @@ -513,6 +556,7 @@ fi %{_bindir}/lsblk %{_bindir}/lscpu %{_bindir}/lslocks +%{_bindir}/lslogins %{_bindir}/mcookie %{_bindir}/more %{_bindir}/mountpoint @@ -560,6 +604,7 @@ fi %{_mandir}/man1/login.1* %{_mandir}/man1/look.1* %{_mandir}/man1/lscpu.1* +%{_mandir}/man1/lslogins.1* %{_mandir}/man1/mcookie.1* %{_mandir}/man1/more.1* %{_mandir}/man1/mountpoint.1* @@ -837,6 +882,33 @@ fi %{_libdir}/pkgconfig/uuid.pc %changelog +* Tue Jan 13 2015 Karel Zak 2.23.2-21 +- fix #1181444 - raw command fails with "Cannot locate raw device" + +* Fri Dec 12 2014 Karel Zak 2.23.2-20 +- fix lslogins patch (#1113043) + +* Mon Oct 27 2014 Karel Zak 2.23.2-19 +- fix #1156352 - blkdiscard progress report and interruptibility of the process + +* Fri Oct 10 2014 Karel Zak 2.23.2-18 +- fix #1149278 - partx fails to update a specific partition other than the first + +* Thu Sep 25 2014 Karel Zak 2.23.2-17 +- fix #1067354 - dmesg -w output not line-buffered +- fix #1072298 - util-linux/lscpu: '--sysroot' broken on XFS +- fix #1072930 - hwclock --systohc can hang on busy or virtual machine +- fix #1077310 - wipefs problem with some live .isos +- fix #1077864 - swapon: allow a more flexible swap discard policy +- fix #1080407 - libblkid XFS detection is too fragile +- fix #1115442 - kill(1) doesn't check errno after calling strtol() +- fix #1127823 - losetup does not accept offset +- fix #1127891 - wipefs 4k disks and gpt partitions +- fix #1136111 - "findmnt" returns incomplete output on kernel-3.14 +- fix #1140591 - improve mount --move documentation +- fix #1131522 - backport --fork and --mount-proc to unshare(1) +- fix #1113043 - backport lslogins(1) + * Fri Mar 28 2014 Karel Zak 2.23.2-16 - fix bugs in patch for #1047376